"use client" import * as React from "react" 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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Checkbox } from "@/components/ui/checkbox" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Badge } from "@/components/ui/badge" import { Plus, Edit, Trash2, GripVertical, Copy } from "lucide-react" import { toast } from "sonner" import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd" import { type TemplateWithVariables, type TemplateVariable } from "@/db/schema" import { addTemplateVariableAction } from "../service" interface TemplateVariableManagerProps { template: TemplateWithVariables onUpdate: (template: TemplateWithVariables) => void } interface VariableFormData { variableName: string variableType: 'string' | 'number' | 'boolean' | 'date' defaultValue: string isRequired: boolean description: string } export function TemplateVariableManager({ template, onUpdate }: TemplateVariableManagerProps) { const [variables, setVariables] = React.useState(template.variables) // const [isAddDialogOpen, setIsAddDialogOpen] = React.useState(false) const [editingVariable, setEditingVariable] = React.useState(null) const [isSubmitting, setIsSubmitting] = React.useState(false) const [isDialogOpen, setIsDialogOpen] = React.useState(false) // 이름 변경 const [formData, setFormData] = React.useState({ variableName: '', variableType: 'string', defaultValue: '', isRequired: false, description: '' }) const isEditMode = editingVariable !== null // 폼 초기화 const resetForm = () => { setFormData({ variableName: '', variableType: 'string', defaultValue: '', isRequired: false, description: '' }) setEditingVariable(null) } const handleEditVariable = (variable: TemplateVariable) => { setEditingVariable(variable) setFormData({ variableName: variable.variableName, variableType: variable.variableType as any, defaultValue: variable.defaultValue || '', isRequired: variable.isRequired || false, description: variable.description || '' }) setIsDialogOpen(true) } const handleSubmitVariable = async () => { if (!formData.variableName.trim()) { toast.error('변수명을 입력해주세요.') return } // 편집 모드가 아닐 때만 중복 검사 if (!isEditMode && variables.some(v => v.variableName === formData.variableName)) { toast.error('이미 존재하는 변수명입니다.') return } // 편집 모드일 때 다른 변수와 중복되는지 검사 if (isEditMode && variables.some(v => v.id !== editingVariable!.id && v.variableName === formData.variableName)) { toast.error('이미 존재하는 변수명입니다.') return } // 변수명 유효성 검사 if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(formData.variableName)) { toast.error('변수명은 영문자, 숫자, 언더스코어만 사용 가능하며 숫자로 시작할 수 없습니다.') return } setIsSubmitting(true) try { if (isEditMode) { // 편집 모드 - TODO: updateTemplateVariableAction 구현 필요 // const result = await updateTemplateVariableAction(template.slug, editingVariable!.id, formData) // 임시로 클라이언트 사이드에서 업데이트 const updatedVariables = variables.map(v => v.id === editingVariable!.id ? { ...v, ...formData } : v ) setVariables(updatedVariables) onUpdate({ ...template, variables: updatedVariables }) toast.success('변수가 수정되었습니다.') } else { // 추가 모드 const result = await addTemplateVariableAction(template.slug, { variableName: formData.variableName, variableType: formData.variableType, defaultValue: formData.defaultValue || undefined, isRequired: formData.isRequired, description: formData.description || undefined, }) if (result.success) { const newVariable = result.data const updatedVariables = [...variables, newVariable] setVariables(updatedVariables) onUpdate({ ...template, variables: updatedVariables }) toast.success('변수가 추가되었습니다.') } else { toast.error(result.error || '변수 추가에 실패했습니다.') return } } setIsDialogOpen(false) resetForm() } catch (error) { toast.error(`변수 ${isEditMode ? '수정' : '추가'} 중 오류가 발생했습니다.`) } finally { setIsSubmitting(false) } } // Dialog 닫기 처리 const handleDialogClose = (open: boolean) => { setIsDialogOpen(open) if (!open) { resetForm() } } // 변수 추가 // const handleAddVariable = async () => { // if (!formData.variableName.trim()) { // toast.error('변수명을 입력해주세요.') // return // } // // 변수명 중복 검사 // if (variables.some(v => v.variableName === formData.variableName)) { // toast.error('이미 존재하는 변수명입니다.') // return // } // // 변수명 유효성 검사 // if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(formData.variableName)) { // toast.error('변수명은 영문자, 숫자, 언더스코어만 사용 가능하며 숫자로 시작할 수 없습니다.') // return // } // setIsSubmitting(true) // try { // const result = await addTemplateVariableAction(template.slug, { // variableName: formData.variableName, // variableType: formData.variableType, // defaultValue: formData.defaultValue || undefined, // isRequired: formData.isRequired, // description: formData.description || undefined, // }) // if (result.success) { // const newVariable = result.data // const updatedVariables = [...variables, newVariable] // setVariables(updatedVariables) // onUpdate({ ...template, variables: updatedVariables }) // toast.success('변수가 추가되었습니다.') // setIsAddDialogOpen(false) // resetForm() // } else { // toast.error(result.error || '변수 추가에 실패했습니다.') // } // } catch (error) { // toast.error('변수 추가 중 오류가 발생했습니다.') // } finally { // setIsSubmitting(false) // } // } // 변수 순서 변경 const handleDragEnd = (result: any) => { if (!result.destination) return const items = Array.from(variables) const [reorderedItem] = items.splice(result.source.index, 1) items.splice(result.destination.index, 0, reorderedItem) // displayOrder 업데이트 const updatedItems = items.map((item, index) => ({ ...item, displayOrder: index })) setVariables(updatedItems) onUpdate({ ...template, variables: updatedItems }) // TODO: 서버에 순서 변경 요청 toast.success('변수 순서가 변경되었습니다.') } // 변수 복사 const handleCopyVariable = (variable: TemplateVariable) => { const copyName = `${variable.variableName}_copy` setFormData({ variableName: copyName, variableType: variable.variableType as any, defaultValue: variable.defaultValue || '', isRequired: variable.isRequired || false, description: variable.description || '' }) setIsDialogOpen(true) } // 변수 삭제 const handleDeleteVariable = async (variableId: string) => { // TODO: 서버에서 변수 삭제 구현 const updatedVariables = variables.filter(v => v.id !== variableId) setVariables(updatedVariables) onUpdate({ ...template, variables: updatedVariables }) toast.success('변수가 삭제되었습니다.') } // 변수 타입에 따른 기본값 예시 const getDefaultValuePlaceholder = (type: string) => { switch (type) { case 'string': return '예: 홍길동' case 'number': return '예: 123' case 'boolean': return 'true 또는 false' case 'date': return '예: 2025-01-01' default: return '' } } // 변수 타입별 아이콘 const getVariableTypeIcon = (type: string) => { switch (type) { case 'string': return '📝' case 'number': return '🔢' case 'boolean': return '✅' case 'date': return '📅' default: return '❓' } } return (
{/* 헤더 */}
{/*

변수 관리

*/}

총 {variables.length}개의 변수가 등록되어 있습니다.

{isEditMode ? '변수 수정' : '새 변수 추가'} {isEditMode ? '기존 변수의 정보를 수정합니다.' : '템플릿에서 사용할 새로운 변수를 추가합니다.' }
setFormData(prev => ({ ...prev, variableName: e.target.value }))} placeholder="예: userName, orderDate" />
setFormData(prev => ({ ...prev, defaultValue: e.target.value }))} placeholder={getDefaultValuePlaceholder(formData.variableType)} />