데이터 표시 오류 수정

This commit is contained in:
kjs 2025-12-01 10:19:20 +09:00
parent 1503dd87bb
commit e4b1f7e4d8
3 changed files with 131 additions and 19 deletions

View File

@ -48,6 +48,12 @@ interface SplitPanelContextValue {
// screenId로 위치 찾기
getPositionByScreenId: (screenId: number) => SplitPanelPosition | null;
// 🆕 우측에 추가된 항목 ID 관리 (좌측 테이블에서 필터링용)
addedItemIds: Set<string>;
addItemIds: (ids: string[]) => void;
removeItemIds: (ids: string[]) => void;
clearItemIds: () => void;
}
const SplitPanelContext = createContext<SplitPanelContextValue | null>(null);
@ -74,6 +80,9 @@ export function SplitPanelProvider({
// 강제 리렌더링용 상태
const [, forceUpdate] = useState(0);
// 🆕 우측에 추가된 항목 ID 상태
const [addedItemIds, setAddedItemIds] = useState<Set<string>>(new Set());
/**
*
@ -191,6 +200,38 @@ export function SplitPanelProvider({
[leftScreenId, rightScreenId]
);
/**
* 🆕 ID
*/
const addItemIds = useCallback((ids: string[]) => {
setAddedItemIds((prev) => {
const newSet = new Set(prev);
ids.forEach((id) => newSet.add(id));
logger.debug(`[SplitPanelContext] 항목 ID 추가: ${ids.length}`, { ids });
return newSet;
});
}, []);
/**
* 🆕 ID
*/
const removeItemIds = useCallback((ids: string[]) => {
setAddedItemIds((prev) => {
const newSet = new Set(prev);
ids.forEach((id) => newSet.delete(id));
logger.debug(`[SplitPanelContext] 항목 ID 제거: ${ids.length}`, { ids });
return newSet;
});
}, []);
/**
* 🆕 ID
*/
const clearItemIds = useCallback(() => {
setAddedItemIds(new Set());
logger.debug(`[SplitPanelContext] 항목 ID 초기화`);
}, []);
// 🆕 useMemo로 value 객체 메모이제이션 (무한 루프 방지)
const value = React.useMemo<SplitPanelContextValue>(() => ({
splitPanelId,
@ -202,6 +243,10 @@ export function SplitPanelProvider({
getOtherSideReceivers,
isInSplitPanel: true,
getPositionByScreenId,
addedItemIds,
addItemIds,
removeItemIds,
clearItemIds,
}), [
splitPanelId,
leftScreenId,
@ -211,6 +256,10 @@ export function SplitPanelProvider({
transferToOtherSide,
getOtherSideReceivers,
getPositionByScreenId,
addedItemIds,
addItemIds,
removeItemIds,
clearItemIds,
]);
return (

View File

@ -120,10 +120,15 @@ const RepeaterFieldGroupComponent: React.FC<ComponentRendererProps> = (props) =>
setGroupedData(items);
// 🆕 원본 데이터 ID 목록 저장 (삭제 추적용)
const itemIds = items.map((item: any) => item.id).filter(Boolean);
const itemIds = items.map((item: any) => String(item.id || item.po_item_id || item.item_id)).filter(Boolean);
setOriginalItemIds(itemIds);
console.log("📋 [RepeaterFieldGroup] 원본 데이터 ID 목록 저장:", itemIds);
// 🆕 SplitPanelContext에 기존 항목 ID 등록 (좌측 테이블 필터링용)
if (splitPanelContext?.addItemIds && itemIds.length > 0) {
splitPanelContext.addItemIds(itemIds);
}
// onChange 호출하여 부모에게 알림
if (onChange && items.length > 0) {
const dataWithMeta = items.map((item: any) => ({
@ -285,6 +290,14 @@ const RepeaterFieldGroupComponent: React.FC<ComponentRendererProps> = (props) =>
// 🆕 groupedData 상태도 직접 업데이트 (UI 즉시 반영)
setGroupedData(newItems);
// 🆕 SplitPanelContext에 추가된 항목 ID 등록 (좌측 테이블 필터링용)
if (splitPanelContext?.addItemIds && addedCount > 0) {
const newItemIds = newItems
.map((item: any) => String(item.id || item.po_item_id || item.item_id))
.filter(Boolean);
splitPanelContext.addItemIds(newItemIds);
}
// JSON 문자열로 변환하여 저장
const jsonValue = JSON.stringify(newItems);
console.log("📥 [RepeaterFieldGroup] onChange/onFormDataChange 호출:", {
@ -380,14 +393,44 @@ const RepeaterFieldGroupComponent: React.FC<ComponentRendererProps> = (props) =>
};
}, [screenContext?.splitPanelPosition, handleReceiveData, component.id]);
// 🆕 RepeaterInput에서 항목 변경 시 SplitPanelContext의 addedItemIds 동기화
const handleRepeaterChange = useCallback((newValue: any[]) => {
// 배열을 JSON 문자열로 변환하여 저장
const jsonValue = JSON.stringify(newValue);
onChange?.(jsonValue);
// 🆕 groupedData 상태도 업데이트
setGroupedData(newValue);
// 🆕 SplitPanelContext의 addedItemIds 동기화
if (splitPanelContext?.isInSplitPanel && screenContext?.splitPanelPosition === "right") {
// 현재 항목들의 ID 목록
const currentIds = newValue
.map((item: any) => String(item.id || item.po_item_id || item.item_id))
.filter(Boolean);
// 기존 addedItemIds와 비교하여 삭제된 ID 찾기
const addedIds = splitPanelContext.addedItemIds;
const removedIds = Array.from(addedIds).filter(id => !currentIds.includes(id));
if (removedIds.length > 0) {
console.log("🗑️ [RepeaterFieldGroup] 삭제된 항목 ID 제거:", removedIds);
splitPanelContext.removeItemIds(removedIds);
}
// 새로 추가된 ID가 있으면 등록
const newIds = currentIds.filter((id: string) => !addedIds.has(id));
if (newIds.length > 0) {
console.log(" [RepeaterFieldGroup] 새 항목 ID 추가:", newIds);
splitPanelContext.addItemIds(newIds);
}
}
}, [onChange, splitPanelContext, screenContext?.splitPanelPosition]);
return (
<RepeaterInput
value={parsedValue}
onChange={(newValue) => {
// 배열을 JSON 문자열로 변환하여 저장
const jsonValue = JSON.stringify(newValue);
onChange?.(jsonValue);
}}
onChange={handleRepeaterChange}
config={config}
disabled={disabled}
readonly={readonly}

View File

@ -330,6 +330,25 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
const [data, setData] = useState<Record<string, any>[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// 🆕 분할 패널에서 우측에 이미 추가된 항목 필터링 (좌측 테이블에만 적용)
const filteredData = useMemo(() => {
// 분할 패널 좌측에 있고, 우측에 추가된 항목이 있는 경우에만 필터링
if (splitPanelPosition === "left" && splitPanelContext?.addedItemIds && splitPanelContext.addedItemIds.size > 0) {
const addedIds = splitPanelContext.addedItemIds;
const filtered = data.filter((row) => {
const rowId = String(row.id || row.po_item_id || row.item_id || "");
return !addedIds.has(rowId);
});
console.log("🔍 [TableList] 우측 추가 항목 필터링:", {
originalCount: data.length,
filteredCount: filtered.length,
addedIdsCount: addedIds.size,
});
return filtered;
}
return data;
}, [data, splitPanelPosition, splitPanelContext?.addedItemIds]);
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(0);
const [totalItems, setTotalItems] = useState(0);
@ -438,8 +457,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
componentType: "table-list",
getSelectedData: () => {
// 선택된 행의 실제 데이터 반환
const selectedData = data.filter((row) => {
// 🆕 필터링된 데이터에서 선택된 행만 반환 (우측에 추가된 항목 제외)
const selectedData = filteredData.filter((row) => {
const rowId = String(row.id || row[tableConfig.selectedTable + "_id"] || "");
return selectedRows.has(rowId);
});
@ -447,7 +466,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
},
getAllData: () => {
return data;
// 🆕 필터링된 데이터 반환
return filteredData;
},
clearSelection: () => {
@ -1375,31 +1395,31 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
});
}
const allRowsSelected = data.every((row, index) => newSelectedRows.has(getRowKey(row, index)));
setIsAllSelected(allRowsSelected && data.length > 0);
const allRowsSelected = filteredData.every((row, index) => newSelectedRows.has(getRowKey(row, index)));
setIsAllSelected(allRowsSelected && filteredData.length > 0);
};
const handleSelectAll = (checked: boolean) => {
if (checked) {
const allKeys = data.map((row, index) => getRowKey(row, index));
const allKeys = filteredData.map((row, index) => getRowKey(row, index));
const newSelectedRows = new Set(allKeys);
setSelectedRows(newSelectedRows);
setIsAllSelected(true);
if (onSelectedRowsChange) {
onSelectedRowsChange(Array.from(newSelectedRows), data, sortColumn || undefined, sortDirection);
onSelectedRowsChange(Array.from(newSelectedRows), filteredData, sortColumn || undefined, sortDirection);
}
if (onFormDataChange) {
onFormDataChange({
selectedRows: Array.from(newSelectedRows),
selectedRowsData: data,
selectedRowsData: filteredData,
});
}
// 🆕 modalDataStore에 전체 데이터 저장
if (tableConfig.selectedTable && data.length > 0) {
if (tableConfig.selectedTable && filteredData.length > 0) {
import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
const modalItems = data.map((row, idx) => ({
const modalItems = filteredData.map((row, idx) => ({
id: getRowKey(row, idx),
originalData: row,
additionalData: {},
@ -2003,11 +2023,11 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
// 데이터 그룹화
const groupedData = useMemo((): GroupedData[] => {
if (groupByColumns.length === 0 || data.length === 0) return [];
if (groupByColumns.length === 0 || filteredData.length === 0) return [];
const grouped = new Map<string, any[]>();
data.forEach((item) => {
filteredData.forEach((item) => {
// 그룹 키 생성: "통화:KRW > 단위:EA"
const keyParts = groupByColumns.map((col) => {
// 카테고리/엔티티 타입인 경우 _name 필드 사용
@ -2706,7 +2726,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
})
) : (
// 일반 렌더링 (그룹 없음)
data.map((row, index) => (
filteredData.map((row, index) => (
<tr
key={index}
className={cn(