From 2f1bef8eeff5d6cd30c4de808402893deb35335d Mon Sep 17 00:00:00 2001 From: 0-Zz-ang Date: Wed, 19 Nov 2025 09:50:12 +0900 Subject: 준법설문조사 리비전관리 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../table/compliance-survey-templates-toolbar.tsx | 7 +- .../table/compliance-template-create-dialog.tsx | 103 +++++++++++++++++---- .../table/compliance-template-edit-sheet.tsx | 16 ---- 3 files changed, 90 insertions(+), 36 deletions(-) (limited to 'lib/compliance/table') diff --git a/lib/compliance/table/compliance-survey-templates-toolbar.tsx b/lib/compliance/table/compliance-survey-templates-toolbar.tsx index 6776b70a..3e5f7f4d 100644 --- a/lib/compliance/table/compliance-survey-templates-toolbar.tsx +++ b/lib/compliance/table/compliance-survey-templates-toolbar.tsx @@ -16,6 +16,11 @@ interface ComplianceSurveyTemplatesToolbarActionsProps { } export function ComplianceSurveyTemplatesToolbarActions({ table }: ComplianceSurveyTemplatesToolbarActionsProps) { + const templates = React.useMemo( + () => table.getPreFilteredRowModel().rows.map((row) => row.original), + [table], + ); + return (
{/** 1) 선택된 로우가 있으면 삭제 다이얼로그 */} @@ -27,7 +32,7 @@ export function ComplianceSurveyTemplatesToolbarActions({ table }: ComplianceSur /> ) : null} - + {/** 2) 레드플래그 담당자 관리 */} diff --git a/lib/compliance/table/compliance-template-create-dialog.tsx b/lib/compliance/table/compliance-template-create-dialog.tsx index 5b7e1092..db4ede4e 100644 --- a/lib/compliance/table/compliance-template-create-dialog.tsx +++ b/lib/compliance/table/compliance-template-create-dialog.tsx @@ -29,19 +29,33 @@ import { Switch } from "@/components/ui/switch" import { Plus, Loader2 } from "lucide-react" import { toast } from "sonner" import { createComplianceSurveyTemplate } from "../services" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { complianceSurveyTemplates } from "@/db/schema/compliance" const createTemplateSchema = z.object({ name: z.string().min(1, "템플릿명을 입력해주세요.").max(100, "템플릿명은 100자 이하여야 합니다."), description: z.string().min(1, "설명을 입력해주세요.").max(500, "설명은 500자 이하여야 합니다."), - version: z.string().min(1, "버전을 입력해주세요.").max(20, "버전은 20자 이하여야 합니다."), isActive: z.boolean().default(true), }) type CreateTemplateFormValues = z.infer -export function ComplianceTemplateCreateDialog() { +interface ComplianceTemplateCreateDialogProps { + templates?: typeof complianceSurveyTemplates.$inferSelect[] +} + +export function ComplianceTemplateCreateDialog({ + templates = [], +}: ComplianceTemplateCreateDialogProps) { const [open, setOpen] = React.useState(false) const [isSubmitting, setIsSubmitting] = React.useState(false) + const [selectedTemplateId, setSelectedTemplateId] = React.useState("none") // react-hook-form 세팅 const form = useForm({ @@ -49,19 +63,54 @@ export function ComplianceTemplateCreateDialog() { defaultValues: { name: "", description: "", - version: "1.0", isActive: true, }, mode: "onChange", }) + const selectedTemplate = React.useMemo(() => { + if (selectedTemplateId === "none") { + return undefined + } + return templates.find((template) => String(template.id) === selectedTemplateId) + }, [selectedTemplateId, templates]) + + const nextVersionLabel = React.useMemo(() => { + if (!selectedTemplate) { + return null + } + const baseVersion = selectedTemplate.version || "1.0" + const numericValue = Number(baseVersion) + if (!Number.isNaN(numericValue)) { + const hasDecimal = baseVersion.includes(".") + const decimalDigits = hasDecimal ? baseVersion.split(".")[1]?.length ?? 0 : 0 + const incremented = numericValue + 1 + return hasDecimal ? incremented.toFixed(decimalDigits) : String(incremented) + } + return "1.0" + }, [selectedTemplate]) + + React.useEffect(() => { + if (selectedTemplate) { + form.setValue("name", selectedTemplate.name ?? "") + form.setValue("description", selectedTemplate.description ?? "") + } + }, [selectedTemplate, form]) + async function onSubmit(data: CreateTemplateFormValues) { setIsSubmitting(true) try { - const result = await createComplianceSurveyTemplate(data) + const baseTemplateNumericId = + selectedTemplateId !== "none" ? Number(selectedTemplateId) : undefined + + const result = await createComplianceSurveyTemplate({ + ...data, + baseTemplateId: baseTemplateNumericId, + }) if (result) { toast.success("새로운 설문조사 템플릿이 생성되었습니다.") form.reset() + setSelectedTemplateId("none") setOpen(false) // 페이지 새로고침으로 데이터 업데이트 window.location.reload() @@ -77,6 +126,7 @@ export function ComplianceTemplateCreateDialog() { function handleDialogOpenChange(nextOpen: boolean) { if (!nextOpen) { form.reset() + setSelectedTemplateId("none") } setOpen(nextOpen) } @@ -133,22 +183,37 @@ export function ComplianceTemplateCreateDialog() { )} /> - ( - - 버전 * - - - - - +
+ 기존 템플릿 불러오기 (선택) + + {selectedTemplate ? ( +

+ 선택한 템플릿을 기반으로 새 버전(v{nextVersionLabel})이 생성되며, + 기존 템플릿은 자동으로 비활성화됩니다. +

+ ) : ( +

+ 템플릿을 선택하지 않으면 새 템플릿이 기본 버전(v1.0)으로 생성됩니다. +

)} - /> +
- ( - - 버전 - - - - - - )} - /> -