3d에서 테이블 가져올 때 테이블, 컬럼 코멘트 같이 가져오기
This commit is contained in:
@@ -95,7 +95,7 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
|
||||
|
||||
// 동적 계층 구조 설정
|
||||
const [hierarchyConfig, setHierarchyConfig] = useState<HierarchyConfig | null>(null);
|
||||
const [availableTables, setAvailableTables] = useState<string[]>([]);
|
||||
const [availableTables, setAvailableTables] = useState<Array<{ table_name: string; description?: string }>>([]);
|
||||
const [loadingTables, setLoadingTables] = useState(false);
|
||||
|
||||
// 레거시: 테이블 매핑 (구 Area/Location 방식 호환용)
|
||||
@@ -210,9 +210,9 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
|
||||
const { getTables } = await import("@/lib/api/digitalTwin");
|
||||
const response = await getTables(selectedDbConnection);
|
||||
if (response.success && response.data) {
|
||||
const tableNames = response.data.map((t) => t.table_name);
|
||||
setAvailableTables(tableNames);
|
||||
console.log("📋 테이블 목록:", tableNames);
|
||||
// 테이블 정보 전체 저장 (이름 + 설명)
|
||||
setAvailableTables(response.data);
|
||||
console.log("📋 테이블 목록:", response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("테이블 목록 조회 실패:", error);
|
||||
@@ -1266,10 +1266,12 @@ export default function DigitalTwinEditor({ layoutId, layoutName, onBack }: Digi
|
||||
try {
|
||||
const response = await ExternalDbConnectionAPI.getTableColumns(selectedDbConnection, tableName);
|
||||
if (response.success && response.data) {
|
||||
// 객체 배열을 문자열 배열로 변환
|
||||
return response.data.map((col: any) =>
|
||||
typeof col === "string" ? col : col.column_name || col.COLUMN_NAME || String(col),
|
||||
);
|
||||
// 컬럼 정보 객체 배열로 반환 (이름 + 설명)
|
||||
return response.data.map((col: any) => ({
|
||||
column_name: typeof col === "string" ? col : col.column_name || col.COLUMN_NAME || String(col),
|
||||
data_type: col.data_type || col.DATA_TYPE,
|
||||
description: col.description || col.COLUMN_COMMENT || undefined,
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
} catch (error) {
|
||||
|
||||
@@ -40,13 +40,24 @@ export interface HierarchyConfig {
|
||||
};
|
||||
}
|
||||
|
||||
interface TableInfo {
|
||||
table_name: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface ColumnInfo {
|
||||
column_name: string;
|
||||
data_type?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface HierarchyConfigPanelProps {
|
||||
externalDbConnectionId: number | null;
|
||||
hierarchyConfig: HierarchyConfig | null;
|
||||
onHierarchyConfigChange: (config: HierarchyConfig) => void;
|
||||
availableTables: string[];
|
||||
availableTables: TableInfo[];
|
||||
onLoadTables: () => Promise<void>;
|
||||
onLoadColumns: (tableName: string) => Promise<string[]>;
|
||||
onLoadColumns: (tableName: string) => Promise<ColumnInfo[]>;
|
||||
}
|
||||
|
||||
export default function HierarchyConfigPanel({
|
||||
@@ -65,7 +76,7 @@ export default function HierarchyConfigPanel({
|
||||
);
|
||||
|
||||
const [loadingColumns, setLoadingColumns] = useState(false);
|
||||
const [columnsCache, setColumnsCache] = useState<{ [tableName: string]: string[] }>({});
|
||||
const [columnsCache, setColumnsCache] = useState<{ [tableName: string]: ColumnInfo[] }>({});
|
||||
|
||||
// 외부에서 변경된 경우 동기화
|
||||
useEffect(() => {
|
||||
@@ -187,8 +198,13 @@ export default function HierarchyConfigPanel({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{availableTables.map((table) => (
|
||||
<SelectItem key={table} value={table} className="text-[10px]">
|
||||
{table}
|
||||
<SelectItem key={table.table_name} value={table.table_name} className="text-[10px]">
|
||||
<div className="flex flex-col">
|
||||
<span>{table.table_name}</span>
|
||||
{table.description && (
|
||||
<span className="text-[9px] text-muted-foreground">{table.description}</span>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -209,8 +225,13 @@ export default function HierarchyConfigPanel({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{columnsCache[localConfig.warehouse.tableName].map((col) => (
|
||||
<SelectItem key={col} value={col} className="text-[10px]">
|
||||
{col}
|
||||
<SelectItem key={col.column_name} value={col.column_name} className="text-[10px]">
|
||||
<div className="flex flex-col">
|
||||
<span>{col.column_name}</span>
|
||||
{col.description && (
|
||||
<span className="text-[8px] text-muted-foreground">{col.description}</span>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -228,8 +249,13 @@ export default function HierarchyConfigPanel({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{columnsCache[localConfig.warehouse.tableName].map((col) => (
|
||||
<SelectItem key={col} value={col} className="text-[10px]">
|
||||
{col}
|
||||
<SelectItem key={col.column_name} value={col.column_name} className="text-[10px]">
|
||||
<div className="flex flex-col">
|
||||
<span>{col.column_name}</span>
|
||||
{col.description && (
|
||||
<span className="text-[8px] text-muted-foreground">{col.description}</span>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -287,8 +313,13 @@ export default function HierarchyConfigPanel({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{availableTables.map((table) => (
|
||||
<SelectItem key={table} value={table} className="text-xs">
|
||||
{table}
|
||||
<SelectItem key={table.table_name} value={table.table_name} className="text-xs">
|
||||
<div className="flex flex-col">
|
||||
<span>{table.table_name}</span>
|
||||
{table.description && (
|
||||
<span className="text-[10px] text-muted-foreground">{table.description}</span>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -308,8 +339,13 @@ export default function HierarchyConfigPanel({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{columnsCache[level.tableName].map((col) => (
|
||||
<SelectItem key={col} value={col} className="text-xs">
|
||||
{col}
|
||||
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
|
||||
<div className="flex flex-col">
|
||||
<span>{col.column_name}</span>
|
||||
{col.description && (
|
||||
<span className="text-[9px] text-muted-foreground">{col.description}</span>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -327,8 +363,13 @@ export default function HierarchyConfigPanel({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{columnsCache[level.tableName].map((col) => (
|
||||
<SelectItem key={col} value={col} className="text-xs">
|
||||
{col}
|
||||
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
|
||||
<div className="flex flex-col">
|
||||
<span>{col.column_name}</span>
|
||||
{col.description && (
|
||||
<span className="text-[9px] text-muted-foreground">{col.description}</span>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -409,8 +450,13 @@ export default function HierarchyConfigPanel({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{availableTables.map((table) => (
|
||||
<SelectItem key={table} value={table} className="text-xs">
|
||||
{table}
|
||||
<SelectItem key={table.table_name} value={table.table_name} className="text-xs">
|
||||
<div className="flex flex-col">
|
||||
<span>{table.table_name}</span>
|
||||
{table.description && (
|
||||
<span className="text-[10px] text-muted-foreground">{table.description}</span>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -430,8 +476,13 @@ export default function HierarchyConfigPanel({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{columnsCache[localConfig.material.tableName].map((col) => (
|
||||
<SelectItem key={col} value={col} className="text-xs">
|
||||
{col}
|
||||
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
|
||||
<div className="flex flex-col">
|
||||
<span>{col.column_name}</span>
|
||||
{col.description && (
|
||||
<span className="text-[9px] text-muted-foreground">{col.description}</span>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -449,8 +500,13 @@ export default function HierarchyConfigPanel({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{columnsCache[localConfig.material.tableName].map((col) => (
|
||||
<SelectItem key={col} value={col} className="text-xs">
|
||||
{col}
|
||||
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
|
||||
<div className="flex flex-col">
|
||||
<span>{col.column_name}</span>
|
||||
{col.description && (
|
||||
<span className="text-[9px] text-muted-foreground">{col.description}</span>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -469,8 +525,13 @@ export default function HierarchyConfigPanel({
|
||||
<SelectContent>
|
||||
<SelectItem value="__none__">없음</SelectItem>
|
||||
{columnsCache[localConfig.material.tableName].map((col) => (
|
||||
<SelectItem key={col} value={col} className="text-xs">
|
||||
{col}
|
||||
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
|
||||
<div className="flex flex-col">
|
||||
<span>{col.column_name}</span>
|
||||
{col.description && (
|
||||
<span className="text-[9px] text-muted-foreground">{col.description}</span>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -489,8 +550,13 @@ export default function HierarchyConfigPanel({
|
||||
<SelectContent>
|
||||
<SelectItem value="__none__">없음</SelectItem>
|
||||
{columnsCache[localConfig.material.tableName].map((col) => (
|
||||
<SelectItem key={col} value={col} className="text-xs">
|
||||
{col}
|
||||
<SelectItem key={col.column_name} value={col.column_name} className="text-xs">
|
||||
<div className="flex flex-col">
|
||||
<span>{col.column_name}</span>
|
||||
{col.description && (
|
||||
<span className="text-[9px] text-muted-foreground">{col.description}</span>
|
||||
)}
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -507,30 +573,35 @@ export default function HierarchyConfigPanel({
|
||||
</p>
|
||||
<div className="max-h-60 space-y-2 overflow-y-auto rounded border p-2">
|
||||
{columnsCache[localConfig.material.tableName].map((col) => {
|
||||
const displayItem = localConfig.material?.displayColumns?.find((d) => d.column === col);
|
||||
const displayItem = localConfig.material?.displayColumns?.find((d) => d.column === col.column_name);
|
||||
const isSelected = !!displayItem;
|
||||
return (
|
||||
<div key={col} className="flex items-center gap-2">
|
||||
<div key={col.column_name} className="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isSelected}
|
||||
onChange={(e) => {
|
||||
const currentDisplay = localConfig.material?.displayColumns || [];
|
||||
const newDisplay = e.target.checked
|
||||
? [...currentDisplay, { column: col, label: col }]
|
||||
: currentDisplay.filter((d) => d.column !== col);
|
||||
? [...currentDisplay, { column: col.column_name, label: col.column_name }]
|
||||
: currentDisplay.filter((d) => d.column !== col.column_name);
|
||||
handleMaterialChange("displayColumns", newDisplay);
|
||||
}}
|
||||
className="h-3 w-3 shrink-0"
|
||||
/>
|
||||
<span className="w-20 shrink-0 text-[10px]">{col}</span>
|
||||
<div className="flex w-24 shrink-0 flex-col">
|
||||
<span className="text-[10px]">{col.column_name}</span>
|
||||
{col.description && (
|
||||
<span className="text-[8px] text-muted-foreground">{col.description}</span>
|
||||
)}
|
||||
</div>
|
||||
{isSelected && (
|
||||
<Input
|
||||
value={displayItem?.label ?? ""}
|
||||
onChange={(e) => {
|
||||
const currentDisplay = localConfig.material?.displayColumns || [];
|
||||
const newDisplay = currentDisplay.map((d) =>
|
||||
d.column === col ? { ...d, label: e.target.value } : d,
|
||||
d.column === col.column_name ? { ...d, label: e.target.value } : d,
|
||||
);
|
||||
handleMaterialChange("displayColumns", newDisplay);
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user