Compare commits

...

3 Commits

Author SHA1 Message Date
hjlee 0a85146564 Merge pull request 'lhj' (#369) from lhj into main
Reviewed-on: http://39.117.244.52:3000/kjs/ERP-node/pulls/369
2026-01-16 17:40:11 +09:00
leeheejin ad3b853d04 Merge branch 'main' of http://39.117.244.52:3000/kjs/ERP-node into lhj
; Please enter a commit message to explain why this merge is necessary,
; especially if it merges an updated upstream into a topic branch.
;
; Lines starting with ';' will be ignored, and an empty message aborts
; the commit.
2026-01-16 17:39:55 +09:00
leeheejin 2a3cc7ba00 배포다시 되게 고쳐놓음 2026-01-16 17:39:35 +09:00
1 changed files with 48 additions and 53 deletions

View File

@ -384,20 +384,36 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
localStorage.setItem(stateStorageKey, JSON.stringify(stateToSave));
}, [fields, pivotState, sortConfig, columnWidths, stateStorageKey]);
// 상태 복원 (localStorage)
// 상태 복원 (localStorage) - 프로덕션 안전성 강화
useEffect(() => {
if (typeof window === "undefined") return;
const savedState = localStorage.getItem(stateStorageKey);
if (savedState) {
try {
const savedState = localStorage.getItem(stateStorageKey);
if (!savedState) return;
const parsed = JSON.parse(savedState);
if (parsed.fields) setFields(parsed.fields);
// 필드 복원 시 유효성 검사 (중요!)
if (parsed.fields && Array.isArray(parsed.fields) && parsed.fields.length > 0) {
// 저장된 필드가 현재 데이터와 호환되는지 확인
const validFields = parsed.fields.filter((f: PivotFieldConfig) =>
f && typeof f.field === "string" && typeof f.area === "string"
);
if (validFields.length > 0) {
setFields(validFields);
}
}
// 나머지 상태 복원
if (parsed.pivotState) setPivotState(parsed.pivotState);
if (parsed.sortConfig) setSortConfig(parsed.sortConfig);
if (parsed.columnWidths) setColumnWidths(parsed.columnWidths);
} catch (e) {
console.warn("피벗 상태 복원 실패:", e);
}
console.warn("피벗 상태 복원 실패, localStorage 초기화:", e);
// 손상된 상태는 제거
localStorage.removeItem(stateStorageKey);
}
}, [stateStorageKey]);
@ -512,15 +528,15 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
return null;
}
const visibleFields = fields.filter((f) => f.visible !== false);
// FieldChooser에서 이미 필드를 완전히 제거하므로 visible 필터링 불필요
// 행, 열, 데이터 영역에 필드가 하나도 없으면 null 반환 (필터는 제외)
if (visibleFields.filter((f) => ["row", "column", "data"].includes(f.area)).length === 0) {
if (fields.filter((f) => ["row", "column", "data"].includes(f.area)).length === 0) {
return null;
}
const result = processPivotData(
filteredData,
visibleFields,
fields,
pivotState.expandedRowPaths,
pivotState.expandedColumnPaths
);
@ -536,32 +552,18 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
});
return result;
}, [
filteredData,
fields,
JSON.stringify(pivotState.expandedRowPaths),
JSON.stringify(pivotState.expandedColumnPaths)
]);
}, [filteredData, fields, pivotState.expandedRowPaths, pivotState.expandedColumnPaths]);
// 🆕 초기 로드 시 첫 레벨 자동 확장
// 초기 로드 시 첫 레벨 자동 확장
useEffect(() => {
if (pivotResult && pivotResult.flatRows.length > 0 && !isInitialExpanded) {
console.log("🔶 피벗 결과 생성됨:", {
flatRowsCount: pivotResult.flatRows.length,
expandedRowPaths: pivotState.expandedRowPaths.length,
isInitialExpanded,
});
// 첫 레벨 행들의 경로 수집 (level 0인 행들)
const firstLevelRows = pivotResult.flatRows.filter(row => row.level === 0 && row.hasChildren);
console.log("🔶 첫 레벨 행 (level 0, hasChildren):", firstLevelRows.map(r => ({ path: r.path, caption: r.caption })));
const firstLevelRows = pivotResult.flatRows.filter((row) => row.level === 0 && row.hasChildren);
// 첫 레벨 행이 있으면 자동 확장
if (firstLevelRows.length > 0) {
const firstLevelPaths = firstLevelRows.map(row => row.path);
console.log("🔶 초기 자동 확장 실행 (한 번만):", firstLevelPaths);
setPivotState(prev => ({
const firstLevelPaths = firstLevelRows.map((row) => row.path);
setPivotState((prev) => ({
...prev,
expandedRowPaths: firstLevelPaths,
}));
@ -725,19 +727,16 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
// 필드 변경
const handleFieldsChange = useCallback(
(newFields: PivotFieldConfig[]) => {
// 🆕 visible: false 필드 제거 (FieldChooser에서 "사용 안함"으로 설정한 필드)
const visibleFields = newFields.filter(f => f.visible !== false);
// FieldChooser에서 이미 필드를 완전히 제거하므로 추가 필터링 불필요
console.log("🔷 [handleFieldsChange] 필드 변경:", {
totalFields: newFields.length,
visibleFields: visibleFields.length,
removedFields: newFields.length - visibleFields.length,
filterFields: visibleFields.filter(f => f.area === "filter").length,
filterFieldNames: visibleFields.filter(f => f.area === "filter").map(f => f.field),
filterFields: newFields.filter(f => f.area === "filter").length,
filterFieldNames: newFields.filter(f => f.area === "filter").map(f => f.field),
rowFields: newFields.filter(f => f.area === "row").length,
columnFields: newFields.filter(f => f.area === "column").length,
dataFields: newFields.filter(f => f.area === "data").length,
});
console.log("🔷 [handleFieldsChange] setFields 호출 전");
setFields(visibleFields);
console.log("🔷 [handleFieldsChange] setFields 호출 후");
setFields(newFields);
},
[]
);
@ -945,6 +944,8 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
// 인쇄 기능 (PDF 내보내기보다 먼저 정의해야 함)
const handlePrint = useCallback(() => {
if (typeof window === "undefined") return;
const printContent = tableRef.current;
if (!printContent) return;
@ -1047,8 +1048,10 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
// 상태 초기화 (확장/축소, 정렬, 필터만 초기화, 필드 설정은 유지)
const handleResetState = useCallback(() => {
// 로컬 스토리지에서 상태 제거
// 로컬 스토리지에서 상태 제거 (SSR 보호)
if (typeof window !== "undefined") {
localStorage.removeItem(stateStorageKey);
}
// 확장/축소, 정렬, 필터 상태만 초기화
setPivotState({
@ -1061,9 +1064,6 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
setColumnWidths({});
setSelectedCell(null);
setSelectionRange(null);
// 🆕 필드 설정은 유지 (initialFields로 되돌리지 않음)
console.log("🔷 피벗 상태가 초기화되었습니다 (필드 설정은 유지)");
}, [stateStorageKey]);
// 필드 숨기기/표시 상태
@ -1081,11 +1081,6 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
});
}, []);
// 숨겨진 필드 제외한 활성 필드들
const visibleFields = useMemo(() => {
return fields.filter((f) => !hiddenFields.has(f.field));
}, [fields, hiddenFields]);
// 숨겨진 필드 목록
const hiddenFieldsList = useMemo(() => {
return fields.filter((f) => hiddenFields.has(f.field));