'use client'; /* IMPORT */ import { addTemplateVariableAction, modifyTemplateVariableAction, removeTemplateVariableAction, } from '../service'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from '@/components/ui/alert-dialog'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; import { Copy, Edit, GripVertical, Plus, Trash2 } from 'lucide-react'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog'; import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Textarea } from '@/components/ui/textarea'; import { toast } from 'sonner'; import { type TemplateWithVariables, type TemplateVariable } from '@/db/schema'; import { useState } from 'react'; // ---------------------------------------------------------------------------------------------------- /* TYPES */ 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] = useState(template.variables) // const [isAddDialogOpen, setIsAddDialogOpen] = useState(false) const [editingVariable, setEditingVariable] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false) const [isDialogOpen, setIsDialogOpen] = useState(false) // 이름 변경 const [formData, setFormData] = 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) { const modifyRes = await modifyTemplateVariableAction(template.slug, editingVariable!.id, formData); if (modifyRes.success && modifyRes.data) { const modifiedVariable = modifyRes.data; const modifiedVariables = variables.map(v => v.id === modifiedVariable.id ? { ...v, ...formData } : v ); setVariables(modifiedVariables); onUpdate({ ...template, variables: modifiedVariables }); toast.success('변수가 수정되었습니다.'); } else { toast.error(modifyRes.error || '변수 수정에 실패했습니다.'); return; } } else { const addRes = await addTemplateVariableAction(template.slug, { variableName: formData.variableName, variableType: formData.variableType, defaultValue: formData.defaultValue || undefined, isRequired: formData.isRequired, description: formData.description || undefined, }); if (addRes.success && addRes.data) { const addedVariable = addRes.data; const addedVariables = [...variables, addedVariable]; setVariables(addedVariables); onUpdate({ ...template, variables: addedVariables }); toast.success('변수가 추가되었습니다.'); } else { toast.error(addRes.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) => { const removeRes = await removeTemplateVariableAction(template.slug, variableId); if (removeRes.success) { const removedVariables = variables.filter(v => v.id !== variableId); setVariables(removedVariables); onUpdate({ ...template, variables: removedVariables }); toast.success('변수가 삭제되었습니다.'); } else { toast.error(removeRes.error || '변수 삭제에 실패했습니다.'); } } // 변수 타입에 따른 기본값 예시 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)} />