Merge pull request '배포 다시..' (#371) from lhj into main

Reviewed-on: http://39.117.244.52:3000/kjs/ERP-node/pulls/371
This commit is contained in:
hjlee 2026-01-19 09:50:47 +09:00
commit 514d852fa6
3 changed files with 44 additions and 30 deletions

View File

@ -376,6 +376,12 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
const parsed = JSON.parse(savedState); const parsed = JSON.parse(savedState);
// 버전 체크 - 버전이 다르면 이전 상태 무시
if (parsed.version !== PIVOT_STATE_VERSION) {
localStorage.removeItem(stateStorageKey);
return;
}
// 필드 복원 시 유효성 검사 (중요!) // 필드 복원 시 유효성 검사 (중요!)
if (parsed.fields && Array.isArray(parsed.fields) && parsed.fields.length > 0) { if (parsed.fields && Array.isArray(parsed.fields) && parsed.fields.length > 0) {
// 저장된 필드가 현재 데이터와 호환되는지 확인 // 저장된 필드가 현재 데이터와 호환되는지 확인
@ -501,19 +507,31 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
if (activeFilters.length === 0) return data; if (activeFilters.length === 0) return data;
return data.filter((row) => { const result = data.filter((row) => {
return activeFilters.every((filter) => { return activeFilters.every((filter) => {
const value = row[filter.field]; const rawValue = row[filter.field];
const filterValues = filter.filterValues || []; const filterValues = filter.filterValues || [];
const filterType = filter.filterType || "include"; const filterType = filter.filterType || "include";
// 타입 안전한 비교: 값을 문자열로 변환하여 비교
const value = rawValue === null || rawValue === undefined
? "(빈 값)"
: String(rawValue);
if (filterType === "include") { if (filterType === "include") {
return filterValues.includes(value); return filterValues.some((fv) => String(fv) === value);
} else { } else {
return !filterValues.includes(value); return filterValues.every((fv) => String(fv) !== value);
} }
}); });
}); });
// 모든 데이터가 필터링되면 경고 (디버깅용)
if (result.length === 0 && data.length > 0) {
console.warn("⚠️ [PivotGrid] 필터로 인해 모든 데이터가 제거됨");
}
return result;
}, [data, fields]); }, [data, fields]);
// ==================== 피벗 처리 ==================== // ==================== 피벗 처리 ====================
@ -1654,7 +1672,10 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
<div <div
ref={tableContainerRef} ref={tableContainerRef}
className="flex-1 overflow-auto focus:outline-none" className="flex-1 overflow-auto focus:outline-none"
style={{ maxHeight: enableVirtualScroll ? containerHeight : undefined }} style={{
maxHeight: enableVirtualScroll && containerHeight > 0 ? containerHeight : undefined,
minHeight: 100 // 최소 높이 보장 - 블라인드 효과 방지
}}
tabIndex={0} tabIndex={0}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
> >
@ -1929,12 +1950,15 @@ export const PivotGridComponent: React.FC<PivotGridProps> = ({
}); });
})()} })()}
{/* 가상 스크롤 하단 여백 */} {/* 가상 스크롤 하단 여백 - 음수 방지 */}
{enableVirtualScroll && ( {enableVirtualScroll && (() => {
<tr style={{ height: virtualScroll.totalHeight - virtualScroll.offsetTop - (visibleFlatRows.length * ROW_HEIGHT) }}> const bottomPadding = Math.max(0, virtualScroll.totalHeight - virtualScroll.offsetTop - (visibleFlatRows.length * ROW_HEIGHT));
<td colSpan={rowFields.length + flatColumns.length + (totals?.showRowGrandTotals ? dataFields.length : 0)} /> return bottomPadding > 0 ? (
</tr> <tr style={{ height: bottomPadding }}>
)} <td colSpan={rowFields.length + flatColumns.length + (totals?.showRowGrandTotals ? dataFields.length : 0)} />
</tr>
) : null;
})()}
{/* 열 총계 행 (하단 위치 - 기본값) */} {/* 열 총계 행 (하단 위치 - 기본값) */}
{totals?.showColumnGrandTotals && totals?.rowGrandTotalPosition !== "top" && ( {totals?.showColumnGrandTotals && totals?.rowGrandTotalPosition !== "top" && (

View File

@ -51,14 +51,18 @@ export function useVirtualScroll(options: VirtualScrollOptions): VirtualScrollRe
// 보이는 아이템 수 // 보이는 아이템 수
const visibleCount = Math.ceil(containerHeight / itemHeight); const visibleCount = Math.ceil(containerHeight / itemHeight);
// 시작/끝 인덱스 계산 // 시작/끝 인덱스 계산 (음수 방지)
const { startIndex, endIndex } = useMemo(() => { const { startIndex, endIndex } = useMemo(() => {
// itemCount가 0이면 빈 배열
if (itemCount === 0) {
return { startIndex: 0, endIndex: -1 };
}
const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan); const start = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
const end = Math.min( const end = Math.min(
itemCount - 1, itemCount - 1,
Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan
); );
return { startIndex: start, endIndex: end }; return { startIndex: start, endIndex: Math.max(start, end) }; // end가 start보다 작지 않도록
}, [scrollTop, itemHeight, containerHeight, itemCount, overscan]); }, [scrollTop, itemHeight, containerHeight, itemCount, overscan]);
// 전체 높이 // 전체 높이

View File

@ -710,23 +710,9 @@ export function processPivotData(
.filter((f) => f.area === "data" && f.visible !== false) .filter((f) => f.area === "data" && f.visible !== false)
.sort((a, b) => (a.areaIndex || 0) - (b.areaIndex || 0)); .sort((a, b) => (a.areaIndex || 0) - (b.areaIndex || 0));
const filterFields = fields.filter( // 참고: 필터링은 PivotGridComponent에서 이미 처리됨
(f) => f.area === "filter" && f.visible !== false // 여기서는 추가 필터링 없이 전달받은 데이터 사용
); const filteredData = data;
// 필터 적용
let filteredData = data;
for (const filterField of filterFields) {
if (filterField.filterValues && filterField.filterValues.length > 0) {
filteredData = filteredData.filter((row) => {
const value = getFieldValue(row, filterField);
if (filterField.filterType === "exclude") {
return !filterField.filterValues!.includes(value);
}
return filterField.filterValues!.includes(value);
});
}
}
// 확장 경로 Set 변환 (잘못된 형식 필터링) // 확장 경로 Set 변환 (잘못된 형식 필터링)
const validRowPaths = (expandedRowPaths || []).filter( const validRowPaths = (expandedRowPaths || []).filter(