Compare commits

...

3 Commits

12 changed files with 179 additions and 122 deletions

View File

@ -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) => {

View File

@ -92,10 +92,10 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
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,

View File

@ -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
});
}}
>
{/* 컨테이너, 그룹, 영역의 자식 컴포넌트들 렌더링 (레이아웃은 독립적으로 렌더링) */}

View File

@ -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]: {

View File

@ -169,20 +169,21 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
};
}
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<ButtonPrimaryComponentProps> = ({
// 실제 액션 실행 함수
const executeAction = async (actionConfig: any, context: ButtonActionContext) => {
console.log("🚀 executeAction 시작:", { actionConfig, context });
// console.log("🚀 executeAction 시작:", { actionConfig, context });
try {
// 기존 토스트가 있다면 먼저 제거

View File

@ -390,7 +390,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
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<TableListComponentProps> = ({
});
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<TableListComponentProps> = ({
// 🎯 표시할 컬럼 상태 업데이트
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<TableListComponentProps> = ({
// 상세설정에 현재 페이지 정보 알림 (필요한 경우)
if (onConfigChange && tableConfig.pagination) {
console.log("📤 테이블에서 페이지 변경을 상세설정에 알림:", newPage);
// console.log("📤 테이블에서 페이지 변경을 상세설정에 알림:", newPage);
onConfigChange({
...tableConfig,
pagination: {
@ -670,7 +670,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
},
});
} else if (!onConfigChange) {
console.warn("⚠️ onConfigChange가 정의되지 않음 - 페이지 변경 상세설정과 연동 불가");
// console.warn("⚠️ onConfigChange가 정의되지 않음 - 페이지 변경 상세설정과 연동 불가");
}
};
@ -839,14 +839,14 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
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<TableListComponentProps> = ({
})
.sort((a, b) => a.order - b.order);
} else {
console.log("🎯 사용할 컬럼이 없음");
// console.log("🎯 사용할 컬럼이 없음");
return [];
}
@ -1522,15 +1522,15 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
<select
value={localPageSize}
onChange={(e) => {
console.log("🚀 페이지 크기 드롭다운 변경 감지:", e.target.value);
// console.log("🚀 페이지 크기 드롭다운 변경 감지:", e.target.value);
const newPageSize = parseInt(e.target.value);
console.log("🎯 페이지 크기 변경 이벤트:", {
from: localPageSize,
to: newPageSize,
hasOnConfigChange: !!onConfigChange,
onConfigChangeType: typeof onConfigChange
});
// console.log("🎯 페이지 크기 변경 이벤트:", {
// from: localPageSize,
// to: newPageSize,
// hasOnConfigChange: !!onConfigChange,
// onConfigChangeType: typeof onConfigChange
// });
// 로컬 상태 업데이트
setLocalPageSize(newPageSize);
@ -1540,7 +1540,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
// 상세설정에 변경사항 알림 (pagination 설정 업데이트)
if (onConfigChange) {
console.log("📤 테이블에서 페이지 크기 변경을 상세설정에 알림:", newPageSize);
// console.log("📤 테이블에서 페이지 크기 변경을 상세설정에 알림:", newPageSize);
onConfigChange({
...tableConfig,
pagination: {
@ -1549,7 +1549,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
},
});
} else {
console.warn("⚠️ onConfigChange가 정의되지 않음 - 상세설정과 연동 불가");
// console.warn("⚠️ onConfigChange가 정의되지 않음 - 상세설정과 연동 불가");
}
// 데이터는 useEffect에서 자동으로 다시 로드됨

View File

@ -32,18 +32,18 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
screenTableName,
tableColumns,
}) => {
console.log("🔍 TableListConfigPanel props:", {
config,
configType: typeof config,
configSelectedTable: config?.selectedTable,
configPagination: config?.pagination,
paginationEnabled: config?.pagination?.enabled,
paginationPageSize: config?.pagination?.pageSize,
configKeys: typeof config === 'object' ? Object.keys(config || {}) : 'not object',
screenTableName,
tableColumns: tableColumns?.length,
tableColumnsSample: tableColumns?.[0],
});
// console.log("🔍 TableListConfigPanel props:", {
// config,
// configType: typeof config,
// configSelectedTable: config?.selectedTable,
// configPagination: config?.pagination,
// paginationEnabled: config?.pagination?.enabled,
// paginationPageSize: config?.pagination?.pageSize,
// configKeys: typeof config === 'object' ? Object.keys(config || {}) : 'not object',
// screenTableName,
// tableColumns: tableColumns?.length,
// tableColumnsSample: tableColumns?.[0],
// });
const [availableTables, setAvailableTables] = useState<Array<{ tableName: string; displayName: string }>>([]);
const [loadingTables, setLoadingTables] = useState(false);
@ -73,6 +73,16 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
const [loadingEntityJoins, setLoadingEntityJoins] = useState(false);
// 🔄 외부에서 config가 변경될 때 내부 상태 동기화 (표의 페이지네이션 변경 감지)
useEffect(() => {
// console.log("🔄 TableListConfigPanel - 외부 config 변경 감지:", {
// configPagination: config?.pagination,
// configPageSize: config?.pagination?.pageSize,
// });
// 현재는 별도 내부 상태가 없어서 자동으로 UI가 업데이트됨
// 만약 내부 상태가 있다면 여기서 동기화 처리
}, [config]);
// 🎯 엔티티 컬럼 표시 설정을 위한 상태
const [entityDisplayConfigs, setEntityDisplayConfigs] = useState<
Record<
@ -216,14 +226,14 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
};
const handleNestedChange = (parentKey: keyof TableListConfig, childKey: string, value: any) => {
console.log("🔧 TableListConfigPanel handleNestedChange:", {
parentKey,
childKey,
value,
parentValue: config[parentKey],
hasOnChange: !!onChange,
onChangeType: typeof onChange,
});
// console.log("🔧 TableListConfigPanel handleNestedChange:", {
// parentKey,
// childKey,
// value,
// parentValue: config[parentKey],
// hasOnChange: !!onChange,
// onChangeType: typeof onChange,
// });
const parentValue = config[parentKey] as any;
const newConfig = {
@ -233,7 +243,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
},
};
console.log("📤 TableListConfigPanel onChange 호출:", newConfig);
// console.log("📤 TableListConfigPanel onChange 호출:", newConfig);
onChange(newConfig);
};
@ -870,8 +880,16 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
<div className="space-y-2">
<Label htmlFor="pageSize"> </Label>
<Select
key={`pageSize-${config.pagination?.pageSize}`}
value={config.pagination?.pageSize?.toString() || "20"}
onValueChange={(value) => 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));
}}
>
<SelectTrigger>
<SelectValue />

View File

@ -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: ["테이블", "데이터", "목록", "그리드"],

View File

@ -58,21 +58,22 @@ export const TextInputComponent: React.FC<TextInputComponentProps> = ({
}
: 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<TextInputComponentProps> = ({
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<TextInputComponentProps> = ({
// 기존 onChange 핸들러도 호출
if (props.onChange) {
console.log(`📤 TextInputComponent -> props.onChange 호출: "${newValue}"`);
// console.log(`📤 TextInputComponent -> props.onChange 호출: "${newValue}"`);
props.onChange(newValue);
}
}}

View File

@ -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;

View File

@ -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;
}
/**

View File

@ -136,6 +136,8 @@ export interface CommonStyle {
lineHeight?: string;
// 라벨 스타일
labelDisplay?: boolean; // 라벨 표시 여부
labelText?: string; // 라벨 텍스트
labelFontSize?: string;
labelColor?: string;
labelFontWeight?: string;