summaryrefslogtreecommitdiff
path: root/lib/docu-list-rule/combo-box-settings/table/combo-box-options-expandable-row.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/docu-list-rule/combo-box-settings/table/combo-box-options-expandable-row.tsx')
-rw-r--r--lib/docu-list-rule/combo-box-settings/table/combo-box-options-expandable-row.tsx263
1 files changed, 263 insertions, 0 deletions
diff --git a/lib/docu-list-rule/combo-box-settings/table/combo-box-options-expandable-row.tsx b/lib/docu-list-rule/combo-box-settings/table/combo-box-options-expandable-row.tsx
new file mode 100644
index 00000000..07b63de5
--- /dev/null
+++ b/lib/docu-list-rule/combo-box-settings/table/combo-box-options-expandable-row.tsx
@@ -0,0 +1,263 @@
+"use client"
+
+import * as React from "react"
+import { useState, useEffect } from "react"
+import { MoreHorizontal, Settings } from "lucide-react"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table"
+import { toast } from "sonner"
+import { codeGroups } from "@/db/schema/codeGroups"
+import { getComboBoxOptions, updateComboBoxOption, deleteComboBoxOption } from "../service"
+import { DocumentClassOptionsSheet } from "./document-class-options-sheet"
+
+
+interface ComboBoxOptionsExpandableRowProps {
+ codeGroup: typeof codeGroups.$inferSelect
+}
+
+interface ComboBoxOption {
+ id: number
+ codeGroupId: number
+ code: string
+ description: string
+ remark: string | null
+ createdAt: Date
+ updatedAt: Date
+}
+
+export function ComboBoxOptionsExpandableRow({ codeGroup }: ComboBoxOptionsExpandableRowProps) {
+ const [options, setOptions] = useState<ComboBoxOption[]>([])
+ const [loading, setLoading] = useState(true)
+ const [editingOption, setEditingOption] = useState<ComboBoxOption | null>(null)
+ const [selectedOptionForSubOptions, setSelectedOptionForSubOptions] = useState<ComboBoxOption | null>(null)
+
+ // 옵션 목록 로드
+ const loadOptions = async () => {
+ try {
+ setLoading(true)
+ const result = await getComboBoxOptions(codeGroup.id)
+ if (result.success && result.data) {
+ setOptions(result.data as ComboBoxOption[])
+ } else {
+ toast.error("옵션 목록을 불러오는데 실패했습니다.")
+ }
+ } catch (error) {
+ console.error("옵션 로드 실패:", error)
+ toast.error("옵션 목록을 불러오는데 실패했습니다.")
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ useEffect(() => {
+ loadOptions()
+ }, [codeGroup.id])
+
+ // 기존 옵션 수정
+ const handleUpdateOption = async (option: ComboBoxOption) => {
+ if (!option.code.trim() || !option.description.trim()) {
+ toast.error("Code와 Description은 필수 입력 항목입니다.")
+ return
+ }
+
+ try {
+ const result = await updateComboBoxOption({
+ id: option.id,
+ code: option.code.trim(),
+ description: option.description.trim(),
+ remark: option.remark || undefined,
+ })
+
+ if (result.success) {
+ await loadOptions() // 목록 새로고침
+ setEditingOption(null) // 편집 모드 종료
+ toast.success("옵션이 수정되었습니다.")
+ } else {
+ toast.error("옵션 수정에 실패했습니다.")
+ }
+ } catch (error) {
+ console.error("옵션 수정 실패:", error)
+ toast.error("옵션 수정에 실패했습니다.")
+ }
+ }
+
+ // 기존 옵션 삭제
+ const handleDeleteOption = async (optionId: number) => {
+ if (!confirm("정말로 이 옵션을 삭제하시겠습니까?")) {
+ return
+ }
+
+ try {
+ const result = await deleteComboBoxOption(optionId)
+ if (result.success) {
+ await loadOptions() // 목록 새로고침
+ toast.success("옵션이 삭제되었습니다.")
+ } else {
+ toast.error("옵션 삭제에 실패했습니다.")
+ }
+ } catch (error) {
+ console.error("옵션 삭제 실패:", error)
+ toast.error("옵션 삭제에 실패했습니다.")
+ }
+ }
+
+ // Document Class인지 확인 (Description이 "Document Class"인 경우)
+ const isDocumentClass = codeGroup.description === "Document Class"
+
+ return (
+ <div className="bg-muted/20 border-t">
+ <div className="space-y-0 ml-[60px]">
+ {/* 커스텀 테이블 */}
+ <div className="border overflow-hidden bg-white">
+ <Table className="w-full table-fixed">
+ <TableHeader>
+ <TableRow className="bg-muted/30">
+ <TableHead className="w-[20%] font-medium text-muted-foreground">Code</TableHead>
+ <TableHead className="w-[30%] font-medium text-muted-foreground">Description</TableHead>
+ <TableHead className="w-[25%] font-medium text-muted-foreground">Remark</TableHead>
+ {isDocumentClass && (
+ <TableHead className="w-[15%] font-medium text-muted-foreground">하위 옵션</TableHead>
+ )}
+ <TableHead className="w-[10%] font-medium text-muted-foreground">작업</TableHead>
+ </TableRow>
+ </TableHeader>
+ <TableBody>
+ {/* 기존 옵션들 */}
+ {options.map((option) => (
+ <TableRow key={option.id} className="hover:bg-muted/30 transition-colors">
+ <TableCell className="font-medium text-sm">
+ {editingOption?.id === option.id ? (
+ <Input
+ value={editingOption.code}
+ onChange={(e) => setEditingOption(prev => prev ? { ...prev, code: e.target.value } : null)}
+ placeholder="Code (*)"
+ className="border-0 focus-visible:ring-1 bg-transparent h-8"
+ />
+ ) : (
+ option.code
+ )}
+ </TableCell>
+ <TableCell className="text-sm">
+ {editingOption?.id === option.id ? (
+ <Input
+ value={editingOption.description}
+ onChange={(e) => setEditingOption(prev => prev ? { ...prev, description: e.target.value } : null)}
+ placeholder="Description (*)"
+ className="border-0 focus-visible:ring-1 bg-transparent h-8"
+ />
+ ) : (
+ option.description
+ )}
+ </TableCell>
+ <TableCell className="text-sm text-muted-foreground">
+ {editingOption?.id === option.id ? (
+ <Input
+ value={editingOption.remark || ""}
+ onChange={(e) => setEditingOption(prev => prev ? { ...prev, remark: e.target.value } : null)}
+ placeholder="Remark"
+ className="border-0 focus-visible:ring-1 bg-transparent h-8"
+ />
+ ) : (
+ option.remark || "-"
+ )}
+ </TableCell>
+ {isDocumentClass && (
+ <TableCell className="text-sm">
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() => setSelectedOptionForSubOptions(option)}
+ className="h-6 px-2 text-xs"
+ >
+ <Settings className="h-3 w-3 mr-1" />
+ 관리
+ </Button>
+ </TableCell>
+ )}
+ <TableCell className="text-sm">
+ {editingOption?.id === option.id ? (
+ <div className="flex gap-1">
+ <Button
+ onClick={() => handleUpdateOption(editingOption)}
+ size="sm"
+ variant="outline"
+ className="h-6 px-2 text-xs"
+ >
+ 저장
+ </Button>
+ <Button
+ onClick={() => setEditingOption(null)}
+ size="sm"
+ variant="ghost"
+ className="h-6 px-2 text-xs"
+ >
+ 취소
+ </Button>
+ </div>
+ ) : (
+ <DropdownMenu>
+ <DropdownMenuTrigger asChild>
+ <Button
+ variant="ghost"
+ className="h-6 w-6 p-0"
+ >
+ <MoreHorizontal className="h-3 w-3" />
+ </Button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent align="end">
+ <DropdownMenuItem onClick={() => setEditingOption(option)}>
+ 수정
+ </DropdownMenuItem>
+ <DropdownMenuSeparator />
+ <DropdownMenuItem onClick={() => handleDeleteOption(option.id)}>
+ 삭제
+ </DropdownMenuItem>
+ </DropdownMenuContent>
+ </DropdownMenu>
+ )}
+ </TableCell>
+ </TableRow>
+ ))}
+
+ {options.length === 0 && (
+ <TableRow>
+ <TableCell colSpan={isDocumentClass ? 5 : 4} className="text-center text-muted-foreground py-8">
+ 등록된 옵션이 없습니다.
+ </TableCell>
+ </TableRow>
+ )}
+ </TableBody>
+ </Table>
+ </div>
+ </div>
+
+ {/* Document Class 하위 옵션 관리 시트 */}
+ {selectedOptionForSubOptions && (
+ <DocumentClassOptionsSheet
+ open={!!selectedOptionForSubOptions}
+ onOpenChange={(open) => !open && setSelectedOptionForSubOptions(null)}
+ comboBoxOption={selectedOptionForSubOptions}
+ onSuccess={() => {
+ setSelectedOptionForSubOptions(null)
+ // 필요시 하위 옵션 목록 새로고침
+ }}
+ />
+ )}
+ </div>
+ )
+} \ No newline at end of file