우측화면 데이터 필터링 수정
This commit is contained in:
parent
4e74c7b5ba
commit
d8329d31e4
|
|
@ -76,7 +76,6 @@ export const EmbeddedScreen = forwardRef<EmbeddedScreenHandle, EmbeddedScreenPro
|
|||
|
||||
// 필드 값 변경 핸들러
|
||||
const handleFieldChange = useCallback((fieldName: string, value: any) => {
|
||||
console.log("📝 [EmbeddedScreen] 필드 값 변경:", { fieldName, value });
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[fieldName]: value,
|
||||
|
|
@ -88,10 +87,9 @@ export const EmbeddedScreen = forwardRef<EmbeddedScreenHandle, EmbeddedScreenPro
|
|||
loadScreenData();
|
||||
}, [embedding.childScreenId]);
|
||||
|
||||
// 🆕 initialFormData 변경 시 formData 업데이트 (수정 모드)
|
||||
// initialFormData 변경 시 formData 업데이트 (수정 모드)
|
||||
useEffect(() => {
|
||||
if (initialFormData && Object.keys(initialFormData).length > 0) {
|
||||
console.log("📝 [EmbeddedScreen] 초기 폼 데이터 설정:", initialFormData);
|
||||
setFormData(initialFormData);
|
||||
}
|
||||
}, [initialFormData]);
|
||||
|
|
@ -135,12 +133,6 @@ export const EmbeddedScreen = forwardRef<EmbeddedScreenHandle, EmbeddedScreenPro
|
|||
});
|
||||
}
|
||||
|
||||
console.log("🔗 [EmbeddedScreen] 우측 폼 데이터 교체:", {
|
||||
allColumnNames,
|
||||
selectedLeftDataKeys: selectedLeftData ? Object.keys(selectedLeftData) : [],
|
||||
initializedFormDataKeys: Object.keys(initializedFormData),
|
||||
});
|
||||
|
||||
setFormData(initializedFormData);
|
||||
setFormDataVersion((v) => v + 1); // 🆕 버전 증가로 컴포넌트 강제 리렌더링
|
||||
}, [position, splitPanelContext, selectedLeftData, layout]);
|
||||
|
|
@ -160,13 +152,6 @@ export const EmbeddedScreen = forwardRef<EmbeddedScreenHandle, EmbeddedScreenPro
|
|||
|
||||
// 화면 정보 로드 (screenApi.getScreen은 직접 ScreenDefinition 객체를 반환)
|
||||
const screenData = await screenApi.getScreen(embedding.childScreenId);
|
||||
console.log("📋 [EmbeddedScreen] 화면 정보 API 응답:", {
|
||||
screenId: embedding.childScreenId,
|
||||
hasData: !!screenData,
|
||||
tableName: screenData?.tableName,
|
||||
screenName: screenData?.name || screenData?.screenName,
|
||||
position,
|
||||
});
|
||||
if (screenData) {
|
||||
setScreenInfo(screenData);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -27,29 +27,12 @@ export function ScreenSplitPanel({ screenId, config, initialFormData }: ScreenSp
|
|||
// config에서 splitRatio 추출 (기본값 50)
|
||||
const configSplitRatio = config?.splitRatio ?? 50;
|
||||
|
||||
console.log("🎯 [ScreenSplitPanel] 렌더링됨!", {
|
||||
screenId,
|
||||
config,
|
||||
leftScreenId: config?.leftScreenId,
|
||||
rightScreenId: config?.rightScreenId,
|
||||
configSplitRatio,
|
||||
parentDataMapping: config?.parentDataMapping,
|
||||
configKeys: config ? Object.keys(config) : [],
|
||||
});
|
||||
|
||||
// 🆕 initialFormData 별도 로그 (명확한 확인)
|
||||
console.log("📝 [ScreenSplitPanel] initialFormData 확인:", {
|
||||
hasInitialFormData: !!initialFormData,
|
||||
initialFormDataKeys: initialFormData ? Object.keys(initialFormData) : [],
|
||||
initialFormData: initialFormData,
|
||||
});
|
||||
|
||||
// 드래그로 조절 가능한 splitRatio 상태
|
||||
const [splitRatio, setSplitRatio] = useState(configSplitRatio);
|
||||
|
||||
// config.splitRatio가 변경되면 동기화 (설정 패널에서 변경 시)
|
||||
React.useEffect(() => {
|
||||
console.log("📐 [ScreenSplitPanel] splitRatio 동기화:", { configSplitRatio, currentSplitRatio: splitRatio });
|
||||
setSplitRatio(configSplitRatio);
|
||||
}, [configSplitRatio]);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,12 +25,6 @@ export function TabsWidget({ component, className, style, menuObjid }: TabsWidge
|
|||
persistSelection = false,
|
||||
} = component;
|
||||
|
||||
console.log("🎨 TabsWidget 렌더링:", {
|
||||
componentId: component.id,
|
||||
tabs,
|
||||
tabsLength: tabs.length,
|
||||
component,
|
||||
});
|
||||
|
||||
const storageKey = `tabs-${component.id}-selected`;
|
||||
|
||||
|
|
@ -67,15 +61,7 @@ export function TabsWidget({ component, className, style, menuObjid }: TabsWidge
|
|||
// 초기 로드 시 선택된 탭의 화면 불러오기
|
||||
useEffect(() => {
|
||||
const currentTab = visibleTabs.find((t) => t.id === selectedTab);
|
||||
console.log("🔄 초기 탭 로드:", {
|
||||
selectedTab,
|
||||
currentTab,
|
||||
hasScreenId: !!currentTab?.screenId,
|
||||
screenId: currentTab?.screenId,
|
||||
});
|
||||
|
||||
if (currentTab && currentTab.screenId && !screenLayouts[currentTab.screenId]) {
|
||||
console.log("📥 초기 화면 로딩 시작:", currentTab.screenId);
|
||||
loadScreenLayout(currentTab.screenId);
|
||||
}
|
||||
}, [selectedTab, visibleTabs]);
|
||||
|
|
@ -83,26 +69,20 @@ export function TabsWidget({ component, className, style, menuObjid }: TabsWidge
|
|||
// 화면 레이아웃 로드
|
||||
const loadScreenLayout = async (screenId: number) => {
|
||||
if (screenLayouts[screenId]) {
|
||||
console.log("✅ 이미 로드된 화면:", screenId);
|
||||
return; // 이미 로드됨
|
||||
}
|
||||
|
||||
console.log("📥 화면 레이아웃 로딩 시작:", screenId);
|
||||
setLoadingScreens((prev) => ({ ...prev, [screenId]: true }));
|
||||
|
||||
try {
|
||||
const { apiClient } = await import("@/lib/api/client");
|
||||
const response = await apiClient.get(`/screen-management/screens/${screenId}/layout`);
|
||||
console.log("📦 API 응답:", { screenId, success: response.data.success, hasData: !!response.data.data });
|
||||
|
||||
if (response.data.success && response.data.data) {
|
||||
console.log("✅ 화면 레이아웃 로드 완료:", screenId);
|
||||
setScreenLayouts((prev) => ({ ...prev, [screenId]: response.data.data }));
|
||||
} else {
|
||||
console.error("❌ 화면 레이아웃 로드 실패 - success false");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ 화면 레이아웃 로드 실패 ${screenId}:`, error);
|
||||
console.error(`화면 레이아웃 로드 실패 ${screenId}:`, error);
|
||||
} finally {
|
||||
setLoadingScreens((prev) => ({ ...prev, [screenId]: false }));
|
||||
}
|
||||
|
|
@ -110,10 +90,9 @@ export function TabsWidget({ component, className, style, menuObjid }: TabsWidge
|
|||
|
||||
// 탭 변경 핸들러
|
||||
const handleTabChange = (tabId: string) => {
|
||||
console.log("🔄 탭 변경:", tabId);
|
||||
setSelectedTab(tabId);
|
||||
|
||||
// 🆕 마운트된 탭 목록에 추가 (한 번 마운트되면 유지)
|
||||
// 마운트된 탭 목록에 추가 (한 번 마운트되면 유지)
|
||||
setMountedTabs(prev => {
|
||||
if (prev.has(tabId)) return prev;
|
||||
const newSet = new Set(prev);
|
||||
|
|
@ -123,10 +102,7 @@ export function TabsWidget({ component, className, style, menuObjid }: TabsWidge
|
|||
|
||||
// 해당 탭의 화면 로드
|
||||
const tab = visibleTabs.find((t) => t.id === tabId);
|
||||
console.log("🔍 선택된 탭 정보:", { tab, hasScreenId: !!tab?.screenId, screenId: tab?.screenId });
|
||||
|
||||
if (tab && tab.screenId && !screenLayouts[tab.screenId]) {
|
||||
console.log("📥 탭 변경 시 화면 로딩:", tab.screenId);
|
||||
loadScreenLayout(tab.screenId);
|
||||
}
|
||||
};
|
||||
|
|
@ -157,7 +133,6 @@ export function TabsWidget({ component, className, style, menuObjid }: TabsWidge
|
|||
};
|
||||
|
||||
if (visibleTabs.length === 0) {
|
||||
console.log("⚠️ 보이는 탭이 없음");
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center rounded border-2 border-dashed border-gray-300 bg-gray-50">
|
||||
<p className="text-muted-foreground text-sm">탭이 없습니다</p>
|
||||
|
|
@ -165,13 +140,6 @@ export function TabsWidget({ component, className, style, menuObjid }: TabsWidge
|
|||
);
|
||||
}
|
||||
|
||||
console.log("🎨 TabsWidget 최종 렌더링:", {
|
||||
visibleTabsCount: visibleTabs.length,
|
||||
selectedTab,
|
||||
screenLayoutsKeys: Object.keys(screenLayouts),
|
||||
loadingScreensKeys: Object.keys(loadingScreens),
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col pt-4" style={style}>
|
||||
<Tabs
|
||||
|
|
@ -233,14 +201,6 @@ export function TabsWidget({ component, className, style, menuObjid }: TabsWidge
|
|||
const layoutData = screenLayouts[tab.screenId];
|
||||
const { components = [], screenResolution } = layoutData;
|
||||
|
||||
// 비활성 탭은 로그 생략
|
||||
if (isActive) {
|
||||
console.log("🎯 렌더링할 화면 데이터:", {
|
||||
screenId: tab.screenId,
|
||||
componentsCount: components.length,
|
||||
screenResolution,
|
||||
});
|
||||
}
|
||||
|
||||
const designWidth = screenResolution?.width || 1920;
|
||||
const designHeight = screenResolution?.height || 1080;
|
||||
|
|
|
|||
|
|
@ -282,10 +282,6 @@ export function SplitPanelProvider({
|
|||
* 🆕 좌측 선택 데이터 설정
|
||||
*/
|
||||
const handleSetSelectedLeftData = useCallback((data: Record<string, any> | null) => {
|
||||
logger.info(`[SplitPanelContext] 좌측 선택 데이터 설정:`, {
|
||||
hasData: !!data,
|
||||
dataKeys: data ? Object.keys(data) : [],
|
||||
});
|
||||
setSelectedLeftData(data);
|
||||
}, []);
|
||||
|
||||
|
|
@ -323,11 +319,6 @@ export function SplitPanelProvider({
|
|||
}
|
||||
}
|
||||
|
||||
logger.info(`[SplitPanelContext] 매핑된 부모 데이터 (자동+명시적):`, {
|
||||
autoMappedKeys: Object.keys(selectedLeftData),
|
||||
explicitMappings: parentDataMapping.length,
|
||||
finalKeys: Object.keys(mappedData),
|
||||
});
|
||||
return mappedData;
|
||||
}, [selectedLeftData, parentDataMapping]);
|
||||
|
||||
|
|
@ -350,7 +341,6 @@ export function SplitPanelProvider({
|
|||
}
|
||||
}
|
||||
|
||||
logger.info(`[SplitPanelContext] 연결 필터 값:`, filterValues);
|
||||
return filterValues;
|
||||
}, [selectedLeftData, linkedFilters]);
|
||||
|
||||
|
|
|
|||
|
|
@ -83,14 +83,8 @@ export const TableOptionsProvider: React.FC<{ children: ReactNode }> = ({
|
|||
const updatedTable = { ...table, dataCount: count };
|
||||
const newMap = new Map(prev);
|
||||
newMap.set(tableId, updatedTable);
|
||||
console.log("🔄 [TableOptionsContext] 데이터 건수 업데이트:", {
|
||||
tableId,
|
||||
count,
|
||||
updated: true,
|
||||
});
|
||||
return newMap;
|
||||
}
|
||||
console.warn("⚠️ [TableOptionsContext] 테이블을 찾을 수 없음:", tableId);
|
||||
return prev;
|
||||
});
|
||||
}, []);
|
||||
|
|
|
|||
|
|
@ -226,43 +226,6 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
|||
// 1. 새 컴포넌트 시스템에서 먼저 조회
|
||||
const newComponent = ComponentRegistry.getComponent(componentType);
|
||||
|
||||
// 🔍 디버깅: screen-split-panel 조회 결과 확인
|
||||
if (componentType === "screen-split-panel") {
|
||||
console.log("🔍 [DynamicComponentRenderer] screen-split-panel 조회:", {
|
||||
componentType,
|
||||
found: !!newComponent,
|
||||
componentId: component.id,
|
||||
componentConfig: component.componentConfig,
|
||||
hasFormData: !!props.formData,
|
||||
formDataKeys: props.formData ? Object.keys(props.formData) : [],
|
||||
registeredComponents: ComponentRegistry.getAllComponents().map(c => c.id),
|
||||
});
|
||||
}
|
||||
|
||||
// 🔍 디버깅: select-basic 조회 결과 확인
|
||||
if (componentType === "select-basic") {
|
||||
console.log("🔍 [DynamicComponentRenderer] select-basic 조회:", {
|
||||
componentType,
|
||||
found: !!newComponent,
|
||||
componentId: component.id,
|
||||
componentConfig: component.componentConfig,
|
||||
});
|
||||
}
|
||||
|
||||
// 🔍 디버깅: text-input 컴포넌트 조회 결과 확인
|
||||
if (componentType === "text-input" || component.id?.includes("text") || (component as any).webType === "text") {
|
||||
console.log("🔍 [DynamicComponentRenderer] text-input 조회:", {
|
||||
componentType,
|
||||
componentId: component.id,
|
||||
componentLabel: component.label,
|
||||
componentConfig: component.componentConfig,
|
||||
webTypeConfig: (component as any).webTypeConfig,
|
||||
autoGeneration: (component as any).autoGeneration,
|
||||
found: !!newComponent,
|
||||
registeredComponents: ComponentRegistry.getAllComponents().map(c => c.id),
|
||||
});
|
||||
}
|
||||
|
||||
if (newComponent) {
|
||||
// 새 컴포넌트 시스템으로 렌더링
|
||||
try {
|
||||
|
|
@ -324,19 +287,6 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
|||
currentValue = formData?.[fieldName] || "";
|
||||
}
|
||||
|
||||
// 🆕 디버깅: text-input 값 추출 확인
|
||||
if (componentType === "text-input" && formData && Object.keys(formData).length > 0) {
|
||||
console.log("🔍 [DynamicComponentRenderer] text-input 값 추출:", {
|
||||
componentId: component.id,
|
||||
componentLabel: component.label,
|
||||
columnName: (component as any).columnName,
|
||||
fieldName,
|
||||
currentValue,
|
||||
hasFormData: !!formData,
|
||||
formDataKeys: Object.keys(formData).slice(0, 10), // 처음 10개만
|
||||
});
|
||||
}
|
||||
|
||||
// onChange 핸들러 - 컴포넌트 타입에 따라 다르게 처리
|
||||
const handleChange = (value: any) => {
|
||||
// autocomplete-search-input, entity-search-input은 자체적으로 onFormDataChange를 호출하므로 중복 저장 방지
|
||||
|
|
|
|||
|
|
@ -388,16 +388,6 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
|||
};
|
||||
}
|
||||
|
||||
// 🔍 디버깅: processedConfig.action 확인
|
||||
console.log("[ButtonPrimaryComponent] processedConfig.action 생성 완료", {
|
||||
actionType: processedConfig.action?.type,
|
||||
enableDataflowControl: processedConfig.action?.enableDataflowControl,
|
||||
dataflowTiming: processedConfig.action?.dataflowTiming,
|
||||
dataflowConfig: processedConfig.action?.dataflowConfig,
|
||||
webTypeConfigRaw: component.webTypeConfig,
|
||||
componentText: component.text,
|
||||
});
|
||||
|
||||
// 스타일 계산
|
||||
// height: 100%로 부모(RealtimePreviewDynamic의 내부 div)의 높이를 따라감
|
||||
// width는 항상 100%로 고정 (부모 컨테이너가 gridColumns로 크기 제어)
|
||||
|
|
@ -839,10 +829,6 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
|||
groupedData.length > 0
|
||||
) {
|
||||
effectiveSelectedRowsData = groupedData;
|
||||
console.log("🔗 [ButtonPrimaryComponent] groupedData에서 부모창 데이터 가져옴:", {
|
||||
count: groupedData.length,
|
||||
data: groupedData,
|
||||
});
|
||||
}
|
||||
|
||||
// modalDataStore에서 선택된 데이터 가져오기 (분할 패널 등에서 선택한 데이터)
|
||||
|
|
@ -858,12 +844,6 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
|||
// originalData가 있으면 그것을 사용, 없으면 item 자체 사용 (하위 호환성)
|
||||
return item.originalData || item;
|
||||
});
|
||||
console.log("🔗 [ButtonPrimaryComponent] modalDataStore에서 선택된 데이터 가져옴:", {
|
||||
tableName: effectiveTableName,
|
||||
count: modalData.length,
|
||||
rawData: modalData,
|
||||
extractedData: effectiveSelectedRowsData,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("modalDataStore 접근 실패:", error);
|
||||
|
|
@ -928,17 +908,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
|||
}
|
||||
}
|
||||
|
||||
// 🆕 디버깅: tableName 확인
|
||||
console.log("🔍 [ButtonPrimaryComponent] context 생성:", {
|
||||
propsTableName: tableName,
|
||||
contextTableName: screenContext?.tableName,
|
||||
effectiveTableName,
|
||||
propsScreenId: screenId,
|
||||
contextScreenId: screenContext?.screenId,
|
||||
effectiveScreenId,
|
||||
});
|
||||
|
||||
// 🆕 분할 패널 부모 데이터 가져오기 (우측 화면에서 저장 시 좌측 선택 데이터 포함)
|
||||
// 분할 패널 부모 데이터 가져오기 (우측 화면에서 저장 시 좌측 선택 데이터 포함)
|
||||
// 조건 완화: splitPanelContext가 있고 selectedLeftData가 있으면 가져옴
|
||||
// (탭 안에서도 분할 패널 컨텍스트에 접근 가능하도록)
|
||||
let splitPanelParentData: Record<string, any> | undefined;
|
||||
|
|
@ -947,13 +917,6 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
|||
// 좌측 화면이 아닌 경우에만 부모 데이터 포함 (좌측에서 저장 시 자신의 데이터를 부모로 포함하면 안됨)
|
||||
if (splitPanelPosition !== "left") {
|
||||
splitPanelParentData = splitPanelContext.getMappedParentData();
|
||||
if (Object.keys(splitPanelParentData).length > 0) {
|
||||
console.log("🔗 [ButtonPrimaryComponent] 분할 패널 부모 데이터 포함:", {
|
||||
splitPanelParentData,
|
||||
splitPanelPosition,
|
||||
isInTab: !splitPanelPosition, // splitPanelPosition이 없으면 탭 안
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -966,22 +929,11 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
|||
// (일반 폼 필드는 props.formData, RepeaterFieldGroup은 screenContext.formData에 있음)
|
||||
let effectiveFormData = { ...propsFormData, ...screenContextFormData };
|
||||
|
||||
// 🆕 분할 패널 우측이고 formData가 비어있으면 splitPanelParentData 사용
|
||||
// 분할 패널 우측이고 formData가 비어있으면 splitPanelParentData 사용
|
||||
if (splitPanelPosition === "right" && Object.keys(effectiveFormData).length === 0 && splitPanelParentData) {
|
||||
effectiveFormData = { ...splitPanelParentData };
|
||||
console.log("🔍 [ButtonPrimary] 분할 패널 우측 - splitPanelParentData 사용:", Object.keys(effectiveFormData));
|
||||
}
|
||||
|
||||
console.log("🔍 [ButtonPrimary] formData 선택:", {
|
||||
hasScreenContextFormData: Object.keys(screenContextFormData).length > 0,
|
||||
screenContextKeys: Object.keys(screenContextFormData),
|
||||
hasPropsFormData: Object.keys(propsFormData).length > 0,
|
||||
propsFormDataKeys: Object.keys(propsFormData),
|
||||
hasSplitPanelParentData: !!splitPanelParentData && Object.keys(splitPanelParentData).length > 0,
|
||||
splitPanelPosition,
|
||||
effectiveFormDataKeys: Object.keys(effectiveFormData),
|
||||
});
|
||||
|
||||
const context: ButtonActionContext = {
|
||||
formData: effectiveFormData,
|
||||
originalData: originalData, // 🔧 빈 객체 대신 undefined 유지 (UPDATE 판단에 사용)
|
||||
|
|
|
|||
|
|
@ -61,20 +61,17 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
// 테이블 데이터 상태 관리
|
||||
const [loadedTableData, setLoadedTableData] = useState<any[]>([]);
|
||||
const [loadedTableColumns, setLoadedTableColumns] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [loading, setLoading] = useState(true); // 초기 로딩 상태를 true로 설정
|
||||
const [initialLoadDone, setInitialLoadDone] = useState(false); // 초기 로드 완료 여부
|
||||
const [hasEverSelectedLeftData, setHasEverSelectedLeftData] = useState(false); // 좌측 데이터 선택 이력
|
||||
|
||||
// 필터 상태 (검색 필터 위젯에서 전달받은 필터)
|
||||
const [filters, setFiltersInternal] = useState<TableFilter[]>([]);
|
||||
|
||||
// 필터 상태 변경 래퍼 (로깅용)
|
||||
// 필터 상태 변경 래퍼
|
||||
const setFilters = useCallback((newFilters: TableFilter[]) => {
|
||||
console.log("🎴 [CardDisplay] setFilters 호출됨:", {
|
||||
componentId: component.id,
|
||||
filtersCount: newFilters.length,
|
||||
filters: newFilters,
|
||||
});
|
||||
setFiltersInternal(newFilters);
|
||||
}, [component.id]);
|
||||
}, []);
|
||||
|
||||
// 카테고리 매핑 상태 (카테고리 코드 -> 라벨/색상)
|
||||
const [columnMeta, setColumnMeta] = useState<
|
||||
|
|
@ -125,10 +122,6 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
// 삭제할 데이터를 배열로 감싸기 (API가 배열을 기대함)
|
||||
const deleteData = [data];
|
||||
|
||||
console.log("🗑️ [CardDisplay] 삭제 요청:", {
|
||||
tableName: tableNameToUse,
|
||||
data: deleteData,
|
||||
});
|
||||
|
||||
// API 호출로 데이터 삭제 (POST 방식으로 변경 - DELETE는 body 전달이 불안정)
|
||||
// 백엔드 API는 DELETE /api/table-management/tables/:tableName/delete 이지만
|
||||
|
|
@ -143,7 +136,6 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
});
|
||||
|
||||
if (response.data.success) {
|
||||
console.log("삭제 완료:", response.data.data?.deletedCount || 1, "건");
|
||||
alert("삭제되었습니다.");
|
||||
|
||||
// 로컬 상태에서 삭제된 항목 제거
|
||||
|
|
@ -157,11 +149,9 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
setSelectedRows(newSelectedRows);
|
||||
}
|
||||
} else {
|
||||
console.error("삭제 실패:", response.data.error);
|
||||
alert(`삭제 실패: ${response.data.message || response.data.error || "알 수 없는 오류"}`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("삭제 중 오류 발생:", error);
|
||||
const errorMessage = error.response?.data?.message || error.message || "알 수 없는 오류";
|
||||
alert(`삭제 중 오류가 발생했습니다: ${errorMessage}`);
|
||||
}
|
||||
|
|
@ -194,8 +184,7 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
// loadTableData();
|
||||
|
||||
} catch (error) {
|
||||
console.error("❌ 편집 저장 실패:", error);
|
||||
alert("❌ 저장에 실패했습니다.");
|
||||
alert("저장에 실패했습니다.");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -204,6 +193,25 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
const loadTableData = async () => {
|
||||
// 디자인 모드에서는 테이블 데이터를 로드하지 않음
|
||||
if (isDesignMode) {
|
||||
setLoading(false);
|
||||
setInitialLoadDone(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// 우측 패널인 경우, 좌측 데이터가 선택되지 않으면 데이터 로드하지 않음 (깜빡임 방지)
|
||||
// splitPanelPosition이 "right"이면 분할 패널 내부이므로 연결 필터가 있을 가능성이 높음
|
||||
const isRightPanelEarly = splitPanelPosition === "right";
|
||||
const hasSelectedLeftDataEarly = splitPanelContext?.selectedLeftData &&
|
||||
Object.keys(splitPanelContext.selectedLeftData).length > 0;
|
||||
|
||||
if (isRightPanelEarly && !hasSelectedLeftDataEarly) {
|
||||
// 우측 패널이고 좌측 데이터가 선택되지 않은 경우 - 기존 데이터 유지 (깜빡임 방지)
|
||||
// 초기 로드가 아닌 경우에는 데이터를 지우지 않음
|
||||
if (!initialLoadDone) {
|
||||
setLoadedTableData([]);
|
||||
}
|
||||
setLoading(false);
|
||||
setInitialLoadDone(true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -211,6 +219,8 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
const tableNameToUse = tableName || component.componentConfig?.tableName || 'user_info'; // 기본 테이블명 설정
|
||||
|
||||
if (!tableNameToUse) {
|
||||
setLoading(false);
|
||||
setInitialLoadDone(true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -251,19 +261,23 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
}
|
||||
linkedFilterValues = tableSpecificFilters;
|
||||
|
||||
console.log("🎴 [CardDisplay] 연결 필터 확인:", {
|
||||
tableNameToUse,
|
||||
hasLinkedFiltersConfigured,
|
||||
hasSelectedLeftData,
|
||||
linkedFilterValues,
|
||||
});
|
||||
}
|
||||
|
||||
// 연결 필터가 설정되어 있지만 좌측에서 데이터가 선택되지 않은 경우 빈 데이터 표시
|
||||
if (splitPanelContext && hasLinkedFiltersConfigured && !hasSelectedLeftData) {
|
||||
console.log("🎴 [CardDisplay] 연결 필터 활성화됨 - 좌측 선택 대기");
|
||||
// 우측 패널이고 연결 필터가 설정되어 있지만 좌측에서 데이터가 선택되지 않은 경우 빈 데이터 표시
|
||||
// 또는 우측 패널이고 linkedFilters 설정이 있으면 좌측 선택 필수
|
||||
// splitPanelPosition은 screenContext에서 가져오거나, splitPanelContext에서 screenId로 확인
|
||||
const isRightPanelFromContext = splitPanelPosition === "right";
|
||||
const isRightPanelFromSplitContext = screenId && splitPanelContext?.getPositionByScreenId
|
||||
? splitPanelContext.getPositionByScreenId(screenId as number) === "right"
|
||||
: false;
|
||||
const isRightPanel = isRightPanelFromContext || isRightPanelFromSplitContext;
|
||||
const hasLinkedFiltersInConfig = splitPanelContext?.linkedFilters && splitPanelContext.linkedFilters.length > 0;
|
||||
|
||||
|
||||
if (isRightPanel && (hasLinkedFiltersConfigured || hasLinkedFiltersInConfig) && !hasSelectedLeftData) {
|
||||
setLoadedTableData([]);
|
||||
setLoading(false);
|
||||
setInitialLoadDone(true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -277,7 +291,6 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
search: Object.keys(linkedFilterValues).length > 0 ? linkedFilterValues : undefined,
|
||||
};
|
||||
|
||||
console.log("🎴 [CardDisplay] API 호출 파라미터:", apiParams);
|
||||
|
||||
// 테이블 데이터, 컬럼 정보, 입력 타입 정보를 병렬로 로드
|
||||
const [dataResponse, columnsResponse, inputTypesResponse] = await Promise.all([
|
||||
|
|
@ -298,7 +311,6 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
codeCategory: item.codeCategory || item.code_category,
|
||||
};
|
||||
});
|
||||
console.log("📋 [CardDisplay] 컬럼 메타 정보:", meta);
|
||||
setColumnMeta(meta);
|
||||
|
||||
// 카테고리 타입 컬럼 찾기 및 매핑 로드
|
||||
|
|
@ -306,17 +318,14 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
.filter(([_, m]) => m.inputType === "category")
|
||||
.map(([columnName]) => columnName);
|
||||
|
||||
console.log("📋 [CardDisplay] 카테고리 컬럼:", categoryColumns);
|
||||
|
||||
if (categoryColumns.length > 0) {
|
||||
const mappings: Record<string, Record<string, { label: string; color?: string }>> = {};
|
||||
|
||||
for (const columnName of categoryColumns) {
|
||||
try {
|
||||
console.log(`📋 [CardDisplay] 카테고리 매핑 로드 시작: ${tableNameToUse}/${columnName}`);
|
||||
const response = await apiClient.get(`/table-categories/${tableNameToUse}/${columnName}/values`);
|
||||
|
||||
console.log(`📋 [CardDisplay] 카테고리 API 응답 [${columnName}]:`, response.data);
|
||||
|
||||
if (response.data.success && response.data.data) {
|
||||
const mapping: Record<string, { label: string; color?: string }> = {};
|
||||
|
|
@ -328,29 +337,27 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
const rawColor = item.color ?? item.badge_color;
|
||||
const color = (rawColor && rawColor !== "none") ? rawColor : undefined;
|
||||
mapping[code] = { label, color };
|
||||
console.log(`📋 [CardDisplay] 매핑 추가: ${code} -> ${label} (color: ${color})`);
|
||||
});
|
||||
mappings[columnName] = mapping;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ CardDisplay: 카테고리 매핑 로드 실패 [${columnName}]`, error);
|
||||
// 카테고리 매핑 로드 실패 시 무시
|
||||
}
|
||||
}
|
||||
|
||||
console.log("📋 [CardDisplay] 최종 카테고리 매핑:", mappings);
|
||||
setCategoryMappings(mappings);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`❌ CardDisplay: 데이터 로딩 실패`, error);
|
||||
setLoadedTableData([]);
|
||||
setLoadedTableColumns([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setInitialLoadDone(true);
|
||||
}
|
||||
};
|
||||
|
||||
loadTableData();
|
||||
}, [isDesignMode, tableName, component.componentConfig?.tableName, splitPanelContext?.selectedLeftData]);
|
||||
}, [isDesignMode, tableName, component.componentConfig?.tableName, splitPanelContext?.selectedLeftData, splitPanelPosition]);
|
||||
|
||||
// 컴포넌트 설정 (기본값 보장)
|
||||
const componentConfig = {
|
||||
|
|
@ -390,8 +397,34 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
componentStyle.borderColor = isSelected ? "hsl(var(--ring))" : "hsl(var(--border))";
|
||||
}
|
||||
|
||||
// 우측 패널 + 좌측 미선택 상태 체크를 위한 값들 (displayData 외부에서 계산)
|
||||
const isRightPanelForDisplay = splitPanelPosition === "right" ||
|
||||
(screenId && splitPanelContext?.getPositionByScreenId?.(screenId as number) === "right");
|
||||
const hasLinkedFiltersForDisplay = splitPanelContext?.linkedFilters && splitPanelContext.linkedFilters.length > 0;
|
||||
const selectedLeftDataForDisplay = splitPanelContext?.selectedLeftData;
|
||||
const hasSelectedLeftDataForDisplay = selectedLeftDataForDisplay &&
|
||||
Object.keys(selectedLeftDataForDisplay).length > 0;
|
||||
|
||||
// 좌측 데이터가 한 번이라도 선택된 적이 있으면 기록
|
||||
useEffect(() => {
|
||||
if (hasSelectedLeftDataForDisplay) {
|
||||
setHasEverSelectedLeftData(true);
|
||||
}
|
||||
}, [hasSelectedLeftDataForDisplay]);
|
||||
|
||||
// 우측 패널이고 연결 필터가 있고, 좌측 데이터가 한 번도 선택된 적이 없는 경우에만 "선택해주세요" 표시
|
||||
// 한 번이라도 선택된 적이 있으면 깜빡임 방지를 위해 기존 데이터 유지
|
||||
const shouldHideDataForRightPanel = isRightPanelForDisplay &&
|
||||
!hasEverSelectedLeftData &&
|
||||
!hasSelectedLeftDataForDisplay;
|
||||
|
||||
// 표시할 데이터 결정 (로드된 테이블 데이터 우선 사용)
|
||||
const displayData = useMemo(() => {
|
||||
// 우측 패널이고 linkedFilters가 설정되어 있지만 좌측 데이터가 선택되지 않은 경우 빈 배열 반환
|
||||
if (shouldHideDataForRightPanel) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 로드된 테이블 데이터가 있으면 항상 우선 사용 (dataSource 설정 무시)
|
||||
if (loadedTableData.length > 0) {
|
||||
return loadedTableData;
|
||||
|
|
@ -408,7 +441,7 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
|
||||
// 데이터가 없으면 빈 배열 반환
|
||||
return [];
|
||||
}, [componentConfig.dataSource, loadedTableData, tableData, componentConfig.staticData]);
|
||||
}, [shouldHideDataForRightPanel, loadedTableData, tableData, componentConfig.staticData]);
|
||||
|
||||
// 실제 사용할 테이블 컬럼 정보 (로드된 컬럼 우선 사용)
|
||||
const actualTableColumns = loadedTableColumns.length > 0 ? loadedTableColumns : tableColumns;
|
||||
|
|
@ -453,13 +486,8 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
additionalData: {},
|
||||
}));
|
||||
useModalDataStore.getState().setData(tableNameToUse, modalItems);
|
||||
console.log("[CardDisplay] modalDataStore에 데이터 저장:", {
|
||||
dataSourceId: tableNameToUse,
|
||||
count: modalItems.length,
|
||||
});
|
||||
} else if (tableNameToUse && selectedRowsData.length === 0) {
|
||||
useModalDataStore.getState().clearData(tableNameToUse);
|
||||
console.log("[CardDisplay] modalDataStore 데이터 제거:", tableNameToUse);
|
||||
}
|
||||
|
||||
// 분할 패널 컨텍스트에 선택된 데이터 저장 (좌측 화면인 경우)
|
||||
|
|
@ -467,13 +495,8 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
if (splitPanelContext && splitPanelPosition === "left" && !splitPanelContext.disableAutoDataTransfer) {
|
||||
if (checked) {
|
||||
splitPanelContext.setSelectedLeftData(data);
|
||||
console.log("[CardDisplay] 분할 패널 좌측 데이터 저장:", {
|
||||
data,
|
||||
parentDataMapping: splitPanelContext.parentDataMapping,
|
||||
});
|
||||
} else {
|
||||
splitPanelContext.setSelectedLeftData(null);
|
||||
console.log("[CardDisplay] 분할 패널 좌측 데이터 초기화");
|
||||
}
|
||||
}
|
||||
}, [displayData, getCardKey, onFormDataChange, componentConfig.dataSource?.tableName, tableName, splitPanelContext, splitPanelPosition]);
|
||||
|
|
@ -540,21 +563,38 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
}, [categoryMappings]);
|
||||
|
||||
// 필터가 변경되면 데이터 다시 로드 (테이블 리스트와 동일한 패턴)
|
||||
// 초기 로드 여부 추적
|
||||
const isInitialLoadRef = useRef(true);
|
||||
// 초기 로드 여부 추적 - 마운트 카운터 사용 (Strict Mode 대응)
|
||||
const mountCountRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
mountCountRef.current += 1;
|
||||
const currentMount = mountCountRef.current;
|
||||
|
||||
if (!tableNameToUse || isDesignMode) return;
|
||||
|
||||
// 초기 로드는 별도 useEffect에서 처리하므로 스킵
|
||||
if (isInitialLoadRef.current) {
|
||||
isInitialLoadRef.current = false;
|
||||
// 우측 패널이고 linkedFilters가 설정되어 있지만 좌측 데이터가 선택되지 않은 경우 스킵
|
||||
const isRightPanel = splitPanelPosition === "right" ||
|
||||
(screenId && splitPanelContext?.getPositionByScreenId?.(screenId as number) === "right");
|
||||
const hasLinkedFiltersInConfig = splitPanelContext?.linkedFilters && splitPanelContext.linkedFilters.length > 0;
|
||||
const hasSelectedLeftData = splitPanelContext?.selectedLeftData &&
|
||||
Object.keys(splitPanelContext.selectedLeftData).length > 0;
|
||||
|
||||
// 우측 패널이고 좌측 데이터가 선택되지 않은 경우 - 기존 데이터 유지 (깜빡임 방지)
|
||||
if (isRightPanel && !hasSelectedLeftData) {
|
||||
// 데이터를 지우지 않고 로딩만 false로 설정
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 첫 2번의 마운트는 초기 로드 useEffect에서 처리 (Strict Mode에서 2번 호출됨)
|
||||
// 필터 변경이 아닌 경우 스킵
|
||||
if (currentMount <= 2 && filters.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loadFilteredData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
// 로딩 상태를 true로 설정하지 않음 - 기존 데이터 유지하면서 새 데이터 로드 (깜빡임 방지)
|
||||
|
||||
// 필터 값을 검색 파라미터로 변환
|
||||
const searchParams: Record<string, any> = {};
|
||||
|
|
@ -564,12 +604,6 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
}
|
||||
});
|
||||
|
||||
console.log("🔍 [CardDisplay] 필터 적용 데이터 로드:", {
|
||||
tableName: tableNameToUse,
|
||||
filtersCount: filters.length,
|
||||
searchParams,
|
||||
});
|
||||
|
||||
// search 파라미터로 검색 조건 전달 (API 스펙에 맞게)
|
||||
const dataResponse = await tableTypeApi.getTableData(tableNameToUse, {
|
||||
page: 1,
|
||||
|
|
@ -584,16 +618,14 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
tableOptionsContext.updateTableDataCount(tableId, dataResponse.data?.length || 0);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ [CardDisplay] 필터 적용 실패:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
// 필터 적용 실패 시 무시
|
||||
}
|
||||
};
|
||||
|
||||
// 필터 변경 시 항상 데이터 다시 로드 (빈 필터 = 전체 데이터)
|
||||
loadFilteredData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [filters, tableNameToUse, isDesignMode, tableId]);
|
||||
}, [filters, tableNameToUse, isDesignMode, tableId, splitPanelContext?.selectedLeftData, splitPanelPosition]);
|
||||
|
||||
// 컬럼 고유 값 조회 함수 (select 타입 필터용)
|
||||
const getColumnUniqueValues = useCallback(async (columnName: string): Promise<Array<{ label: string; value: string }>> => {
|
||||
|
|
@ -616,7 +648,6 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
label: mapping?.[value]?.label || value,
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error(`❌ [CardDisplay] 고유 값 조회 실패: ${columnName}`, error);
|
||||
return [];
|
||||
}
|
||||
}, [tableNameToUse]);
|
||||
|
|
@ -663,10 +694,6 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
|
||||
// onFilterChange는 ref를 통해 최신 함수를 호출하는 래퍼 사용
|
||||
const onFilterChangeWrapper = (newFilters: TableFilter[]) => {
|
||||
console.log("🎴 [CardDisplay] onFilterChange 래퍼 호출:", {
|
||||
tableId,
|
||||
filtersCount: newFilters.length,
|
||||
});
|
||||
setFiltersRef.current(newFilters);
|
||||
};
|
||||
|
||||
|
|
@ -686,20 +713,12 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
getColumnUniqueValues: getColumnUniqueValuesWrapper,
|
||||
};
|
||||
|
||||
console.log("📋 [CardDisplay] TableOptionsContext에 등록:", {
|
||||
tableId,
|
||||
tableName: tableNameToUse,
|
||||
columnsCount: columns.length,
|
||||
dataCount: loadedTableData.length,
|
||||
});
|
||||
|
||||
registerTableRef.current(registration);
|
||||
|
||||
const unregister = unregisterTableRef.current;
|
||||
const currentTableId = tableId;
|
||||
|
||||
return () => {
|
||||
console.log("📋 [CardDisplay] TableOptionsContext에서 해제:", currentTableId);
|
||||
unregister(currentTableId);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -711,8 +730,34 @@ export const CardDisplayComponent: React.FC<CardDisplayComponentProps> = ({
|
|||
columnsKey, // 컬럼 변경 시에만 재등록
|
||||
]);
|
||||
|
||||
// 로딩 중인 경우 로딩 표시
|
||||
if (loading) {
|
||||
// 우측 패널이고 좌측 데이터가 한 번도 선택된 적이 없는 경우에만 "선택해주세요" 표시
|
||||
// 한 번이라도 선택된 적이 있으면 로딩 중에도 기존 데이터 유지 (깜빡임 방지)
|
||||
if (shouldHideDataForRightPanel) {
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
style={{
|
||||
...componentStyle,
|
||||
...style,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
padding: "20px",
|
||||
background: "#f8fafc",
|
||||
borderRadius: "12px",
|
||||
}}
|
||||
>
|
||||
<div className="text-muted-foreground text-center">
|
||||
<div className="text-lg mb-2">좌측에서 항목을 선택해주세요</div>
|
||||
<div className="text-sm text-gray-400">선택한 항목의 관련 데이터가 여기에 표시됩니다</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 로딩 중이고 데이터가 없는 경우에만 로딩 표시
|
||||
// 데이터가 있으면 로딩 중에도 기존 데이터 유지 (깜빡임 방지)
|
||||
if (loading && displayData.length === 0 && !hasEverSelectedLeftData) {
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
|
|
|
|||
|
|
@ -66,31 +66,10 @@ class ScreenSplitPanelRenderer extends AutoRegisteringComponentRenderer {
|
|||
};
|
||||
|
||||
render() {
|
||||
console.log("🚀 [ScreenSplitPanelRenderer] render() 호출됨!", this.props);
|
||||
|
||||
const { component, style = {}, componentConfig, config, screenId, formData } = this.props as any;
|
||||
|
||||
// componentConfig 또는 config 또는 component.componentConfig 사용
|
||||
const finalConfig = componentConfig || config || component?.componentConfig || {};
|
||||
|
||||
console.log("🔍 [ScreenSplitPanelRenderer] 설정 분석:", {
|
||||
hasComponentConfig: !!componentConfig,
|
||||
hasConfig: !!config,
|
||||
hasComponentComponentConfig: !!component?.componentConfig,
|
||||
finalConfig,
|
||||
splitRatio: finalConfig.splitRatio,
|
||||
leftScreenId: finalConfig.leftScreenId,
|
||||
rightScreenId: finalConfig.rightScreenId,
|
||||
componentType: component?.componentType,
|
||||
componentId: component?.id,
|
||||
});
|
||||
|
||||
// 🆕 formData 별도 로그 (명확한 확인)
|
||||
console.log("📝 [ScreenSplitPanelRenderer] formData 확인:", {
|
||||
hasFormData: !!formData,
|
||||
formDataKeys: formData ? Object.keys(formData) : [],
|
||||
formData: formData,
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={{ width: "100%", height: "100%", ...style }}>
|
||||
|
|
|
|||
|
|
@ -1268,18 +1268,9 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
});
|
||||
}
|
||||
|
||||
console.log(`📡 [TableList] API 호출 시작 [${columnName}]:`, {
|
||||
url: `/table-categories/${targetTable}/${targetColumn}/values`,
|
||||
});
|
||||
|
||||
const response = await apiClient.get(`/table-categories/${targetTable}/${targetColumn}/values`);
|
||||
|
||||
console.log(`📡 [TableList] API 응답 [${columnName}]:`, {
|
||||
success: response.data.success,
|
||||
dataLength: response.data.data?.length,
|
||||
rawData: response.data,
|
||||
items: response.data.data,
|
||||
});
|
||||
|
||||
if (response.data.success && response.data.data && Array.isArray(response.data.data)) {
|
||||
const mapping: Record<string, { label: string; color?: string }> = {};
|
||||
|
|
@ -1291,18 +1282,11 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
label: item.valueLabel,
|
||||
color: item.color,
|
||||
};
|
||||
console.log(` 🔑 [${columnName}] "${key}" => "${item.valueLabel}" (색상: ${item.color})`);
|
||||
});
|
||||
|
||||
if (Object.keys(mapping).length > 0) {
|
||||
// 🆕 원래 컬럼명(item_info.material)으로 매핑 저장
|
||||
mappings[columnName] = mapping;
|
||||
console.log(`✅ [TableList] 카테고리 매핑 로드 완료 [${columnName}]:`, {
|
||||
columnName,
|
||||
mappingCount: Object.keys(mapping).length,
|
||||
mappingKeys: Object.keys(mapping),
|
||||
mapping,
|
||||
});
|
||||
} else {
|
||||
console.warn(`⚠️ [TableList] 매핑 데이터가 비어있음 [${columnName}]`);
|
||||
}
|
||||
|
|
@ -1342,7 +1326,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
col.columnName,
|
||||
})) || [];
|
||||
|
||||
console.log("🔍 [TableList] additionalJoinInfo 컬럼:", additionalJoinColumns);
|
||||
|
||||
// 조인 테이블별로 그룹화
|
||||
const joinedTableColumns: Record<string, { columnName: string; actualColumn: string }[]> = {};
|
||||
|
|
@ -1375,7 +1358,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
});
|
||||
}
|
||||
|
||||
console.log("🔍 [TableList] 조인 테이블별 컬럼:", joinedTableColumns);
|
||||
|
||||
// 조인된 테이블별로 inputType 정보 가져오기
|
||||
const newJoinedColumnMeta: Record<string, { webType?: string; codeCategory?: string; inputType?: string }> = {};
|
||||
|
|
@ -1421,9 +1403,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
|
||||
if (Object.keys(mapping).length > 0) {
|
||||
mappings[col.columnName] = mapping;
|
||||
console.log(`✅ [TableList] 조인 테이블 카테고리 매핑 로드 완료 [${col.columnName}]:`, {
|
||||
mappingCount: Object.keys(mapping).length,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -1442,16 +1421,9 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
console.log("✅ [TableList] 조인 컬럼 메타데이터 설정:", newJoinedColumnMeta);
|
||||
}
|
||||
|
||||
console.log("📊 [TableList] 전체 카테고리 매핑 설정:", {
|
||||
mappingsCount: Object.keys(mappings).length,
|
||||
mappingsKeys: Object.keys(mappings),
|
||||
mappings,
|
||||
});
|
||||
|
||||
if (Object.keys(mappings).length > 0) {
|
||||
setCategoryMappings(mappings);
|
||||
setCategoryMappingsKey((prev) => prev + 1);
|
||||
console.log("✅ [TableList] setCategoryMappings 호출 완료");
|
||||
} else {
|
||||
console.warn("⚠️ [TableList] 매핑이 비어있어 상태 업데이트 스킵");
|
||||
}
|
||||
|
|
@ -1473,11 +1445,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
// ========================================
|
||||
|
||||
const fetchTableDataInternal = useCallback(async () => {
|
||||
console.log("📡 [TableList] fetchTableDataInternal 호출됨", {
|
||||
tableName: tableConfig.selectedTable,
|
||||
isDesignMode,
|
||||
currentPage,
|
||||
});
|
||||
|
||||
if (!tableConfig.selectedTable || isDesignMode) {
|
||||
setData([]);
|
||||
|
|
@ -1501,13 +1468,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
let hasLinkedFiltersConfigured = false; // 연결 필터가 설정되어 있는지 여부
|
||||
let hasSelectedLeftData = false; // 좌측에서 데이터가 선택되었는지 여부
|
||||
|
||||
console.log("🔍 [TableList] 분할 패널 컨텍스트 확인:", {
|
||||
hasSplitPanelContext: !!splitPanelContext,
|
||||
tableName: tableConfig.selectedTable,
|
||||
selectedLeftData: splitPanelContext?.selectedLeftData,
|
||||
linkedFilters: splitPanelContext?.linkedFilters,
|
||||
splitPanelPosition: splitPanelPosition,
|
||||
});
|
||||
|
||||
if (splitPanelContext) {
|
||||
// 연결 필터 설정 여부 확인 (현재 테이블에 해당하는 필터가 있는지)
|
||||
|
|
@ -1523,7 +1483,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
splitPanelContext.selectedLeftData && Object.keys(splitPanelContext.selectedLeftData).length > 0;
|
||||
|
||||
const allLinkedFilters = splitPanelContext.getLinkedFilterValues();
|
||||
console.log("🔗 [TableList] 연결 필터 원본:", allLinkedFilters);
|
||||
|
||||
// 현재 테이블에 해당하는 필터만 추출 (테이블명.컬럼명 형식에서)
|
||||
// 연결 필터는 코드 값이므로 정확한 매칭(equals)을 사용해야 함
|
||||
|
|
@ -1655,7 +1614,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
};
|
||||
});
|
||||
|
||||
console.log("🎯 [TableList] 화면별 엔티티 설정:", screenEntityConfigs);
|
||||
|
||||
// 🆕 제외 필터 처리 (다른 테이블에 이미 존재하는 데이터 제외)
|
||||
let excludeFilterParam: any = undefined;
|
||||
|
|
@ -2146,16 +2104,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
// 분할 패널 컨텍스트가 있고, 좌측 화면인 경우에만 행 선택 및 데이터 전달
|
||||
const effectiveSplitPosition = splitPanelPosition || currentSplitPosition;
|
||||
|
||||
console.log("🔗 [TableList] 셀 클릭 - 분할 패널 위치 확인:", {
|
||||
rowIndex,
|
||||
colIndex,
|
||||
splitPanelPosition,
|
||||
currentSplitPosition,
|
||||
effectiveSplitPosition,
|
||||
hasSplitPanelContext: !!splitPanelContext,
|
||||
isCurrentlySelected,
|
||||
});
|
||||
|
||||
if (splitPanelContext && effectiveSplitPosition === "left" && !splitPanelContext.disableAutoDataTransfer) {
|
||||
// 이미 선택된 행과 다른 행을 클릭한 경우에만 처리
|
||||
if (!isCurrentlySelected) {
|
||||
|
|
@ -2165,10 +2113,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
|
||||
// 분할 패널 컨텍스트에 데이터 저장
|
||||
splitPanelContext.setSelectedLeftData(row);
|
||||
console.log("🔗 [TableList] 셀 클릭으로 분할 패널 좌측 데이터 저장:", {
|
||||
row,
|
||||
parentDataMapping: splitPanelContext.parentDataMapping,
|
||||
});
|
||||
|
||||
// onSelectedRowsChange 콜백 호출
|
||||
if (onSelectedRowsChange) {
|
||||
|
|
@ -2888,7 +2832,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
|
||||
try {
|
||||
localStorage.setItem(tableStateKey, JSON.stringify(state));
|
||||
console.log("✅ 테이블 상태 저장:", tableStateKey);
|
||||
} catch (error) {
|
||||
console.error("❌ 테이블 상태 저장 실패:", error);
|
||||
}
|
||||
|
|
@ -2930,7 +2873,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
setHeaderFilters(filters);
|
||||
}
|
||||
|
||||
console.log("✅ 테이블 상태 복원:", tableStateKey);
|
||||
} catch (error) {
|
||||
console.error("❌ 테이블 상태 복원 실패:", error);
|
||||
}
|
||||
|
|
@ -2951,7 +2893,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
setShowGridLines(true);
|
||||
setHeaderFilters({});
|
||||
toast.success("테이블 설정이 초기화되었습니다.");
|
||||
console.log("✅ 테이블 상태 초기화:", tableStateKey);
|
||||
} catch (error) {
|
||||
console.error("❌ 테이블 상태 초기화 실패:", error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,21 +115,9 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
|
|||
|
||||
// 필터링된 결과가 없으면 모든 테이블 반환 (폴백)
|
||||
if (filteredTables.length === 0) {
|
||||
console.log("🔍 [TableSearchWidget] 대상 패널에 테이블 없음, 전체 테이블 사용:", {
|
||||
targetPanelPosition,
|
||||
allTablesCount: allTableList.length,
|
||||
allTableIds: allTableList.map(t => t.tableId),
|
||||
});
|
||||
return allTableList;
|
||||
}
|
||||
|
||||
console.log("🔍 [TableSearchWidget] 테이블 필터링:", {
|
||||
targetPanelPosition,
|
||||
allTablesCount: allTableList.length,
|
||||
filteredCount: filteredTables.length,
|
||||
filteredTableIds: filteredTables.map(t => t.tableId),
|
||||
});
|
||||
|
||||
return filteredTables;
|
||||
}, [allTableList, targetPanelPosition]);
|
||||
|
||||
|
|
@ -159,11 +147,6 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
|
|||
// 현재 선택된 테이블이 대상 패널에 없으면 대상 패널의 첫 번째 테이블 선택
|
||||
if (!selectedTableId || !isCurrentTableInTarget) {
|
||||
const targetTable = tableList[0];
|
||||
console.log("🔍 [TableSearchWidget] 대상 패널 테이블 자동 선택:", {
|
||||
targetPanelPosition,
|
||||
selectedTableId: targetTable.tableId,
|
||||
tableName: targetTable.tableName,
|
||||
});
|
||||
setSelectedTableId(targetTable.tableId);
|
||||
}
|
||||
}, [tableList, selectedTableId, autoSelectFirstTable, setSelectedTableId, targetPanelPosition]);
|
||||
|
|
@ -374,12 +357,6 @@ export function TableSearchWidget({ component, screenId, onHeightChange }: Table
|
|||
return true;
|
||||
});
|
||||
|
||||
console.log("🔍 [TableSearchWidget] 필터 적용:", {
|
||||
currentTableId: currentTable?.tableId,
|
||||
currentTableName: currentTable?.tableName,
|
||||
filtersCount: filtersWithValues.length,
|
||||
filtersWithValues,
|
||||
});
|
||||
currentTable?.onFilterChange(filtersWithValues);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -23,12 +23,6 @@ const TabsWidgetWrapper: React.FC<any> = (props) => {
|
|||
persistSelection: tabsConfig.persistSelection || false,
|
||||
};
|
||||
|
||||
console.log("🎨 TabsWidget 렌더링:", {
|
||||
componentId: component.id,
|
||||
tabs: tabsComponent.tabs,
|
||||
tabsLength: tabsComponent.tabs.length,
|
||||
component,
|
||||
});
|
||||
|
||||
// TabsWidget 동적 로드
|
||||
const TabsWidget = require("@/components/screen/widgets/TabsWidget").TabsWidget;
|
||||
|
|
|
|||
Loading…
Reference in New Issue