feat: 중첩 구조 지원을 위한 컴포넌트 선택 및 기본값 적용 기능 추가

- ScreenManagementService에서 company_code 저장 로직을 개선하여 SUPER_ADMIN의 경우 화면 정의에 따라 company_code를 저장하도록 수정하였습니다.
- ScreenDesigner에서 중첩 구조를 지원하는 탭 내부 컴포넌트 선택 상태 및 핸들러를 추가하였습니다.
- SplitPanelLayoutComponent에서 분할 패널 내부 컴포넌트의 기본값을 재귀적으로 적용하는 헬퍼 함수를 구현하였습니다.
- TimelineSchedulerConfigPanel에서 필드 매핑 업데이트 로직을 개선하여 이전 형식과 새 형식을 모두 지원하도록 하였습니다.
- useTimelineData 훅에서 필드 매핑을 JSON 문자열로 안정화하여 객체 참조 변경 방지를 위한 메모이제이션을 적용하였습니다.
This commit is contained in:
kjs
2026-02-02 17:11:00 +09:00
parent 4e7aa0c3b9
commit 7043f26ac8
9 changed files with 1079 additions and 231 deletions

View File

@@ -67,6 +67,20 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
...props
}) => {
const componentConfig = (component.componentConfig || {}) as SplitPanelLayoutConfig;
// 🐛 디버깅: 로드 시 rightPanel.components 확인
const rightComps = componentConfig.rightPanel?.components || [];
const finishedTimeline = rightComps.find((c: any) => c.id === "finished_timeline");
if (finishedTimeline) {
const fm = finishedTimeline.componentConfig?.fieldMapping;
console.log("🔍 [SplitPanelLayout] finished_timeline fieldMapping:", {
componentId: finishedTimeline.id,
fieldMapping: fm ? JSON.stringify(fm) : "undefined",
fieldMappingKeys: fm ? Object.keys(fm) : [],
fieldMappingId: fm?.id,
fullComponentConfig: JSON.stringify(finishedTimeline.componentConfig || {}, null, 2),
});
}
// 🆕 프리뷰용 회사 코드 오버라이드 (최고 관리자만 사용 가능)
const companyCode = (props as any).companyCode as string | undefined;
@@ -231,6 +245,33 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
[component, componentConfig, onUpdateComponent]
);
// 🆕 중첩된 컴포넌트 업데이트 핸들러 (탭 컴포넌트 내부 위치 변경 등)
const handleNestedComponentUpdate = useCallback(
(panelSide: "left" | "right", compId: string, updatedNestedComponent: any) => {
if (!onUpdateComponent) return;
const panelKey = panelSide === "left" ? "leftPanel" : "rightPanel";
const panelConfig = componentConfig[panelKey] || {};
const panelComponents = panelConfig.components || [];
const updatedComponents = panelComponents.map((c: PanelInlineComponent) =>
c.id === compId ? { ...c, ...updatedNestedComponent, id: c.id } : c
);
onUpdateComponent({
...component,
componentConfig: {
...componentConfig,
[panelKey]: {
...panelConfig,
components: updatedComponents,
},
},
});
},
[component, componentConfig, onUpdateComponent]
);
// 🆕 커스텀 모드: 드래그 시작 핸들러
const handlePanelDragStart = useCallback(
(e: React.MouseEvent, panelSide: "left" | "right", comp: PanelInlineComponent) => {
@@ -2293,6 +2334,7 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
)}
<CardContent className="flex-1 overflow-auto p-4">
{/* 좌측 데이터 목록/테이블/커스텀 */}
{console.log("🔍 [SplitPanel] 왼쪽 패널 displayMode:", componentConfig.leftPanel?.displayMode, "isDesignMode:", isDesignMode)}
{componentConfig.leftPanel?.displayMode === "custom" ? (
// 🆕 커스텀 모드: 패널 안에 자유롭게 컴포넌트 배치
<div
@@ -2398,11 +2440,42 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
height: displayHeight,
}}
>
<div className="pointer-events-none h-full w-full">
{/* 🆕 컨테이너 컴포넌트(탭, 분할 패널)는 드롭 이벤트를 받을 수 있어야 함 */}
<div className={cn(
"h-full w-full",
// 탭/분할 패널 같은 컨테이너 컴포넌트는 pointer-events 활성화
(comp.componentType === "v2-tabs-widget" ||
comp.componentType === "tabs-widget" ||
comp.componentType === "v2-split-panel-layout" ||
comp.componentType === "split-panel-layout")
? ""
: "pointer-events-none"
)}>
<DynamicComponentRenderer
component={componentData as any}
isDesignMode={true}
formData={{}}
// 🆕 중첩된 컴포넌트 업데이트 핸들러 전달
onUpdateComponent={(updatedComp: any) => {
handleNestedComponentUpdate("left", comp.id, updatedComp);
}}
// 🆕 중첩된 탭 내부 컴포넌트 선택 핸들러 - 부모 분할 패널 정보 포함
onSelectTabComponent={(tabId: string, compId: string, tabComp: any) => {
console.log("🔍 [SplitPanel-Left] onSelectTabComponent 호출:", { tabId, compId, tabComp, parentSplitPanelId: component.id });
// 부모 분할 패널 정보와 함께 전역 이벤트 발생
const event = new CustomEvent("nested-tab-component-select", {
detail: {
tabsComponentId: comp.id,
tabId,
componentId: compId,
component: tabComp,
parentSplitPanelId: component.id,
parentPanelSide: "left",
},
});
window.dispatchEvent(event);
}}
selectedTabComponentId={undefined}
/>
</div>
@@ -3079,11 +3152,42 @@ export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps>
height: displayHeight,
}}
>
<div className="pointer-events-none h-full w-full">
{/* 🆕 컨테이너 컴포넌트(탭, 분할 패널)는 드롭 이벤트를 받을 수 있어야 함 */}
<div className={cn(
"h-full w-full",
// 탭/분할 패널 같은 컨테이너 컴포넌트는 pointer-events 활성화
(comp.componentType === "v2-tabs-widget" ||
comp.componentType === "tabs-widget" ||
comp.componentType === "v2-split-panel-layout" ||
comp.componentType === "split-panel-layout")
? ""
: "pointer-events-none"
)}>
<DynamicComponentRenderer
component={componentData as any}
isDesignMode={true}
formData={{}}
// 🆕 중첩된 컴포넌트 업데이트 핸들러 전달
onUpdateComponent={(updatedComp: any) => {
handleNestedComponentUpdate("right", comp.id, updatedComp);
}}
// 🆕 중첩된 탭 내부 컴포넌트 선택 핸들러 - 부모 분할 패널 정보 포함
onSelectTabComponent={(tabId: string, compId: string, tabComp: any) => {
console.log("🔍 [SplitPanel-Right] onSelectTabComponent 호출:", { tabId, compId, tabComp, parentSplitPanelId: component.id });
// 부모 분할 패널 정보와 함께 전역 이벤트 발생
const event = new CustomEvent("nested-tab-component-select", {
detail: {
tabsComponentId: comp.id,
tabId,
componentId: compId,
component: tabComp,
parentSplitPanelId: component.id,
parentPanelSide: "right",
},
});
window.dispatchEvent(event);
}}
selectedTabComponentId={undefined}
/>
</div>