"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 { Switch } from "@/components/ui/switch"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Check, ChevronsUpDown, ChevronRight, Link2, Move, Trash2 } from "lucide-react"; import { cn } from "@/lib/utils"; import { SplitPanelLayoutConfig } from "../types"; import { PanelInlineComponent } from "../types"; import { ColumnInfo, TableInfo } 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, GroupByColumnsSelector } from "./SharedComponents"; export interface RightPanelConfigTabProps { config: SplitPanelLayoutConfig; onChange: (config: SplitPanelLayoutConfig) => void; updateRightPanel: (updates: Partial) => void; relationshipType: "join" | "detail"; localTitles: { left: string; right: string }; setLocalTitles: (fn: (prev: { left: string; right: string }) => { left: string; right: string }) => void; setIsUserEditing: (v: boolean) => void; rightTableOpen: boolean; setRightTableOpen: (open: boolean) => void; availableRightTables: TableInfo[]; rightTableColumns: 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; renderAdditionalTabs?: () => React.ReactNode; } export const RightPanelConfigTab: React.FC = ({ config, onChange, updateRightPanel, relationshipType, localTitles, setLocalTitles, setIsUserEditing, rightTableOpen, setRightTableOpen, availableRightTables, rightTableColumns, entityJoinColumns, menuObjid, renderAdditionalTabs, }) => { const dbNumericTypes = ["numeric", "decimal", "integer", "bigint", "double precision", "real", "smallint", "int4", "int8", "float4", "float8"]; const inputNumericTypes = ["number", "decimal", "currency", "integer"]; return (

우측 패널 설정 ({relationshipType === "detail" ? "선택 시 표시" : "연관 목록"})

