'use client'; import * as React from 'react'; import { useMutation, useSuspenseQuery } from '@tanstack/react-query'; import { toast } from 'sonner'; import { Icons } from '@/components/icons'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle } from '@/components/ui/sheet'; import { Switch } from '@/components/ui/switch'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Textarea } from '@/components/ui/textarea'; import { TemplateJsonSheet } from './template-json-sheet'; import { TemplateVersionManagementPanel } from './template-version-management-panel'; import { createDocumentTemplateMappingMutation, createDocumentTemplateMutation, createDocumentTemplateVersionMutation, deleteDocumentTemplateMappingMutation, updateDocumentTemplateMappingMutation, updateDocumentTemplateMutation, updateDocumentTemplateVersionMutation } from '../mutations'; import { documentTemplateByIdOptions, documentTemplatesQueryOptions } from '../queries'; import type { DocumentTemplateDetail, DocumentTemplateMappingMutationPayload, DocumentTemplateMutationPayload, DocumentTemplateVersionDetail, DocumentTemplateVersionMutationPayload } from '../types'; type TemplateFormState = { documentType: string; productType: string; fileType: string; templateName: string; description: string; isDefault: boolean; isActive: boolean; }; type VersionFormState = { version: string; filePath: string; schemaJson: string; previewImageUrl: string; isActive: boolean; }; type MappingColumnState = { columnName: string; sourceField: string; columnLetter: string; sortOrder: string; formatMask: string; }; type MappingFormState = { placeholderKey: string; sourcePath: string; dataType: 'scalar' | 'multiline' | 'table' | 'image'; sheetName: string; defaultValue: string; formatMask: string; sortOrder: string; columns: MappingColumnState[]; }; function toTemplateState(template?: DocumentTemplateDetail): TemplateFormState { return { documentType: template?.documentType ?? 'quotation', productType: template?.productType ?? 'default', fileType: template?.fileType ?? 'pdfme', templateName: template?.templateName ?? '', description: template?.description ?? '', isDefault: template?.isDefault ?? false, isActive: template?.isActive ?? true }; } function toVersionState(version?: DocumentTemplateVersionDetail): VersionFormState { return { version: version?.version ?? '', filePath: version?.filePath ?? '', schemaJson: version ? JSON.stringify(version.schemaJson, null, 2) : '{}', previewImageUrl: version?.previewImageUrl ?? '', isActive: version?.isActive ?? true }; } function toMappingState(mapping?: DocumentTemplateVersionDetail['mappings'][number]): MappingFormState { return { placeholderKey: mapping?.placeholderKey ?? '', sourcePath: mapping?.sourcePath ?? '', dataType: mapping?.dataType ?? 'scalar', sheetName: mapping?.sheetName ?? '', defaultValue: mapping?.defaultValue ?? '', formatMask: mapping?.formatMask ?? '', sortOrder: String(mapping?.sortOrder ?? 0), columns: mapping?.columns.map((column) => ({ columnName: column.columnName, sourceField: column.sourceField, columnLetter: column.columnLetter ?? '', sortOrder: String(column.sortOrder), formatMask: column.formatMask ?? '' })) ?? [] }; } function buildTemplatePayload(state: TemplateFormState): DocumentTemplateMutationPayload { return { documentType: state.documentType, productType: state.productType, fileType: state.fileType, templateName: state.templateName, description: state.description || null, isDefault: state.isDefault, isActive: state.isActive }; } function buildVersionPayload(state: VersionFormState): DocumentTemplateVersionMutationPayload { let schemaJson: unknown; try { schemaJson = JSON.parse(state.schemaJson); } catch { throw new Error('Schema JSON must be valid JSON'); } return { version: state.version, filePath: state.filePath || null, schemaJson, previewImageUrl: state.previewImageUrl || null, isActive: state.isActive }; } function buildMappingPayload(state: MappingFormState): DocumentTemplateMappingMutationPayload { return { placeholderKey: state.placeholderKey, sourcePath: state.sourcePath, dataType: state.dataType, sheetName: state.sheetName || null, defaultValue: state.defaultValue || null, formatMask: state.formatMask || null, sortOrder: Number(state.sortOrder || 0), columns: state.dataType === 'table' ? state.columns.map((column) => ({ columnName: column.columnName, sourceField: column.sourceField, columnLetter: column.columnLetter || null, sortOrder: Number(column.sortOrder || 0), formatMask: column.formatMask || null })) : [] }; } function Field({ label, children }: { label: string; children: React.ReactNode; }) { return (
{label}
{children}
); } function getTableColumnCount(version: DocumentTemplateVersionDetail) { return version.mappings.reduce((total, mapping) => total + mapping.columns.length, 0); } function TemplateDialog({ open, onOpenChange, template }: { open: boolean; onOpenChange: (open: boolean) => void; template?: DocumentTemplateDetail; }) { const [state, setState] = React.useState(toTemplateState(template)); const createMutation = useMutation({ ...createDocumentTemplateMutation, onSuccess: () => { toast.success('Template created'); onOpenChange(false); }, onError: (error) => toast.error(error instanceof Error ? error.message : 'Create failed') }); const updateMutation = useMutation({ ...updateDocumentTemplateMutation, onSuccess: () => { toast.success('Template updated'); onOpenChange(false); }, onError: (error) => toast.error(error instanceof Error ? error.message : 'Update failed') }); React.useEffect(() => { if (open) { setState(toTemplateState(template)); } }, [open, template]); async function onSubmit(event: React.FormEvent) { event.preventDefault(); const payload = buildTemplatePayload(state); if (template) { await updateMutation.mutateAsync({ id: template.id, values: payload }); return; } await createMutation.mutateAsync(payload); } const isPending = createMutation.isPending || updateMutation.isPending; return ( {template ? 'Edit Template' : 'Create Template'} Manage template metadata for CRM document generation.
setState((current) => ({ ...current, templateName: e.target.value }))} /> setState((current) => ({ ...current, documentType: e.target.value }))} /> setState((current) => ({ ...current, productType: e.target.value }))} /> setState((current) => ({ ...current, fileType: e.target.value }))} />