"use client"; import React from "react"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Checkbox } from "@/components/ui/checkbox"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Check, ChevronsUpDown, ChevronRight, Database, Link2, Move, Trash2 } from "lucide-react"; import { cn } from "@/lib/utils"; import { SplitPanelLayoutConfig } from "../types"; import { PanelInlineComponent } from "../types"; import { ColumnInfo } from "@/types/screen"; import { DataFilterConfigPanel } from "@/components/screen/config-panels/DataFilterConfigPanel"; import { DndContext, closestCenter, type DragEndEvent } from "@dnd-kit/core"; import { SortableContext, verticalListSortingStrategy, arrayMove } from "@dnd-kit/sortable"; import { SortableColumnRow, ScreenSelector } from "./SharedComponents"; export interface LeftPanelConfigTabProps { config: SplitPanelLayoutConfig; onChange: (config: SplitPanelLayoutConfig) => void; updateLeftPanel: (updates: Partial) => void; screenTableName?: string; allTables: any[]; leftTableOpen: boolean; setLeftTableOpen: (open: boolean) => void; localTitles: { left: string; right: string }; setLocalTitles: (fn: (prev: { left: string; right: string }) => { left: string; right: string }) => void; isUserEditing: boolean; setIsUserEditing: (v: boolean) => void; leftTableColumns: ColumnInfo[]; entityJoinColumns: Record; joinTables: Array<{ tableName: string; currentDisplayColumn: string; joinConfig?: any; availableColumns: Array<{ columnName: string; columnLabel: string; dataType: string; inputType?: string; description?: string }> }>; }>; menuObjid?: number; } export const LeftPanelConfigTab: React.FC = ({ config, onChange, updateLeftPanel, screenTableName, allTables, leftTableOpen, setLeftTableOpen, localTitles, setLocalTitles, setIsUserEditing, leftTableColumns, entityJoinColumns, menuObjid, }) => { const dbNumericTypes = ["numeric", "decimal", "integer", "bigint", "double precision", "real", "smallint", "int4", "int8", "float4", "float8"]; const inputNumericTypes = ["number", "decimal", "currency", "integer"]; return (

좌측 패널 설정 (마스터)

테이블을 찾을 수 없습니다. {screenTableName && ( { updateLeftPanel({ tableName: screenTableName, columns: [] }); setLeftTableOpen(false); }} className="text-xs" > {allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.tableLabel || allTables.find((t) => (t.tableName || t.table_name) === screenTableName)?.displayName || screenTableName} )} {allTables .filter((t) => (t.tableName || t.table_name) !== screenTableName) .map((table) => { const tableName = table.tableName || table.table_name; const displayName = table.tableLabel || table.displayName || tableName; return ( { updateLeftPanel({ tableName, columns: [] }); setLeftTableOpen(false); }} className="text-xs" > {displayName} ); })} {config.leftPanel?.tableName && config.leftPanel?.tableName !== screenTableName && (

화면 기본 테이블이 아닌 다른 테이블의 데이터를 표시합니다.

)}
{ setIsUserEditing(true); setLocalTitles((prev) => ({ ...prev, left: e.target.value })); }} onBlur={() => { setIsUserEditing(false); updateLeftPanel({ title: localTitles.left }); }} placeholder="좌측 패널 제목" />
{config.leftPanel?.displayMode === "custom" && (

화면 디자이너에서 좌측 패널에 컴포넌트를 드래그하여 배치하세요.

)}
{config.leftPanel?.displayMode === "custom" && (
{!config.leftPanel?.components || config.leftPanel.components.length === 0 ? (

디자인 화면에서 컴포넌트를 드래그하여 추가하세요

) : (
{config.leftPanel.components.map((comp: PanelInlineComponent) => (

{comp.label || comp.componentType}

{comp.componentType} | 위치: ({comp.position?.x || 0}, {comp.position?.y || 0}) | 크기: {comp.size?.width || 0}x{comp.size?.height || 0}

))}
)}
)} {config.leftPanel?.displayMode !== "custom" && (() => { const selectedColumns = config.leftPanel?.columns || []; const filteredTableColumns = leftTableColumns.filter((c) => !["company_code", "company_name"].includes(c.columnName)); const unselectedColumns = filteredTableColumns.filter((c) => !selectedColumns.some((sc) => sc.name === c.columnName)); const handleLeftDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (over && active.id !== over.id) { const oldIndex = selectedColumns.findIndex((c) => c.name === active.id); const newIndex = selectedColumns.findIndex((c) => c.name === over.id); if (oldIndex !== -1 && newIndex !== -1) { updateLeftPanel({ columns: arrayMove([...selectedColumns], oldIndex, newIndex) }); } } }; return (
{leftTableColumns.length === 0 ? (

컬럼 로딩 중...

) : ( <> {selectedColumns.length > 0 && ( c.name)} strategy={verticalListSortingStrategy}>
{selectedColumns.map((col, index) => { const colInfo = leftTableColumns.find((c) => c.columnName === col.name); const isNumeric = colInfo && ( dbNumericTypes.includes(colInfo.dataType?.toLowerCase() || "") || inputNumericTypes.includes(colInfo.input_type?.toLowerCase() || "") || inputNumericTypes.includes(colInfo.webType?.toLowerCase() || "") ); return ( { const newColumns = [...selectedColumns]; newColumns[index] = { ...newColumns[index], label: value }; updateLeftPanel({ columns: newColumns }); }} onWidthChange={(value) => { const newColumns = [...selectedColumns]; newColumns[index] = { ...newColumns[index], width: value }; updateLeftPanel({ columns: newColumns }); }} onFormatChange={(checked) => { const newColumns = [...selectedColumns]; newColumns[index] = { ...newColumns[index], format: { ...newColumns[index].format, type: "number", thousandSeparator: checked } }; updateLeftPanel({ columns: newColumns }); }} onRemove={() => updateLeftPanel({ columns: selectedColumns.filter((_, i) => i !== index) })} /> ); })}
)} {selectedColumns.length > 0 && unselectedColumns.length > 0 && (
미선택 컬럼
)}
{unselectedColumns.map((column) => (
{ updateLeftPanel({ columns: [...selectedColumns, { name: column.columnName, label: column.columnLabel || column.columnName, width: 100 }] }); }} > {column.columnLabel || column.columnName}
))}
{(() => { const leftTable = config.leftPanel?.tableName || screenTableName; const joinData = leftTable ? entityJoinColumns[leftTable] : null; if (!joinData || joinData.joinTables.length === 0) return null; return joinData.joinTables.map((joinTable, tableIndex) => { const joinColumnsToShow = joinTable.availableColumns.filter((column) => { const matchingJoinColumn = joinData.availableColumns.find( (jc) => jc.tableName === joinTable.tableName && jc.columnName === column.columnName, ); if (!matchingJoinColumn) return false; return !selectedColumns.some((c) => c.name === matchingJoinColumn.joinAlias); }); const addedCount = joinTable.availableColumns.length - joinColumnsToShow.length; if (joinColumnsToShow.length === 0 && addedCount === 0) return null; return (
{joinTable.tableName} {addedCount > 0 && ( {addedCount}개 선택 )} {joinColumnsToShow.length}개 남음
{joinColumnsToShow.map((column, colIndex) => { const matchingJoinColumn = joinData.availableColumns.find( (jc) => jc.tableName === joinTable.tableName && jc.columnName === column.columnName, ); if (!matchingJoinColumn) return null; return (
{ updateLeftPanel({ columns: [...selectedColumns, { name: matchingJoinColumn.joinAlias, label: matchingJoinColumn.suggestedLabel || matchingJoinColumn.columnLabel, width: 100, isEntityJoin: true, joinInfo: { sourceTable: leftTable!, sourceColumn: (joinTable as any).joinConfig?.sourceColumn || "", referenceTable: matchingJoinColumn.tableName, joinAlias: matchingJoinColumn.joinAlias, }, }], }); }} > {column.columnLabel || column.columnName}
); })} {joinColumnsToShow.length === 0 && (

모든 컬럼이 이미 추가되었습니다

)}
); }); })()} )}
); })()}

