"use client";
import React from "react";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { ColumnConfig } from "../types";
import { Database, Link2, GripVertical, X, Check, ChevronsUpDown, Lock, Unlock } from "lucide-react";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { cn } from "@/lib/utils";
import { DndContext, closestCenter, type DragEndEvent } from "@dnd-kit/core";
import { SortableContext, useSortable, verticalListSortingStrategy, arrayMove } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
function SortableColumnRow({
id,
col,
index,
isEntityJoin,
onLabelChange,
onWidthChange,
onRemove,
}: {
id: string;
col: ColumnConfig;
index: number;
isEntityJoin?: boolean;
onLabelChange: (value: string) => void;
onWidthChange: (value: number) => void;
onRemove: () => void;
}) {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
const style = { transform: CSS.Transform.toString(transform), transition };
return (
{isEntityJoin ? (
) : (
#{index + 1}
)}
onLabelChange(e.target.value)}
placeholder="표시명"
className="h-6 min-w-0 flex-1 text-xs"
/>
onWidthChange(parseInt(e.target.value) || 100)}
placeholder="너비"
className="h-6 w-14 shrink-0 text-xs"
/>
);
}
export interface ColumnsConfigPanelProps {
config: any;
onChange: (key: string, value: any) => void;
screenTableName?: string;
targetTableName: string | undefined;
availableColumns: Array<{ columnName: string; dataType: string; label?: string; input_type?: string }>;
tableColumns?: any[];
entityJoinColumns: {
availableColumns: Array<{
tableName: string;
columnName: string;
columnLabel: string;
dataType: string;
joinAlias: string;
suggestedLabel: string;
}>;
joinTables: Array<{
tableName: string;
currentDisplayColumn: string;
availableColumns: Array<{
columnName: string;
columnLabel: string;
dataType: string;
description?: string;
}>;
}>;
};
entityDisplayConfigs: Record<
string,
{
sourceColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
joinColumns: Array<{ columnName: string; displayName: string; dataType: string }>;
selectedColumns: string[];
separator: string;
}
>;
onAddColumn: (columnName: string) => void;
onAddEntityColumn: (joinColumn: {
tableName: string;
columnName: string;
columnLabel: string;
dataType: string;
joinAlias: string;
suggestedLabel: string;
}) => void;
onRemoveColumn: (columnName: string) => void;
onUpdateColumn: (columnName: string, updates: Partial) => void;
onToggleEntityDisplayColumn: (columnName: string, selectedColumn: string) => void;
onUpdateEntityDisplaySeparator: (columnName: string, separator: string) => void;
}
/**
* 컬럼 설정 패널: 컬럼 선택, Entity 조인, DnD 순서 변경
*/
export const ColumnsConfigPanel: React.FC = ({
config,
onChange,
screenTableName,
targetTableName,
availableColumns,
tableColumns,
entityJoinColumns,
entityDisplayConfigs,
onAddColumn,
onAddEntityColumn,
onRemoveColumn,
onUpdateColumn,
onToggleEntityDisplayColumn,
onUpdateEntityDisplaySeparator,
}) => {
const handleChange = (key: string, value: any) => {
onChange(key, value);
};
return (
{/* 엔티티 컬럼 표시 설정 섹션 */}
{config.columns?.some((col: ColumnConfig) => col.isEntityJoin) && (
{config.columns
?.filter((col: ColumnConfig) => col.isEntityJoin && col.entityDisplayConfig)
.map((column: ColumnConfig) => (
{column.displayName || column.columnName}
{entityDisplayConfigs[column.columnName] ? (
onUpdateEntityDisplaySeparator(column.columnName, e.target.value)}
className="h-6 w-full text-xs"
style={{ fontSize: "12px" }}
placeholder=" - "
/>
{entityDisplayConfigs[column.columnName].sourceColumns.length === 0 &&
entityDisplayConfigs[column.columnName].joinColumns.length === 0 ? (
표시 가능한 컬럼이 없습니다.
{!column.entityDisplayConfig?.joinTable && (
테이블 타입 관리에서 참조 테이블을 설정하면 더 많은 컬럼을 선택할 수 있습니다.
)}
) : (
컬럼을 찾을 수 없습니다.
{entityDisplayConfigs[column.columnName].sourceColumns.length > 0 && (
{entityDisplayConfigs[column.columnName].sourceColumns.map((col) => (
onToggleEntityDisplayColumn(column.columnName, col.columnName)}
className="text-xs"
>
{col.displayName}
))}
)}
{entityDisplayConfigs[column.columnName].joinColumns.length > 0 && (
{entityDisplayConfigs[column.columnName].joinColumns.map((col) => (
onToggleEntityDisplayColumn(column.columnName, col.columnName)}
className="text-xs"
>
{col.displayName}
))}
)}
)}
{!column.entityDisplayConfig?.joinTable &&
entityDisplayConfigs[column.columnName].sourceColumns.length > 0 && (
현재 기본 테이블 컬럼만 표시됩니다. 테이블 타입 관리에서 참조 테이블을 설정하면 조인된
테이블의 컬럼도 선택할 수 있습니다.
)}
{entityDisplayConfigs[column.columnName].selectedColumns.length > 0 && (
{entityDisplayConfigs[column.columnName].selectedColumns.map((colName, idx) => (
{colName}
{idx < entityDisplayConfigs[column.columnName].selectedColumns.length - 1 && (
{entityDisplayConfigs[column.columnName].separator}
)}
))}
)}
) : (
컬럼 정보 로딩 중...
)}
))}
)}
{!targetTableName ? (
테이블이 선택되지 않았습니다.
기본 설정 탭에서 테이블을 선택하세요.
) : availableColumns.length === 0 ? (
컬럼을 추가하려면 먼저 컴포넌트에 테이블을 명시적으로 선택하거나
기본 설정 탭에서 테이블을 설정해주세요.
현재 화면 테이블: {screenTableName}
) : (
<>
{availableColumns.map((column) => {
const isAdded = config.columns?.some((c: ColumnConfig) => c.columnName === column.columnName);
return (
{
if (isAdded) {
handleChange(
"columns",
config.columns?.filter((c: ColumnConfig) => c.columnName !== column.columnName) || [],
);
} else {
onAddColumn(column.columnName);
}
}}
>
{
if (isAdded) {
handleChange(
"columns",
config.columns?.filter((c: ColumnConfig) => c.columnName !== column.columnName) || [],
);
} else {
onAddColumn(column.columnName);
}
}}
className="pointer-events-none h-3.5 w-3.5"
/>
{column.label || column.columnName}
{isAdded && (
)}
{column.input_type || column.dataType}
);
})}
{entityJoinColumns.joinTables.length > 0 && (
Entity 조인 컬럼
연관 테이블의 컬럼을 선택하세요
{entityJoinColumns.joinTables.map((joinTable, tableIndex) => (
{joinTable.tableName}
{joinTable.currentDisplayColumn}
{joinTable.availableColumns.map((column, colIndex) => {
const matchingJoinColumn = entityJoinColumns.availableColumns.find(
(jc) => jc.tableName === joinTable.tableName && jc.columnName === column.columnName,
);
const isAlreadyAdded = config.columns?.some(
(col: ColumnConfig) => col.columnName === matchingJoinColumn?.joinAlias,
);
if (!matchingJoinColumn) return null;
return (
{
if (isAlreadyAdded) {
handleChange(
"columns",
config.columns?.filter(
(c: ColumnConfig) => c.columnName !== matchingJoinColumn.joinAlias,
) || [],
);
} else {
onAddEntityColumn(matchingJoinColumn);
}
}}
>
{
if (isAlreadyAdded) {
handleChange(
"columns",
config.columns?.filter(
(c: ColumnConfig) => c.columnName !== matchingJoinColumn.joinAlias,
) || [],
);
} else {
onAddEntityColumn(matchingJoinColumn);
}
}}
className="pointer-events-none h-3.5 w-3.5"
/>
{column.columnLabel}
{column.inputType || column.dataType}
);
})}
))}
)}
>
)}
{config.columns && config.columns.length > 0 && (
표시할 컬럼 ({config.columns.length}개 선택)
드래그하여 순서를 변경하거나 표시명/너비를 수정할 수 있습니다
{
const { active, over } = event;
if (!over || active.id === over.id) return;
const columns = [...(config.columns || [])];
const oldIndex = columns.findIndex((c: ColumnConfig) => c.columnName === active.id);
const newIndex = columns.findIndex((c: ColumnConfig) => c.columnName === over.id);
if (oldIndex !== -1 && newIndex !== -1) {
const reordered = arrayMove(columns, oldIndex, newIndex);
reordered.forEach((col: ColumnConfig, idx: number) => {
col.order = idx;
});
handleChange("columns", reordered);
}
}}
>
c.columnName)}
strategy={verticalListSortingStrategy}
>
{(config.columns || []).map((column: ColumnConfig, idx: number) => {
const resolvedLabel =
column.displayName && column.displayName !== column.columnName
? column.displayName
: availableColumns.find((c) => c.columnName === column.columnName)?.label ||
column.displayName ||
column.columnName;
const colWithLabel = { ...column, displayName: resolvedLabel };
return (
onUpdateColumn(column.columnName, { displayName: value })}
onWidthChange={(value) => onUpdateColumn(column.columnName, { width: value })}
onRemove={() => onRemoveColumn(column.columnName)}
/>
);
})}
)}
);
};