fix: 엔티티 컬럼 표시 설정 문제 해결
- TableListComponent에서 엔티티 컬럼 라벨을 기준 테이블 라벨로 표시 - TableListConfigPanel에서 input_type 필드로 엔티티 컬럼 감지 - ScreenDesigner에서 컬럼 정보 로드 시 input_type 필드 포함 - UnifiedColumnInfo 타입에 input_type 필드 추가 - 엔티티 컬럼 감지 로직에 디버깅 로그 추가 이제 화면 편집기에서 엔티티 컬럼의 표시 컬럼 설정이 정상적으로 보여야 함
This commit is contained in:
parent
de6c7a8008
commit
9d346a3d3a
|
|
@ -867,46 +867,6 @@ export default function TableManagementPage() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* 표시 컬럼 */}
|
||||
{column.referenceTable && column.referenceTable !== "none" && (
|
||||
<div>
|
||||
<label className="mb-1 block text-xs text-gray-600">표시 컬럼</label>
|
||||
<Select
|
||||
value={column.displayColumn || "none"}
|
||||
onValueChange={(value) =>
|
||||
handleDetailSettingsChange(
|
||||
column.columnName,
|
||||
"entity_display_column",
|
||||
value,
|
||||
)
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="h-7 bg-white text-xs">
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">-- 선택 안함 --</SelectItem>
|
||||
{referenceTableColumns[column.referenceTable]?.map((refCol, index) => (
|
||||
<SelectItem
|
||||
key={`display-col-${refCol.columnName}-${index}`}
|
||||
value={refCol.columnName}
|
||||
>
|
||||
<span className="font-medium">{refCol.columnName}</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
{(!referenceTableColumns[column.referenceTable] ||
|
||||
referenceTableColumns[column.referenceTable].length === 0) && (
|
||||
<SelectItem value="loading" disabled>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-3 w-3 animate-spin rounded-full border border-blue-500 border-t-transparent"></div>
|
||||
로딩중
|
||||
</div>
|
||||
</SelectItem>
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 설정 완료 표시 - 간소화 */}
|
||||
|
|
|
|||
|
|
@ -664,6 +664,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD
|
|||
columnLabel: col.displayName || col.columnLabel || col.column_label || col.columnName || col.column_name,
|
||||
dataType: col.dataType || col.data_type || col.dbType,
|
||||
webType: col.webType || col.web_type,
|
||||
input_type: col.inputType || col.input_type, // 🎯 input_type 필드 추가
|
||||
widgetType: col.widgetType || col.widget_type || col.webType || col.web_type,
|
||||
isNullable: col.isNullable || col.is_nullable,
|
||||
required: col.required !== undefined ? col.required : col.isNullable === "NO" || col.is_nullable === "NO",
|
||||
|
|
|
|||
|
|
@ -194,10 +194,10 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
|
||||
// Entity 타입인 경우
|
||||
if (column.webType === "entity") {
|
||||
// 백엔드에서 받은 displayColumnLabel을 사용하거나, 없으면 기본값 사용
|
||||
displayLabel = column.displayColumnLabel || column.displayColumn || `${column.columnName}_name`;
|
||||
// 우선 기준 테이블의 컬럼 라벨을 사용
|
||||
displayLabel = column.displayName || column.columnName;
|
||||
console.log(
|
||||
`🎯 Entity 조인 컬럼 라벨 설정: ${column.columnName} → "${displayLabel}" (${column.displayColumn || "기본값"})`,
|
||||
`🎯 Entity 조인 컬럼 라벨 설정: ${column.columnName} → "${displayLabel}" (기준 테이블 라벨 사용)`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,12 +62,17 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
const [loadingEntityJoins, setLoadingEntityJoins] = useState(false);
|
||||
|
||||
// 🎯 엔티티 컬럼 표시 설정을 위한 상태
|
||||
const [entityDisplayConfigs, setEntityDisplayConfigs] = useState<Record<string, {
|
||||
sourceColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
|
||||
joinColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
|
||||
selectedColumns: string[];
|
||||
separator: string;
|
||||
}>>({});
|
||||
const [entityDisplayConfigs, setEntityDisplayConfigs] = useState<
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
sourceColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
|
||||
joinColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
|
||||
selectedColumns: string[];
|
||||
separator: string;
|
||||
}
|
||||
>
|
||||
>({});
|
||||
|
||||
// 화면 테이블명이 있으면 자동으로 설정
|
||||
useEffect(() => {
|
||||
|
|
@ -284,6 +289,72 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
handleChange("columns", updatedColumns);
|
||||
};
|
||||
|
||||
// 🎯 기존 컬럼들을 체크하여 엔티티 타입인 경우 isEntityJoin 플래그 설정
|
||||
useEffect(() => {
|
||||
console.log("🔍 엔티티 컬럼 감지 useEffect 실행:", {
|
||||
hasColumns: !!config.columns,
|
||||
columnsCount: config.columns?.length || 0,
|
||||
hasTableColumns: !!tableColumns,
|
||||
tableColumnsCount: tableColumns?.length || 0,
|
||||
selectedTable: config.selectedTable
|
||||
});
|
||||
|
||||
if (!config.columns || !tableColumns) {
|
||||
console.log("⚠️ 컬럼 또는 테이블 컬럼 정보가 없어서 엔티티 감지 스킵");
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedColumns = config.columns.map((column) => {
|
||||
// 이미 isEntityJoin이 설정된 경우 스킵
|
||||
if (column.isEntityJoin) {
|
||||
console.log("✅ 이미 엔티티 플래그 설정됨:", column.columnName);
|
||||
return column;
|
||||
}
|
||||
|
||||
// 테이블 컬럼 정보에서 해당 컬럼 찾기
|
||||
const tableColumn = tableColumns.find((tc) => tc.column_name === column.columnName);
|
||||
console.log("🔍 컬럼 검색:", {
|
||||
columnName: column.columnName,
|
||||
found: !!tableColumn,
|
||||
inputType: tableColumn?.input_type,
|
||||
webType: tableColumn?.web_type
|
||||
});
|
||||
|
||||
// 엔티티 타입인 경우 isEntityJoin 플래그 설정 (input_type 또는 web_type 확인)
|
||||
if (tableColumn && (tableColumn.input_type === "entity" || tableColumn.web_type === "entity")) {
|
||||
console.log("🎯 엔티티 컬럼 감지 및 플래그 설정:", column.columnName);
|
||||
|
||||
return {
|
||||
...column,
|
||||
isEntityJoin: true,
|
||||
entityJoinInfo: {
|
||||
sourceTable: config.selectedTable || "",
|
||||
sourceColumn: column.columnName,
|
||||
joinAlias: column.columnName,
|
||||
},
|
||||
entityDisplayConfig: {
|
||||
displayColumns: [], // 빈 배열로 초기화
|
||||
separator: " - ",
|
||||
sourceTable: config.selectedTable || "",
|
||||
joinTable: tableColumn.reference_table || "",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return column;
|
||||
});
|
||||
|
||||
// 변경사항이 있는 경우에만 업데이트
|
||||
const hasChanges = updatedColumns.some((col, index) => col.isEntityJoin !== config.columns![index].isEntityJoin);
|
||||
|
||||
if (hasChanges) {
|
||||
console.log("🎯 엔티티 컬럼 플래그 업데이트:", updatedColumns);
|
||||
handleChange("columns", updatedColumns);
|
||||
} else {
|
||||
console.log("ℹ️ 엔티티 컬럼 변경사항 없음");
|
||||
}
|
||||
}, [config.columns, tableColumns, config.selectedTable]);
|
||||
|
||||
// 🎯 엔티티 컬럼의 표시 컬럼 정보 로드
|
||||
const loadEntityDisplayConfig = async (column: ColumnConfig) => {
|
||||
if (!column.isEntityJoin || !column.entityJoinInfo || !column.entityDisplayConfig) return;
|
||||
|
|
@ -304,7 +375,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
const sourceColumns = sourceResult.columns || [];
|
||||
const joinColumns = joinResult.columns || [];
|
||||
|
||||
setEntityDisplayConfigs(prev => ({
|
||||
setEntityDisplayConfigs((prev) => ({
|
||||
...prev,
|
||||
[configKey]: {
|
||||
sourceColumns,
|
||||
|
|
@ -325,10 +396,10 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
if (!config) return;
|
||||
|
||||
const newSelectedColumns = config.selectedColumns.includes(selectedColumn)
|
||||
? config.selectedColumns.filter(col => col !== selectedColumn)
|
||||
? config.selectedColumns.filter((col) => col !== selectedColumn)
|
||||
: [...config.selectedColumns, selectedColumn];
|
||||
|
||||
setEntityDisplayConfigs(prev => ({
|
||||
setEntityDisplayConfigs((prev) => ({
|
||||
...prev,
|
||||
[configKey]: {
|
||||
...prev[configKey],
|
||||
|
|
@ -351,7 +422,7 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
const config = entityDisplayConfigs[configKey];
|
||||
if (!config) return;
|
||||
|
||||
setEntityDisplayConfigs(prev => ({
|
||||
setEntityDisplayConfigs((prev) => ({
|
||||
...prev,
|
||||
[configKey]: {
|
||||
...prev[configKey],
|
||||
|
|
@ -791,6 +862,135 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
{/* 컬럼 설정 탭 */}
|
||||
<TabsContent value="columns" className="space-y-4">
|
||||
<ScrollArea className="h-[600px] pr-4">
|
||||
{/* 🎯 엔티티 컬럼 표시 설정 섹션 - 컬럼 설정 패널 바깥으로 분리 */}
|
||||
{config.columns?.some((col) => col.isEntityJoin) && (
|
||||
<Card className="border-l-4 border-l-orange-500">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-base">🎯 엔티티 컬럼 표시 설정</CardTitle>
|
||||
<CardDescription>엔티티 타입 컬럼의 표시할 컬럼들을 조합하여 설정하세요</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{config.columns
|
||||
?.filter((col) => col.isEntityJoin && col.entityDisplayConfig)
|
||||
.map((column) => (
|
||||
<div key={column.columnName} className="space-y-3 rounded-lg border bg-orange-50 p-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline" className="border-orange-300 text-orange-600">
|
||||
{column.columnName}
|
||||
</Badge>
|
||||
<span className="text-sm font-medium">{column.displayName}</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => loadEntityDisplayConfig(column)}
|
||||
className="h-6 text-xs"
|
||||
>
|
||||
<Plus className="mr-1 h-3 w-3" />
|
||||
컬럼 로드
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{entityDisplayConfigs[column.columnName] && (
|
||||
<div className="space-y-3">
|
||||
{/* 구분자 설정 */}
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">구분자</Label>
|
||||
<Input
|
||||
value={entityDisplayConfigs[column.columnName].separator}
|
||||
onChange={(e) => updateEntityDisplaySeparator(column.columnName, e.target.value)}
|
||||
className="h-7 text-xs"
|
||||
placeholder=" - "
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 기본 테이블 컬럼 */}
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-blue-600">
|
||||
기본 테이블: {column.entityDisplayConfig?.sourceTable}
|
||||
</Label>
|
||||
<div className="grid max-h-20 grid-cols-2 gap-1 overflow-y-auto">
|
||||
{entityDisplayConfigs[column.columnName].sourceColumns.map((col) => (
|
||||
<div key={col.columnName} className="flex items-center space-x-1">
|
||||
<Checkbox
|
||||
id={`source-${column.columnName}-${col.columnName}`}
|
||||
checked={entityDisplayConfigs[column.columnName].selectedColumns.includes(
|
||||
col.columnName,
|
||||
)}
|
||||
onCheckedChange={() =>
|
||||
toggleEntityDisplayColumn(column.columnName, col.columnName)
|
||||
}
|
||||
className="h-3 w-3"
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`source-${column.columnName}-${col.columnName}`}
|
||||
className="flex-1 cursor-pointer text-xs"
|
||||
>
|
||||
{col.displayName}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 조인 테이블 컬럼 */}
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-green-600">
|
||||
조인 테이블: {column.entityDisplayConfig?.joinTable}
|
||||
</Label>
|
||||
<div className="grid max-h-20 grid-cols-2 gap-1 overflow-y-auto">
|
||||
{entityDisplayConfigs[column.columnName].joinColumns.map((col) => (
|
||||
<div key={col.columnName} className="flex items-center space-x-1">
|
||||
<Checkbox
|
||||
id={`join-${column.columnName}-${col.columnName}`}
|
||||
checked={entityDisplayConfigs[column.columnName].selectedColumns.includes(
|
||||
col.columnName,
|
||||
)}
|
||||
onCheckedChange={() =>
|
||||
toggleEntityDisplayColumn(column.columnName, col.columnName)
|
||||
}
|
||||
className="h-3 w-3"
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`join-${column.columnName}-${col.columnName}`}
|
||||
className="flex-1 cursor-pointer text-xs"
|
||||
>
|
||||
{col.displayName}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 선택된 컬럼 미리보기 */}
|
||||
{entityDisplayConfigs[column.columnName].selectedColumns.length > 0 && (
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">미리보기</Label>
|
||||
<div className="flex flex-wrap gap-1 rounded bg-gray-50 p-2 text-xs">
|
||||
{entityDisplayConfigs[column.columnName].selectedColumns.map((colName, idx) => (
|
||||
<React.Fragment key={colName}>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{colName}
|
||||
</Badge>
|
||||
{idx < entityDisplayConfigs[column.columnName].selectedColumns.length - 1 && (
|
||||
<span className="text-gray-400">
|
||||
{entityDisplayConfigs[column.columnName].separator}
|
||||
</span>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{!screenTableName ? (
|
||||
<Card>
|
||||
<CardContent className="pt-6">
|
||||
|
|
@ -921,105 +1121,17 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
/>
|
||||
</div>
|
||||
|
||||
{/* 🎯 엔티티 타입 컬럼일 때 표시 컬럼 선택 UI */}
|
||||
{column.isEntityJoin && column.entityDisplayConfig && (
|
||||
<div className="col-span-2 space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-xs font-medium">표시 컬럼 조합</Label>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => loadEntityDisplayConfig(column)}
|
||||
className="h-6 text-xs"
|
||||
>
|
||||
<Plus className="h-3 w-3 mr-1" />
|
||||
컬럼 로드
|
||||
</Button>
|
||||
{/* 엔티티 타입 컬럼 표시 */}
|
||||
{column.isEntityJoin && (
|
||||
<div className="col-span-2">
|
||||
<div className="flex items-center gap-2 rounded bg-orange-50 p-2">
|
||||
<Badge variant="outline" className="border-orange-300 text-orange-600">
|
||||
엔티티 타입
|
||||
</Badge>
|
||||
<span className="text-xs text-orange-600">
|
||||
표시 컬럼 설정은 상단의 "🎯 엔티티 컬럼 표시 설정" 섹션에서 하세요
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{entityDisplayConfigs[column.columnName] && (
|
||||
<div className="space-y-3">
|
||||
{/* 구분자 설정 */}
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">구분자</Label>
|
||||
<Input
|
||||
value={entityDisplayConfigs[column.columnName].separator}
|
||||
onChange={(e) => updateEntityDisplaySeparator(column.columnName, e.target.value)}
|
||||
className="h-7 text-xs"
|
||||
placeholder=" - "
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 기본 테이블 컬럼 */}
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-blue-600">
|
||||
기본 테이블: {column.entityDisplayConfig.sourceTable}
|
||||
</Label>
|
||||
<div className="grid grid-cols-2 gap-1 max-h-20 overflow-y-auto">
|
||||
{entityDisplayConfigs[column.columnName].sourceColumns.map((col) => (
|
||||
<div key={col.columnName} className="flex items-center space-x-1">
|
||||
<Checkbox
|
||||
id={`source-${column.columnName}-${col.columnName}`}
|
||||
checked={entityDisplayConfigs[column.columnName].selectedColumns.includes(col.columnName)}
|
||||
onCheckedChange={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||
className="h-3 w-3"
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`source-${column.columnName}-${col.columnName}`}
|
||||
className="text-xs cursor-pointer flex-1"
|
||||
>
|
||||
{col.displayName}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 조인 테이블 컬럼 */}
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-green-600">
|
||||
조인 테이블: {column.entityDisplayConfig.joinTable}
|
||||
</Label>
|
||||
<div className="grid grid-cols-2 gap-1 max-h-20 overflow-y-auto">
|
||||
{entityDisplayConfigs[column.columnName].joinColumns.map((col) => (
|
||||
<div key={col.columnName} className="flex items-center space-x-1">
|
||||
<Checkbox
|
||||
id={`join-${column.columnName}-${col.columnName}`}
|
||||
checked={entityDisplayConfigs[column.columnName].selectedColumns.includes(col.columnName)}
|
||||
onCheckedChange={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||
className="h-3 w-3"
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`join-${column.columnName}-${col.columnName}`}
|
||||
className="text-xs cursor-pointer flex-1"
|
||||
>
|
||||
{col.displayName}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 선택된 컬럼 미리보기 */}
|
||||
{entityDisplayConfigs[column.columnName].selectedColumns.length > 0 && (
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">미리보기</Label>
|
||||
<div className="flex flex-wrap gap-1 rounded bg-gray-50 p-2 text-xs">
|
||||
{entityDisplayConfigs[column.columnName].selectedColumns.map((colName, idx) => (
|
||||
<React.Fragment key={colName}>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{colName}
|
||||
</Badge>
|
||||
{idx < entityDisplayConfigs[column.columnName].selectedColumns.length - 1 && (
|
||||
<span className="text-gray-400">{entityDisplayConfigs[column.columnName].separator}</span>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -1528,7 +1640,6 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
</ScrollArea>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ export interface UnifiedColumnInfo {
|
|||
|
||||
// 입력 설정
|
||||
inputType: "direct" | "auto";
|
||||
input_type?: string; // 🎯 데이터베이스의 input_type 필드 (entity, text, number 등)
|
||||
detailSettings?: Record<string, unknown>; // JSON 파싱된 객체
|
||||
description?: string;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue