diff options
Diffstat (limited to 'lib/project-doc-templates/table/template-edit-sheet.tsx')
| -rw-r--r-- | lib/project-doc-templates/table/template-edit-sheet.tsx | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/lib/project-doc-templates/table/template-edit-sheet.tsx b/lib/project-doc-templates/table/template-edit-sheet.tsx new file mode 100644 index 00000000..a745045c --- /dev/null +++ b/lib/project-doc-templates/table/template-edit-sheet.tsx @@ -0,0 +1,305 @@ +// lib/project-doc-template/template-edit-sheet.tsx +"use client"; + +import * as React from "react"; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, + SheetFooter, +} from "@/components/ui/sheet"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { Badge } from "@/components/ui/badge"; +import { Switch } from "@/components/ui/switch"; +import { toast } from "sonner"; +import { updateProjectDocTemplate } from "@/lib/project-doc-templates/service"; +import type { ProjectDocTemplate, DocTemplateVariable } from "@/db/schema/project-doc-templates"; +import { Plus, X, Save, Loader2 } from "lucide-react"; + +interface TemplateEditSheetProps { + template: ProjectDocTemplate | null; + open: boolean; + onOpenChange: (open: boolean) => void; + onSuccess?: () => void; +} + +export function TemplateEditSheet({ + template, + open, + onOpenChange, + onSuccess, +}: TemplateEditSheetProps) { + const [isLoading, setIsLoading] = React.useState(false); + const [formData, setFormData] = React.useState({ + templateName: "", + description: "", + variables: [] as DocTemplateVariable[], + status: "ACTIVE", + }); + + // 템플릿 데이터 로드 + React.useEffect(() => { + if (template && open) { + setFormData({ + templateName: template.templateName || "", + description: template.description || "", + variables: template.variables || [], + status: template.status || "ACTIVE", + }); + } + }, [template, open]); + + // 변수 추가 + const addVariable = () => { + setFormData({ + ...formData, + variables: [ + ...formData.variables, + { + name: "", + displayName: "", + type: "text", + required: false, + defaultValue: "", + description: "", + }, + ], + }); + }; + + // 변수 제거 + const removeVariable = (index: number) => { + setFormData({ + ...formData, + variables: formData.variables.filter((_, i) => i !== index), + }); + }; + + // 변수 업데이트 + const updateVariable = (index: number, field: string, value: any) => { + const updatedVariables = [...formData.variables]; + updatedVariables[index] = { + ...updatedVariables[index], + [field]: value, + }; + setFormData({ + ...formData, + variables: updatedVariables, + }); + }; + + // 저장 처리 + const handleSave = async () => { + if (!template) return; + + setIsLoading(true); + try { + const result = await updateProjectDocTemplate(template.id, { + templateName: formData.templateName, + description: formData.description, + variables: formData.variables, + status: formData.status, + }); + + if (result.success) { + toast.success("템플릿이 수정되었습니다."); + onOpenChange(false); + onSuccess?.(); + } else { + throw new Error(result.error); + } + } catch (error) { + console.error("Failed to update template:", error); + toast.error(error instanceof Error ? error.message : "템플릿 수정에 실패했습니다."); + } finally { + setIsLoading(false); + } + }; + + return ( + <Sheet open={open} onOpenChange={onOpenChange}> + <SheetContent className="w-[600px] sm:max-w-[600px] overflow-y-auto"> + <SheetHeader> + <SheetTitle>템플릿 수정</SheetTitle> + <SheetDescription> + 템플릿 정보와 변수를 수정할 수 있습니다. 파일을 변경하려면 새 버전을 생성하세요. + </SheetDescription> + </SheetHeader> + + <div className="space-y-6 py-6"> + {/* 기본 정보 */} + <div className="space-y-4"> + <div> + <Label htmlFor="templateName">템플릿 이름</Label> + <Input + id="templateName" + value={formData.templateName} + onChange={(e) => setFormData({ ...formData, templateName: e.target.value })} + placeholder="템플릿 이름을 입력하세요" + /> + </div> + + <div> + <Label htmlFor="description">설명</Label> + <Textarea + id="description" + value={formData.description} + onChange={(e) => setFormData({ ...formData, description: e.target.value })} + placeholder="템플릿 설명을 입력하세요" + className="min-h-[80px]" + /> + </div> + + <div> + <Label htmlFor="status">상태</Label> + <select + id="status" + className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm" + value={formData.status} + onChange={(e) => setFormData({ ...formData, status: e.target.value })} + > + <option value="ACTIVE">활성</option> + <option value="INACTIVE">비활성</option> + <option value="DRAFT">초안</option> + <option value="ARCHIVED">보관</option> + </select> + </div> + </div> + + {/* 변수 관리 */} + <div> + <div className="flex items-center justify-between mb-4"> + <Label>템플릿 변수</Label> + <Button + type="button" + variant="outline" + size="sm" + onClick={addVariable} + > + <Plus className="mr-2 h-4 w-4" /> + 변수 추가 + </Button> + </div> + + {/* 기본 변수 표시 */} + <div className="mb-4 p-3 bg-muted/50 rounded-lg"> + <p className="text-xs font-medium mb-2">기본 변수 (수정 불가)</p> + <div className="space-y-1"> + <Badge variant="secondary">document_number</Badge> + <Badge variant="secondary" className="ml-2">project_code</Badge> + <Badge variant="secondary" className="ml-2">project_name</Badge> + </div> + </div> + + {/* 사용자 정의 변수 */} + {formData.variables.length > 0 ? ( + <div className="space-y-3"> + {formData.variables.map((variable, index) => ( + <div key={index} className="p-3 border rounded-lg space-y-3"> + <div className="flex items-center justify-between"> + <span className="text-sm font-medium">변수 #{index + 1}</span> + <Button + type="button" + variant="ghost" + size="sm" + onClick={() => removeVariable(index)} + > + <X className="h-4 w-4" /> + </Button> + </div> + + <div className="grid grid-cols-2 gap-3"> + <div> + <Label>변수명</Label> + <Input + value={variable.name} + onChange={(e) => updateVariable(index, "name", e.target.value)} + placeholder="예: vendor_name" + /> + </div> + <div> + <Label>표시명</Label> + <Input + value={variable.displayName} + onChange={(e) => updateVariable(index, "displayName", e.target.value)} + placeholder="예: 벤더명" + /> + </div> + </div> + + <div className="grid grid-cols-2 gap-3"> + <div> + <Label>타입</Label> + <select + className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm" + value={variable.type} + onChange={(e) => updateVariable(index, "type", e.target.value)} + > + <option value="text">텍스트</option> + <option value="number">숫자</option> + <option value="date">날짜</option> + <option value="select">선택</option> + </select> + </div> + <div className="flex items-center justify-between rounded-lg border p-2"> + <Label className="text-sm">필수</Label> + <Switch + checked={variable.required} + onCheckedChange={(checked) => updateVariable(index, "required", checked)} + /> + </div> + </div> + + <div> + <Label>설명</Label> + <Input + value={variable.description || ""} + onChange={(e) => updateVariable(index, "description", e.target.value)} + placeholder="변수 설명 (선택사항)" + /> + </div> + </div> + ))} + </div> + ) : ( + <div className="text-center py-4 text-sm text-muted-foreground"> + 사용자 정의 변수가 없습니다. + </div> + )} + </div> + </div> + + <SheetFooter> + <Button + variant="outline" + onClick={() => onOpenChange(false)} + disabled={isLoading} + > + 취소 + </Button> + <Button + onClick={handleSave} + disabled={isLoading} + > + {isLoading ? ( + <> + <Loader2 className="mr-2 h-4 w-4 animate-spin" /> + 저장 중... + </> + ) : ( + <> + <Save className="mr-2 h-4 w-4" /> + 저장 + </> + )} + </Button> + </SheetFooter> + </SheetContent> + </Sheet> + ); +}
\ No newline at end of file |