{ setIsUserEditing(true); setLocalTitles((prev) => ({ ...prev, right: e.target.value })); }} onBlur={() => { setIsUserEditing(false); updateRightPanel({ title: localTitles.right }); }} placeholder="우측 패널 제목" />
테이블을 찾을 수 없습니다. {availableRightTables.map((table) => { const tableName = (table as any).tableName || (table as any).table_name; return ( { updateRightPanel({ tableName }); setRightTableOpen(false); }} > {(table as any).displayName || tableName} {(table as any).displayName && ({tableName})} ); })}
{config.rightPanel?.displayMode === "custom" && (

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

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

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

) : (
{config.rightPanel.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.rightPanel?.displayMode || "list") === "list" && (
{ const value = parseInt(e.target.value) || 3; updateRightPanel({ summaryColumnCount: value }); }} className="bg-white" />

접기 전에 표시할 컬럼 개수 (기본: 3개)

컬럼명 표시 여부

{ updateRightPanel({ summaryShowLabel: checked as boolean }); }} />
)} {config.rightPanel?.displayMode !== "custom" && (() => { const selectedColumns = config.rightPanel?.columns || []; const filteredTableColumns = rightTableColumns.filter((c) => !["company_code", "company_name"].includes(c.columnName)); const unselectedColumns = filteredTableColumns.filter((c) => !selectedColumns.some((sc) => sc.name === c.columnName)); const handleRightDragEnd = (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) { updateRightPanel({ columns: arrayMove([...selectedColumns], oldIndex, newIndex) }); } } }; return (
{rightTableColumns.length === 0 ? (

테이블을 선택해주세요

) : ( <> {selectedColumns.length > 0 && ( c.name)} strategy={verticalListSortingStrategy}>
{selectedColumns.map((col, index) => { const colInfo = rightTableColumns.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 }; updateRightPanel({ columns: newColumns }); }} onWidthChange={(value) => { const newColumns = [...selectedColumns]; newColumns[index] = { ...newColumns[index], width: value }; updateRightPanel({ columns: newColumns }); }} onFormatChange={(checked) => { const newColumns = [...selectedColumns]; newColumns[index] = { ...newColumns[index], format: { ...newColumns[index].format, type: "number", thousandSeparator: checked } }; updateRightPanel({ columns: newColumns }); }} onRemove={() => updateRightPanel({ columns: selectedColumns.filter((_, i) => i !== index) })} onShowInSummaryChange={(checked) => { const newColumns = [...selectedColumns]; newColumns[index] = { ...newColumns[index], showInSummary: checked }; updateRightPanel({ columns: newColumns }); }} onShowInDetailChange={(checked) => { const newColumns = [...selectedColumns]; newColumns[index] = { ...newColumns[index], showInDetail: checked }; updateRightPanel({ columns: newColumns }); }} /> ); })}
)} {selectedColumns.length > 0 && unselectedColumns.length > 0 && (
미선택 컬럼
)}
{unselectedColumns.map((column) => (
{ updateRightPanel({ columns: [...selectedColumns, { name: column.columnName, label: column.columnLabel || column.columnName, width: 100 }] }); }} > {column.columnLabel || column.columnName}
))}
{(() => { const rightTable = config.rightPanel?.tableName; const joinData = rightTable ? entityJoinColumns[rightTable] : 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 (
{ updateRightPanel({ columns: [...selectedColumns, { name: matchingJoinColumn.joinAlias, label: matchingJoinColumn.suggestedLabel || matchingJoinColumn.columnLabel, width: 100, isEntityJoin: true, joinInfo: { sourceTable: rightTable!, 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.rightPanel?.dataFilter} onConfigChange={(dataFilter) => updateRightPanel({ dataFilter })} menuObjid={menuObjid} />

중복 데이터 제거

같은 값을 가진 데이터를 하나로 통합하여 표시

{ if (checked) { updateRightPanel({ deduplication: { enabled: true, groupByColumn: "", keepStrategy: "latest", sortColumn: "start_date", }, }); } else { updateRightPanel({ deduplication: undefined }); } }} />
{config.rightPanel?.deduplication?.enabled && (

이 컬럼의 값이 같은 데이터들 중 하나만 표시합니다

{(config.rightPanel?.deduplication?.keepStrategy === "latest" || config.rightPanel?.deduplication?.keepStrategy === "earliest") && (
)}
)}
{/* 수정 버튼 설정 */} {config.rightPanel?.showEdit && (

수정 버튼 설정

{config.rightPanel?.editButton?.mode === "modal" && ( { updateRightPanel({ editButton: { ...config.rightPanel?.editButton!, groupByColumns: columns, enabled: config.rightPanel?.editButton?.enabled ?? true, mode: config.rightPanel?.editButton?.mode || "auto", }, }); }} /> )} {config.rightPanel?.editButton?.mode === "modal" && (
updateRightPanel({ editButton: { ...config.rightPanel?.editButton!, modalScreenId: screenId, }, }) } />
)}
updateRightPanel({ editButton: { ...config.rightPanel?.editButton!, buttonLabel: e.target.value || undefined, enabled: config.rightPanel?.editButton?.enabled ?? true, mode: config.rightPanel?.editButton?.mode || "auto", }, }) } placeholder="수정" className="h-8 text-xs" />
)} {/* 추가 버튼 설정 */} {config.rightPanel?.showAdd && (

추가 버튼 설정

{config.rightPanel?.addButton?.mode === "modal" && (
updateRightPanel({ addButton: { ...config.rightPanel?.addButton!, modalScreenId: screenId, }, }) } />
)}
updateRightPanel({ addButton: { ...config.rightPanel?.addButton!, buttonLabel: e.target.value, enabled: true, mode: config.rightPanel?.addButton?.mode || "auto", }, }) } placeholder="추가" className="h-8 text-xs" />
)} {/* 삭제 버튼 설정 */}

삭제 버튼 설정

{ updateRightPanel({ deleteButton: { enabled: checked, buttonLabel: config.rightPanel?.deleteButton?.buttonLabel, buttonVariant: config.rightPanel?.deleteButton?.buttonVariant, }, }); }} />
{(config.rightPanel?.deleteButton?.enabled ?? true) && (
{ updateRightPanel({ deleteButton: { ...config.rightPanel?.deleteButton!, buttonLabel: e.target.value || undefined, enabled: config.rightPanel?.deleteButton?.enabled ?? true, }, }); }} className="h-8 text-xs" />
{ updateRightPanel({ deleteButton: { ...config.rightPanel?.deleteButton!, confirmMessage: e.target.value || undefined, enabled: config.rightPanel?.deleteButton?.enabled ?? true, }, }); }} className="h-8 text-xs" />
)}
{/* 추가 탭 */} {renderAdditionalTabs && (

추가 탭

우측 패널에 다른 테이블 데이터를 탭으로 추가합니다

{renderAdditionalTabs()}
)}
); };