summaryrefslogtreecommitdiff
path: root/components/form-data/spreadJS-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'components/form-data/spreadJS-dialog.tsx')
-rw-r--r--components/form-data/spreadJS-dialog.tsx166
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"
)}