반응형 및 테이블 리스트 컴포넌트 오류 수정
This commit is contained in:
@@ -155,14 +155,26 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
onSelectedRowsChange,
|
||||
onConfigChange,
|
||||
refreshKey,
|
||||
tableName, // 화면의 기본 테이블명 (screenInfo에서 전달)
|
||||
}) => {
|
||||
// 컴포넌트 설정
|
||||
const tableConfig = {
|
||||
...config,
|
||||
...component.config,
|
||||
...componentConfig,
|
||||
// selectedTable이 없으면 화면의 기본 테이블 사용
|
||||
selectedTable:
|
||||
componentConfig?.selectedTable || component.config?.selectedTable || config?.selectedTable || tableName,
|
||||
} as TableListConfig;
|
||||
|
||||
console.log("🔍 TableListComponent 초기화:", {
|
||||
componentConfigSelectedTable: componentConfig?.selectedTable,
|
||||
componentConfigSelectedTable2: component.config?.selectedTable,
|
||||
configSelectedTable: config?.selectedTable,
|
||||
screenTableName: tableName,
|
||||
finalSelectedTable: tableConfig.selectedTable,
|
||||
});
|
||||
|
||||
// 🎨 동적 색상 설정 (속성편집 모달의 "색상" 필드와 연동)
|
||||
const buttonColor = component.style?.labelColor || "#212121"; // 기본 파란색
|
||||
const buttonTextColor = component.config?.buttonTextColor || "#ffffff";
|
||||
@@ -424,20 +436,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
// 디바운싱된 테이블 데이터 가져오기
|
||||
const fetchTableDataDebounced = useCallback(
|
||||
debouncedApiCall(
|
||||
`fetchTableData_${tableConfig.selectedTable}_${currentPage}_${localPageSize}`,
|
||||
async () => {
|
||||
return fetchTableDataInternal();
|
||||
},
|
||||
200, // 200ms 디바운스
|
||||
),
|
||||
[tableConfig.selectedTable, currentPage, localPageSize, searchTerm, sortColumn, sortDirection, searchValues],
|
||||
);
|
||||
|
||||
// 실제 테이블 데이터 가져오기 함수
|
||||
const fetchTableDataInternal = async () => {
|
||||
const fetchTableDataInternal = useCallback(async () => {
|
||||
if (!tableConfig.selectedTable) {
|
||||
setData([]);
|
||||
return;
|
||||
@@ -448,81 +448,54 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
|
||||
try {
|
||||
// 🎯 Entity 조인 API 사용 - Entity 조인이 포함된 데이터 조회
|
||||
console.log("🔗 Entity 조인 데이터 조회 시작:", tableConfig.selectedTable);
|
||||
|
||||
// Entity 조인 컬럼 추출 (isEntityJoin === true인 컬럼들)
|
||||
const entityJoinColumns = tableConfig.columns?.filter((col) => col.isEntityJoin && col.entityJoinInfo) || [];
|
||||
|
||||
// 🎯 조인 탭에서 추가한 컬럼들도 포함 (실제로 존재하는 컬럼만)
|
||||
const joinTabColumns =
|
||||
tableConfig.columns?.filter(
|
||||
(col) =>
|
||||
!col.isEntityJoin &&
|
||||
col.columnName.includes("_") &&
|
||||
(col.columnName.includes("dept_code_") ||
|
||||
col.columnName.includes("_dept_code") ||
|
||||
col.columnName.includes("_company_") ||
|
||||
col.columnName.includes("_user_")), // 조인 탭에서 추가한 컬럼 패턴들
|
||||
) || [];
|
||||
// 🎯 조인 탭에서 추가한 컬럼들 추출 (additionalJoinInfo가 있는 컬럼들)
|
||||
const manualJoinColumns =
|
||||
tableConfig.columns?.filter((col) => {
|
||||
return col.additionalJoinInfo !== undefined;
|
||||
}) || [];
|
||||
|
||||
console.log(
|
||||
"🔍 조인 탭 컬럼들:",
|
||||
joinTabColumns.map((c) => c.columnName),
|
||||
"🔗 수동 조인 컬럼 감지:",
|
||||
manualJoinColumns.map((c) => ({
|
||||
columnName: c.columnName,
|
||||
additionalJoinInfo: c.additionalJoinInfo,
|
||||
})),
|
||||
);
|
||||
|
||||
const additionalJoinColumns = [
|
||||
...entityJoinColumns.map((col) => ({
|
||||
// 🎯 추가 조인 컬럼 정보 구성
|
||||
const additionalJoinColumns: Array<{
|
||||
sourceTable: string;
|
||||
sourceColumn: string;
|
||||
joinAlias: string;
|
||||
referenceTable?: string;
|
||||
}> = [];
|
||||
|
||||
// Entity 조인 컬럼들
|
||||
entityJoinColumns.forEach((col) => {
|
||||
additionalJoinColumns.push({
|
||||
sourceTable: col.entityJoinInfo!.sourceTable,
|
||||
sourceColumn: col.entityJoinInfo!.sourceColumn,
|
||||
joinAlias: col.entityJoinInfo!.joinAlias,
|
||||
})),
|
||||
// 🎯 조인 탭에서 추가한 컬럼들도 추가 (실제로 존재하는 컬럼만)
|
||||
...joinTabColumns
|
||||
.filter((col) => {
|
||||
// 실제 API 응답에 존재하는 컬럼만 필터링
|
||||
const validJoinColumns = ["dept_code_name", "dept_name"];
|
||||
const isValid = validJoinColumns.includes(col.columnName);
|
||||
if (!isValid) {
|
||||
console.log(`🔍 조인 탭 컬럼 제외: ${col.columnName} (유효하지 않음)`);
|
||||
}
|
||||
return isValid;
|
||||
})
|
||||
.map((col) => {
|
||||
// 실제 존재하는 조인 컬럼만 처리
|
||||
let sourceTable = tableConfig.selectedTable;
|
||||
let sourceColumn = col.columnName;
|
||||
});
|
||||
});
|
||||
|
||||
if (col.columnName === "dept_code_name" || col.columnName === "dept_name") {
|
||||
sourceTable = "dept_info";
|
||||
sourceColumn = "dept_code";
|
||||
}
|
||||
|
||||
console.log(`🔍 조인 탭 컬럼 처리: ${col.columnName} -> ${sourceTable}.${sourceColumn}`);
|
||||
|
||||
return {
|
||||
sourceTable: sourceTable || tableConfig.selectedTable || "",
|
||||
sourceColumn: sourceColumn,
|
||||
joinAlias: col.columnName,
|
||||
};
|
||||
}),
|
||||
];
|
||||
|
||||
// 🎯 화면별 엔티티 표시 설정 생성
|
||||
const screenEntityConfigs: Record<string, any> = {};
|
||||
entityJoinColumns.forEach((col) => {
|
||||
if (col.entityDisplayConfig) {
|
||||
const sourceColumn = col.entityJoinInfo!.sourceColumn;
|
||||
screenEntityConfigs[sourceColumn] = {
|
||||
displayColumns: col.entityDisplayConfig.displayColumns,
|
||||
separator: col.entityDisplayConfig.separator || " - ",
|
||||
};
|
||||
// 수동 조인 컬럼들 - 저장된 조인 정보 사용
|
||||
manualJoinColumns.forEach((col) => {
|
||||
if (col.additionalJoinInfo) {
|
||||
additionalJoinColumns.push({
|
||||
sourceTable: col.additionalJoinInfo.sourceTable,
|
||||
sourceColumn: col.additionalJoinInfo.sourceColumn,
|
||||
joinAlias: col.additionalJoinInfo.joinAlias,
|
||||
referenceTable: col.additionalJoinInfo.referenceTable,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
console.log("🔗 Entity 조인 컬럼:", entityJoinColumns);
|
||||
console.log("🔗 조인 탭 컬럼:", joinTabColumns);
|
||||
console.log("🔗 추가 Entity 조인 컬럼:", additionalJoinColumns);
|
||||
// console.log("🎯 화면별 엔티티 설정:", screenEntityConfigs);
|
||||
console.log("🔗 최종 추가 조인 컬럼:", additionalJoinColumns);
|
||||
|
||||
const result = await entityJoinApi.getTableDataWithJoins(tableConfig.selectedTable, {
|
||||
page: currentPage,
|
||||
@@ -591,7 +564,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
sortOrder: sortDirection,
|
||||
enableEntityJoin: true, // 🎯 Entity 조인 활성화
|
||||
additionalJoinColumns: additionalJoinColumns.length > 0 ? additionalJoinColumns : undefined, // 추가 조인 컬럼
|
||||
screenEntityConfigs: Object.keys(screenEntityConfigs).length > 0 ? screenEntityConfigs : undefined, // 🎯 화면별 엔티티 설정
|
||||
});
|
||||
|
||||
if (result) {
|
||||
@@ -661,16 +633,16 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
const actualApiColumns = Object.keys(result.data[0]);
|
||||
console.log("🔍 API 응답의 실제 컬럼들:", actualApiColumns);
|
||||
|
||||
// 🎯 조인 컬럼 매핑 테이블 (사용자 설정 → API 응답)
|
||||
// 실제 API 응답에 존재하는 컬럼만 매핑
|
||||
const newJoinColumnMapping: Record<string, string> = {
|
||||
dept_code_dept_code: "dept_code", // user_info.dept_code
|
||||
dept_code_status: "status", // user_info.status (dept_info.status가 조인되지 않음)
|
||||
dept_code_company_name: "dept_name", // dept_info.dept_name (company_name이 조인되지 않음)
|
||||
dept_code_name: "dept_code_name", // dept_info.dept_name
|
||||
dept_name: "dept_name", // dept_info.dept_name
|
||||
status: "status", // user_info.status
|
||||
};
|
||||
// 🎯 조인 컬럼 매핑 테이블 - 동적 생성
|
||||
// API 응답에 실제로 존재하는 컬럼과 사용자 설정 컬럼을 비교하여 자동 매핑
|
||||
const newJoinColumnMapping: Record<string, string> = {};
|
||||
|
||||
processedColumns.forEach((col) => {
|
||||
// API 응답에 정확히 일치하는 컬럼이 있으면 그대로 사용
|
||||
if (actualApiColumns.includes(col.columnName)) {
|
||||
newJoinColumnMapping[col.columnName] = col.columnName;
|
||||
}
|
||||
});
|
||||
|
||||
// 🎯 조인 컬럼 매핑 상태 업데이트
|
||||
setJoinColumnMapping(newJoinColumnMapping);
|
||||
@@ -795,7 +767,37 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
}, [
|
||||
tableConfig.selectedTable,
|
||||
tableConfig.columns,
|
||||
currentPage,
|
||||
localPageSize,
|
||||
searchTerm,
|
||||
sortColumn,
|
||||
sortDirection,
|
||||
searchValues,
|
||||
]);
|
||||
|
||||
// 디바운싱된 테이블 데이터 가져오기
|
||||
const fetchTableDataDebounced = useCallback(
|
||||
debouncedApiCall(
|
||||
`fetchTableData_${tableConfig.selectedTable}_${currentPage}_${localPageSize}`,
|
||||
async () => {
|
||||
return fetchTableDataInternal();
|
||||
},
|
||||
200, // 200ms 디바운스
|
||||
),
|
||||
[
|
||||
tableConfig.selectedTable,
|
||||
currentPage,
|
||||
localPageSize,
|
||||
searchTerm,
|
||||
sortColumn,
|
||||
sortDirection,
|
||||
searchValues,
|
||||
fetchTableDataInternal,
|
||||
],
|
||||
);
|
||||
|
||||
// 페이지 변경
|
||||
const handlePageChange = (newPage: number) => {
|
||||
@@ -947,12 +949,37 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
}
|
||||
}, [columnLabels]);
|
||||
|
||||
// 🎯 컬럼 개수와 컬럼명을 문자열로 변환하여 의존성 추적
|
||||
const columnsKey = useMemo(() => {
|
||||
if (!tableConfig.columns) return "";
|
||||
return tableConfig.columns.map((col) => col.columnName).join(",");
|
||||
}, [tableConfig.columns]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tableConfig.autoLoad && !isDesignMode) {
|
||||
fetchTableDataDebounced();
|
||||
// autoLoad가 undefined거나 true일 때 자동 로드 (기본값: true)
|
||||
const shouldAutoLoad = tableConfig.autoLoad !== false;
|
||||
|
||||
console.log("🔍 TableList 데이터 로드 조건 체크:", {
|
||||
shouldAutoLoad,
|
||||
isDesignMode,
|
||||
selectedTable: tableConfig.selectedTable,
|
||||
autoLoadSetting: tableConfig.autoLoad,
|
||||
willLoad: shouldAutoLoad && !isDesignMode,
|
||||
});
|
||||
|
||||
if (shouldAutoLoad && !isDesignMode) {
|
||||
console.log("✅ 테이블 데이터 로드 시작:", tableConfig.selectedTable);
|
||||
fetchTableDataInternal();
|
||||
} else {
|
||||
console.warn("⚠️ 테이블 데이터 로드 차단:", {
|
||||
reason: !shouldAutoLoad ? "autoLoad=false" : "isDesignMode=true",
|
||||
shouldAutoLoad,
|
||||
isDesignMode,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
tableConfig.selectedTable,
|
||||
columnsKey, // 🎯 컬럼이 추가/변경될 때 데이터 다시 로드 (문자열 비교)
|
||||
localPageSize,
|
||||
currentPage,
|
||||
searchTerm,
|
||||
@@ -960,6 +987,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
sortDirection,
|
||||
columnLabels,
|
||||
searchValues,
|
||||
fetchTableDataInternal, // 의존성 배열에 추가
|
||||
]);
|
||||
|
||||
// refreshKey 변경 시 테이블 데이터 새로고침
|
||||
@@ -992,7 +1020,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
};
|
||||
|
||||
window.addEventListener("refreshTable", handleRefreshTable);
|
||||
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("refreshTable", handleRefreshTable);
|
||||
};
|
||||
@@ -1314,35 +1342,18 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
onDragEnd,
|
||||
};
|
||||
|
||||
// 디자인 모드에서의 플레이스홀더
|
||||
if (isDesignMode && !tableConfig.selectedTable) {
|
||||
return (
|
||||
<div style={componentStyle} className={className} {...domProps}>
|
||||
<div className="flex h-full items-center justify-center rounded-2xl border-2 border-dashed border-blue-200 bg-gradient-to-br from-blue-50/30 to-indigo-50/20">
|
||||
<div className="p-8 text-center">
|
||||
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-blue-100 to-indigo-100 shadow-sm">
|
||||
<TableIcon className="h-8 w-8 text-blue-600" />
|
||||
</div>
|
||||
<div className="mb-2 text-lg font-semibold text-slate-700">테이블 리스트</div>
|
||||
<div className="rounded-full bg-white/60 px-4 py-2 text-sm text-slate-500">
|
||||
설정 패널에서 테이블을 선택해주세요
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// 플레이스홀더 제거 - 디자인 모드에서도 바로 테이블 표시
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ ...componentStyle, zIndex: 10 }} // 🎯 componentStyle + z-index 추가
|
||||
className={cn(
|
||||
"relative overflow-hidden",
|
||||
"bg-white border border-gray-200/60",
|
||||
"border border-gray-200/60 bg-white",
|
||||
"rounded-2xl shadow-sm",
|
||||
"backdrop-blur-sm",
|
||||
"transition-all duration-300 ease-out",
|
||||
isSelected && "ring-2 ring-blue-500/20 shadow-lg shadow-blue-500/10",
|
||||
isSelected && "shadow-lg ring-2 shadow-blue-500/10 ring-blue-500/20",
|
||||
className,
|
||||
)}
|
||||
{...domProps}
|
||||
@@ -1359,7 +1370,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
>
|
||||
<div className="flex items-center space-x-4">
|
||||
{(tableConfig.title || tableLabel) && (
|
||||
<h3 className="text-xl font-semibold text-gray-800 tracking-tight">{tableConfig.title || tableLabel}</h3>
|
||||
<h3 className="text-xl font-semibold tracking-tight text-gray-800">{tableConfig.title || tableLabel}</h3>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1377,16 +1388,14 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
size="sm"
|
||||
onClick={handleRefresh}
|
||||
disabled={loading}
|
||||
className="group relative rounded-xl border-gray-200/60 bg-white/80 backdrop-blur-sm shadow-sm hover:shadow-md transition-all duration-200 hover:bg-gray-50/80"
|
||||
className="group relative rounded-xl border-gray-200/60 bg-white/80 shadow-sm backdrop-blur-sm transition-all duration-200 hover:bg-gray-50/80 hover:shadow-md"
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="relative">
|
||||
<RefreshCw className={cn("h-4 w-4 text-gray-600", loading && "animate-spin")} />
|
||||
{loading && <div className="absolute -inset-1 animate-pulse rounded-full bg-blue-100/40"></div>}
|
||||
</div>
|
||||
<span className="text-sm font-medium text-gray-700">
|
||||
{loading ? "새로고침 중..." : "새로고침"}
|
||||
</span>
|
||||
<span className="text-sm font-medium text-gray-700">{loading ? "새로고침 중..." : "새로고침"}</span>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
@@ -1424,7 +1433,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
|
||||
{/* 테이블 컨텐츠 */}
|
||||
<div
|
||||
className={`w-full overflow-auto flex-1`}
|
||||
className={`w-full flex-1 overflow-auto`}
|
||||
style={{
|
||||
width: "100%",
|
||||
maxWidth: "100%",
|
||||
@@ -1622,7 +1631,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
<TableCell
|
||||
key={column.columnName}
|
||||
className={cn(
|
||||
"h-12 px-6 py-4 align-middle text-sm transition-all duration-200 text-gray-600",
|
||||
"h-12 px-6 py-4 align-middle text-sm text-gray-600 transition-all duration-200",
|
||||
`text-${column.align}`,
|
||||
)}
|
||||
style={{
|
||||
@@ -1687,7 +1696,8 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
</div>
|
||||
|
||||
{/* 푸터/페이지네이션 */}
|
||||
{tableConfig.showFooter && tableConfig.pagination?.enabled && (
|
||||
{/* showFooter와 pagination.enabled의 기본값은 true */}
|
||||
{tableConfig.showFooter !== false && tableConfig.pagination?.enabled !== false && (
|
||||
<div
|
||||
className="flex flex-col items-center justify-center space-y-4 border-t border-gray-200 bg-gray-100/80 p-6"
|
||||
style={{
|
||||
@@ -1749,7 +1759,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
|
||||
// 데이터는 useEffect에서 자동으로 다시 로드됨
|
||||
}}
|
||||
className="rounded-xl border border-gray-200/60 bg-white/80 backdrop-blur-sm px-4 py-2 text-sm font-medium text-gray-700 shadow-sm transition-all duration-200 hover:border-gray-300/60 hover:bg-white hover:shadow-md"
|
||||
className="rounded-xl border border-gray-200/60 bg-white/80 px-4 py-2 text-sm font-medium text-gray-700 shadow-sm backdrop-blur-sm transition-all duration-200 hover:border-gray-300/60 hover:bg-white hover:shadow-md"
|
||||
>
|
||||
{(tableConfig.pagination?.pageSizeOptions || [10, 20, 50, 100]).map((size) => (
|
||||
<option key={size} value={size}>
|
||||
@@ -1760,13 +1770,13 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
)}
|
||||
|
||||
{/* 페이지네이션 버튼 */}
|
||||
<div className="flex items-center space-x-2 rounded-xl border border-gray-200/60 bg-white/80 backdrop-blur-sm p-1 shadow-sm">
|
||||
<div className="flex items-center space-x-2 rounded-xl border border-gray-200/60 bg-white/80 p-1 shadow-sm backdrop-blur-sm">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => handlePageChange(1)}
|
||||
disabled={currentPage === 1}
|
||||
className="h-8 w-8 p-0 rounded-lg border-gray-200/60 hover:border-gray-300/60 hover:bg-gray-50/80 hover:text-gray-700 disabled:opacity-50 transition-all duration-200"
|
||||
className="h-8 w-8 rounded-lg border-gray-200/60 p-0 transition-all duration-200 hover:border-gray-300/60 hover:bg-gray-50/80 hover:text-gray-700 disabled:opacity-50"
|
||||
>
|
||||
<ChevronsLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -1775,7 +1785,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
size="sm"
|
||||
onClick={() => handlePageChange(currentPage - 1)}
|
||||
disabled={currentPage === 1}
|
||||
className="h-8 w-8 p-0 rounded-lg border-gray-200/60 hover:border-gray-300/60 hover:bg-gray-50/80 hover:text-gray-700 disabled:opacity-50 transition-all duration-200"
|
||||
className="h-8 w-8 rounded-lg border-gray-200/60 p-0 transition-all duration-200 hover:border-gray-300/60 hover:bg-gray-50/80 hover:text-gray-700 disabled:opacity-50"
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -1791,7 +1801,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
size="sm"
|
||||
onClick={() => handlePageChange(currentPage + 1)}
|
||||
disabled={currentPage === totalPages}
|
||||
className="h-8 w-8 p-0 rounded-lg border-gray-200/60 hover:border-gray-300/60 hover:bg-gray-50/80 hover:text-gray-700 disabled:opacity-50 transition-all duration-200"
|
||||
className="h-8 w-8 rounded-lg border-gray-200/60 p-0 transition-all duration-200 hover:border-gray-300/60 hover:bg-gray-50/80 hover:text-gray-700 disabled:opacity-50"
|
||||
>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -1800,7 +1810,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||
size="sm"
|
||||
onClick={() => handlePageChange(totalPages)}
|
||||
disabled={currentPage === totalPages}
|
||||
className="h-8 w-8 p-0 rounded-lg border-gray-200/60 hover:border-gray-300/60 hover:bg-gray-50/80 hover:text-gray-700 disabled:opacity-50 transition-all duration-200"
|
||||
className="h-8 w-8 rounded-lg border-gray-200/60 p-0 transition-all duration-200 hover:border-gray-300/60 hover:bg-gray-50/80 hover:text-gray-700 disabled:opacity-50"
|
||||
>
|
||||
<ChevronsRight className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
@@ -97,13 +97,20 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
>
|
||||
>({});
|
||||
|
||||
// 화면 테이블명이 있으면 자동으로 설정
|
||||
// 화면 테이블명이 있으면 자동으로 설정 (초기 한 번만)
|
||||
useEffect(() => {
|
||||
if (screenTableName && (!config.selectedTable || config.selectedTable !== screenTableName)) {
|
||||
console.log("🔄 화면 테이블명 자동 설정:", screenTableName);
|
||||
onChange({ selectedTable: screenTableName });
|
||||
if (screenTableName && !config.selectedTable) {
|
||||
// 기존 config의 모든 속성을 유지하면서 selectedTable만 추가/업데이트
|
||||
const updatedConfig = {
|
||||
...config,
|
||||
selectedTable: screenTableName,
|
||||
// 컬럼이 있으면 유지, 없으면 빈 배열
|
||||
columns: config.columns || [],
|
||||
};
|
||||
onChange(updatedConfig);
|
||||
}
|
||||
}, [screenTableName, config.selectedTable, onChange]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [screenTableName]); // config.selectedTable이 없을 때만 실행되도록 의존성 최소화
|
||||
|
||||
// 테이블 목록 가져오기
|
||||
useEffect(() => {
|
||||
@@ -137,25 +144,32 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
screenTableName,
|
||||
);
|
||||
|
||||
// 컴포넌트에 명시적으로 테이블이 선택되었거나, 화면에 연결된 테이블이 있는 경우에만 컬럼 목록 표시
|
||||
const shouldShowColumns = config.selectedTable || (screenTableName && config.columns && config.columns.length > 0);
|
||||
// 컴포넌트에 명시적으로 테이블이 선택되었거나, 화면에 연결된 테이블이 있는 경우 컬럼 목록 표시
|
||||
const shouldShowColumns = config.selectedTable || screenTableName;
|
||||
|
||||
if (!shouldShowColumns) {
|
||||
console.log("🔧 컬럼 목록 숨김 - 명시적 테이블 선택 또는 설정된 컬럼이 없음");
|
||||
console.log("🔧 컬럼 목록 숨김 - 테이블이 선택되지 않음");
|
||||
setAvailableColumns([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// tableColumns prop을 우선 사용하되, 컴포넌트가 명시적으로 설정되었을 때만
|
||||
if (tableColumns && tableColumns.length > 0 && (config.selectedTable || config.columns?.length > 0)) {
|
||||
console.log("🔧 tableColumns prop 사용:", tableColumns);
|
||||
// tableColumns prop을 우선 사용
|
||||
if (tableColumns && tableColumns.length > 0) {
|
||||
const mappedColumns = tableColumns.map((column: any) => ({
|
||||
columnName: column.columnName || column.name,
|
||||
dataType: column.dataType || column.type || "text",
|
||||
label: column.label || column.displayName || column.columnLabel || column.columnName || column.name,
|
||||
}));
|
||||
console.log("🏷️ availableColumns 설정됨:", mappedColumns);
|
||||
setAvailableColumns(mappedColumns);
|
||||
|
||||
// selectedTable이 없으면 screenTableName으로 설정
|
||||
if (!config.selectedTable && screenTableName) {
|
||||
onChange({
|
||||
...config,
|
||||
selectedTable: screenTableName,
|
||||
columns: config.columns || [],
|
||||
});
|
||||
}
|
||||
} else if (config.selectedTable || screenTableName) {
|
||||
// API에서 컬럼 정보 가져오기
|
||||
const fetchColumns = async () => {
|
||||
@@ -190,7 +204,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
} else {
|
||||
setAvailableColumns([]);
|
||||
}
|
||||
}, [config.selectedTable, screenTableName, tableColumns, config.columns]);
|
||||
}, [config.selectedTable, screenTableName, tableColumns]);
|
||||
|
||||
// Entity 조인 컬럼 정보 가져오기
|
||||
useEffect(() => {
|
||||
@@ -235,7 +249,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
// hasOnChange: !!onChange,
|
||||
// onChangeType: typeof onChange,
|
||||
// });
|
||||
|
||||
|
||||
const parentValue = config[parentKey] as any;
|
||||
const newConfig = {
|
||||
[parentKey]: {
|
||||
@@ -243,7 +257,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
[childKey]: value,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// console.log("📤 TableListConfigPanel onChange 호출:", newConfig);
|
||||
onChange(newConfig);
|
||||
};
|
||||
@@ -275,8 +289,30 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
|
||||
// 🎯 조인 컬럼 추가 (조인 탭에서 추가하는 컬럼들은 일반 컬럼으로 처리)
|
||||
const addEntityColumn = (joinColumn: (typeof entityJoinColumns.availableColumns)[0]) => {
|
||||
console.log("🔗 조인 컬럼 추가 요청:", {
|
||||
joinColumn,
|
||||
joinAlias: joinColumn.joinAlias,
|
||||
columnLabel: joinColumn.columnLabel,
|
||||
tableName: joinColumn.tableName,
|
||||
columnName: joinColumn.columnName,
|
||||
});
|
||||
|
||||
const existingColumn = config.columns?.find((col) => col.columnName === joinColumn.joinAlias);
|
||||
if (existingColumn) return;
|
||||
if (existingColumn) {
|
||||
console.warn("⚠️ 이미 존재하는 컬럼:", joinColumn.joinAlias);
|
||||
return;
|
||||
}
|
||||
|
||||
// 🎯 joinTables에서 sourceColumn 찾기
|
||||
const joinTableInfo = entityJoinColumns.joinTables?.find((jt: any) => jt.tableName === joinColumn.tableName);
|
||||
const sourceColumn = joinTableInfo?.joinConfig?.sourceColumn || "";
|
||||
|
||||
console.log("🔍 조인 정보 추출:", {
|
||||
tableName: joinColumn.tableName,
|
||||
foundJoinTable: !!joinTableInfo,
|
||||
sourceColumn,
|
||||
joinConfig: joinTableInfo?.joinConfig,
|
||||
});
|
||||
|
||||
// 조인 탭에서 추가하는 컬럼들은 일반 컬럼으로 처리 (isEntityJoin: false)
|
||||
const newColumn: ColumnConfig = {
|
||||
@@ -289,10 +325,21 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
format: "text",
|
||||
order: config.columns?.length || 0,
|
||||
isEntityJoin: false, // 조인 탭에서 추가하는 컬럼은 엔티티 타입이 아님
|
||||
// 🎯 추가 조인 정보 저장
|
||||
additionalJoinInfo: {
|
||||
sourceTable: config.selectedTable || screenTableName || "", // 기준 테이블 (예: user_info)
|
||||
sourceColumn: sourceColumn, // 기준 컬럼 (예: dept_code) - joinTables에서 추출
|
||||
referenceTable: joinColumn.tableName, // 참조 테이블 (예: dept_info)
|
||||
joinAlias: joinColumn.joinAlias, // 조인 별칭 (예: dept_code_company_name)
|
||||
},
|
||||
};
|
||||
|
||||
handleChange("columns", [...(config.columns || []), newColumn]);
|
||||
console.log("🔗 조인 컬럼 추가됨 (일반 컬럼으로 처리):", newColumn);
|
||||
console.log("✅ 조인 컬럼 추가 완료:", {
|
||||
columnName: newColumn.columnName,
|
||||
displayName: newColumn.displayName,
|
||||
totalColumns: (config.columns?.length || 0) + 1,
|
||||
});
|
||||
};
|
||||
|
||||
// 컬럼 제거
|
||||
@@ -309,17 +356,31 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
};
|
||||
|
||||
// 🎯 기존 컬럼들을 체크하여 엔티티 타입인 경우 isEntityJoin 플래그 설정
|
||||
// useRef로 이전 컬럼 개수를 추적하여 새 컬럼 추가 시에만 실행
|
||||
const prevColumnsLengthRef = React.useRef<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
const currentLength = config.columns?.length || 0;
|
||||
const prevLength = prevColumnsLengthRef.current;
|
||||
|
||||
console.log("🔍 엔티티 컬럼 감지 useEffect 실행:", {
|
||||
hasColumns: !!config.columns,
|
||||
columnsCount: config.columns?.length || 0,
|
||||
columnsCount: currentLength,
|
||||
prevColumnsCount: prevLength,
|
||||
hasTableColumns: !!tableColumns,
|
||||
tableColumnsCount: tableColumns?.length || 0,
|
||||
selectedTable: config.selectedTable,
|
||||
});
|
||||
|
||||
if (!config.columns || !tableColumns) {
|
||||
if (!config.columns || !tableColumns || config.columns.length === 0) {
|
||||
console.log("⚠️ 컬럼 또는 테이블 컬럼 정보가 없어서 엔티티 감지 스킵");
|
||||
prevColumnsLengthRef.current = currentLength;
|
||||
return;
|
||||
}
|
||||
|
||||
// 컬럼 개수가 변경되지 않았고, 이미 체크한 적이 있으면 스킵
|
||||
if (currentLength === prevLength && prevLength > 0) {
|
||||
console.log("ℹ️ 컬럼 개수 변경 없음, 엔티티 감지 스킵");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -352,14 +413,14 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
...column,
|
||||
isEntityJoin: true,
|
||||
entityJoinInfo: {
|
||||
sourceTable: config.selectedTable || "",
|
||||
sourceTable: config.selectedTable || screenTableName || "",
|
||||
sourceColumn: column.columnName,
|
||||
joinAlias: column.columnName,
|
||||
},
|
||||
entityDisplayConfig: {
|
||||
displayColumns: [], // 빈 배열로 초기화
|
||||
separator: " - ",
|
||||
sourceTable: config.selectedTable || "",
|
||||
sourceTable: config.selectedTable || screenTableName || "",
|
||||
joinTable: tableColumn.reference_table || tableColumn.referenceTable || "",
|
||||
},
|
||||
};
|
||||
@@ -377,7 +438,11 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
} else {
|
||||
console.log("ℹ️ 엔티티 컬럼 변경사항 없음");
|
||||
}
|
||||
}, [config.columns, tableColumns, config.selectedTable]);
|
||||
|
||||
// 현재 컬럼 개수를 저장
|
||||
prevColumnsLengthRef.current = currentLength;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [config.columns?.length, tableColumns, config.selectedTable]); // 컬럼 개수 변경 시에만 실행
|
||||
|
||||
// 🎯 엔티티 컬럼의 표시 컬럼 정보 로드
|
||||
const loadEntityDisplayConfig = async (column: ColumnConfig) => {
|
||||
@@ -400,6 +465,15 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
// entityDisplayConfig가 없으면 초기화
|
||||
if (!column.entityDisplayConfig) {
|
||||
console.log("🔧 entityDisplayConfig 초기화:", column.columnName);
|
||||
|
||||
// sourceTable을 결정: entityJoinInfo -> config.selectedTable -> screenTableName 순서
|
||||
const initialSourceTable = column.entityJoinInfo?.sourceTable || config.selectedTable || screenTableName;
|
||||
|
||||
if (!initialSourceTable) {
|
||||
console.warn("⚠️ sourceTable을 결정할 수 없어서 초기화 실패:", column.columnName);
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedColumns = config.columns?.map((col) => {
|
||||
if (col.columnName === column.columnName) {
|
||||
return {
|
||||
@@ -407,7 +481,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
entityDisplayConfig: {
|
||||
displayColumns: [],
|
||||
separator: " - ",
|
||||
sourceTable: config.selectedTable || "",
|
||||
sourceTable: initialSourceTable,
|
||||
joinTable: "",
|
||||
},
|
||||
};
|
||||
@@ -430,15 +504,34 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
console.log("🔍 entityDisplayConfig 전체 구조:", column.entityDisplayConfig);
|
||||
console.log("🔍 entityDisplayConfig 키들:", Object.keys(column.entityDisplayConfig));
|
||||
|
||||
// sourceTable과 joinTable이 없으면 entityJoinInfo에서 가져오기
|
||||
let sourceTable = column.entityDisplayConfig.sourceTable;
|
||||
// sourceTable 결정 우선순위:
|
||||
// 1. entityDisplayConfig.sourceTable
|
||||
// 2. entityJoinInfo.sourceTable
|
||||
// 3. config.selectedTable
|
||||
// 4. screenTableName
|
||||
let sourceTable =
|
||||
column.entityDisplayConfig.sourceTable ||
|
||||
column.entityJoinInfo?.sourceTable ||
|
||||
config.selectedTable ||
|
||||
screenTableName;
|
||||
|
||||
let joinTable = column.entityDisplayConfig.joinTable;
|
||||
|
||||
if (!sourceTable && column.entityJoinInfo) {
|
||||
sourceTable = column.entityJoinInfo.sourceTable;
|
||||
// sourceTable이 여전히 비어있으면 에러
|
||||
if (!sourceTable) {
|
||||
console.error("❌ sourceTable이 비어있어서 처리 불가:", {
|
||||
columnName: column.columnName,
|
||||
entityDisplayConfig: column.entityDisplayConfig,
|
||||
entityJoinInfo: column.entityJoinInfo,
|
||||
configSelectedTable: config.selectedTable,
|
||||
screenTableName,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!joinTable) {
|
||||
console.log("✅ sourceTable 결정됨:", sourceTable);
|
||||
|
||||
if (!joinTable && sourceTable) {
|
||||
// joinTable이 없으면 tableTypeApi로 조회해서 설정
|
||||
try {
|
||||
console.log("🔍 joinTable이 없어서 tableTypeApi로 조회:", sourceTable);
|
||||
@@ -464,10 +557,15 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
if (updatedColumns) {
|
||||
handleChange("columns", updatedColumns);
|
||||
}
|
||||
} else {
|
||||
console.warn("⚠️ tableTypeApi에서 조인 테이블 정보를 찾지 못함:", column.columnName);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("tableTypeApi 컬럼 정보 조회 실패:", error);
|
||||
console.log("❌ 조회 실패 상세:", { sourceTable, columnName: column.columnName });
|
||||
}
|
||||
} else if (!joinTable) {
|
||||
console.warn("⚠️ sourceTable이 없어서 joinTable 조회 불가:", column.columnName);
|
||||
}
|
||||
|
||||
console.log("🔍 최종 추출한 값:", { sourceTable, joinTable });
|
||||
@@ -789,15 +887,13 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
<div className="space-y-4 border-t pt-4">
|
||||
<div className="space-y-3">
|
||||
<Label className="text-sm font-medium">카드 레이아웃 설정</Label>
|
||||
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="cards-per-row">한 행당 카드 수</Label>
|
||||
<Select
|
||||
value={config.cardConfig?.cardsPerRow?.toString() || "3"}
|
||||
onValueChange={(value) =>
|
||||
handleNestedChange("cardConfig", "cardsPerRow", parseInt(value))
|
||||
}
|
||||
onValueChange={(value) => handleNestedChange("cardConfig", "cardsPerRow", parseInt(value))}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
@@ -819,9 +915,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
id="card-spacing"
|
||||
type="number"
|
||||
value={config.cardConfig?.cardSpacing || 16}
|
||||
onChange={(e) =>
|
||||
handleNestedChange("cardConfig", "cardSpacing", parseInt(e.target.value))
|
||||
}
|
||||
onChange={(e) => handleNestedChange("cardConfig", "cardSpacing", parseInt(e.target.value))}
|
||||
min="0"
|
||||
max="50"
|
||||
/>
|
||||
@@ -830,15 +924,13 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
|
||||
<div className="space-y-3">
|
||||
<Label className="text-sm font-medium">카드 필드 매핑</Label>
|
||||
|
||||
|
||||
<div className="grid grid-cols-1 gap-3">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="id-column">ID 컬럼 (사번 등)</Label>
|
||||
<Select
|
||||
value={config.cardConfig?.idColumn || ""}
|
||||
onValueChange={(value) =>
|
||||
handleNestedChange("cardConfig", "idColumn", value)
|
||||
}
|
||||
onValueChange={(value) => handleNestedChange("cardConfig", "idColumn", value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="ID 컬럼 선택" />
|
||||
@@ -857,9 +949,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
<Label htmlFor="title-column">제목 컬럼 (이름 등)</Label>
|
||||
<Select
|
||||
value={config.cardConfig?.titleColumn || ""}
|
||||
onValueChange={(value) =>
|
||||
handleNestedChange("cardConfig", "titleColumn", value)
|
||||
}
|
||||
onValueChange={(value) => handleNestedChange("cardConfig", "titleColumn", value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="제목 컬럼 선택" />
|
||||
@@ -878,9 +968,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
<Label htmlFor="subtitle-column">서브 제목 컬럼 (부서 등)</Label>
|
||||
<Select
|
||||
value={config.cardConfig?.subtitleColumn || ""}
|
||||
onValueChange={(value) =>
|
||||
handleNestedChange("cardConfig", "subtitleColumn", value)
|
||||
}
|
||||
onValueChange={(value) => handleNestedChange("cardConfig", "subtitleColumn", value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="서브 제목 컬럼 선택 (선택사항)" />
|
||||
@@ -900,9 +988,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
<Label htmlFor="description-column">설명 컬럼</Label>
|
||||
<Select
|
||||
value={config.cardConfig?.descriptionColumn || ""}
|
||||
onValueChange={(value) =>
|
||||
handleNestedChange("cardConfig", "descriptionColumn", value)
|
||||
}
|
||||
onValueChange={(value) => handleNestedChange("cardConfig", "descriptionColumn", value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="설명 컬럼 선택 (선택사항)" />
|
||||
@@ -924,7 +1010,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
<Checkbox
|
||||
id="show-card-actions"
|
||||
checked={config.cardConfig?.showActions !== false}
|
||||
onCheckedChange={(checked) =>
|
||||
onCheckedChange={(checked) =>
|
||||
handleNestedChange("cardConfig", "showActions", checked as boolean)
|
||||
}
|
||||
/>
|
||||
@@ -1270,7 +1356,34 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => loadEntityDisplayConfig(column)}
|
||||
onClick={() => {
|
||||
// sourceTable 정보가 있는지 확인
|
||||
const hasSourceTable =
|
||||
column.entityDisplayConfig?.sourceTable ||
|
||||
column.entityJoinInfo?.sourceTable ||
|
||||
config.selectedTable ||
|
||||
screenTableName;
|
||||
|
||||
if (!hasSourceTable) {
|
||||
console.error("❌ sourceTable 정보를 찾을 수 없어서 컬럼 로드 불가:", {
|
||||
columnName: column.columnName,
|
||||
entityDisplayConfig: column.entityDisplayConfig,
|
||||
entityJoinInfo: column.entityJoinInfo,
|
||||
configSelectedTable: config.selectedTable,
|
||||
screenTableName,
|
||||
});
|
||||
alert("컬럼 정보를 로드할 수 없습니다. 테이블 정보가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
loadEntityDisplayConfig(column);
|
||||
}}
|
||||
disabled={
|
||||
!column.entityDisplayConfig?.sourceTable &&
|
||||
!column.entityJoinInfo?.sourceTable &&
|
||||
!config.selectedTable &&
|
||||
!screenTableName
|
||||
}
|
||||
className="h-6 text-xs"
|
||||
>
|
||||
<Plus className="mr-1 h-3 w-3" />
|
||||
|
||||
@@ -72,21 +72,29 @@ export interface ColumnConfig {
|
||||
// 새로운 기능들
|
||||
hidden?: boolean; // 숨김 기능 (편집기에서는 연하게, 실제 화면에서는 숨김)
|
||||
autoGeneration?: AutoGenerationConfig; // 자동생성 설정
|
||||
|
||||
// 🎯 추가 조인 컬럼 정보 (조인 탭에서 추가한 컬럼들)
|
||||
additionalJoinInfo?: {
|
||||
sourceTable: string; // 원본 테이블
|
||||
sourceColumn: string; // 원본 컬럼 (예: dept_code)
|
||||
referenceTable?: string; // 참조 테이블 (예: dept_info)
|
||||
joinAlias: string; // 조인 별칭 (예: dept_code_company_name)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 카드 디스플레이 설정
|
||||
*/
|
||||
export interface CardDisplayConfig {
|
||||
idColumn: string; // ID 컬럼 (사번 등)
|
||||
titleColumn: string; // 제목 컬럼 (이름 등)
|
||||
subtitleColumn?: string; // 부제목 컬럼 (부서 등)
|
||||
idColumn: string; // ID 컬럼 (사번 등)
|
||||
titleColumn: string; // 제목 컬럼 (이름 등)
|
||||
subtitleColumn?: string; // 부제목 컬럼 (부서 등)
|
||||
descriptionColumn?: string; // 설명 컬럼
|
||||
imageColumn?: string; // 이미지 컬럼
|
||||
cardsPerRow: number; // 한 행당 카드 수 (기본: 3)
|
||||
cardSpacing: number; // 카드 간격 (기본: 16px)
|
||||
showActions: boolean; // 액션 버튼 표시 여부
|
||||
cardHeight?: number; // 카드 높이 (기본: auto)
|
||||
imageColumn?: string; // 이미지 컬럼
|
||||
cardsPerRow: number; // 한 행당 카드 수 (기본: 3)
|
||||
cardSpacing: number; // 카드 간격 (기본: 16px)
|
||||
showActions: boolean; // 액션 버튼 표시 여부
|
||||
cardHeight?: number; // 카드 높이 (기본: auto)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,11 +171,11 @@ export interface CheckboxConfig {
|
||||
*/
|
||||
export interface TableListConfig extends ComponentConfig {
|
||||
// 표시 모드 설정
|
||||
displayMode?: "table" | "card"; // 기본: "table"
|
||||
|
||||
displayMode?: "table" | "card"; // 기본: "table"
|
||||
|
||||
// 카드 디스플레이 설정 (displayMode가 "card"일 때 사용)
|
||||
cardConfig?: CardDisplayConfig;
|
||||
|
||||
|
||||
// 테이블 기본 설정
|
||||
selectedTable?: string;
|
||||
tableName?: string;
|
||||
|
||||
Reference in New Issue
Block a user