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