diff --git a/frontend/components/layout/AppLayout.tsx b/frontend/components/layout/AppLayout.tsx index 9e484905..959ba363 100644 --- a/frontend/components/layout/AppLayout.tsx +++ b/frontend/components/layout/AppLayout.tsx @@ -240,7 +240,8 @@ function AppLayoutInner({ children }: AppLayoutProps) { const isAdminMode = pathname.startsWith("/admin") || searchParams.get("mode") === "admin"; // 현재 모드에 따라 표시할 메뉴 결정 - const currentMenus = isAdminMode ? adminMenus : userMenus; + // 관리자 모드에서는 관리자 메뉴 + 사용자 메뉴(툴 생성 메뉴 포함)를 모두 표시 + const currentMenus = isAdminMode ? [...adminMenus, ...userMenus] : userMenus; // 메뉴 토글 함수 const toggleMenu = (menuId: string) => { diff --git a/frontend/components/screen/RealtimePreviewDynamic.tsx b/frontend/components/screen/RealtimePreviewDynamic.tsx index 5260b3e5..6ebd8b5a 100644 --- a/frontend/components/screen/RealtimePreviewDynamic.tsx +++ b/frontend/components/screen/RealtimePreviewDynamic.tsx @@ -92,10 +92,10 @@ export const RealtimePreviewDynamic: React.FC = ({ left: `${position.x}px`, top: `${position.y}px`, width: component.componentConfig?.type === "table-list" - ? `${Math.max(size?.width || 400, 400)}px` // table-list는 최소 400px + ? `${Math.max(size?.width || 120, 120)}px` // table-list 디폴트를 그리드 1컬럼 크기로 축소 (120px) : `${size?.width || 100}px`, height: component.componentConfig?.type === "table-list" - ? `${Math.max(size?.height || 300, 300)}px` // table-list는 최소 300px + ? `${Math.max(size?.height || 200, 200)}px` // table-list 디폴트 높이도 축소 (200px) : `${size?.height || 36}px`, zIndex: component.type === "layout" ? 1 : position.z || 2, // 레이아웃은 z-index 1, 다른 컴포넌트는 2 이상 ...componentStyle, diff --git a/frontend/components/screen/ScreenDesigner.tsx b/frontend/components/screen/ScreenDesigner.tsx index 79d6daae..39c8e34e 100644 --- a/frontend/components/screen/ScreenDesigner.tsx +++ b/frontend/components/screen/ScreenDesigner.tsx @@ -1518,12 +1518,15 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD defaultConfig: component.defaultConfig, }); - // 카드 디스플레이 컴포넌트의 경우 gridColumns에 맞는 width 계산 + // 컴포넌트별 gridColumns 설정 및 크기 계산 let componentSize = component.defaultSize; const isCardDisplay = component.id === "card-display"; - const gridColumns = isCardDisplay ? 8 : 1; + const isTableList = component.id === "table-list"; + + // 컴포넌트별 기본 그리드 컬럼 수 설정 + const gridColumns = isCardDisplay ? 8 : isTableList ? 1 : 1; - if (isCardDisplay && layout.gridSettings?.snapToGrid && gridInfo) { + if ((isCardDisplay || isTableList) && layout.gridSettings?.snapToGrid && gridInfo) { // gridColumns에 맞는 정확한 너비 계산 const calculatedWidth = calculateWidthFromColumns( gridColumns, @@ -1531,12 +1534,15 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD layout.gridSettings as GridUtilSettings, ); + // 컴포넌트별 최소 크기 보장 + const minWidth = isTableList ? 120 : isCardDisplay ? 400 : 100; + componentSize = { ...component.defaultSize, - width: calculatedWidth, + width: Math.max(calculatedWidth, minWidth), }; - console.log("📐 카드 디스플레이 초기 크기 자동 조정:", { + console.log(`📐 ${component.name} 초기 크기 자동 조정:`, { componentId: component.id, gridColumns, defaultWidth: component.defaultSize.width, @@ -1554,7 +1560,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD componentType: component.id, // 새 컴포넌트 시스템의 ID (DynamicComponentRenderer용) position: snappedPosition, size: componentSize, - gridColumns: gridColumns, // 카드 디스플레이 컴포넌트는 기본 8그리드 + gridColumns: gridColumns, // 컴포넌트별 그리드 컬럼 수 적용 componentConfig: { type: component.id, // 새 컴포넌트 시스템의 ID 사용 webType: component.webType, // 웹타입 정보 추가 @@ -1998,22 +2004,12 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD // 마지막 선택된 컴포넌트를 selectedComponent로 설정 if (!isSelected) { - console.log("🎯 컴포넌트 선택 (다중 모드):", { - componentId: component.id, - componentType: component.type, - webTypeConfig: component.type === "widget" ? (component as any).webTypeConfig : null, - timestamp: new Date().toISOString(), - }); + // console.log("🎯 컴포넌트 선택 (다중 모드):", component.id); setSelectedComponent(component); } } else { // 단일 선택 모드 - console.log("🎯 컴포넌트 선택 (단일 모드):", { - componentId: component.id, - componentType: component.type, - webTypeConfig: component.type === "widget" ? (component as any).webTypeConfig : null, - timestamp: new Date().toISOString(), - }); + // console.log("🎯 컴포넌트 선택 (단일 모드):", component.id); setSelectedComponent(component); setGroupState((prev) => ({ ...prev, @@ -3277,10 +3273,34 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD onZoneClick={handleZoneClick} // 설정 변경 핸들러 (테이블 페이지 크기 등 설정을 상세설정에 반영) onConfigChange={(config) => { - console.log("📤 테이블 설정 변경을 상세설정에 알림:", config); - // 여기서 DetailSettingsPanel의 상태를 업데이트하거나 - // 컴포넌트의 componentConfig를 업데이트할 수 있습니다 - // TODO: 실제 구현은 DetailSettingsPanel과의 연동 필요 + console.log("📤 테이블 설정 변경을 상세설정에 반영:", config); + + // 컴포넌트의 componentConfig 업데이트 + const updatedComponents = layout.components.map(comp => { + if (comp.id === component.id) { + return { + ...comp, + componentConfig: { + ...comp.componentConfig, + ...config + } + }; + } + return comp; + }); + + const newLayout = { + ...layout, + components: updatedComponents + }; + + setLayout(newLayout); + saveToHistory(newLayout); + + console.log("✅ 컴포넌트 설정 업데이트 완료:", { + componentId: component.id, + updatedConfig: config + }); }} > {/* 컨테이너, 그룹, 영역의 자식 컴포넌트들 렌더링 (레이아웃은 독립적으로 렌더링) */} diff --git a/frontend/hooks/usePanelState.ts b/frontend/hooks/usePanelState.ts index a2e08a2a..407a6953 100644 --- a/frontend/hooks/usePanelState.ts +++ b/frontend/hooks/usePanelState.ts @@ -71,10 +71,7 @@ export const usePanelState = (panels: PanelConfig[]) => { // 패널 열기 const openPanel = useCallback((panelId: string) => { - console.log("📂 패널 열기:", { - panelId, - timestamp: new Date().toISOString(), - }); + // console.log("📂 패널 열기:", panelId); setPanelStates((prev) => ({ ...prev, [panelId]: { diff --git a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx index caf2d5f5..b9eab149 100644 --- a/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx +++ b/frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx @@ -169,20 +169,21 @@ export const ButtonPrimaryComponent: React.FC = ({ }; } - console.log("🔧 버튼 컴포넌트 설정:", { - originalConfig: componentConfig, - processedConfig, - actionConfig: processedConfig.action, - webTypeConfig: component.webTypeConfig, - enableDataflowControl: component.webTypeConfig?.enableDataflowControl, - dataflowConfig: component.webTypeConfig?.dataflowConfig, - screenId, - tableName, - onRefresh, - onClose, - selectedRows, - selectedRowsData, - }); + // 디버그 로그 (필요시 주석 해제) + // console.log("🔧 버튼 컴포넌트 설정:", { + // originalConfig: componentConfig, + // processedConfig, + // actionConfig: processedConfig.action, + // webTypeConfig: component.webTypeConfig, + // enableDataflowControl: component.webTypeConfig?.enableDataflowControl, + // dataflowConfig: component.webTypeConfig?.dataflowConfig, + // screenId, + // tableName, + // onRefresh, + // onClose, + // selectedRows, + // selectedRowsData, + // }); // 스타일 계산 (위치는 RealtimePreviewDynamic에서 처리하므로 제외) const componentStyle: React.CSSProperties = { @@ -203,7 +204,7 @@ export const ButtonPrimaryComponent: React.FC = ({ // 실제 액션 실행 함수 const executeAction = async (actionConfig: any, context: ButtonActionContext) => { - console.log("🚀 executeAction 시작:", { actionConfig, context }); + // console.log("🚀 executeAction 시작:", { actionConfig, context }); try { // 기존 토스트가 있다면 먼저 제거 diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index a3d63041..e78d5c21 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -390,7 +390,7 @@ export const TableListComponent: React.FC = ({ console.log("🔗 Entity 조인 컬럼:", entityJoinColumns); console.log("🔗 조인 탭 컬럼:", joinTabColumns); console.log("🔗 추가 Entity 조인 컬럼:", additionalJoinColumns); - console.log("🎯 화면별 엔티티 설정:", screenEntityConfigs); + // console.log("🎯 화면별 엔티티 설정:", screenEntityConfigs); const result = await entityJoinApi.getTableDataWithJoins(tableConfig.selectedTable, { page: currentPage, @@ -463,10 +463,10 @@ export const TableListComponent: React.FC = ({ }); if (result) { - console.log("🎯 API 응답 결과:", result); - console.log("🎯 데이터 개수:", result.data?.length || 0); - console.log("🎯 전체 페이지:", result.totalPages); - console.log("🎯 총 아이템:", result.total); + // console.log("🎯 API 응답 결과:", result); + // console.log("🎯 데이터 개수:", result.data?.length || 0); + // console.log("🎯 전체 페이지:", result.totalPages); + // console.log("🎯 총 아이템:", result.total); setData(result.data || []); setTotalPages(result.totalPages || 1); setTotalItems(result.total || 0); @@ -642,9 +642,9 @@ export const TableListComponent: React.FC = ({ // 🎯 표시할 컬럼 상태 업데이트 setDisplayColumns(processedColumns); - console.log("🎯 displayColumns 업데이트됨:", processedColumns); - console.log("🎯 데이터 개수:", result.data?.length || 0); - console.log("🎯 전체 데이터:", result.data); + // console.log("🎯 displayColumns 업데이트됨:", processedColumns); + // console.log("🎯 데이터 개수:", result.data?.length || 0); + // console.log("🎯 전체 데이터:", result.data); } } catch (err) { console.error("테이블 데이터 로딩 오류:", err); @@ -661,7 +661,7 @@ export const TableListComponent: React.FC = ({ // 상세설정에 현재 페이지 정보 알림 (필요한 경우) if (onConfigChange && tableConfig.pagination) { - console.log("📤 테이블에서 페이지 변경을 상세설정에 알림:", newPage); + // console.log("📤 테이블에서 페이지 변경을 상세설정에 알림:", newPage); onConfigChange({ ...tableConfig, pagination: { @@ -670,7 +670,7 @@ export const TableListComponent: React.FC = ({ }, }); } else if (!onConfigChange) { - console.warn("⚠️ onConfigChange가 정의되지 않음 - 페이지 변경 상세설정과 연동 불가"); + // console.warn("⚠️ onConfigChange가 정의되지 않음 - 페이지 변경 상세설정과 연동 불가"); } }; @@ -839,14 +839,14 @@ export const TableListComponent: React.FC = ({ useEffect(() => { // 페이지 크기 동기화 if (tableConfig.pagination?.pageSize && tableConfig.pagination.pageSize !== localPageSize) { - console.log("🔄 상세설정에서 페이지 크기 변경 감지:", tableConfig.pagination.pageSize); + // console.log("🔄 상세설정에서 페이지 크기 변경 감지:", tableConfig.pagination.pageSize); setLocalPageSize(tableConfig.pagination.pageSize); setCurrentPage(1); // 페이지를 1로 리셋 } // 현재 페이지 동기화 (상세설정에서 페이지를 직접 변경한 경우) if (tableConfig.pagination?.currentPage && tableConfig.pagination.currentPage !== currentPage) { - console.log("🔄 상세설정에서 현재 페이지 변경 감지:", tableConfig.pagination.currentPage); + // console.log("🔄 상세설정에서 현재 페이지 변경 감지:", tableConfig.pagination.currentPage); setCurrentPage(tableConfig.pagination.currentPage); } }, [tableConfig.pagination?.pageSize, tableConfig.pagination?.currentPage]); @@ -890,7 +890,7 @@ export const TableListComponent: React.FC = ({ }) .sort((a, b) => a.order - b.order); } else { - console.log("🎯 사용할 컬럼이 없음"); + // console.log("🎯 사용할 컬럼이 없음"); return []; } @@ -1522,15 +1522,15 @@ export const TableListComponent: React.FC = ({ handleNestedChange("pagination", "pageSize", parseInt(value))} + onValueChange={(value) => { + // console.log("🎯 상세설정에서 페이지 크기 변경:", { + // from: config.pagination?.pageSize, + // to: parseInt(value), + // currentConfigPageSize: config.pagination?.pageSize + // }); + handleNestedChange("pagination", "pageSize", parseInt(value)); + }} > diff --git a/frontend/lib/registry/components/table-list/index.ts b/frontend/lib/registry/components/table-list/index.ts index fe3eb7ff..5236b80f 100644 --- a/frontend/lib/registry/components/table-list/index.ts +++ b/frontend/lib/registry/components/table-list/index.ts @@ -83,7 +83,7 @@ export const TableListDefinition = createComponentDefinition({ // 데이터 로딩 autoLoad: true, }, - defaultSize: { width: 800, height: 960 }, + defaultSize: { width: 120, height: 200 }, // 그리드 1컬럼 크기로 축소 configPanel: TableListConfigPanel, icon: "Table", tags: ["테이블", "데이터", "목록", "그리드"], diff --git a/frontend/lib/registry/components/text-input/TextInputComponent.tsx b/frontend/lib/registry/components/text-input/TextInputComponent.tsx index f4fe7a9e..54caec16 100644 --- a/frontend/lib/registry/components/text-input/TextInputComponent.tsx +++ b/frontend/lib/registry/components/text-input/TextInputComponent.tsx @@ -58,21 +58,22 @@ export const TextInputComponent: React.FC = ({ } : autoGeneration; - console.log("🔧 텍스트 입력 컴포넌트 설정:", { - config, - componentConfig, - component: component, - autoGeneration, - testAutoGeneration, - isTestMode: component.label?.toLowerCase().includes("test"), - isHidden, - isInteractive, - formData, - columnName: component.columnName, - currentFormValue: formData?.[component.columnName], - componentValue: component.value, - autoGeneratedValue, - }); + // 디버그 로그 (필요시 주석 해제) + // console.log("🔧 텍스트 입력 컴포넌트 설정:", { + // config, + // componentConfig, + // component: component, + // autoGeneration, + // testAutoGeneration, + // isTestMode: component.label?.toLowerCase().includes("test"), + // isHidden, + // isInteractive, + // formData, + // columnName: component.columnName, + // currentFormValue: formData?.[component.columnName], + // componentValue: component.value, + // autoGeneratedValue, + // }); // 자동생성 값 생성 (컴포넌트 마운트 시 또는 폼 데이터 변경 시) useEffect(() => { @@ -248,18 +249,18 @@ export const TextInputComponent: React.FC = ({ onDragEnd={onDragEnd} onChange={(e) => { const newValue = e.target.value; - console.log("🎯 TextInputComponent onChange 호출:", { - componentId: component.id, - columnName: component.columnName, - newValue, - isInteractive, - hasOnFormDataChange: !!onFormDataChange, - hasOnChange: !!props.onChange, - }); + // console.log("🎯 TextInputComponent onChange 호출:", { + // componentId: component.id, + // columnName: component.columnName, + // newValue, + // isInteractive, + // hasOnFormDataChange: !!onFormDataChange, + // hasOnChange: !!props.onChange, + // }); // isInteractive 모드에서는 formData 업데이트 if (isInteractive && onFormDataChange && component.columnName) { - console.log(`📤 TextInputComponent -> onFormDataChange 호출: ${component.columnName} = "${newValue}"`); + // console.log(`📤 TextInputComponent -> onFormDataChange 호출: ${component.columnName} = "${newValue}"`); console.log("🔍 onFormDataChange 함수 정보:", { functionName: onFormDataChange.name, functionString: onFormDataChange.toString().substring(0, 200), @@ -275,7 +276,7 @@ export const TextInputComponent: React.FC = ({ // 기존 onChange 핸들러도 호출 if (props.onChange) { - console.log(`📤 TextInputComponent -> props.onChange 호출: "${newValue}"`); + // console.log(`📤 TextInputComponent -> props.onChange 호출: "${newValue}"`); props.onChange(newValue); } }} diff --git a/frontend/lib/utils/gridUtils.ts b/frontend/lib/utils/gridUtils.ts index 29c1ffab..b23c0ec0 100644 --- a/frontend/lib/utils/gridUtils.ts +++ b/frontend/lib/utils/gridUtils.ts @@ -1,11 +1,5 @@ import { Position, Size } from "@/types/screen"; - -export interface GridSettings { - columns: number; - gap: number; - padding: number; - snapToGrid: boolean; -} +import { GridSettings } from "@/types/screen-management"; export interface GridInfo { columnWidth: number; diff --git a/frontend/types/screen-management.ts b/frontend/types/screen-management.ts index 80aa6e72..1d6734bb 100644 --- a/frontend/types/screen-management.ts +++ b/frontend/types/screen-management.ts @@ -34,6 +34,12 @@ export interface BaseComponent { readonly?: boolean; style?: ComponentStyle; className?: string; + // 새 컴포넌트 시스템에서 필요한 속성들 + gridColumns?: number; // 그리드에서 차지할 컬럼 수 (1-12) + zoneId?: string; // 레이아웃 존 ID + componentConfig?: any; // 컴포넌트별 설정 + componentType?: string; // 새 컴포넌트 시스템의 ID + webTypeConfig?: WebTypeConfig; // 웹타입별 설정 } /** @@ -110,10 +116,20 @@ export interface FileComponent extends BaseComponent { uploadedFiles?: UploadedFile[]; } +/** + * 새로운 컴포넌트 시스템 컴포넌트 + */ +export interface ComponentComponent extends BaseComponent { + type: "component"; + widgetType: WebType; // 웹타입 (기존 호환성) + componentType: string; // 새 컴포넌트 시스템의 ID + componentConfig: any; // 컴포넌트별 설정 +} + /** * 통합 컴포넌트 데이터 타입 */ -export type ComponentData = WidgetComponent | ContainerComponent | GroupComponent | DataTableComponent | FileComponent; +export type ComponentData = WidgetComponent | ContainerComponent | GroupComponent | DataTableComponent | FileComponent | ComponentComponent; // ===== 웹타입별 설정 인터페이스 ===== @@ -370,6 +386,13 @@ export interface GridSettings { color: string; opacity: number; snapToGrid: boolean; + // gridUtils에서 필요한 속성들 추가 + columns: number; + gap: number; + padding: number; + showGrid?: boolean; + gridColor?: string; + gridOpacity?: number; } /** diff --git a/frontend/types/unified-core.ts b/frontend/types/unified-core.ts index bf9d3b18..291c3a5d 100644 --- a/frontend/types/unified-core.ts +++ b/frontend/types/unified-core.ts @@ -136,6 +136,8 @@ export interface CommonStyle { lineHeight?: string; // 라벨 스타일 + labelDisplay?: boolean; // 라벨 표시 여부 + labelText?: string; // 라벨 텍스트 labelFontSize?: string; labelColor?: string; labelFontWeight?: string;