좌측 패널 데이터 필터링

특정 컬럼 값으로 좌측 패널 데이터를 필터링합니다

({ columnName: col.columnName, columnLabel: col.columnLabel || col.columnName, dataType: col.dataType || "text", input_type: (col as any).input_type, }) as any, )} config={config.leftPanel?.dataFilter} onConfigChange={(dataFilter) => updateLeftPanel({ dataFilter })} menuObjid={menuObjid} />

좌측 패널 버튼 설정

updateLeftPanel({ showSearch: !!checked })} />
updateLeftPanel({ showAdd: !!checked })} />
updateLeftPanel({ showEdit: !!checked })} />
updateLeftPanel({ showDelete: !!checked })} />
{config.leftPanel?.showAdd && (

추가 버튼 설정

{config.leftPanel?.addButton?.mode === "modal" && (
updateLeftPanel({ addButton: { ...config.leftPanel?.addButton, enabled: true, mode: "modal", modalScreenId: screenId }, }) } />
)}
updateLeftPanel({ addButton: { ...config.leftPanel?.addButton, enabled: true, mode: config.leftPanel?.addButton?.mode || "auto", buttonLabel: e.target.value || undefined, }, }) } placeholder="추가" className="h-7 text-xs" />
)} {(config.leftPanel?.showEdit ?? true) && (

수정 버튼 설정

{config.leftPanel?.editButton?.mode === "modal" && (
updateLeftPanel({ editButton: { ...config.leftPanel?.editButton, enabled: true, mode: "modal", modalScreenId: screenId }, }) } />
)}
updateLeftPanel({ editButton: { ...config.leftPanel?.editButton, enabled: true, mode: config.leftPanel?.editButton?.mode || "auto", buttonLabel: e.target.value || undefined, }, }) } placeholder="수정" className="h-7 text-xs" />
)}
); };