ERP-node/frontend/lib/utils/getComponentConfigPanel.tsx

205 lines
7.4 KiB
TypeScript
Raw Normal View History

2025-09-12 14:24:25 +09:00
/**
* ID로 ConfigPanel을
*/
import React from "react";
// 컴포넌트별 ConfigPanel 동적 import 맵
const CONFIG_PANEL_MAP: Record<string, () => Promise<any>> = {
"text-input": () => import("@/lib/registry/components/text-input/TextInputConfigPanel"),
"number-input": () => import("@/lib/registry/components/number-input/NumberInputConfigPanel"),
"date-input": () => import("@/lib/registry/components/date-input/DateInputConfigPanel"),
"textarea-basic": () => import("@/lib/registry/components/textarea-basic/TextareaBasicConfigPanel"),
"select-basic": () => import("@/lib/registry/components/select-basic/SelectBasicConfigPanel"),
"checkbox-basic": () => import("@/lib/registry/components/checkbox-basic/CheckboxBasicConfigPanel"),
"radio-basic": () => import("@/lib/registry/components/radio-basic/RadioBasicConfigPanel"),
"toggle-switch": () => import("@/lib/registry/components/toggle-switch/ToggleSwitchConfigPanel"),
"file-upload": () => import("@/lib/registry/components/file-upload/FileUploadConfigPanel"),
"button-primary": () => import("@/lib/registry/components/button-primary/ButtonPrimaryConfigPanel"),
"text-display": () => import("@/lib/registry/components/text-display/TextDisplayConfigPanel"),
"slider-basic": () => import("@/lib/registry/components/slider-basic/SliderBasicConfigPanel"),
"image-display": () => import("@/lib/registry/components/image-display/ImageDisplayConfigPanel"),
"divider-line": () => import("@/lib/registry/components/divider-line/DividerLineConfigPanel"),
2025-09-12 16:47:02 +09:00
"accordion-basic": () => import("@/lib/registry/components/accordion-basic/AccordionBasicConfigPanel"),
2025-09-15 11:43:59 +09:00
"table-list": () => import("@/lib/registry/components/table-list/TableListConfigPanel"),
2025-09-15 17:10:46 +09:00
"card-display": () => import("@/lib/registry/components/card-display/CardDisplayConfigPanel"),
2025-09-12 14:24:25 +09:00
};
// ConfigPanel 컴포넌트 캐시
const configPanelCache = new Map<string, React.ComponentType<any>>();
/**
* ID로 ConfigPanel
*/
export async function getComponentConfigPanel(componentId: string): Promise<React.ComponentType<any> | null> {
// 캐시에서 먼저 확인
if (configPanelCache.has(componentId)) {
return configPanelCache.get(componentId)!;
}
// 매핑에서 import 함수 찾기
const importFn = CONFIG_PANEL_MAP[componentId];
if (!importFn) {
console.warn(`컴포넌트 "${componentId}"에 대한 ConfigPanel을 찾을 수 없습니다.`);
return null;
}
try {
console.log(`🔧 ConfigPanel 로드 중: ${componentId}`);
const module = await importFn();
2025-09-12 16:47:02 +09:00
2025-09-12 14:24:25 +09:00
// 모듈에서 ConfigPanel 컴포넌트 추출
const ConfigPanelComponent = module[`${toPascalCase(componentId)}ConfigPanel`] || module.default;
2025-09-12 16:47:02 +09:00
2025-09-12 14:24:25 +09:00
if (!ConfigPanelComponent) {
console.error(`컴포넌트 "${componentId}"의 ConfigPanel을 모듈에서 찾을 수 없습니다.`);
return null;
}
// 캐시에 저장
configPanelCache.set(componentId, ConfigPanelComponent);
console.log(`✅ ConfigPanel 로드 완료: ${componentId}`);
2025-09-12 16:47:02 +09:00
2025-09-12 14:24:25 +09:00
return ConfigPanelComponent;
} catch (error) {
console.error(`컴포넌트 "${componentId}"의 ConfigPanel 로드 실패:`, error);
return null;
}
}
/**
* ID가 ConfigPanel을
*/
export function hasComponentConfigPanel(componentId: string): boolean {
return componentId in CONFIG_PANEL_MAP;
}
/**
* ID
*/
export function getSupportedConfigPanelComponents(): string[] {
return Object.keys(CONFIG_PANEL_MAP);
}
/**
* kebab-case를 PascalCase로
* text-input TextInput
*/
function toPascalCase(str: string): string {
return str
2025-09-12 16:47:02 +09:00
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join("");
2025-09-12 14:24:25 +09:00
}
/**
* React
*/
export interface ComponentConfigPanelProps {
componentId: string;
config: Record<string, any>;
onChange: (config: Record<string, any>) => void;
2025-09-12 16:47:02 +09:00
screenTableName?: string; // 화면에서 지정한 테이블명
tableColumns?: any[]; // 테이블 컬럼 정보
2025-09-12 14:24:25 +09:00
}
export const DynamicComponentConfigPanel: React.FC<ComponentConfigPanelProps> = ({
componentId,
config,
onChange,
2025-09-12 16:47:02 +09:00
screenTableName,
tableColumns,
2025-09-12 14:24:25 +09:00
}) => {
console.log(`🔥 DynamicComponentConfigPanel 렌더링 시작: ${componentId}`);
2025-09-12 14:24:25 +09:00
const [ConfigPanelComponent, setConfigPanelComponent] = React.useState<React.ComponentType<any> | null>(null);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState<string | null>(null);
React.useEffect(() => {
let mounted = true;
async function loadConfigPanel() {
try {
console.log(`🔧 DynamicComponentConfigPanel: ${componentId} 로드 시작`);
setLoading(true);
setError(null);
2025-09-12 16:47:02 +09:00
2025-09-12 14:24:25 +09:00
const component = await getComponentConfigPanel(componentId);
console.log(`🔧 DynamicComponentConfigPanel: ${componentId} 로드 결과:`, component);
2025-09-12 16:47:02 +09:00
2025-09-12 14:24:25 +09:00
if (mounted) {
setConfigPanelComponent(() => component);
setLoading(false);
}
} catch (err) {
console.error(`❌ DynamicComponentConfigPanel: ${componentId} 로드 실패:`, err);
if (mounted) {
setError(err instanceof Error ? err.message : String(err));
setLoading(false);
}
}
}
loadConfigPanel();
return () => {
mounted = false;
};
}, [componentId]);
if (loading) {
return (
<div className="rounded-md border border-dashed border-gray-300 bg-gray-50 p-4">
<div className="flex items-center gap-2 text-gray-600">
<span className="text-sm font-medium"> ...</span>
</div>
<p className="mt-1 text-xs text-gray-500"> .</p>
</div>
);
}
if (error) {
return (
<div className="rounded-md border border-dashed border-red-300 bg-red-50 p-4">
<div className="flex items-center gap-2 text-red-600">
<span className="text-sm font-medium"> </span>
</div>
<p className="mt-1 text-xs text-red-500"> : {error}</p>
</div>
);
}
if (!ConfigPanelComponent) {
console.warn(`⚠️ DynamicComponentConfigPanel: ${componentId} ConfigPanelComponent가 null`);
return (
<div className="rounded-md border border-dashed border-yellow-300 bg-yellow-50 p-4">
<div className="flex items-center gap-2 text-yellow-600">
<span className="text-sm font-medium"> </span>
</div>
<p className="mt-1 text-xs text-yellow-500"> "{componentId}" .</p>
</div>
);
}
console.log(`🔧 DynamicComponentConfigPanel 렌더링:`, {
componentId,
ConfigPanelComponent: ConfigPanelComponent?.name,
config,
configType: typeof config,
configKeys: typeof config === 'object' ? Object.keys(config || {}) : 'not object',
screenTableName,
tableColumns: Array.isArray(tableColumns) ? tableColumns.length : tableColumns
});
2025-09-12 16:47:02 +09:00
return (
<ConfigPanelComponent
config={config}
onChange={onChange}
onConfigChange={onChange} // TableListConfigPanel을 위한 추가 prop
2025-09-12 16:47:02 +09:00
screenTableName={screenTableName}
tableColumns={tableColumns}
/>
);
2025-09-12 14:24:25 +09:00
};