ERP-node/frontend/components/screen/panels/TablesPanel.tsx

153 lines
5.4 KiB
TypeScript
Raw Normal View History

2025-09-02 16:18:38 +09:00
"use client";
2025-10-28 16:26:55 +09:00
import React from "react";
2025-09-02 16:18:38 +09:00
import { Badge } from "@/components/ui/badge";
2025-10-28 16:26:55 +09:00
import { Database, Type, Hash, Calendar, CheckSquare, List, AlignLeft, Code, Building, File } from "lucide-react";
2025-09-02 16:18:38 +09:00
import { TableInfo, WebType } from "@/types/screen";
interface TablesPanelProps {
tables: TableInfo[];
searchTerm: string;
onSearchChange: (term: string) => void;
onDragStart: (e: React.DragEvent, table: TableInfo, column?: any) => void;
selectedTableName?: string;
placedColumns?: Set<string>; // 이미 배치된 컬럼명 집합 (tableName.columnName 형식)
2025-09-02 16:18:38 +09:00
}
// 위젯 타입별 아이콘
const getWidgetIcon = (widgetType: WebType) => {
switch (widgetType) {
case "text":
case "email":
case "tel":
2025-10-17 16:21:08 +09:00
return <Type className="text-primary h-3 w-3" />;
2025-09-02 16:18:38 +09:00
case "number":
case "decimal":
return <Hash className="h-3 w-3 text-green-600" />;
case "date":
case "datetime":
return <Calendar className="h-3 w-3 text-purple-600" />;
case "select":
case "dropdown":
return <List className="h-3 w-3 text-orange-600" />;
case "textarea":
case "text_area":
return <AlignLeft className="h-3 w-3 text-indigo-600" />;
case "boolean":
case "checkbox":
2025-10-17 16:21:08 +09:00
return <CheckSquare className="text-primary h-3 w-3" />;
2025-09-02 16:18:38 +09:00
case "code":
2025-10-17 16:21:08 +09:00
return <Code className="text-muted-foreground h-3 w-3" />;
2025-09-02 16:18:38 +09:00
case "entity":
return <Building className="h-3 w-3 text-cyan-600" />;
case "file":
return <File className="h-3 w-3 text-yellow-600" />;
default:
return <Type className="h-3 w-3 text-gray-500" />;
}
};
export const TablesPanel: React.FC<TablesPanelProps> = ({
tables,
searchTerm,
onDragStart,
placedColumns = new Set(),
2025-09-02 16:18:38 +09:00
}) => {
// 이미 배치된 컬럼을 제외한 테이블 정보 생성
const tablesWithAvailableColumns = tables.map((table) => ({
...table,
columns: table.columns.filter((col) => {
const columnKey = `${table.tableName}.${col.columnName}`;
return !placedColumns.has(columnKey);
}),
}));
2025-10-28 16:26:55 +09:00
// 검색어가 있으면 컬럼 필터링
const filteredTables = tablesWithAvailableColumns
2025-10-28 16:26:55 +09:00
.map((table) => {
if (!searchTerm) {
return table;
}
const searchLower = searchTerm.toLowerCase();
// 테이블명이 검색어와 일치하면 모든 컬럼 표시
if (
table.tableName.toLowerCase().includes(searchLower) ||
(table.tableLabel && table.tableLabel.toLowerCase().includes(searchLower))
) {
return table;
}
// 그렇지 않으면 컬럼명/라벨이 검색어와 일치하는 컬럼만 필터링
const filteredColumns = table.columns.filter(
(col) =>
col.columnName.toLowerCase().includes(searchLower) ||
(col.columnLabel && col.columnLabel.toLowerCase().includes(searchLower)),
);
return {
...table,
columns: filteredColumns,
};
})
.filter((table) => table.columns.length > 0); // 컬럼이 있는 테이블만 표시
2025-09-02 16:18:38 +09:00
return (
<div className="flex h-full flex-col">
2025-10-28 16:26:55 +09:00
{/* 테이블과 컬럼 평면 목록 */}
<div className="flex-1 overflow-y-auto p-3">
<div className="space-y-2">
{filteredTables.map((table) => (
<div key={table.tableName} className="space-y-1">
{/* 테이블 헤더 */}
<div className="bg-muted/50 flex items-center justify-between rounded-md p-2">
<div className="flex items-center gap-2">
<Database className="text-primary h-3.5 w-3.5" />
<span className="text-xs font-semibold">{table.tableLabel || table.tableName}</span>
<Badge variant="secondary" className="h-4 px-1.5 text-[10px]">
{table.columns.length}
</Badge>
</div>
</div>
2025-09-02 16:18:38 +09:00
2025-10-28 16:26:55 +09:00
{/* 컬럼 목록 (항상 표시) */}
<div className="space-y-1 pl-2">
{table.columns.map((column) => (
<div
key={column.columnName}
className="hover:bg-accent/50 flex cursor-grab items-center justify-between rounded-md p-2 transition-colors"
2025-09-02 16:18:38 +09:00
draggable
2025-10-28 16:26:55 +09:00
onDragStart={(e) => onDragStart(e, table, column)}
2025-09-02 16:18:38 +09:00
>
2025-10-28 16:26:55 +09:00
<div className="flex min-w-0 flex-1 items-center gap-2">
{getWidgetIcon(column.widgetType)}
<div className="min-w-0 flex-1">
<div className="truncate text-xs font-medium">{column.columnLabel || column.columnName}</div>
<div className="text-muted-foreground truncate text-[10px]">{column.dataType}</div>
</div>
</div>
2025-09-02 16:18:38 +09:00
2025-10-28 16:26:55 +09:00
<div className="flex flex-shrink-0 items-center gap-1">
<Badge variant="secondary" className="h-4 px-1.5 text-[10px]">
{column.widgetType}
</Badge>
{column.required && (
<Badge variant="destructive" className="h-4 px-1 text-[10px]">
</Badge>
2025-09-02 16:18:38 +09:00
)}
</div>
</div>
2025-10-28 16:26:55 +09:00
))}
2025-09-02 16:18:38 +09:00
</div>
2025-10-28 16:26:55 +09:00
</div>
))}
2025-09-02 16:18:38 +09:00
</div>
</div>
</div>
);
};
export default TablesPanel;