"use client"; import * as React from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { GenericData } from "./export-excel-form"; import { SpreadSheets, Worksheet, Column } from "@mescius/spread-sheets-react"; import * as GC from "@mescius/spread-sheets"; import { toast } from "sonner"; import { updateFormDataInDB } from "@/lib/forms/services"; import { Loader, Save } from "lucide-react"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; interface TemplateItem { TMPL_ID: string; NAME: string; TMPL_TYPE: string; SPR_LST_SETUP: { ACT_SHEET: string; HIDN_SHEETS: Array; CONTENT?: string; // SpreadSheets JSON DATA_SHEETS: Array<{ SHEET_NAME: string; REG_TYPE_ID: string; MAP_CELL_ATT: Array<{ ATT_ID: string; IN: string; }>; }>; }; GRD_LST_SETUP: { REG_TYPE_ID: string; SPR_ITM_IDS: Array; ATTS: Array<{}>; }; SPR_ITM_LST_SETUP: { ACT_SHEET: string; HIDN_SHEETS: Array; CONTENT?: string; // SpreadSheets JSON DATA_SHEETS: Array<{ SHEET_NAME: string; REG_TYPE_ID: string; MAP_CELL_ATT: Array<{ ATT_ID: string; IN: string; }>; }>; }; } interface TemplateViewDialogProps { isOpen: boolean; onClose: () => void; templateData: TemplateItem[] | any; // 배열 또는 기존 형태 selectedRow: GenericData; formCode: string; contractItemId: number; /** 업데이트 성공 시 호출될 콜백 */ onUpdateSuccess?: (updatedValues: Record) => void; } export function TemplateViewDialog({ isOpen, onClose, templateData, selectedRow, formCode, contractItemId, onUpdateSuccess }: TemplateViewDialogProps) { const [hostStyle, setHostStyle] = React.useState({ width: '100%', height: '100%' }); const [isPending, setIsPending] = React.useState(false); const [hasChanges, setHasChanges] = React.useState(false); const [currentSpread, setCurrentSpread] = React.useState(null); const [selectedTemplateId, setSelectedTemplateId] = React.useState(""); // 템플릿 데이터를 배열로 정규화하고 CONTENT가 있는 것만 필터링 const normalizedTemplates = React.useMemo((): TemplateItem[] => { if (!templateData) return []; let templates: TemplateItem[]; // 이미 배열인 경우 if (Array.isArray(templateData)) { templates = templateData as TemplateItem[]; } else { // 기존 형태인 경우 (하위 호환성) templates = [templateData as TemplateItem]; } // CONTENT가 있는 템플릿만 필터링 return templates.filter(template => { const sprContent = template.SPR_LST_SETUP?.CONTENT; const sprItmContent = template.SPR_ITM_LST_SETUP?.CONTENT; return sprContent || sprItmContent; }); }, [templateData]); // 선택된 템플릿 가져오기 const selectedTemplate = React.useMemo(() => { if (!selectedTemplateId) return normalizedTemplates[0]; // 기본값: 첫 번째 템플릿 return normalizedTemplates.find(t => t.TMPL_ID === selectedTemplateId) || normalizedTemplates[0]; }, [normalizedTemplates, selectedTemplateId]); // 템플릿 변경 시 기본 선택 React.useEffect(() => { if (normalizedTemplates.length > 0 && !selectedTemplateId) { setSelectedTemplateId(normalizedTemplates[0].TMPL_ID); } }, [normalizedTemplates, selectedTemplateId]); const initSpread = React.useCallback((spread: any) => { if (!spread || !selectedTemplate) return; try { setCurrentSpread(spread); setHasChanges(false); // 템플릿 로드 시 변경사항 초기화 // CONTENT 찾기 (SPR_LST_SETUP 또는 SPR_ITM_LST_SETUP 중 하나) let contentJson = null; if (selectedTemplate.SPR_LST_SETUP?.CONTENT) { contentJson = selectedTemplate.SPR_LST_SETUP.CONTENT; console.log('Using SPR_LST_SETUP.CONTENT for template:', selectedTemplate.NAME); } else if (selectedTemplate.SPR_ITM_LST_SETUP?.CONTENT) { contentJson = selectedTemplate.SPR_ITM_LST_SETUP.CONTENT; console.log('Using SPR_ITM_LST_SETUP.CONTENT for template:', selectedTemplate.NAME); } if (contentJson) { console.log('Loading template content for:', selectedTemplate.NAME); const jsonData = typeof contentJson === 'string' ? JSON.parse(contentJson) : contentJson; // fromJSON으로 템플릿 구조 로드 spread.fromJSON(jsonData); } else { console.warn('No CONTENT found in template:', selectedTemplate.NAME); return; } // 값 변경 이벤트 리스너 추가 (간단한 변경사항 감지만) const activeSheet = spread.getActiveSheet(); activeSheet.bind(GC.Spread.Sheets.Events.CellChanged, (event: any, info: any) => { console.log('Cell changed:', info); setHasChanges(true); }); activeSheet.bind(GC.Spread.Sheets.Events.ValueChanged, (event: any, info: any) => { console.log('Value changed:', info); setHasChanges(true); }); } catch (error) { console.error('Error initializing spread:', error); toast.error('Failed to load template'); } }, [selectedTemplate]); // 템플릿 변경 핸들러 const handleTemplateChange = (templateId: string) => { setSelectedTemplateId(templateId); setHasChanges(false); // 템플릿 변경 시 변경사항 초기화 // SpreadSheets 재초기화는 useCallback 의존성에 의해 자동으로 처리됨 if (currentSpread) { // 강제로 재초기화 setTimeout(() => { initSpread(currentSpread); }, 100); } }; // 변경사항 저장 함수 const handleSaveChanges = React.useCallback(async () => { if (!currentSpread || !hasChanges) { toast.info("No changes to save"); return; } try { setIsPending(true); // SpreadSheets에서 현재 데이터를 JSON으로 추출 const spreadJson = currentSpread.toJSON(); console.log('Current spread data:', spreadJson); // 실제 데이터 추출 방법은 SpreadSheets 구조에 따라 달라질 수 있음 // 여기서는 기본적인 예시만 제공 const activeSheet = currentSpread.getActiveSheet(); // 간단한 예시: 특정 범위의 데이터를 추출하여 selectedRow 형태로 변환 // 실제 구현에서는 템플릿의 구조에 맞춰 데이터를 추출해야 함 const dataToSave = { ...selectedRow, // 기본값으로 원본 데이터 사용 // 여기에 SpreadSheets에서 변경된 값들을 추가 // 예: TAG_DESC: activeSheet.getValue(특정행, 특정열) }; // TAG_NO는 절대 변경되지 않도록 원본 값으로 강제 설정 dataToSave.TAG_NO = selectedRow?.TAG_NO; console.log('Data to save (TAG_NO preserved):', dataToSave); const { success, message } = await updateFormDataInDB( formCode, contractItemId, dataToSave ); if (!success) { toast.error(message); return; } toast.success("Changes saved successfully!"); const updatedData = { ...selectedRow, ...dataToSave, }; onUpdateSuccess?.(updatedData); setHasChanges(false); } catch (error) { console.error("Error saving changes:", error); toast.error("An unexpected error occurred while saving"); } finally { setIsPending(false); } }, [currentSpread, hasChanges, formCode, contractItemId, selectedRow, onUpdateSuccess]); if (!isOpen) return null; return ( SEDP Template - {formCode} {selectedRow && `Selected TAG_NO: ${selectedRow.TAG_NO || 'N/A'}`} {hasChanges && ( • Unsaved changes )}
Template content will be loaded directly. Manual data entry may be required.
{/* 템플릿 선택 UI */} {normalizedTemplates.length > 1 && (
({normalizedTemplates.length} templates available)
)} {/* SpreadSheets 컴포넌트 영역 */}
{selectedTemplate ? ( ) : (
No template available
)}
{hasChanges && ( )}
); }