summaryrefslogtreecommitdiff
path: root/lib/project-doc-templates/table/template-edit-sheet.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/project-doc-templates/table/template-edit-sheet.tsx')
-rw-r--r--lib/project-doc-templates/table/template-edit-sheet.tsx305
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