From aa86729f9a2ab95346a2851e3837de1c367aae17 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 20 Jun 2025 11:37:31 +0000 Subject: (대표님) 20250620 작업사항 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/form-data/spreadJS-dialog.tsx | 375 ++++++++++++++++++++++++++----- 1 file changed, 323 insertions(+), 52 deletions(-) (limited to 'components/form-data/spreadJS-dialog.tsx') diff --git a/components/form-data/spreadJS-dialog.tsx b/components/form-data/spreadJS-dialog.tsx index 69232508..4a8550cb 100644 --- a/components/form-data/spreadJS-dialog.tsx +++ b/components/form-data/spreadJS-dialog.tsx @@ -1,69 +1,340 @@ +"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: any; - selectedRow: GenericData; - formCode: string; - } + 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 - }: TemplateViewDialogProps) { - return ( - - - - SEDP Template - {formCode} - - {selectedRow && `Selected TAG_NO: ${selectedRow.TAG_NO || 'N/A'}`} - - - - {/* 스크롤 가능한 콘텐츠 영역 */} -
- {/* 여기에 템플릿 데이터나 다른 콘텐츠가 들어갈 예정 */} -
-

- Template content will be displayed here... -

- - {/* 임시로 templateData 표시 */} - {templateData && ( -
-                  {JSON.stringify(templateData, null, 2)}
-                
- )} + 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 && ( - -
-
- ); - } \ No newline at end of file + )} + + + +
+ ); +} \ No newline at end of file -- cgit v1.2.3