diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-17 10:50:28 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-07-17 10:50:28 +0000 |
| commit | fb276ed3db86fe4fc0c0fcd870fd3d085b034be0 (patch) | |
| tree | 4a8ab1027d7fd14602a0f837d4e18b04e2169e58 /components/form-data/spreadJS-dialog.tsx | |
| parent | 4eb7532f822c821fb6b69bf103bd075fefba769b (diff) | |
(대표님) 벤더데이터 S-EDP 변경사항 대응(seperator), 정기평가 점수오류, dim 준비
Diffstat (limited to 'components/form-data/spreadJS-dialog.tsx')
| -rw-r--r-- | components/form-data/spreadJS-dialog.tsx | 166 |
1 files changed, 121 insertions, 45 deletions
diff --git a/components/form-data/spreadJS-dialog.tsx b/components/form-data/spreadJS-dialog.tsx index c106f926..1cf23369 100644 --- a/components/form-data/spreadJS-dialog.tsx +++ b/components/form-data/spreadJS-dialog.tsx @@ -10,6 +10,7 @@ import { toast } from "sonner"; import { updateFormDataInDB } from "@/lib/forms/services"; import { Loader, Save } from "lucide-react"; import '@mescius/spread-sheets/styles/gc.spread.sheets.excel2016colorful.css'; +import { DataTableColumnJSON } from "./form-data-table-columns"; // SpreadSheets를 동적으로 import (SSR 비활성화) const SpreadSheets = dynamic( @@ -71,9 +72,10 @@ interface TemplateViewDialogProps { isOpen: boolean; onClose: () => void; templateData: TemplateItem[] | any; - selectedRow?: GenericData; // SPR_ITM_LST_SETUP용 - tableData?: GenericData[]; // SPR_LST_SETUP용 + selectedRow?: GenericData; // SPREAD_ITEM용 + tableData?: GenericData[]; // SPREAD_LIST용 formCode: string; + columnsJSON: DataTableColumnJSON[] contractItemId: number; editableFieldsMap?: Map<string, string[]>; onUpdateSuccess?: (updatedValues: Record<string, any> | GenericData[]) => void; @@ -87,6 +89,7 @@ export function TemplateViewDialog({ tableData = [], formCode, contractItemId, + columnsJSON, editableFieldsMap = new Map(), onUpdateSuccess }: TemplateViewDialogProps) { @@ -100,14 +103,14 @@ export function TemplateViewDialog({ const [currentSpread, setCurrentSpread] = React.useState<any>(null); const [cellMappings, setCellMappings] = React.useState<Array<{attId: string, cellAddress: string, isEditable: boolean}>>([]); const [isClient, setIsClient] = React.useState(false); - const [templateType, setTemplateType] = React.useState<'SPR_LST_SETUP' | 'SPR_ITM_LST_SETUP' | null>(null); + const [templateType, setTemplateType] = React.useState<'SPREAD_LIST' | 'SPREAD_ITEM' | null>(null); // 클라이언트 사이드에서만 렌더링되도록 보장 React.useEffect(() => { setIsClient(true); }, []); - // 템플릿 데이터를 배열로 정규화하고 CONTENT가 있는 것 찾기 + // 템플릿 데이터를 배열로 정규화하고 TMPL_TYPE에 따라 템플릿 타입 결정 const { normalizedTemplate, detectedTemplateType } = React.useMemo(() => { if (!templateData) return { normalizedTemplate: null, detectedTemplateType: null }; @@ -118,13 +121,16 @@ export function TemplateViewDialog({ templates = [templateData as TemplateItem]; } - // CONTENT가 있는 템플릿 찾기 + // TMPL_TYPE이 SPREAD_LIST 또는 SPREAD_ITEM인 템플릿 찾기 for (const template of templates) { - if (template.SPR_LST_SETUP?.CONTENT) { - return { normalizedTemplate: template, detectedTemplateType: 'SPR_LST_SETUP' as const }; - } - if (template.SPR_ITM_LST_SETUP?.CONTENT) { - return { normalizedTemplate: template, detectedTemplateType: 'SPR_ITM_LST_SETUP' as const }; + if (template.TMPL_TYPE === "SPREAD_LIST" || template.TMPL_TYPE === "SPREAD_ITEM") { + // SPR_LST_SETUP.CONTENT 또는 SPR_ITM_LST_SETUP.CONTENT 중 하나라도 있으면 해당 템플릿 사용 + if (template.SPR_LST_SETUP?.CONTENT || template.SPR_ITM_LST_SETUP?.CONTENT) { + return { + normalizedTemplate: template, + detectedTemplateType: template.TMPL_TYPE as 'SPREAD_LIST' | 'SPREAD_ITEM' + }; + } } } @@ -136,21 +142,64 @@ export function TemplateViewDialog({ setTemplateType(detectedTemplateType); }, [detectedTemplateType]); + const editableFields = React.useMemo(() => { + if (!selectedRow?.TAG_NO || !editableFieldsMap.has(selectedRow.TAG_NO)) { + return []; + } + return editableFieldsMap.get(selectedRow.TAG_NO) || []; + }, [selectedRow?.TAG_NO, editableFieldsMap]); + // 필드가 편집 가능한지 판별하는 함수 - const isFieldEditable = React.useCallback((attId: string) => { - // TAG_NO와 TAG_DESC는 기본적으로 편집 가능 + // const isFieldEditable = React.useCallback((attId: string) => { + // // columnsJSON에서 해당 attId의 shi 값 확인 + // const columnConfig = columnsJSON.find(col => col.key === attId); + // if (columnConfig?.shi === true) { + // return false; // columnsJSON에서 shi가 true이면 편집 불가 + // } + + // // TAG_NO와 TAG_DESC는 기본적으로 편집 가능 (columnsJSON의 shi가 false인 경우) + // if (attId === "TAG_NO" || attId === "TAG_DESC") { + // return true; + // } + + // if (selectedRow?.TAG_NO && editableFieldsMap.has(selectedRow.TAG_NO)) { + // return editableFields.includes(attId); + // } + + + + // // SPREAD_LIST인 경우는 기본적으로 편집 가능 (개별 행의 shi 상태는 저장시 확인) + // return true; + // }, [templateType, selectedRow, columnsJSON, editableFields]); + + const isFieldEditable = React.useCallback((attId: string, rowData?: GenericData) => { + // columnsJSON에서 해당 attId의 shi 값 확인 + const columnConfig = columnsJSON.find(col => col.key === attId); + if (columnConfig?.shi === true) { + return false; // columnsJSON에서 shi가 true이면 편집 불가 + } + + // TAG_NO와 TAG_DESC는 기본적으로 편집 가능 (columnsJSON의 shi가 false인 경우) if (attId === "TAG_NO" || attId === "TAG_DESC") { return true; } + + // SPREAD_ITEM 모드일 때는 selectedRow 사용 + // if (templateType === 'SPREAD_ITEM' && selectedRow?.TAG_NO && editableFieldsMap.has(selectedRow.TAG_NO)) { + // const editableFields = editableFieldsMap.get(selectedRow.TAG_NO) || []; + // return editableFields.includes(attId); + // } - // SPR_ITM_LST_SETUP인 경우 selectedRow.shi 확인 - if (templateType === 'SPR_ITM_LST_SETUP' && selectedRow) { - return selectedRow.shi !== true; - } + // // SPREAD_LIST 모드일 때는 각 행의 데이터 사용 + // if (templateType === 'SPREAD_LIST' && rowData?.TAG_NO && editableFieldsMap.has(rowData.TAG_NO)) { + // const editableFields = editableFieldsMap.get(rowData.TAG_NO) || []; + // return editableFields.includes(attId); + // } - // SPR_LST_SETUP인 경우는 기본적으로 편집 가능 (개별 행의 shi 상태는 저장시 확인) + // SPREAD_LIST인 경우는 기본적으로 편집 가능 (개별 행의 shi 상태는 저장시 확인) return true; - }, [templateType, selectedRow]); + }, [templateType, selectedRow, columnsJSON, editableFieldsMap]); + // 편집 가능한 필드 개수 계산 const editableFieldsCount = React.useMemo(() => { @@ -184,18 +233,21 @@ export function TemplateViewDialog({ setCurrentSpread(spread); setHasChanges(false); - // 템플릿 타입에 따라 CONTENT와 DATA_SHEETS 가져오기 + // SPR_LST_SETUP.CONTENT와 SPR_ITM_LST_SETUP.CONTENT 중에서 값이 있는 것을 찾아서 사용 let contentJson = null; let dataSheets = null; - if (templateType === 'SPR_LST_SETUP') { + // SPR_LST_SETUP.CONTENT가 있으면 우선 사용 + if (normalizedTemplate.SPR_LST_SETUP?.CONTENT) { contentJson = normalizedTemplate.SPR_LST_SETUP.CONTENT; dataSheets = normalizedTemplate.SPR_LST_SETUP.DATA_SHEETS; - console.log('Using SPR_LST_SETUP.CONTENT for template:', normalizedTemplate.NAME); - } else if (templateType === 'SPR_ITM_LST_SETUP') { + console.log('Using SPR_LST_SETUP.CONTENT for template:', normalizedTemplate.NAME, '(TMPL_TYPE:', normalizedTemplate.TMPL_TYPE, ')'); + } + // SPR_ITM_LST_SETUP.CONTENT가 있으면 사용 + else if (normalizedTemplate.SPR_ITM_LST_SETUP?.CONTENT) { contentJson = normalizedTemplate.SPR_ITM_LST_SETUP.CONTENT; dataSheets = normalizedTemplate.SPR_ITM_LST_SETUP.DATA_SHEETS; - console.log('Using SPR_ITM_LST_SETUP.CONTENT for template:', normalizedTemplate.NAME); + console.log('Using SPR_ITM_LST_SETUP.CONTENT for template:', normalizedTemplate.NAME, '(TMPL_TYPE:', normalizedTemplate.TMPL_TYPE, ')'); } if (!contentJson) { @@ -203,7 +255,7 @@ export function TemplateViewDialog({ return; } - console.log(`Loading template content for: ${normalizedTemplate.NAME} (Type: ${templateType})`); + console.log(`Loading template content for: ${normalizedTemplate.NAME} (Type: ${normalizedTemplate.TMPL_TYPE})`); const jsonData = typeof contentJson === 'string' ? JSON.parse(contentJson) @@ -242,7 +294,7 @@ export function TemplateViewDialog({ }); // 템플릿 타입에 따라 다른 데이터 처리 - if (templateType === 'SPR_ITM_LST_SETUP' && selectedRow) { + if (templateType === 'SPREAD_ITEM' && selectedRow) { // 단일 행 처리 (기존 로직) const cell = activeSheet.getCell(cellPos.row, cellPos.col); const value = selectedRow[ATT_ID]; @@ -250,6 +302,10 @@ export function TemplateViewDialog({ cell.value(value); } + if (value === undefined || value === null) { + cell.value(null); + } + // 스타일 적용 cell.locked(!isEditable); const existingStyle = activeSheet.getStyle(cellPos.row, cellPos.col); @@ -264,7 +320,7 @@ export function TemplateViewDialog({ activeSheet.setStyle(cellPos.row, cellPos.col, newStyle); - } else if (templateType === 'SPR_LST_SETUP' && tableData.length > 0) { + } else if (templateType === 'SPREAD_LIST' && tableData.length > 0) { // 복수 행 처리 - 첫 번째 행부터 시작해서 아래로 채움 tableData.forEach((rowData, index) => { const targetRow = cellPos.row + index; @@ -274,16 +330,23 @@ export function TemplateViewDialog({ if (value !== undefined && value !== null) { cell.value(value); } + + if (value === undefined || value === null) { + cell.value(null); + } - // 개별 행의 편집 가능 여부 확인 (shi 필드 기준) - const rowEditable = isEditable && (rowData.shi !== true); - cell.locked(!rowEditable); + // 개별 행의 편집 가능 여부 확인 (행의 shi + columnsJSON의 shi 모두 확인) + // const columnConfig = columnsJSON.find(col => col.key === ATT_ID); + // const cellEditable = columnConfig?.shi !== true; // columnsJSON에서 shi가 true이면 편집 불가 + + const cellEditable = isFieldEditable(ATT_ID, rowData); // 각 행의 데이터를 전달 + cell.locked(!cellEditable); // 스타일 적용 const existingStyle = activeSheet.getStyle(targetRow, cellPos.col); const newStyle = existingStyle ? Object.assign(new GC.Spread.Sheets.Style(), existingStyle) : new GC.Spread.Sheets.Style(); - if (rowEditable) { + if (cellEditable) { newStyle.backColor = "#f0fdf4"; } else { newStyle.backColor = "#f9fafb"; @@ -334,8 +397,16 @@ export function TemplateViewDialog({ }); if (mapping) { - // SPR_LST_SETUP인 경우 해당 행의 데이터에서 shi 확인 - if (templateType === 'SPR_LST_SETUP') { + // columnsJSON에서 해당 필드의 shi 확인 + const columnConfig = columnsJSON.find(col => col.key === mapping.attId); + if (columnConfig?.shi === true) { + toast.warning(`${mapping.attId} field is read-only (Column configuration)`); + info.cancel = true; + return; + } + + // SPREAD_LIST인 경우 해당 행의 데이터에서 shi 확인 + if (templateType === 'SPREAD_LIST') { const dataRowIndex = info.row - parseCellAddress(mapping.cellAddress)!.row; const rowData = tableData[dataRowIndex]; if (rowData && rowData.shi === true) { @@ -363,7 +434,7 @@ export function TemplateViewDialog({ spread.resumePaint(); } } - }, [normalizedTemplate, templateType, selectedRow, tableData, isFieldEditable]); + }, [normalizedTemplate, templateType, selectedRow, tableData, isFieldEditable, columnsJSON]); // 변경사항 저장 함수 const handleSaveChanges = React.useCallback(async () => { @@ -377,7 +448,7 @@ export function TemplateViewDialog({ const activeSheet = currentSpread.getActiveSheet(); - if (templateType === 'SPR_ITM_LST_SETUP' && selectedRow) { + if (templateType === 'SPREAD_ITEM' && selectedRow) { // 단일 행 저장 (기존 로직) const dataToSave = { ...selectedRow }; @@ -407,7 +478,7 @@ export function TemplateViewDialog({ toast.success("Changes saved successfully!"); onUpdateSuccess?.(dataToSave); - } else if (templateType === 'SPR_LST_SETUP' && tableData.length > 0) { + } else if (templateType === 'SPREAD_LIST' && tableData.length > 0) { // 복수 행 저장 const updatedRows: GenericData[] = []; let saveCount = 0; @@ -419,7 +490,12 @@ export function TemplateViewDialog({ // 각 매핑에 대해 해당 행의 값 확인 cellMappings.forEach(mapping => { - if (mapping.isEditable && originalRow.shi !== true) { // shi가 true인 행은 편집 불가 + // columnsJSON에서 해당 필드의 shi 확인 + const columnConfig = columnsJSON.find(col => col.key === mapping.attId); + const isColumnEditable = columnConfig?.shi !== true; + const isRowEditable = originalRow.shi !== true; + + if (mapping.isEditable && isColumnEditable && isRowEditable) { const cellPos = parseCellAddress(mapping.cellAddress); if (cellPos) { const targetRow = cellPos.row + i; @@ -469,13 +545,13 @@ export function TemplateViewDialog({ } finally { setIsPending(false); } - }, [currentSpread, hasChanges, templateType, selectedRow, tableData, formCode, contractItemId, onUpdateSuccess, cellMappings]); + }, [currentSpread, hasChanges, templateType, selectedRow, tableData, formCode, contractItemId, onUpdateSuccess, cellMappings, columnsJSON]); if (!isOpen) return null; // 데이터 유효성 검사 - const isDataValid = templateType === 'SPR_ITM_LST_SETUP' ? !!selectedRow : tableData.length > 0; - const dataCount = templateType === 'SPR_ITM_LST_SETUP' ? 1 : tableData.length; + const isDataValid = templateType === 'SPREAD_ITEM' ? !!selectedRow : tableData.length > 0; + const dataCount = templateType === 'SPREAD_ITEM' ? 1 : tableData.length; return ( <Dialog open={isOpen} onOpenChange={onClose}> @@ -486,15 +562,15 @@ export function TemplateViewDialog({ <DialogHeader className="flex-shrink-0"> <DialogTitle>SEDP Template - {formCode}</DialogTitle> <DialogDescription> - {templateType && ( + {normalizedTemplate && ( <span className="font-medium text-blue-600"> - Template Type: {templateType === 'SPR_LST_SETUP' ? 'List View' : 'Item View'} + Template Type: {normalizedTemplate.TMPL_TYPE === 'SPREAD_LIST' ? 'List View (SPREAD_LIST)' : 'Item View (SPREAD_ITEM)'} </span> )} - {templateType === 'SPR_ITM_LST_SETUP' && selectedRow && ( + {templateType === 'SPREAD_ITEM' && selectedRow && ( <span className="ml-2">• Selected TAG_NO: {selectedRow.TAG_NO || 'N/A'}</span> )} - {templateType === 'SPR_LST_SETUP' && ( + {templateType === 'SPREAD_LIST' && ( <span className="ml-2">• {dataCount} rows</span> )} {hasChanges && ( @@ -525,7 +601,7 @@ export function TemplateViewDialog({ <div className="flex-1 overflow-hidden"> {normalizedTemplate && isClient && isDataValid ? ( <SpreadSheets - key={`${templateType}-${normalizedTemplate.TMPL_ID}`} + key={`${normalizedTemplate.TMPL_TYPE}-${normalizedTemplate.TMPL_ID}`} workbookInitialized={initSpread} hostStyle={hostStyle} /> @@ -539,7 +615,7 @@ export function TemplateViewDialog({ ) : !normalizedTemplate ? ( "No template available" ) : !isDataValid ? ( - `No ${templateType === 'SPR_ITM_LST_SETUP' ? 'selected row' : 'data'} available` + `No ${templateType === 'SPREAD_ITEM' ? 'selected row' : 'data'} available` ) : ( "Template not ready" )} |
