diff options
| author | 0-Zz-ang <s1998319@gmail.com> | 2025-08-04 14:59:15 +0900 |
|---|---|---|
| committer | 0-Zz-ang <s1998319@gmail.com> | 2025-08-04 14:59:15 +0900 |
| commit | 59b5715ebb3e1fd7bd4eb02ce50399715734f865 (patch) | |
| tree | 39ccd16482c1b90b6583ead73384822157254d88 /lib/docu-list-rule/combo-box-settings/table/document-class-options-sheet.tsx | |
| parent | f0213de0d2fb5fcb931b3ddaddcbb6581cab5d28 (diff) | |
(박서영) docu-list-rule detail sheet 컴포넌트 추가 및 검색 필터 기능 오류 수정
Diffstat (limited to 'lib/docu-list-rule/combo-box-settings/table/document-class-options-sheet.tsx')
| -rw-r--r-- | lib/docu-list-rule/combo-box-settings/table/document-class-options-sheet.tsx | 436 |
1 files changed, 0 insertions, 436 deletions
diff --git a/lib/docu-list-rule/combo-box-settings/table/document-class-options-sheet.tsx b/lib/docu-list-rule/combo-box-settings/table/document-class-options-sheet.tsx deleted file mode 100644 index 8585d9a3..00000000 --- a/lib/docu-list-rule/combo-box-settings/table/document-class-options-sheet.tsx +++ /dev/null @@ -1,436 +0,0 @@ -"use client" - -import * as React from "react" -import { useState, useEffect } from "react" -import { Plus, Trash2, Save, X } from "lucide-react" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { - Sheet, - SheetContent, - SheetDescription, - SheetHeader, - SheetTitle, -} from "@/components/ui/sheet" -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table" -import { toast } from "sonner" -import { - getDocumentClassOptions, - createDocumentClassOption, - updateDocumentClassOption, - deleteDocumentClassOption -} from "../service" - -interface DocumentClassOptionsSheetProps { - open: boolean - onOpenChange: (open: boolean) => void - comboBoxOption: { - id: number - code: string - description: string - } - onSuccess: () => void -} - -interface DocumentClassOption { - id: number - comboBoxSettingId: number - optionValue: string - optionCode: string | null - sortOrder: number - isActive: boolean - createdAt: Date - updatedAt: Date -} - -interface NewOptionRow { - id: string - optionValue: string - optionCode: string - sortOrder: number -} - -export function DocumentClassOptionsSheet({ - open, - onOpenChange, - comboBoxOption, - onSuccess -}: DocumentClassOptionsSheetProps) { - const [options, setOptions] = useState<DocumentClassOption[]>([]) - const [loading, setLoading] = useState(true) - const [newOptionRows, setNewOptionRows] = useState<NewOptionRow[]>([]) - const [editingOption, setEditingOption] = useState<DocumentClassOption | null>(null) - - // 하위 옵션 목록 로드 - const loadOptions = async () => { - try { - setLoading(true) - const result = await getDocumentClassOptions(comboBoxOption.id) - if (result.success && result.data) { - setOptions(result.data) - } else { - toast.error("하위 옵션 목록을 불러오는데 실패했습니다.") - } - } catch (error) { - console.error("하위 옵션 로드 실패:", error) - toast.error("하위 옵션 목록을 불러오는데 실패했습니다.") - } finally { - setLoading(false) - } - } - - useEffect(() => { - if (open) { - loadOptions() - } - }, [open, comboBoxOption.id]) - - // 새 행 추가 - const addNewRow = () => { - const newRow: NewOptionRow = { - id: `new-${Date.now()}-${Math.random()}`, - optionValue: "", - optionCode: "", - sortOrder: options.length + newOptionRows.length + 1, - } - setNewOptionRows(prev => [...prev, newRow]) - } - - // 새 행 삭제 - const removeNewRow = (id: string) => { - setNewOptionRows(prev => prev.filter(row => row.id !== id)) - } - - // 새 행 업데이트 - const updateNewRow = (id: string, field: keyof NewOptionRow, value: string | number) => { - setNewOptionRows(prev => - prev.map(row => - row.id === id ? { ...row, [field]: value } : row - ) - ) - } - - // 새 하위 옵션 저장 - const handleSaveNewOptions = async () => { - const validRows = newOptionRows.filter(row => row.optionValue.trim()) - - if (validRows.length === 0) { - toast.error("최소 하나의 옵션 값을 입력해주세요.") - return - } - - try { - let successCount = 0 - let errorCount = 0 - - for (const row of validRows) { - try { - const result = await createDocumentClassOption({ - comboBoxSettingId: comboBoxOption.id, - optionValue: row.optionValue.trim(), - optionCode: row.optionCode.trim() || undefined, - sortOrder: row.sortOrder, - }) - - if (result.success) { - successCount++ - } else { - errorCount++ - } - } catch (error) { - console.error("하위 옵션 추가 실패:", error) - errorCount++ - } - } - - if (successCount > 0) { - toast.success(`${successCount}개의 하위 옵션이 추가되었습니다.${errorCount > 0 ? ` (${errorCount}개 실패)` : ''}`) - setNewOptionRows([]) - await loadOptions() - onSuccess() - } else { - toast.error("모든 하위 옵션 추가에 실패했습니다.") - } - } catch (error) { - console.error("하위 옵션 추가 실패:", error) - toast.error("하위 옵션 추가에 실패했습니다.") - } - } - - // 기존 하위 옵션 수정 - const handleUpdateOption = async (option: DocumentClassOption) => { - if (!option.optionValue.trim()) { - toast.error("옵션 값은 필수 입력 항목입니다.") - return - } - - try { - const result = await updateDocumentClassOption({ - id: option.id, - optionValue: option.optionValue.trim(), - optionCode: option.optionCode || undefined, - sortOrder: option.sortOrder, - isActive: option.isActive, - }) - - 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 deleteDocumentClassOption(optionId) - if (result.success) { - await loadOptions() - toast.success("하위 옵션이 삭제되었습니다.") - } else { - toast.error("하위 옵션 삭제에 실패했습니다.") - } - } catch (error) { - console.error("하위 옵션 삭제 실패:", error) - toast.error("하위 옵션 삭제에 실패했습니다.") - } - } - - return ( - <Sheet open={open} onOpenChange={onOpenChange}> - <SheetContent className="w-[600px] sm:max-w-[600px] overflow-y-auto"> - <SheetHeader> - <SheetTitle>하위 옵션 관리</SheetTitle> - <SheetDescription> - {comboBoxOption.description} ({comboBoxOption.code})의 하위 옵션을 관리합니다. - </SheetDescription> - </SheetHeader> - - <div className="space-y-4 mt-6"> - {/* 새 하위 옵션 추가 */} - <div className="space-y-2"> - <div className="flex items-center justify-between"> - <h4 className="text-sm font-medium">새 하위 옵션 추가</h4> - <Button - type="button" - variant="outline" - size="sm" - onClick={addNewRow} - className="h-8" - > - <Plus className="h-4 w-4 mr-1" /> - 옵션 추가 - </Button> - </div> - - {newOptionRows.length > 0 && ( - <div className="border rounded-lg overflow-hidden"> - <Table> - <TableHeader> - <TableRow className="bg-muted/30"> - <TableHead className="w-[40%]">옵션 값 *</TableHead> - <TableHead className="w-[30%]">옵션 코드</TableHead> - <TableHead className="w-[20%]">순서</TableHead> - <TableHead className="w-[10%]"></TableHead> - </TableRow> - </TableHeader> - <TableBody> - {newOptionRows.map((row) => ( - <TableRow key={row.id} className="hover:bg-muted/30"> - <TableCell> - <Input - value={row.optionValue} - onChange={(e) => updateNewRow(row.id, "optionValue", e.target.value)} - placeholder="옵션 값" - className="border-0 focus-visible:ring-1 bg-transparent h-8" - /> - </TableCell> - <TableCell> - <Input - value={row.optionCode} - onChange={(e) => updateNewRow(row.id, "optionCode", e.target.value)} - placeholder="옵션 코드 (선택)" - className="border-0 focus-visible:ring-1 bg-transparent h-8" - /> - </TableCell> - <TableCell> - <Input - type="number" - value={row.sortOrder} - onChange={(e) => updateNewRow(row.id, "sortOrder", parseInt(e.target.value) || 0)} - className="border-0 focus-visible:ring-1 bg-transparent h-8" - /> - </TableCell> - <TableCell> - <Button - onClick={() => removeNewRow(row.id)} - size="sm" - variant="ghost" - className="h-6 w-6 p-0" - > - <X className="h-3 w-3" /> - </Button> - </TableCell> - </TableRow> - ))} - </TableBody> - </Table> - <div className="p-3 border-t"> - <Button - onClick={handleSaveNewOptions} - size="sm" - className="h-8" - > - <Save className="h-4 w-4 mr-1" /> - 저장 - </Button> - </div> - </div> - )} - </div> - - {/* 기존 하위 옵션 목록 */} - <div className="space-y-2"> - <h4 className="text-sm font-medium">기존 하위 옵션</h4> - <div className="border rounded-lg overflow-hidden"> - <Table> - <TableHeader> - <TableRow className="bg-muted/30"> - <TableHead className="w-[35%]">옵션 값</TableHead> - <TableHead className="w-[25%]">옵션 코드</TableHead> - <TableHead className="w-[15%]">순서</TableHead> - <TableHead className="w-[15%]">상태</TableHead> - <TableHead className="w-[10%]">작업</TableHead> - </TableRow> - </TableHeader> - <TableBody> - {loading ? ( - <TableRow> - <TableCell colSpan={5} className="text-center py-8"> - 로딩 중... - </TableCell> - </TableRow> - ) : options.length === 0 ? ( - <TableRow> - <TableCell colSpan={5} className="text-center text-muted-foreground py-8"> - 등록된 하위 옵션이 없습니다. - </TableCell> - </TableRow> - ) : ( - options.map((option) => ( - <TableRow key={option.id} className="hover:bg-muted/30"> - <TableCell className="text-sm"> - {editingOption?.id === option.id ? ( - <Input - value={editingOption.optionValue} - onChange={(e) => setEditingOption(prev => prev ? { ...prev, optionValue: e.target.value } : null)} - placeholder="옵션 값" - className="border-0 focus-visible:ring-1 bg-transparent h-8" - /> - ) : ( - option.optionValue - )} - </TableCell> - <TableCell className="text-sm"> - {editingOption?.id === option.id ? ( - <Input - value={editingOption.optionCode || ""} - onChange={(e) => setEditingOption(prev => prev ? { ...prev, optionCode: e.target.value } : null)} - placeholder="옵션 코드" - className="border-0 focus-visible:ring-1 bg-transparent h-8" - /> - ) : ( - option.optionCode || "-" - )} - </TableCell> - <TableCell className="text-sm"> - {editingOption?.id === option.id ? ( - <Input - type="number" - value={editingOption.sortOrder} - onChange={(e) => setEditingOption(prev => prev ? { ...prev, sortOrder: parseInt(e.target.value) || 0 } : null)} - className="border-0 focus-visible:ring-1 bg-transparent h-8" - /> - ) : ( - option.sortOrder - )} - </TableCell> - <TableCell className="text-sm"> - <span className={`px-2 py-1 rounded text-xs ${ - option.isActive - ? "bg-green-100 text-green-800" - : "bg-red-100 text-red-800" - }`}> - {option.isActive ? "활성" : "비활성"} - </span> - </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> - ) : ( - <div className="flex gap-1"> - <Button - onClick={() => setEditingOption(option)} - size="sm" - variant="outline" - className="h-6 px-2 text-xs" - > - 수정 - </Button> - <Button - onClick={() => handleDeleteOption(option.id)} - size="sm" - variant="ghost" - className="h-6 px-2 text-xs text-red-600 hover:text-red-700" - > - <Trash2 className="h-3 w-3" /> - </Button> - </div> - )} - </TableCell> - </TableRow> - )) - )} - </TableBody> - </Table> - </div> - </div> - </div> - </SheetContent> - </Sheet> - ) -}
\ No newline at end of file |
