"use client";

import React, { useState, useCallback, useEffect, useMemo, useRef } from "react";
import { ComponentRendererProps } from "../../types";
import { SplitPanelLayoutConfig } from "./types";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import {
  Plus,
  Search,
  GripVertical,
  Loader2,
  ChevronDown,
  ChevronUp,
  Save,
  ChevronRight,
  Pencil,
  Trash2,
  Settings,
  Move,
  FileSpreadsheet,
} from "lucide-react";
import { dataApi } from "@/lib/api/data";
import { entityJoinApi } from "@/lib/api/entityJoin";
import { formatNumber as centralFormatNumber } from "@/lib/formatting";
import { useToast } from "@/hooks/use-toast";
import { tableTypeApi } from "@/lib/api/screen";
import { apiClient, getFullImageUrl } from "@/lib/api/client";
import { getFilePreviewUrl } from "@/lib/api/file";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogFooter,
  DialogDescription,
} from "@/components/ui/dialog";
import { Label } from "@/components/ui/label";
import { useTableOptions } from "@/contexts/TableOptionsContext";
import { TableFilter, ColumnVisibility, GroupSumConfig } from "@/types/table-options";
import { useAuth } from "@/hooks/useAuth";
import { useSplitPanel } from "./SplitPanelContext";
import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer";
import { PanelInlineComponent } from "./types";
import { cn } from "@/lib/utils";
import { ResponsiveGridRenderer } from "@/components/screen/ResponsiveGridRenderer";
import { BomExcelUploadModal } from "../v2-bom-tree/BomExcelUploadModal";

export interface SplitPanelLayoutComponentProps extends ComponentRendererProps {
  // 추가 props
  onUpdateComponent?: (component: any) => void;
  // 🆕 패널 내부 컴포넌트 선택 콜백 (탭 컴포넌트와 동일 구조)
  onSelectPanelComponent?: (panelSide: "left" | "right", compId: string, comp: PanelInlineComponent) => void;
  selectedPanelComponentId?: string;
}

// 이미지 셀 렌더링 컴포넌트 (objid 또는 파일 경로 지원)
const SplitPanelCellImage: React.FC<{ value: string }> = React.memo(({ value }) => {
  const [imgSrc, setImgSrc] = React.useState<string | null>(null);

  React.useEffect(() => {
    if (!value) return;
    const strVal = String(value).trim();
    if (!strVal || strVal === "-") return;

    if (strVal.startsWith("http") || strVal.startsWith("/uploads/") || strVal.startsWith("/api/")) {
      setImgSrc(getFullImageUrl(strVal));
    } else {
      const previewUrl = getFilePreviewUrl(strVal);
      fetch(previewUrl, { credentials: "include" })
        .then((res) => {
          if (!res.ok) throw new Error("fetch failed");
          return res.blob();
        })
        .then((blob) => setImgSrc(URL.createObjectURL(blob)))
        .catch(() => setImgSrc(null));
    }
  }, [value]);

  if (!imgSrc) return <span className="text-muted-foreground text-xs">-</span>;

  return (
    <img
      src={imgSrc}
      alt=""
      className="h-8 w-8 rounded object-cover"
      onError={() => setImgSrc(null)}
    />
  );
});
SplitPanelCellImage.displayName = "SplitPanelCellImage";

/**
 * 커스텀 모드 런타임: 디자이너 좌표를 비례 스케일링하여 렌더링
 */
const ScaledCustomPanel: React.FC<{
  components: PanelInlineComponent[];
  formData: Record<string, any>;
  onFormDataChange: (fieldName: string, value: any) => void;
  tableName?: string;
  menuObjid?: number;
  screenId?: number;
  userId?: string;
  userName?: string;
  companyCode?: string;
  allComponents?: any;
  selectedRowsData?: any[];
  onSelectedRowsChange?: any;
}> = ({ components, formData, onFormDataChange, tableName, ...restProps }) => {
  const containerRef = React.useRef<HTMLDivElement>(null);
  const [containerWidth, setContainerWidth] = React.useState(0);

  React.useEffect(() => {
    const el = containerRef.current;
    if (!el) return;
    const ro = new ResizeObserver((entries) => {
      const w = entries[0]?.contentRect.width;
      if (w && w > 0) setContainerWidth(w);
    });
    ro.observe(el);
    return () => ro.disconnect();
  }, []);

  const canvasW = Math.max(
    ...components.map((c) => (c.position?.x || 0) + (c.size?.width || 200)),
    400,
  );
  const canvasH = Math.max(
    ...components.map((c) => (c.position?.y || 0) + (c.size?.height || 100)),
    200,
  );
  return (
    <div ref={containerRef} className="relative w-full" style={{ height: `${canvasH}px` }}>
      {containerWidth > 0 &&
        components.map((comp) => {
          const x = comp.position?.x || 0;
          const y = comp.position?.y || 0;
          const w = comp.size?.width || 200;
          const h = comp.size?.height || 36;

          const componentData = {
            id: comp.id,
            type: "component" as const,
            componentType: comp.componentType,
            label: comp.label,
            position: { x, y },
            size: { width: undefined, height: h },
            componentConfig: comp.componentConfig || {},
            style: { ...(comp.style || {}), width: "100%", height: "100%" },
            tableName: comp.componentConfig?.tableName,
            columnName: comp.componentConfig?.columnName,
            webType: comp.componentConfig?.webType,
            inputType: (comp as any).inputType || comp.componentConfig?.inputType,
          };

          return (
            <div
              key={comp.id}
              className="absolute"
              style={{
                left: `${(x / canvasW) * 100}%`,
                top: `${y}px`,
                width: `${(w / canvasW) * 100}%`,
                minHeight: `${h}px`,
              }}
            >
              <DynamicComponentRenderer
                component={componentData as any}
                isDesignMode={false}
                isInteractive={true}
                formData={formData}
                onFormDataChange={onFormDataChange}
                tableName={tableName}
                menuObjid={restProps.menuObjid}
                screenId={restProps.screenId}
                userId={restProps.userId}
                userName={restProps.userName}
                companyCode={restProps.companyCode}
                allComponents={restProps.allComponents}
                selectedRowsData={restProps.selectedRowsData}
                onSelectedRowsChange={restProps.onSelectedRowsChange}
              />
            </div>
          );
        })}
    </div>
  );
};

/**
 * SplitPanelLayout 컴포넌트
 * 마스터-디테일 패턴의 좌우 분할 레이아웃
 */
export const SplitPanelLayoutComponent: React.FC<SplitPanelLayoutComponentProps> = ({
  component,
  isDesignMode = false,
  isSelected = false,
  isPreview = false,
  onClick,
  onUpdateComponent,
  onSelectPanelComponent,
  selectedPanelComponentId: externalSelectedPanelComponentId,
  ...props
}) => {
  const componentConfig = (component.componentConfig || {}) as SplitPanelLayoutConfig;

  // 🐛 디버깅: 로드 시 rightPanel.components 확인
  const rightComps = componentConfig.rightPanel?.components || [];
  const finishedTimeline = rightComps.find((c: any) => c.id === "finished_timeline");
  if (finishedTimeline) {
    const fm = finishedTimeline.componentConfig?.fieldMapping;
    console.log("🔍 [SplitPanelLayout] finished_timeline fieldMapping:", {
      componentId: finishedTimeline.id,
      fieldMapping: fm ? JSON.stringify(fm) : "undefined",
      fieldMappingKeys: fm ? Object.keys(fm) : [],
      fieldMappingId: fm?.id,
      fullComponentConfig: JSON.stringify(finishedTimeline.componentConfig || {}, null, 2),
    });
  }
  // 🆕 프리뷰용 회사 코드 오버라이드 (최고 관리자만 사용 가능)
  const companyCode = (props as any).companyCode as string | undefined;

  // 기본 설정값
  const splitRatio = componentConfig.splitRatio || 30;
  const resizable = componentConfig.resizable ?? true;
  const minLeftWidth = componentConfig.minLeftWidth || 200;
  const minRightWidth = componentConfig.minRightWidth || 300;

  // 필드 표시 유틸리티 (하드코딩 제거, 동적으로 작동)
  const shouldShowField = (fieldName: string): boolean => {
    const lower = fieldName.toLowerCase();

    // 기본 제외: id, 비밀번호, 토큰, 회사코드
    if (lower === "id" || lower === "company_code" || lower === "company_name") return false;
    if (lower.includes("password") || lower.includes("token")) return false;

    // 나머지는 모두 표시!
    return true;
  };

  // 🆕 엔티티 조인 컬럼명 변환 헬퍼
  // "테이블명.컬럼명" 형식을 "원본컬럼_조인컬럼명" 형식으로 변환하여 데이터 접근
  const getEntityJoinValue = useCallback(
    (item: any, columnName: string, entityColumnMap?: Record<string, string>): any => {
      // 🆕 백엔드가 제공하는 _label 필드 우선 사용
      // 백엔드는 "표시 컬럼"이 설정된 경우 columnName_label을 자동 생성
      const labelKey = `${columnName}_label`;
      if (item[labelKey] !== undefined && item[labelKey] !== "" && item[labelKey] !== null) {
        return item[labelKey];
      }

      // 직접 매칭 시도 (JOIN된 값이 없으면 원본 값 반환)
      if (item[columnName] !== undefined) {
        return item[columnName];
      }

      // "테이블명.컬럼명" 형식인 경우 (예: item_info.item_name)
      if (columnName.includes(".")) {
        const [tableName, fieldName] = columnName.split(".");

        // 🔍 엔티티 조인 컬럼 값 추출
        // 예: item_info.item_name, item_info.standard, item_info.unit

        // 1️⃣ 소스 컬럼 추론 (item_info → item_code, warehouse_info → warehouse_id 등)
        const inferredSourceColumn = tableName.replace("_info", "_code").replace("_mng", "_id");

        // 2️⃣ 정확한 키 매핑 시도: 소스컬럼_필드명
        // 예: item_code_item_name, item_code_standard, item_code_unit
        const exactKey = `${inferredSourceColumn}_${fieldName}`;
        if (item[exactKey] !== undefined) {
          return item[exactKey];
        }

        // 🆕 2-1️⃣ item_id 패턴 시도 (백엔드가 item_id_xxx 형식으로 반환하는 경우)
        // 예: item_info.item_name → item_id_item_name
        const idPatternKey = `${tableName.replace("_info", "_id").replace("_mng", "_id")}_${fieldName}`;
        if (item[idPatternKey] !== undefined) {
          return item[idPatternKey];
        }

        // 3️⃣ 별칭 패턴: 소스컬럼_name (기본 표시 컬럼용)
        // 예: item_code_name (item_name의 별칭)
        if (fieldName === "item_name" || fieldName === "name") {
          const aliasKey = `${inferredSourceColumn}_name`;
          if (item[aliasKey] !== undefined) {
            return item[aliasKey];
          }
          // 🆕 item_id_name 패턴도 시도
          const idAliasKey = `${tableName.replace("_info", "_id").replace("_mng", "_id")}_name`;
          if (item[idAliasKey] !== undefined) {
            return item[idAliasKey];
          }
        }

        // 4️⃣ entityColumnMap에서 매핑 찾기 (화면 설정에서 지정된 경우)
        if (entityColumnMap && entityColumnMap[tableName]) {
          const sourceColumn = entityColumnMap[tableName];
          const joinedColumnName = `${sourceColumn}_${fieldName}`;
          if (item[joinedColumnName] !== undefined) {
            return item[joinedColumnName];
          }
        }

        // 5️⃣ 테이블명_컬럼명 형식으로 시도
        const underscoreKey = `${tableName}_${fieldName}`;
        if (item[underscoreKey] !== undefined) {
          return item[underscoreKey];
        }
      }

      return undefined;
    },
    [],
  );

  // TableOptions Context
  const { registerTable, unregisterTable } = useTableOptions();
  const [leftFilters, setLeftFilters] = useState<TableFilter[]>([]);
  const [leftGrouping, setLeftGrouping] = useState<string[]>([]);
  const [leftColumnVisibility, setLeftColumnVisibility] = useState<ColumnVisibility[]>([]);
  const [leftColumnOrder, setLeftColumnOrder] = useState<string[]>([]); // 🔧 컬럼 순서
  const [leftGroupSumConfig, setLeftGroupSumConfig] = useState<GroupSumConfig | null>(null); // 🆕 그룹별 합산 설정
  const [rightFilters, setRightFilters] = useState<TableFilter[]>([]);
  const [rightGrouping, setRightGrouping] = useState<string[]>([]);
  const [rightColumnVisibility, setRightColumnVisibility] = useState<ColumnVisibility[]>([]);

  // 데이터 상태
  const [leftData, setLeftData] = useState<any[]>([]);
  const [rightData, setRightData] = useState<any[] | any>(null); // 조인 모드는 배열, 상세 모드는 객체
  const [selectedLeftItem, setSelectedLeftItem] = useState<any>(null);
  const [expandedRightItems, setExpandedRightItems] = useState<Set<string | number>>(new Set()); // 확장된 우측 아이템
  const [customLeftSelectedData, setCustomLeftSelectedData] = useState<Record<string, any>>({}); // 커스텀 모드: 좌측 선택 데이터
  // 커스텀 모드: 탭/버튼 간 공유할 selectedRowsData 자체 관리 (항상 로컬 상태 사용)
  const [localSelectedRowsData, setLocalSelectedRowsData] = useState<any[]>([]);
  const handleLocalSelectedRowsChange = useCallback(
    (selectedRows: any[], selectedRowsDataNew: any[], sortBy?: string, sortOrder?: "asc" | "desc", columnOrder?: string[]) => {
      setLocalSelectedRowsData(selectedRowsDataNew);
      if ((props as any).onSelectedRowsChange) {
        (props as any).onSelectedRowsChange(selectedRows, selectedRowsDataNew, sortBy, sortOrder, columnOrder);
      }
    },
    [(props as any).onSelectedRowsChange],
  );
  const [leftSearchQuery, setLeftSearchQuery] = useState("");
  const [rightSearchQuery, setRightSearchQuery] = useState("");
  const [isLoadingLeft, setIsLoadingLeft] = useState(false);
  const [isLoadingRight, setIsLoadingRight] = useState(false);
  const [rightTableColumns, setRightTableColumns] = useState<any[]>([]); // 우측 테이블 컬럼 정보
  const [columnInputTypes, setColumnInputTypes] = useState<Record<string, string>>({});
  const [expandedItems, setExpandedItems] = useState<Set<any>>(new Set()); // 펼쳐진 항목들

  // 추가 탭 관련 상태
  const [activeTabIndex, setActiveTabIndex] = useState(0); // 0 = 기본 탭, 1+ = 추가 탭
  const [tabsData, setTabsData] = useState<Record<number, any[]>>({}); // 탭별 데이터
  const [tabsLoading, setTabsLoading] = useState<Record<number, boolean>>({}); // 탭별 로딩 상태
  const [leftColumnLabels, setLeftColumnLabels] = useState<Record<string, string>>({}); // 좌측 컬럼 라벨
  const [rightColumnLabels, setRightColumnLabels] = useState<Record<string, string>>({}); // 우측 컬럼 라벨
  const [leftCategoryMappings, setLeftCategoryMappings] = useState<
    Record<string, Record<string, { label: string; color?: string }>>
  >({}); // 좌측 카테고리 매핑
  const [rightCategoryMappings, setRightCategoryMappings] = useState<
    Record<string, Record<string, { label: string; color?: string }>>
  >({}); // 우측 카테고리 매핑

  // 🆕 커스텀 모드: 드래그/리사이즈 상태
  const [draggingCompId, setDraggingCompId] = useState<string | null>(null);
  const [dragPosition, setDragPosition] = useState<{ x: number; y: number } | null>(null);
  const [resizingCompId, setResizingCompId] = useState<string | null>(null);
  const [resizeSize, setResizeSize] = useState<{ width: number; height: number } | null>(null);
  // 내부 선택 상태 (외부 prop 없을 때 fallback)
  const [internalSelectedCompId, setInternalSelectedCompId] = useState<string | null>(null);
  const selectedPanelComponentId = externalSelectedPanelComponentId ?? internalSelectedCompId;
  // 🆕 커스텀 모드: 분할패널 내 탭 컴포넌트의 선택 상태 관리
  const [nestedTabSelectedCompId, setNestedTabSelectedCompId] = useState<string | undefined>(undefined);
  const rafRef = useRef<number | null>(null);

  // 🆕 10px 단위 스냅 함수
  const snapTo10 = useCallback((value: number) => Math.round(value / 10) * 10, []);

  // 🆕 커스텀 모드: 컴포넌트 삭제 핸들러
  const handleRemovePanelComponent = useCallback(
    (panelSide: "left" | "right", compId: string) => {
      if (!onUpdateComponent) return;
      
      const panelKey = panelSide === "left" ? "leftPanel" : "rightPanel";
      const panelConfig = componentConfig[panelKey] || {};
      const updatedComponents = (panelConfig.components || []).filter(
        (c: PanelInlineComponent) => c.id !== compId
      );
      
      onUpdateComponent({
        ...component,
        componentConfig: {
          ...componentConfig,
          [panelKey]: {
            ...panelConfig,
            components: updatedComponents,
          },
        },
      });
    },
    [component, componentConfig, onUpdateComponent]
  );

  // 🆕 중첩된 컴포넌트 업데이트 핸들러 (탭 컴포넌트 내부 위치 변경 등)
  const handleNestedComponentUpdate = useCallback(
    (panelSide: "left" | "right", compId: string, updatedNestedComponent: any) => {
      if (!onUpdateComponent) return;
      
      const panelKey = panelSide === "left" ? "leftPanel" : "rightPanel";
      const panelConfig = componentConfig[panelKey] || {};
      const panelComponents = panelConfig.components || [];
      
      const updatedComponents = panelComponents.map((c: PanelInlineComponent) =>
        c.id === compId ? { ...c, ...updatedNestedComponent, id: c.id } : c
      );
      
      onUpdateComponent({
        ...component,
        componentConfig: {
          ...componentConfig,
          [panelKey]: {
            ...panelConfig,
            components: updatedComponents,
          },
        },
      });
    },
    [component, componentConfig, onUpdateComponent]
  );

  // 🆕 커스텀 모드: 드래그 시작 핸들러
  const handlePanelDragStart = useCallback(
    (e: React.MouseEvent, panelSide: "left" | "right", comp: PanelInlineComponent) => {
      e.stopPropagation();
      e.preventDefault();
      
      const startMouseX = e.clientX;
      const startMouseY = e.clientY;
      const startLeft = comp.position?.x || 0;
      const startTop = comp.position?.y || 0;
      
      setDraggingCompId(comp.id);
      setDragPosition({ x: startLeft, y: startTop });

      const handleMouseMove = (moveEvent: MouseEvent) => {
        if (rafRef.current) {
          cancelAnimationFrame(rafRef.current);
        }
        
        rafRef.current = requestAnimationFrame(() => {
          const deltaX = moveEvent.clientX - startMouseX;
          const deltaY = moveEvent.clientY - startMouseY;
          // 10px 단위 스냅 적용
          const newX = snapTo10(Math.max(0, startLeft + deltaX));
          const newY = snapTo10(Math.max(0, startTop + deltaY));
          setDragPosition({ x: newX, y: newY });
        });
      };

      const handleMouseUp = (upEvent: MouseEvent) => {
        document.removeEventListener("mousemove", handleMouseMove);
        document.removeEventListener("mouseup", handleMouseUp);
        
        if (rafRef.current) {
          cancelAnimationFrame(rafRef.current);
          rafRef.current = null;
        }
        
        const deltaX = upEvent.clientX - startMouseX;
        const deltaY = upEvent.clientY - startMouseY;
        // 10px 단위 스냅 적용
        const newX = snapTo10(Math.max(0, startLeft + deltaX));
        const newY = snapTo10(Math.max(0, startTop + deltaY));
        
        setDraggingCompId(null);
        setDragPosition(null);

        if (onUpdateComponent) {
          const panelKey = panelSide === "left" ? "leftPanel" : "rightPanel";
          const panelConfig = componentConfig[panelKey] || {};
          const updatedComponents = (panelConfig.components || []).map((c: PanelInlineComponent) =>
            c.id === comp.id
              ? { ...c, position: { x: newX, y: newY } }
              : c
          );
          
          onUpdateComponent({
            ...component,
            componentConfig: {
              ...componentConfig,
              [panelKey]: {
                ...panelConfig,
                components: updatedComponents,
              },
            },
          });
        }
      };

      document.addEventListener("mousemove", handleMouseMove);
      document.addEventListener("mouseup", handleMouseUp);
    },
    [component, componentConfig, onUpdateComponent, snapTo10]
  );

  // 🆕 커스텀 모드: 리사이즈 시작 핸들러
  const handlePanelResizeStart = useCallback(
    (e: React.MouseEvent, panelSide: "left" | "right", comp: PanelInlineComponent, direction: "e" | "s" | "se") => {
      e.stopPropagation();
      e.preventDefault();
      
      const startMouseX = e.clientX;
      const startMouseY = e.clientY;
      const startWidth = comp.size?.width || 200;
      const startHeight = comp.size?.height || 100;
      
      setResizingCompId(comp.id);
      setResizeSize({ width: startWidth, height: startHeight });

      const handleMouseMove = (moveEvent: MouseEvent) => {
        if (rafRef.current) {
          cancelAnimationFrame(rafRef.current);
        }
        
        rafRef.current = requestAnimationFrame(() => {
          const deltaX = moveEvent.clientX - startMouseX;
          const deltaY = moveEvent.clientY - startMouseY;
          
          let newWidth = startWidth;
          let newHeight = startHeight;
          
          if (direction === "e" || direction === "se") {
            newWidth = snapTo10(Math.max(50, startWidth + deltaX));
          }
          if (direction === "s" || direction === "se") {
            newHeight = snapTo10(Math.max(30, startHeight + deltaY));
          }
          
          setResizeSize({ width: newWidth, height: newHeight });
        });
      };

      const handleMouseUp = (upEvent: MouseEvent) => {
        document.removeEventListener("mousemove", handleMouseMove);
        document.removeEventListener("mouseup", handleMouseUp);
        
        if (rafRef.current) {
          cancelAnimationFrame(rafRef.current);
          rafRef.current = null;
        }
        
        const deltaX = upEvent.clientX - startMouseX;
        const deltaY = upEvent.clientY - startMouseY;
        
        let newWidth = startWidth;
        let newHeight = startHeight;
        
        if (direction === "e" || direction === "se") {
          newWidth = snapTo10(Math.max(50, startWidth + deltaX));
        }
        if (direction === "s" || direction === "se") {
          newHeight = snapTo10(Math.max(30, startHeight + deltaY));
        }
        
        setResizingCompId(null);
        setResizeSize(null);

        if (onUpdateComponent) {
          const panelKey = panelSide === "left" ? "leftPanel" : "rightPanel";
          const panelConfig = componentConfig[panelKey] || {};
          const updatedComponents = (panelConfig.components || []).map((c: PanelInlineComponent) =>
            c.id === comp.id
              ? { ...c, size: { width: newWidth, height: newHeight } }
              : c
          );
          
          onUpdateComponent({
            ...component,
            componentConfig: {
              ...componentConfig,
              [panelKey]: {
                ...panelConfig,
                components: updatedComponents,
              },
            },
          });
        }
      };

      document.addEventListener("mousemove", handleMouseMove);
      document.addEventListener("mouseup", handleMouseUp);
    },
    [component, componentConfig, onUpdateComponent, snapTo10]
  );

  const { toast } = useToast();

  // 추가 모달 상태
  const [showAddModal, setShowAddModal] = useState(false);
  const [addModalPanel, setAddModalPanel] = useState<"left" | "right" | "left-item" | null>(null);
  const [addModalFormData, setAddModalFormData] = useState<Record<string, any>>({});
  const [bomExcelUploadOpen, setBomExcelUploadOpen] = useState(false);

  // 수정 모달 상태
  const [showEditModal, setShowEditModal] = useState(false);
  const [editModalPanel, setEditModalPanel] = useState<"left" | "right" | null>(null);
  const [editModalItem, setEditModalItem] = useState<any>(null);
  const [editModalFormData, setEditModalFormData] = useState<Record<string, any>>({});

  // 삭제 확인 모달 상태
  const [showDeleteModal, setShowDeleteModal] = useState(false);
  const [deleteModalPanel, setDeleteModalPanel] = useState<"left" | "right" | null>(null);
  const [deleteModalItem, setDeleteModalItem] = useState<any>(null);
  const [deleteModalTableName, setDeleteModalTableName] = useState<string | null>(null); // 추가 탭 삭제 시 테이블명

  // 리사이저 드래그 상태
  const [isDragging, setIsDragging] = useState(false);
  const [leftWidth, setLeftWidth] = useState(splitRatio);
  const containerRef = React.useRef<HTMLDivElement>(null);

  // 🆕 SplitPanel Resize Context 연동 (버튼 등 외부 컴포넌트와 드래그 리사이즈 상태 공유)
  const splitPanelContext = useSplitPanel();
  const {
    registerSplitPanel: ctxRegisterSplitPanel,
    unregisterSplitPanel: ctxUnregisterSplitPanel,
    updateSplitPanel: ctxUpdateSplitPanel,
  } = splitPanelContext;
  const splitPanelId = `split-panel-${component.id}`;

  // 디버깅: Context 연결 상태 확인
  console.log("🔗 [SplitPanelLayout] Context 연결 상태:", {
    componentId: component.id,
    splitPanelId,
    hasRegisterFunc: typeof ctxRegisterSplitPanel === "function",
    splitPanelsSize: splitPanelContext.splitPanels?.size ?? "없음",
  });

  // Context에 분할 패널 등록 (좌표 정보 포함) - 마운트 시 1회만 실행
  const ctxRegisterRef = useRef(ctxRegisterSplitPanel);
  const ctxUnregisterRef = useRef(ctxUnregisterSplitPanel);
  ctxRegisterRef.current = ctxRegisterSplitPanel;
  ctxUnregisterRef.current = ctxUnregisterSplitPanel;

  useEffect(() => {
    // 컴포넌트의 위치와 크기 정보
    const panelX = component.position?.x || 0;
    const panelY = component.position?.y || 0;
    const panelWidth = component.size?.width || component.style?.width || 800;
    const panelHeight = component.size?.height || component.style?.height || 600;

    const panelInfo = {
      x: panelX,
      y: panelY,
      width: typeof panelWidth === "number" ? panelWidth : parseInt(String(panelWidth)) || 800,
      height: typeof panelHeight === "number" ? panelHeight : parseInt(String(panelHeight)) || 600,
      leftWidthPercent: splitRatio, // 초기값은 splitRatio 사용
      initialLeftWidthPercent: splitRatio,
      isDragging: false,
    };

    console.log("📦 [SplitPanelLayout] Context에 분할 패널 등록:", {
      splitPanelId,
      panelInfo,
    });

    ctxRegisterRef.current(splitPanelId, panelInfo);

    return () => {
      console.log("📦 [SplitPanelLayout] Context에서 분할 패널 해제:", splitPanelId);
      ctxUnregisterRef.current(splitPanelId);
    };
    // 마운트/언마운트 시에만 실행, 위치/크기 변경은 별도 업데이트로 처리
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [splitPanelId]);

  // 위치/크기 변경 시 Context 업데이트 (등록 후)
  const ctxUpdateRef = useRef(ctxUpdateSplitPanel);
  ctxUpdateRef.current = ctxUpdateSplitPanel;

  useEffect(() => {
    const panelX = component.position?.x || 0;
    const panelY = component.position?.y || 0;
    const panelWidth = component.size?.width || component.style?.width || 800;
    const panelHeight = component.size?.height || component.style?.height || 600;

    ctxUpdateRef.current(splitPanelId, {
      x: panelX,
      y: panelY,
      width: typeof panelWidth === "number" ? panelWidth : parseInt(String(panelWidth)) || 800,
      height: typeof panelHeight === "number" ? panelHeight : parseInt(String(panelHeight)) || 600,
    });
  }, [
    splitPanelId,
    component.position?.x,
    component.position?.y,
    component.size?.width,
    component.size?.height,
    component.style?.width,
    component.style?.height,
  ]);

  // leftWidth 변경 시 Context 업데이트
  useEffect(() => {
    ctxUpdateRef.current(splitPanelId, { leftWidthPercent: leftWidth });
  }, [leftWidth, splitPanelId]);

  // 드래그 상태 변경 시 Context 업데이트
  // 이전 드래그 상태를 추적하여 드래그 종료 시점을 감지
  const prevIsDraggingRef = useRef(false);

  useEffect(() => {
    const wasJustDragging = prevIsDraggingRef.current && !isDragging;

    if (isDragging) {
      // 드래그 시작 시: 현재 비율을 초기 비율로 저장
      ctxUpdateRef.current(splitPanelId, {
        isDragging: true,
        initialLeftWidthPercent: leftWidth,
      });
    } else if (wasJustDragging) {
      // 드래그 종료 시: 최종 비율을 초기 비율로 업데이트 (버튼 위치 고정)
      ctxUpdateRef.current(splitPanelId, {
        isDragging: false,
        initialLeftWidthPercent: leftWidth,
      });
      console.log("🛑 [SplitPanelLayout] 드래그 종료 - 버튼 위치 고정:", {
        splitPanelId,
        finalLeftWidthPercent: leftWidth,
      });
    }

    prevIsDraggingRef.current = isDragging;
  }, [isDragging, splitPanelId, leftWidth]);

  // 🆕 그룹별 합산된 데이터 계산
  const summedLeftData = useMemo(() => {
    console.log("🔍 [그룹합산] leftGroupSumConfig:", leftGroupSumConfig);

    // 그룹핑이 비활성화되었거나 그룹 기준 컬럼이 없으면 원본 데이터 반환
    if (!leftGroupSumConfig?.enabled || !leftGroupSumConfig?.groupByColumn) {
      console.log("🔍 [그룹합산] 그룹핑 비활성화 - 원본 데이터 반환");
      return leftData;
    }

    const groupByColumn = leftGroupSumConfig.groupByColumn;
    const groupMap = new Map<string, any>();

    // 조인 컬럼인지 확인하고 실제 키 추론
    const getActualKey = (columnName: string, item: any): string => {
      if (columnName.includes(".")) {
        const [refTable, fieldName] = columnName.split(".");
        const inferredSourceColumn = refTable.replace("_info", "_code").replace("_mng", "_id");
        const exactKey = `${inferredSourceColumn}_${fieldName}`;
        console.log("🔍 [그룹합산] 조인 컬럼 키 변환:", { columnName, exactKey, hasKey: item[exactKey] !== undefined });
        if (item[exactKey] !== undefined) return exactKey;
        if (fieldName === "item_name" || fieldName === "name") {
          const aliasKey = `${inferredSourceColumn}_name`;
          if (item[aliasKey] !== undefined) return aliasKey;
        }
      }
      return columnName;
    };

    // 숫자 타입인지 확인하는 함수
    const isNumericValue = (value: any): boolean => {
      if (value === null || value === undefined || value === "") return false;
      const num = parseFloat(String(value));
      return !isNaN(num) && isFinite(num);
    };

    // 그룹핑 수행
    leftData.forEach((item) => {
      const actualKey = getActualKey(groupByColumn, item);
      const groupValue = String(item[actualKey] || item[groupByColumn] || "");

      // 원본 ID 추출 (id, ID, 또는 첫 번째 값)
      const originalId = item.id || item.ID || Object.values(item)[0];

      if (!groupMap.has(groupValue)) {
        // 첫 번째 항목을 기준으로 초기화 + 원본 ID 배열 + 원본 데이터 배열
        groupMap.set(groupValue, {
          ...item,
          _groupCount: 1,
          _originalIds: [originalId],
          _originalItems: [item], // 🆕 원본 데이터 전체 저장
        });
      } else {
        const existing = groupMap.get(groupValue);
        existing._groupCount += 1;
        existing._originalIds.push(originalId);
        existing._originalItems.push(item); // 🆕 원본 데이터 추가

        // 모든 키에 대해 숫자면 합산
        Object.keys(item).forEach((key) => {
          const value = item[key];
          if (isNumericValue(value) && key !== groupByColumn && !key.endsWith("_id") && !key.includes("code")) {
            const numValue = parseFloat(String(value));
            const existingValue = parseFloat(String(existing[key] || 0));
            existing[key] = existingValue + numValue;
          }
        });

        groupMap.set(groupValue, existing);
      }
    });

    const result = Array.from(groupMap.values());
    console.log("🔗 [분할패널] 그룹별 합산 결과:", {
      원본개수: leftData.length,
      그룹개수: result.length,
      그룹기준: groupByColumn,
    });

    return result;
  }, [leftData, leftGroupSumConfig]);

  // 컴포넌트 스타일
  // height: component.size?.height 우선, 없으면 component.style?.height, 기본 600px
  const getHeightValue = () => {
    const sizeH = component.size?.height;
    if (sizeH && typeof sizeH === "number" && sizeH > 0) return `${sizeH}px`;
    const height = component.style?.height;
    if (!height) return "600px";
    if (typeof height === "string") return height;
    return `${height}px`;
  };

  const componentStyle: React.CSSProperties = isDesignMode
    ? {
        width: "100%",
        height: "100%",
        minHeight: getHeightValue(),
        cursor: "pointer",
        border: isSelected ? "2px solid #3b82f6" : "1px solid #e5e7eb",
      }
    : {
        position: "relative",
        width: "100%",
        height: getHeightValue(),
      };

  // 계층 구조 빌드 함수 (트리 구조 유지)
  const buildHierarchy = useCallback(
    (items: any[]): any[] => {
      if (!items || items.length === 0) return [];

      const itemAddConfig = componentConfig.leftPanel?.itemAddConfig;
      if (!itemAddConfig) return items.map((item) => ({ ...item, children: [] })); // 계층 설정이 없으면 평면 목록

      const { sourceColumn, parentColumn } = itemAddConfig;
      if (!sourceColumn || !parentColumn) return items.map((item) => ({ ...item, children: [] }));

      // ID를 키로 하는 맵 생성
      const itemMap = new Map<any, any>();
      const rootItems: any[] = [];

      // 모든 항목을 맵에 추가하고 children 배열 초기화
      items.forEach((item) => {
        const id = item[sourceColumn];
        itemMap.set(id, { ...item, children: [], level: 0 });
      });

      // 부모-자식 관계 설정
      items.forEach((item) => {
        const id = item[sourceColumn];
        const parentId = item[parentColumn];
        const currentItem = itemMap.get(id);

        if (!currentItem) return;

        if (!parentId || parentId === null || parentId === "") {
          // 최상위 항목
          rootItems.push(currentItem);
        } else {
          // 부모가 있는 항목
          const parentItem = itemMap.get(parentId);
          if (parentItem) {
            currentItem.level = parentItem.level + 1;
            parentItem.children.push(currentItem);
          } else {
            // 부모를 찾을 수 없으면 최상위로 처리
            rootItems.push(currentItem);
          }
        }
      });

      return rootItems;
    },
    [componentConfig.leftPanel?.itemAddConfig],
  );

  // 🔧 사용자 ID 가져오기
  const { userId: currentUserId } = useAuth();

  // 🔄 필터를 searchValues 형식으로 변환
  const searchValues = useMemo(() => {
    if (!leftFilters || leftFilters.length === 0) return {};

    const values: Record<string, any> = {};
    leftFilters.forEach((filter) => {
      if (filter.value !== undefined && filter.value !== null && filter.value !== "") {
        values[filter.columnName] = {
          value: filter.value,
          operator: filter.operator || "contains",
        };
      }
    });
    return values;
  }, [leftFilters]);

  // 🔄 컬럼 가시성 및 순서 처리
  const visibleLeftColumns = useMemo(() => {
    const displayColumns = componentConfig.leftPanel?.columns || [];

    if (displayColumns.length === 0) return [];

    let columns = displayColumns;

    // columnVisibility가 있으면 가시성 적용
    if (leftColumnVisibility.length > 0) {
      const visibilityMap = new Map(leftColumnVisibility.map((cv) => [cv.columnName, cv.visible]));
      columns = columns.filter((col: any) => {
        const colName = typeof col === "string" ? col : col.name || col.columnName;
        return visibilityMap.get(colName) !== false;
      });
    }

    // 🔧 컬럼 순서 적용
    if (leftColumnOrder.length > 0) {
      const orderMap = new Map(leftColumnOrder.map((name, index) => [name, index]));
      columns = [...columns].sort((a, b) => {
        const aName = typeof a === "string" ? a : a.name || a.columnName;
        const bName = typeof b === "string" ? b : b.name || b.columnName;
        const aIndex = orderMap.get(aName) ?? 999;
        const bIndex = orderMap.get(bName) ?? 999;
        return aIndex - bIndex;
      });
    }

    return columns;
  }, [componentConfig.leftPanel?.columns, leftColumnVisibility, leftColumnOrder]);

  // 🔄 데이터 그룹화
  const groupedLeftData = useMemo(() => {
    if (!leftGrouping || leftGrouping.length === 0 || leftData.length === 0) return [];

    const grouped = new Map<string, any[]>();

    leftData.forEach((item) => {
      // 각 그룹 컬럼의 값을 조합하여 그룹 키 생성
      const groupKey = leftGrouping
        .map((col) => {
          const value = item[col];
          // null/undefined 처리
          return value === null || value === undefined ? "(비어있음)" : String(value);
        })
        .join(" > ");

      if (!grouped.has(groupKey)) {
        grouped.set(groupKey, []);
      }
      grouped.get(groupKey)!.push(item);
    });

    return Array.from(grouped.entries()).map(([key, items]) => ({
      groupKey: key,
      items,
      count: items.length,
    }));
  }, [leftData, leftGrouping]);

  // 날짜 포맷팅 헬퍼 함수
  const formatDateValue = useCallback((value: any, dateFormat: string): string => {
    if (!value) return "-";
    const date = new Date(value);
    if (isNaN(date.getTime())) return String(value);

    if (dateFormat === "relative") {
      // 상대 시간 (예: 3일 전, 2시간 전)
      const now = new Date();
      const diffMs = now.getTime() - date.getTime();
      const diffSec = Math.floor(diffMs / 1000);
      const diffMin = Math.floor(diffSec / 60);
      const diffHour = Math.floor(diffMin / 60);
      const diffDay = Math.floor(diffHour / 24);
      const diffMonth = Math.floor(diffDay / 30);
      const diffYear = Math.floor(diffMonth / 12);

      if (diffYear > 0) return `${diffYear}년 전`;
      if (diffMonth > 0) return `${diffMonth}개월 전`;
      if (diffDay > 0) return `${diffDay}일 전`;
      if (diffHour > 0) return `${diffHour}시간 전`;
      if (diffMin > 0) return `${diffMin}분 전`;
      return "방금 전";
    }

    // 포맷 문자열 치환
    return dateFormat
      .replace("YYYY", String(date.getFullYear()))
      .replace("MM", String(date.getMonth() + 1).padStart(2, "0"))
      .replace("DD", String(date.getDate()).padStart(2, "0"))
      .replace("HH", String(date.getHours()).padStart(2, "0"))
      .replace("mm", String(date.getMinutes()).padStart(2, "0"))
      .replace("ss", String(date.getSeconds()).padStart(2, "0"));
  }, []);

  // 숫자 포맷팅 헬퍼 함수 (공통 formatNumber 기반)
  const formatNumberValue = useCallback((value: any, format: any): string => {
    if (value === null || value === undefined || value === "") return "-";
    const num = typeof value === "number" ? value : parseFloat(String(value));
    if (isNaN(num)) return String(value);

    let result: string;
    if (format?.thousandSeparator === false) {
      const dec = format?.decimalPlaces ?? 0;
      result = num.toFixed(dec);
    } else {
      result = centralFormatNumber(num, format?.decimalPlaces);
    }

    if (format?.prefix) result = format.prefix + result;
    if (format?.suffix) result = result + format.suffix;
    return result;
  }, []);

  // 프로그레스바 셀 렌더링 (부모 값 대비 자식 값 비율)
  const renderProgressCell = useCallback(
    (col: any, item: any, parentData: any) => {
      const current = Number(item[col.numerator] || 0);
      const max = Number(parentData?.[col.denominator] || item[col.denominator] || 0);
      const percentage = max > 0 ? Math.round((current / max) * 100) : 0;
      const barWidth = Math.min(percentage, 100);
      const barColor =
        percentage > 100
          ? "bg-red-600"
          : percentage >= 90
            ? "bg-red-500"
            : percentage >= 70
              ? "bg-amber-500"
              : "bg-emerald-500";

      return (
        <div className="flex min-w-[120px] items-center gap-2">
          <div className="flex-1">
            <div className="bg-muted h-2 w-full rounded-full">
              <div
                className={`h-2 rounded-full transition-all ${barColor}`}
                style={{ width: `${barWidth}%` }}
              />
            </div>
            <div className="text-muted-foreground mt-0.5 text-[10px]">
              {current.toLocaleString()} / {max.toLocaleString()}
            </div>
          </div>
          <span className="shrink-0 text-xs font-medium">{percentage}%</span>
        </div>
      );
    },
    [],
  );

  // 셀 값 포맷팅 함수 (카테고리 타입 처리 + 날짜/숫자 포맷)
  const formatCellValue = useCallback(
    (
      columnName: string,
      value: any,
      categoryMappings: Record<string, Record<string, { label: string; color?: string }>>,
      format?: {
        type?: "number" | "currency" | "date" | "text";
        thousandSeparator?: boolean;
        decimalPlaces?: number;
        prefix?: string;
        suffix?: string;
        dateFormat?: string;
      },
    ) => {
      if (value === null || value === undefined) return "-";

      // 이미지 타입 컬럼 처리
      const colInputType = columnInputTypes[columnName];
      if (colInputType === "image" && value) {
        return <SplitPanelCellImage value={String(value)} />;
      }

      // 🆕 날짜 포맷 적용
      if (format?.type === "date" || format?.dateFormat) {
        return formatDateValue(value, format?.dateFormat || "YYYY-MM-DD");
      }

      // 숫자 포맷 적용 (format 설정이 있거나 input_type이 number/decimal이면 자동 적용)
      const isNumericByInputType = colInputType === "number" || colInputType === "decimal";
      if (
        format?.type === "number" ||
        format?.type === "currency" ||
        format?.thousandSeparator ||
        format?.decimalPlaces !== undefined ||
        isNumericByInputType
      ) {
        return formatNumberValue(value, format || { thousandSeparator: true });
      }

      // 카테고리 매핑 찾기 (여러 키 형태 시도)
      // 1. 전체 컬럼명 (예: "item_info.material")
      // 2. 컬럼명만 (예: "material")
      // 3. 전역 폴백: 모든 매핑에서 value 검색
      let mapping = categoryMappings[columnName];

      if (!mapping && columnName.includes(".")) {
        const simpleColumnName = columnName.split(".").pop() || columnName;
        mapping = categoryMappings[simpleColumnName];
      }

      const strValue = String(value);

      if (mapping && mapping[strValue]) {
        const categoryData = mapping[strValue];
        const displayLabel = categoryData.label || strValue;
        const displayColor = categoryData.color || "#64748b";

        return (
          <Badge
            style={{
              backgroundColor: displayColor,
              borderColor: displayColor,
            }}
            className="text-white"
          >
            {displayLabel}
          </Badge>
        );
      }

      // 전역 폴백: 컬럼명으로 매핑을 못 찾았을 때, 전체 매핑에서 값 검색
      if (!mapping && (strValue.startsWith("CAT_") || strValue.startsWith("CATEGORY_"))) {
        for (const key of Object.keys(categoryMappings)) {
          const m = categoryMappings[key];
          if (m && m[strValue]) {
            const categoryData = m[strValue];
            const displayLabel = categoryData.label || strValue;
            const displayColor = categoryData.color || "#64748b";
            return (
              <Badge
                style={{
                  backgroundColor: displayColor,
                  borderColor: displayColor,
                }}
                className="text-white"
              >
                {displayLabel}
              </Badge>
            );
          }
        }
      }

      // 🆕 자동 날짜 감지 (ISO 8601 형식 또는 Date 객체)
      if (typeof value === "string" && value.match(/^\d{4}-\d{2}-\d{2}(T|\s)/)) {
        return formatDateValue(value, "YYYY-MM-DD");
      }

      // 🆕 자동 숫자 감지 (숫자 또는 숫자 문자열) - 소수점 있으면 정수로 변환
      if (typeof value === "number") {
        // 숫자인 경우 정수로 표시 (소수점 제거)
        return Number.isInteger(value) ? String(value) : String(Math.round(value * 100) / 100);
      }
      if (typeof value === "string" && /^-?\d+\.?\d*$/.test(value.trim())) {
        // 숫자 문자열인 경우 (예: "5.00" → "5")
        const num = parseFloat(value);
        if (!isNaN(num)) {
          return Number.isInteger(num) ? String(num) : String(Math.round(num * 100) / 100);
        }
      }

      // 일반 값
      return String(value);
    },
    [formatDateValue, formatNumberValue, columnInputTypes],
  );

  // 🆕 패널 config의 columns에서 additionalJoinColumns 추출하는 헬퍼
  const extractAdditionalJoinColumns = useCallback((columns: any[] | undefined, tableName: string) => {
    if (!columns || columns.length === 0) return undefined;

    const joinColumns: Array<{
      sourceTable: string;
      sourceColumn: string;
      referenceTable: string;
      joinAlias: string;
    }> = [];

    columns.forEach((col: any) => {
      // 방법 1: isEntityJoin 플래그가 있는 경우 (설정 패널에서 Entity 조인 컬럼으로 추가한 경우)
      if (col.isEntityJoin && col.joinInfo) {
        const existing = joinColumns.find(
          (j) => j.referenceTable === col.joinInfo.referenceTable && j.joinAlias === col.joinInfo.joinAlias
        );
        if (!existing) {
          joinColumns.push({
            sourceTable: col.joinInfo.sourceTable || tableName,
            sourceColumn: col.joinInfo.sourceColumn,
            referenceTable: col.joinInfo.referenceTable,
            joinAlias: col.joinInfo.joinAlias,
          });
        }
        return;
      }

      // 방법 2: "테이블명.컬럼명" 형식 (기존 좌측 패널 방식)
      const colName = typeof col === "string" ? col : col.name || col.columnName;
      if (colName && colName.includes(".")) {
        const [refTable, refColumn] = colName.split(".");
        const inferredSourceColumn = refTable.replace("_info", "_code").replace("_mng", "_id");
        const existing = joinColumns.find(
          (j) => j.referenceTable === refTable && j.sourceColumn === inferredSourceColumn
        );
        if (!existing) {
          joinColumns.push({
            sourceTable: tableName,
            sourceColumn: inferredSourceColumn,
            referenceTable: refTable,
            joinAlias: `${inferredSourceColumn}_${refColumn}`,
          });
        } else {
          // 이미 추가된 테이블이면 별칭만 추가
          const newAlias = `${inferredSourceColumn}_${refColumn}`;
          if (!joinColumns.find((j) => j.joinAlias === newAlias)) {
            joinColumns.push({
              sourceTable: tableName,
              sourceColumn: inferredSourceColumn,
              referenceTable: refTable,
              joinAlias: newAlias,
            });
          }
        }
      }
    });

    return joinColumns.length > 0 ? joinColumns : undefined;
  }, []);

  // 좌측 데이터 로드
  const loadLeftData = useCallback(async () => {
    const leftTableName = componentConfig.leftPanel?.tableName;
    if (!leftTableName || isDesignMode) return;

    setIsLoadingLeft(true);
    try {
      // 🎯 필터 조건을 API에 전달 (entityJoinApi 사용)
      const filters = Object.keys(searchValues).length > 0 ? searchValues : undefined;

      // 🆕 좌측 패널 config의 Entity 조인 컬럼 추출 (헬퍼 함수 사용)
      const leftJoinColumns = extractAdditionalJoinColumns(
        componentConfig.leftPanel?.columns,
        leftTableName,
      );

      console.log("🔗 [분할패널] 좌측 additionalJoinColumns:", leftJoinColumns);

      const result = await entityJoinApi.getTableDataWithJoins(leftTableName, {
        page: 1,
        size: 100,
        search: filters,
        enableEntityJoin: true,
        dataFilter: componentConfig.leftPanel?.dataFilter,
        additionalJoinColumns: leftJoinColumns,
        companyCodeOverride: companyCode,
      });

      // 🔍 디버깅: API 응답 데이터의 키 확인
      if (result.data && result.data.length > 0) {
        console.log("🔗 [분할패널] API 응답 첫 번째 데이터 키:", Object.keys(result.data[0]));
        console.log("🔗 [분할패널] API 응답 첫 번째 데이터:", result.data[0]);
      }

      // 좌측 패널 dataFilter 클라이언트 사이드 적용
      let filteredLeftData = result.data || [];
      const leftDataFilter = componentConfig.leftPanel?.dataFilter;
      if (leftDataFilter?.enabled && leftDataFilter.filters?.length > 0) {
        const matchFn = leftDataFilter.matchType === "any" ? "some" : "every";
        filteredLeftData = filteredLeftData.filter((item: any) => {
          return leftDataFilter.filters[matchFn]((cond: any) => {
            const val = item[cond.columnName];
            switch (cond.operator) {
              case "equals":
                return val === cond.value;
              case "not_equals":
                return val !== cond.value;
              case "in": {
                const arr = Array.isArray(cond.value) ? cond.value : [cond.value];
                return arr.includes(val);
              }
              case "not_in": {
                const arr = Array.isArray(cond.value) ? cond.value : [cond.value];
                return !arr.includes(val);
              }
              case "contains":
                return String(val || "").includes(String(cond.value));
              case "is_null":
                return val === null || val === undefined || val === "";
              case "is_not_null":
                return val !== null && val !== undefined && val !== "";
              default:
                return true;
            }
          });
        });
      }

      // 가나다순 정렬 (좌측 패널의 표시 컬럼 기준)
      const leftColumn = componentConfig.rightPanel?.relation?.leftColumn;
      if (leftColumn && filteredLeftData.length > 0) {
        filteredLeftData.sort((a, b) => {
          const aValue = String(a[leftColumn] || "");
          const bValue = String(b[leftColumn] || "");
          return aValue.localeCompare(bValue, "ko-KR");
        });
      }

      // 계층 구조 빌드
      const hierarchicalData = buildHierarchy(filteredLeftData);
      setLeftData(hierarchicalData);
    } catch (error) {
      console.error("좌측 데이터 로드 실패:", error);
      toast({
        title: "데이터 로드 실패",
        description: "좌측 패널 데이터를 불러올 수 없습니다.",
        variant: "destructive",
      });
    } finally {
      setIsLoadingLeft(false);
    }
  }, [
    componentConfig.leftPanel?.tableName,
    componentConfig.leftPanel?.columns,
    componentConfig.leftPanel?.dataFilter,
    componentConfig.rightPanel?.relation?.leftColumn,
    isDesignMode,
    toast,
    buildHierarchy,
    searchValues,
  ]);

  // 우측 데이터 로드 (leftItem이 null이면 전체 데이터 로드)
  const loadRightData = useCallback(
    async (leftItem: any) => {
      const relationshipType = componentConfig.rightPanel?.relation?.type || "detail";
      const rightTableName = componentConfig.rightPanel?.tableName;

      if (!rightTableName || isDesignMode) return;

      // 좌측 미선택 시: 전체 데이터 로드 (dataFilter 적용)
      if (!leftItem && relationshipType === "join") {
        setIsLoadingRight(true);
        try {
          const rightJoinColumns = extractAdditionalJoinColumns(
            componentConfig.rightPanel?.columns,
            rightTableName,
          );

          const result = await entityJoinApi.getTableDataWithJoins(rightTableName, {
            enableEntityJoin: true,
            size: 1000,
            companyCodeOverride: companyCode,
            additionalJoinColumns: rightJoinColumns,
            dataFilter: componentConfig.rightPanel?.dataFilter,
          });

          // dataFilter 적용
          let filteredData = result.data || [];
          const dataFilter = componentConfig.rightPanel?.dataFilter;
          if (dataFilter?.enabled && dataFilter.filters?.length > 0) {
            filteredData = filteredData.filter((item: any) => {
              return dataFilter.filters.every((cond: any) => {
                const value = item[cond.columnName];
                switch (cond.operator) {
                  case "equals":
                    return value === cond.value;
                  case "notEquals":
                  case "not_equals":
                    return value !== cond.value;
                  case "in": {
                    const arr = Array.isArray(cond.value) ? cond.value : [cond.value];
                    return arr.includes(value);
                  }
                  case "not_in": {
                    const arr = Array.isArray(cond.value) ? cond.value : [cond.value];
                    return !arr.includes(value);
                  }
                  case "contains":
                    return String(value || "").includes(String(cond.value));
                  case "is_null":
                    return value === null || value === undefined || value === "";
                  case "is_not_null":
                    return value !== null && value !== undefined && value !== "";
                  default:
                    return true;
                }
              });
            });
          }

          // conditions 형식 dataFilter도 지원 (하위 호환성)
          const dataFilterConditions = componentConfig.rightPanel?.dataFilter;
          if (dataFilterConditions?.enabled && dataFilterConditions.conditions?.length > 0) {
            filteredData = filteredData.filter((item: any) => {
              return dataFilterConditions.conditions.every((cond: any) => {
                const value = item[cond.column];
                switch (cond.operator) {
                  case "equals":
                    return value === cond.value;
                  case "notEquals":
                    return value !== cond.value;
                  case "contains":
                    return String(value || "").includes(String(cond.value));
                  default:
                    return true;
                }
              });
            });
          }

          setRightData(filteredData);
        } catch (error) {
          console.error("우측 전체 데이터 로드 실패:", error);
        } finally {
          setIsLoadingRight(false);
        }
        return;
      }

      // leftItem이 null이면 join 모드 이외에는 데이터 로드 불가
      // detail 모드: 선택 안 하면 아무것도 안 뜸, 선택하면 필터링
      // join 모드: 선택 안 하면 전체, 선택하면 필터링
      if (!leftItem) return;

      setIsLoadingRight(true);
      setRightData([]);
      try {
        // detail / join 모두 동일한 필터링 로직 사용
        // (차이점: 초기 로드 여부만 다름 - detail은 초기 로드 안 함)
        {
          // 조인 모드: 다른 테이블의 관련 데이터 (여러 개)
          const keys = componentConfig.rightPanel?.relation?.keys;
          const leftTable = componentConfig.leftPanel?.tableName;

          // 🆕 그룹 합산된 항목인 경우: 원본 데이터들로 우측 패널 표시
          if (leftItem._originalItems && leftItem._originalItems.length > 0) {
            console.log("🔗 [분할패널] 그룹 합산 항목 - 원본 개수:", leftItem._originalItems.length);

            // 정렬 기준 컬럼 (복합키의 leftColumn들)
            const sortColumns = keys?.map((k: any) => k.leftColumn).filter(Boolean) || [];
            console.log("🔗 [분할패널] 정렬 기준 컬럼:", sortColumns);

            // 정렬 함수
            const sortByKeys = (data: any[]) => {
              if (sortColumns.length === 0) return data;
              return [...data].sort((a, b) => {
                for (const col of sortColumns) {
                  const aVal = String(a[col] || "");
                  const bVal = String(b[col] || "");
                  const cmp = aVal.localeCompare(bVal, "ko-KR");
                  if (cmp !== 0) return cmp;
                }
                return 0;
              });
            };

            // 원본 데이터를 그대로 우측 패널에 표시 (이력 테이블과 동일 테이블인 경우)
            if (leftTable === rightTableName) {
              const sortedData = sortByKeys(leftItem._originalItems);
              console.log("🔗 [분할패널] 동일 테이블 - 정렬된 원본 데이터:", sortedData.length);
              setRightData(sortedData);
              return;
            }

            // 다른 테이블인 경우: 원본 ID들로 조회
            const { entityJoinApi } = await import("@/lib/api/entityJoin");
            const allResults: any[] = [];

            // 🆕 우측 패널 Entity 조인 컬럼 추출 (그룹 합산용)
            const rightJoinColumnsForGroup = extractAdditionalJoinColumns(
              componentConfig.rightPanel?.columns,
              rightTableName,
            );

            // 각 원본 항목에 대해 조회
            for (const originalItem of leftItem._originalItems) {
              const searchConditions: Record<string, any> = {};
              keys?.forEach((key: any) => {
                if (key.leftColumn && key.rightColumn && originalItem[key.leftColumn] !== undefined) {
                  searchConditions[key.rightColumn] = { value: originalItem[key.leftColumn], operator: "equals" };
                }
              });

              if (Object.keys(searchConditions).length > 0) {
                const result = await entityJoinApi.getTableDataWithJoins(rightTableName, {
                  search: searchConditions,
                  enableEntityJoin: true,
                  size: 1000,
                  companyCodeOverride: companyCode,
                  additionalJoinColumns: rightJoinColumnsForGroup, // 🆕 Entity 조인 컬럼 전달
                });
                if (result.data) {
                  allResults.push(...result.data);
                }
              }
            }

            // 정렬 적용
            const sortedResults = sortByKeys(allResults);
            console.log("🔗 [분할패널] 그룹 합산 - 우측 패널 정렬된 데이터:", sortedResults.length);
            setRightData(sortedResults);
            return;
          }

          // 🆕 복합키 지원
          if (keys && keys.length > 0 && leftTable) {
            // 복합키: 여러 조건으로 필터링
            const { entityJoinApi } = await import("@/lib/api/entityJoin");

            // 복합키 조건 생성 (FK 필터링이므로 equals 연산자 사용)
            const searchConditions: Record<string, any> = {};
            keys.forEach((key) => {
              if (key.leftColumn && key.rightColumn && leftItem[key.leftColumn] !== undefined) {
                searchConditions[key.rightColumn] = { value: leftItem[key.leftColumn], operator: "equals" };
              }
            });

            console.log("🔗 [분할패널] 복합키 조건:", searchConditions);

            // 🆕 우측 패널 config의 Entity 조인 컬럼 추출
            const rightJoinColumns = extractAdditionalJoinColumns(
              componentConfig.rightPanel?.columns,
              rightTableName,
            );
            if (rightJoinColumns) {
              console.log("🔗 [분할패널] 우측 패널 additionalJoinColumns:", rightJoinColumns);
            }

            // 엔티티 조인 API로 데이터 조회
            const result = await entityJoinApi.getTableDataWithJoins(rightTableName, {
              search: searchConditions,
              enableEntityJoin: true,
              size: 1000,
              companyCodeOverride: companyCode,
              additionalJoinColumns: rightJoinColumns,
            });

            console.log("🔗 [분할패널] 복합키 조회 결과:", result);

            setRightData(result.data || []);
          } else {
            // 단일키 (하위 호환성) → entityJoinApi 사용으로 전환 (entity 조인 컬럼 지원)
            const leftColumn = componentConfig.rightPanel?.relation?.leftColumn;
            const rightColumn = componentConfig.rightPanel?.relation?.foreignKey;

            if (leftColumn && rightColumn && leftTable) {
              const leftValue = leftItem[leftColumn];
              const { entityJoinApi } = await import("@/lib/api/entityJoin");

              console.log("🔗 [분할패널] 단일키 조건:", { leftColumn, rightColumn, leftValue, rightTableName });

              // 단일키를 복합키 형식으로 변환 (entity 컬럼이므로 equals 연산자 필수)
              const searchConditions: Record<string, any> = {};
              searchConditions[rightColumn] = { value: leftValue, operator: "equals" };

              // Entity 조인 컬럼 추출
              const rightJoinColumnsLegacy = extractAdditionalJoinColumns(
                componentConfig.rightPanel?.columns,
                rightTableName,
              );
              if (rightJoinColumnsLegacy) {
                console.log("🔗 [분할패널] 단일키 모드 additionalJoinColumns:", rightJoinColumnsLegacy);
              }

              const result = await entityJoinApi.getTableDataWithJoins(rightTableName, {
                search: searchConditions,
                enableEntityJoin: true,
                size: 1000,
                companyCodeOverride: companyCode,
                additionalJoinColumns: rightJoinColumnsLegacy,
              });

              setRightData(result.data || []);
            }
          }
        }
      } catch (error) {
        console.error("우측 데이터 로드 실패:", error);
        setRightData([]);
        toast({
          title: "데이터 로드 실패",
          description: "우측 패널 데이터를 불러올 수 없습니다.",
          variant: "destructive",
        });
      } finally {
        setIsLoadingRight(false);
      }
    },
    [
      componentConfig.rightPanel?.tableName,
      componentConfig.rightPanel?.relation,
      componentConfig.leftPanel?.tableName,
      isDesignMode,
      toast,
    ],
  );

  // 추가 탭 데이터 로딩 함수 (leftItem이 null이면 전체 데이터 로드)
  const loadTabData = useCallback(
    async (tabIndex: number, leftItem: any) => {
      const tabConfig = componentConfig.rightPanel?.additionalTabs?.[tabIndex - 1];
      if (!tabConfig || isDesignMode) return;

      const tabTableName = tabConfig.tableName;
      if (!tabTableName) return;

      setTabsLoading((prev) => ({ ...prev, [tabIndex]: true }));
      try {
        const keys = tabConfig.relation?.keys;
        const leftColumn = tabConfig.relation?.leftColumn || keys?.[0]?.leftColumn;
        const rightColumn = tabConfig.relation?.foreignKey || keys?.[0]?.rightColumn;

        // 탭 config의 Entity 조인 컬럼 추출
        const tabJoinColumns = extractAdditionalJoinColumns(tabConfig.columns, tabTableName);
        if (tabJoinColumns) {
          console.log(`🔗 [분할패널] 탭 ${tabIndex} additionalJoinColumns:`, tabJoinColumns);
        }

        let resultData: any[] = [];

        // 탭의 dataFilter (API 전달용)
        const tabDataFilterForApi = (tabConfig as any).dataFilter;

        // 탭의 relation type 확인 (detail이면 초기 전체 로드 안 함)
        const tabRelationType = tabConfig.relation?.type || "join";

        if (!leftItem) {
          if (tabRelationType === "detail") {
            // detail 모드: 선택 안 하면 아무것도 안 뜸
            resultData = [];
          } else {
            // join 모드: 좌측 미선택 시 전체 데이터 로드 (dataFilter는 API에 전달)
            const result = await entityJoinApi.getTableDataWithJoins(tabTableName, {
              enableEntityJoin: true,
              size: 1000,
              companyCodeOverride: companyCode,
              additionalJoinColumns: tabJoinColumns,
              dataFilter: tabDataFilterForApi,
            });
            resultData = result.data || [];
          }
        } else if (leftColumn && rightColumn) {
          const searchConditions: Record<string, any> = {};

          if (keys && keys.length > 0) {
            keys.forEach((key: any) => {
              if (key.leftColumn && key.rightColumn && leftItem[key.leftColumn] !== undefined) {
                searchConditions[key.rightColumn] = {
                  value: leftItem[key.leftColumn],
                  operator: "equals",
                };
              }
            });
          } else {
            const leftValue = leftItem[leftColumn];
            if (leftValue !== undefined) {
              searchConditions[rightColumn] = {
                value: leftValue,
                operator: "equals",
              };
            }
          }

          const result = await entityJoinApi.getTableDataWithJoins(tabTableName, {
            search: searchConditions,
            enableEntityJoin: true,
            size: 1000,
            companyCodeOverride: companyCode,
            additionalJoinColumns: tabJoinColumns,
            dataFilter: tabDataFilterForApi,
          });
          resultData = result.data || [];
        } else {
          const result = await entityJoinApi.getTableDataWithJoins(tabTableName, {
            enableEntityJoin: true,
            size: 1000,
            companyCodeOverride: companyCode,
            additionalJoinColumns: tabJoinColumns,
            dataFilter: tabDataFilterForApi,
          });
          resultData = result.data || [];
        }

        // 탭별 dataFilter 적용
        const tabDataFilter = (tabConfig as any).dataFilter;
        if (tabDataFilter?.enabled && tabDataFilter.filters?.length > 0) {
          resultData = resultData.filter((item: any) => {
            return tabDataFilter.filters.every((cond: any) => {
              const value = item[cond.columnName];
              switch (cond.operator) {
                case "equals":
                  return value === cond.value;
                case "notEquals":
                case "not_equals":
                  return value !== cond.value;
                case "in": {
                  const arr = Array.isArray(cond.value) ? cond.value : [cond.value];
                  return arr.includes(value);
                }
                case "not_in": {
                  const arr = Array.isArray(cond.value) ? cond.value : [cond.value];
                  return !arr.includes(value);
                }
                case "contains":
                  return String(value || "").includes(String(cond.value));
                case "is_null":
                  return value === null || value === undefined || value === "";
                case "is_not_null":
                  return value !== null && value !== undefined && value !== "";
                default:
                  return true;
              }
            });
          });
        }

        setTabsData((prev) => ({ ...prev, [tabIndex]: resultData }));
      } catch (error) {
        console.error(`추가탭 ${tabIndex} 데이터 로드 실패:`, error);
        toast({
          title: "데이터 로드 실패",
          description: `탭 데이터를 불러올 수 없습니다.`,
          variant: "destructive",
        });
      } finally {
        setTabsLoading((prev) => ({ ...prev, [tabIndex]: false }));
      }
    },
    [componentConfig.rightPanel?.additionalTabs, isDesignMode, toast],
  );

  // 탭 변경 핸들러
  const handleTabChange = useCallback(
    (newTabIndex: number) => {
      setActiveTabIndex(newTabIndex);

      // 메인 패널이 "detail"(선택 시 표시)이면 좌측 미선택 시 데이터 로드하지 않음
      const mainRelationType = componentConfig.rightPanel?.relation?.type || "detail";
      const requireSelection = mainRelationType === "detail";

      if (newTabIndex === 0) {
        if (!rightData || (Array.isArray(rightData) && rightData.length === 0)) {
          if (!requireSelection || selectedLeftItem) {
            loadRightData(selectedLeftItem);
          }
        }
      } else {
        if (!tabsData[newTabIndex]) {
          if (!requireSelection || selectedLeftItem) {
            loadTabData(newTabIndex, selectedLeftItem);
          }
        }
      }
    },
    [selectedLeftItem, rightData, tabsData, loadRightData, loadTabData, componentConfig.rightPanel?.relation?.type],
  );

  // 좌측 항목 선택 핸들러 (동일 항목 재클릭 시 선택 해제 → 전체 데이터 표시)
  const handleLeftItemSelect = useCallback(
    (item: any) => {
      // 동일 항목 클릭 시 선택 해제 (전체 보기로 복귀)
      const leftPk = componentConfig.rightPanel?.relation?.leftColumn || 
                     componentConfig.rightPanel?.relation?.keys?.[0]?.leftColumn;
      const isSameItem = selectedLeftItem && leftPk && 
                        selectedLeftItem[leftPk] === item[leftPk];

      if (isSameItem) {
        // 선택 해제
        setSelectedLeftItem(null);
        setCustomLeftSelectedData({});
        setExpandedRightItems(new Set());
        setTabsData({});

        const mainRelationType = componentConfig.rightPanel?.relation?.type || "detail";
        if (mainRelationType === "detail") {
          // "선택 시 표시" 모드: 선택 해제 시 데이터 비움
          setRightData(null);
        } else {
          // "연관 목록" 모드: 선택 해제 시 전체 데이터 로드
          if (activeTabIndex === 0) {
            loadRightData(null);
          } else {
            loadTabData(activeTabIndex, null);
          }
          const tabs = componentConfig.rightPanel?.additionalTabs;
          if (tabs && tabs.length > 0) {
            tabs.forEach((_: any, idx: number) => {
              if (idx + 1 !== activeTabIndex) {
                loadTabData(idx + 1, null);
              }
            });
          }
        }
        return;
      }

      setSelectedLeftItem(item);
      setCustomLeftSelectedData(item); // 커스텀 모드 우측 폼에 선택된 데이터 전달
      setExpandedRightItems(new Set()); // 좌측 항목 변경 시 우측 확장 초기화
      setTabsData({}); // 모든 탭 데이터 초기화

      // 현재 활성 탭에 따라 데이터 로드
      if (activeTabIndex === 0) {
        loadRightData(item);
      } else {
        loadTabData(activeTabIndex, item);
      }

      // modalDataStore에 선택된 좌측 항목 저장 (단일 선택)
      const leftTableName = componentConfig.leftPanel?.tableName;
      if (leftTableName && !isDesignMode) {
        import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
          useModalDataStore.getState().setData(leftTableName, [item]);
          console.log(`✅ 분할 패널 좌측 선택: ${leftTableName}`, item);
        });
      }
    },
    [loadRightData, loadTabData, activeTabIndex, componentConfig.leftPanel?.tableName, componentConfig.rightPanel?.relation, componentConfig.rightPanel?.additionalTabs, isDesignMode, selectedLeftItem],
  );

  // 우측 항목 확장/축소 토글
  const toggleRightItemExpansion = useCallback((itemId: string | number) => {
    setExpandedRightItems((prev) => {
      const newSet = new Set(prev);
      if (newSet.has(itemId)) {
        newSet.delete(itemId);
      } else {
        newSet.add(itemId);
      }
      return newSet;
    });
  }, []);

  // 컬럼명을 라벨로 변환하는 함수
  const getColumnLabel = useCallback(
    (columnName: string) => {
      const column = rightTableColumns.find((col) => col.columnName === columnName || col.column_name === columnName);
      return column?.columnLabel || column?.column_label || column?.displayName || columnName;
    },
    [rightTableColumns],
  );

  // 🔧 컬럼의 고유값 가져오기 함수
  const getLeftColumnUniqueValues = useCallback(
    async (columnName: string) => {
      const leftTableName = componentConfig.leftPanel?.tableName;
      if (!leftTableName) return [];

      // 1단계: 카테고리 API 시도 (DB에서 라벨 조회)
      try {
        const { apiClient } = await import("@/lib/api/client");
        const response = await apiClient.get(`/table-categories/${leftTableName}/${columnName}/values`);
        if (response.data.success && response.data.data && response.data.data.length > 0) {
          return response.data.data.map((item: any) => ({
            value: item.valueCode,
            label: item.valueLabel,
          }));
        }
      } catch {
        // 카테고리 API 실패 시 다음 단계로
      }

      // 2단계: DISTINCT API (백엔드 라벨 변환 포함)
      try {
        const { apiClient } = await import("@/lib/api/client");
        const response = await apiClient.get(`/entity/${leftTableName}/distinct/${columnName}`);
        if (response.data.success && response.data.data && response.data.data.length > 0) {
          return response.data.data.map((item: any) => ({
            value: String(item.value),
            label: String(item.label),
          }));
        }
      } catch {
        // DISTINCT API 실패 시 다음 단계로
      }

      // 3단계: 로컬 데이터에서 고유값 추출 (최종 fallback)
      if (leftData.length === 0) return [];

      const uniqueValuesMap = new Map<string, string>();

      leftData.forEach((item) => {
        let value: any;

        if (columnName.includes(".")) {
          const [refTable, fieldName] = columnName.split(".");
          const inferredSourceColumn = refTable.replace("_info", "_code").replace("_mng", "_id");

          const exactKey = `${inferredSourceColumn}_${fieldName}`;
          value = item[exactKey];

          if (value === undefined) {
            const idPatternKey = `${refTable.replace("_info", "_id").replace("_mng", "_id")}_${fieldName}`;
            value = item[idPatternKey];
          }

          if (value === undefined && (fieldName === "item_name" || fieldName === "name")) {
            const aliasKey = `${inferredSourceColumn}_name`;
            value = item[aliasKey];
            if (value === undefined) {
              const idAliasKey = `${refTable.replace("_info", "_id").replace("_mng", "_id")}_name`;
              value = item[idAliasKey];
            }
          }
        } else {
          value = item[columnName];
        }

        if (value !== null && value !== undefined && value !== "") {
          const strValue = String(value);
          const nameField = item[`${columnName}_name`];
          const label = nameField || strValue;
          uniqueValuesMap.set(strValue, label);
        }
      });

      return Array.from(uniqueValuesMap.entries())
        .map(([value, label]) => ({ value, label }))
        .sort((a, b) => a.label.localeCompare(b.label));
    },
    [componentConfig.leftPanel?.tableName, leftData],
  );

  // 좌측 테이블 등록 (Context에 등록)
  useEffect(() => {
    const leftTableName = componentConfig.leftPanel?.tableName;
    if (!leftTableName || isDesignMode) return;

    const leftTableId = `split-panel-left-${component.id}`;
    // 🔧 화면에 표시되는 컬럼 사용 (columns 속성)
    const configuredColumns = componentConfig.leftPanel?.columns || [];

    // 🆕 설정에서 지정한 라벨 맵 생성
    const configuredLabels: Record<string, string> = {};
    configuredColumns.forEach((col: any) => {
      if (typeof col === "object" && col.name && col.label) {
        configuredLabels[col.name] = col.label;
      }
    });

    const displayColumns = configuredColumns
      .map((col: any) => {
        if (typeof col === "string") return col;
        return col.columnName || col.name || col;
      })
      .filter(Boolean);

    // 화면에 설정된 컬럼이 없으면 등록하지 않음
    if (displayColumns.length === 0) return;

    // 테이블명이 있으면 등록
    registerTable({
      tableId: leftTableId,
      label: `${component.title || "분할 패널"} (좌측)`,
      tableName: leftTableName,
      columns: displayColumns.map((col: string) => ({
        columnName: col,
        // 🆕 우선순위: 1) 설정에서 지정한 라벨 2) DB 라벨 3) 컬럼명
        columnLabel: configuredLabels[col] || leftColumnLabels[col] || col,
        inputType: "text",
        visible: true,
        width: 150,
        sortable: true,
        filterable: true,
      })),
      onFilterChange: setLeftFilters,
      onGroupChange: setLeftGrouping,
      onColumnVisibilityChange: setLeftColumnVisibility,
      onColumnOrderChange: setLeftColumnOrder, // 🔧 컬럼 순서 변경 콜백 추가
      getColumnUniqueValues: getLeftColumnUniqueValues, // 🔧 고유값 가져오기 함수 추가
      onGroupSumChange: setLeftGroupSumConfig, // 🆕 그룹별 합산 설정 콜백
    });

    return () => unregisterTable(leftTableId);
  }, [
    component.id,
    componentConfig.leftPanel?.tableName,
    componentConfig.leftPanel?.columns,
    leftColumnLabels,
    component.title,
    isDesignMode,
    getLeftColumnUniqueValues,
  ]);

  // 우측 테이블은 검색 컴포넌트 등록 제외 (좌측 마스터 테이블만 검색 가능)
  // useEffect(() => {
  //   const rightTableName = componentConfig.rightPanel?.tableName;
  //   if (!rightTableName || isDesignMode) return;
  //
  //   const rightTableId = `split-panel-right-${component.id}`;
  //   // 🔧 화면에 표시되는 컬럼만 등록 (displayColumns 또는 columns)
  //   const displayColumns = componentConfig.rightPanel?.columns || [];
  //   const rightColumns = displayColumns.map((col: any) => col.columnName || col.name || col).filter(Boolean);
  //
  //   if (rightColumns.length > 0) {
  //     registerTable({
  //       tableId: rightTableId,
  //       label: `${component.title || "분할 패널"} (우측)`,
  //       tableName: rightTableName,
  //       columns: rightColumns.map((col: string) => ({
  //         columnName: col,
  //         columnLabel: rightColumnLabels[col] || col,
  //         inputType: "text",
  //         visible: true,
  //         width: 150,
  //         sortable: true,
  //         filterable: true,
  //       })),
  //       onFilterChange: setRightFilters,
  //       onGroupChange: setRightGrouping,
  //       onColumnVisibilityChange: setRightColumnVisibility,
  //     });
  //
  //     return () => unregisterTable(rightTableId);
  //   }
  // }, [component.id, componentConfig.rightPanel?.tableName, componentConfig.rightPanel?.columns, rightColumnLabels, component.title, isDesignMode]);

  // 좌측 테이블 컬럼 라벨 로드
  useEffect(() => {
    const loadLeftColumnLabels = async () => {
      const leftTableName = componentConfig.leftPanel?.tableName;
      if (!leftTableName || isDesignMode) return;

      try {
        const columnsResponse = await tableTypeApi.getColumns(leftTableName);
        const labels: Record<string, string> = {};
        columnsResponse.forEach((col: any) => {
          const columnName = col.columnName || col.column_name;
          const label = col.columnLabel || col.column_label || col.displayName || columnName;
          if (columnName) {
            labels[columnName] = label;
          }
        });
        setLeftColumnLabels(labels);
        console.log("✅ 좌측 컬럼 라벨 로드:", labels);
      } catch (error) {
        console.error("좌측 테이블 컬럼 라벨 로드 실패:", error);
      }
    };

    loadLeftColumnLabels();
  }, [componentConfig.leftPanel?.tableName, isDesignMode]);

  // 우측 테이블 컬럼 정보 로드
  useEffect(() => {
    const loadRightTableColumns = async () => {
      const rightTableName = componentConfig.rightPanel?.tableName;
      if (!rightTableName || isDesignMode) return;

      try {
        const columnsResponse = await tableTypeApi.getColumns(rightTableName);
        setRightTableColumns(columnsResponse || []);

        // 우측 컬럼 라벨도 함께 로드
        const labels: Record<string, string> = {};
        columnsResponse.forEach((col: any) => {
          const columnName = col.columnName || col.column_name;
          const label = col.columnLabel || col.column_label || col.displayName || columnName;
          if (columnName) {
            labels[columnName] = label;
          }
        });
        setRightColumnLabels(labels);

        // 컬럼 inputType 로드 (이미지 등 특수 렌더링을 위해)
        const tablesToLoad = new Set<string>([rightTableName]);
        const additionalTabs = componentConfig.rightPanel?.additionalTabs || [];
        additionalTabs.forEach((tab: any) => {
          if (tab.tableName) tablesToLoad.add(tab.tableName);
        });

        const inputTypes: Record<string, string> = {};
        for (const tbl of tablesToLoad) {
          try {
            const inputTypesResponse = await tableTypeApi.getColumnInputTypes(tbl);
            inputTypesResponse.forEach((col: any) => {
              const colName = col.columnName || col.column_name;
              if (colName) {
                inputTypes[colName] = col.inputType || "text";
              }
            });
          } catch {
            // ignore
          }
        }
        setColumnInputTypes(inputTypes);
      } catch (error) {
        console.error("우측 테이블 컬럼 정보 로드 실패:", error);
      }
    };

    loadRightTableColumns();
  }, [componentConfig.rightPanel?.tableName, componentConfig.rightPanel?.additionalTabs, isDesignMode]);

  // 좌측 테이블 카테고리 매핑 로드 (조인된 테이블 포함)
  useEffect(() => {
    const loadLeftCategoryMappings = async () => {
      const leftTableName = componentConfig.leftPanel?.tableName;
      if (!leftTableName || isDesignMode) return;

      try {
        const mappings: Record<string, Record<string, { label: string; color?: string }>> = {};
        const tablesToLoad = new Set<string>([leftTableName]);

        // 좌측 패널 컬럼 설정에서 조인된 테이블 추출
        const leftColumns = componentConfig.leftPanel?.columns || [];
        leftColumns.forEach((col: any) => {
          const colName = col.name || col.columnName;
          if (colName && colName.includes(".")) {
            const joinTableName = colName.split(".")[0];
            tablesToLoad.add(joinTableName);
          }
        });

        // 각 테이블에 대해 카테고리 매핑 로드
        for (const tableName of tablesToLoad) {
          try {
            const columnsResponse = await tableTypeApi.getColumns(tableName);
            const categoryColumns = columnsResponse.filter((col: any) => col.inputType === "category");

            for (const col of categoryColumns) {
              const columnName = col.columnName || col.column_name;
              try {
                const response = await apiClient.get(`/table-categories/${tableName}/${columnName}/values?includeInactive=true`);

                if (response.data.success && response.data.data) {
                  const valueMap: Record<string, { label: string; color?: string }> = {};
                  response.data.data.forEach((item: any) => {
                    valueMap[item.value_code || item.valueCode] = {
                      label: item.value_label || item.valueLabel,
                      color: item.color,
                    };
                  });

                  // 조인된 테이블은 "테이블명.컬럼명" 형태로도 저장
                  const mappingKey = tableName === leftTableName ? columnName : `${tableName}.${columnName}`;
                  mappings[mappingKey] = valueMap;

                  // 컬럼명만으로도 접근 가능하도록 추가 저장
                  mappings[columnName] = { ...(mappings[columnName] || {}), ...valueMap };
                }
              } catch (error) {
                console.error(`좌측 카테고리 값 조회 실패 [${tableName}.${columnName}]:`, error);
              }
            }
          } catch (error) {
            console.error(`좌측 카테고리 테이블 컬럼 조회 실패 [${tableName}]:`, error);
          }
        }

        setLeftCategoryMappings(mappings);
      } catch (error) {
        console.error("좌측 카테고리 매핑 로드 실패:", error);
      }
    };

    loadLeftCategoryMappings();
  }, [componentConfig.leftPanel?.tableName, componentConfig.leftPanel?.columns, isDesignMode]);

  // 우측 테이블 카테고리 매핑 로드 (조인된 테이블 포함)
  useEffect(() => {
    const loadRightCategoryMappings = async () => {
      const rightTableName = componentConfig.rightPanel?.tableName;
      if (!rightTableName || isDesignMode) return;

      try {
        const mappings: Record<string, Record<string, { label: string; color?: string }>> = {};

        // 🆕 우측 패널 컬럼 설정에서 조인된 테이블 추출
        const rightColumns = componentConfig.rightPanel?.columns || [];
        const tablesToLoad = new Set<string>([rightTableName]);

        // 컬럼명에서 테이블명 추출 (예: "item_info.material" -> "item_info")
        rightColumns.forEach((col: any) => {
          const colName = col.name || col.columnName;
          if (colName && colName.includes(".")) {
            const joinTableName = colName.split(".")[0];
            tablesToLoad.add(joinTableName);
          }
        });

        // 🆕 추가 탭의 테이블도 카테고리 로드 대상에 포함
        const additionalTabs = componentConfig.rightPanel?.additionalTabs || [];
        additionalTabs.forEach((tab: any) => {
          if (tab.tableName) {
            tablesToLoad.add(tab.tableName);
          }
          // 추가 탭 컬럼에서 조인된 테이블 추출
          (tab.columns || []).forEach((col: any) => {
            const colName = col.name || col.columnName;
            if (colName && colName.includes(".")) {
              const joinTableName = colName.split(".")[0];
              tablesToLoad.add(joinTableName);
            }
          });
        });

        console.log("🔍 우측 패널 카테고리 로드 대상 테이블:", Array.from(tablesToLoad));

        // 각 테이블에 대해 카테고리 매핑 로드
        for (const tableName of tablesToLoad) {
          try {
            // 1. 컬럼 메타 정보 조회
            const columnsResponse = await tableTypeApi.getColumns(tableName);
            const categoryColumns = columnsResponse.filter((col: any) => col.inputType === "category");

            // 2. 각 카테고리 컬럼에 대한 값 조회
            for (const col of categoryColumns) {
              const columnName = col.columnName || col.column_name;
              try {
                const response = await apiClient.get(`/table-categories/${tableName}/${columnName}/values?includeInactive=true`);

                if (response.data.success && response.data.data) {
                  const valueMap: Record<string, { label: string; color?: string }> = {};
                  response.data.data.forEach((item: any) => {
                    valueMap[item.value_code || item.valueCode] = {
                      label: item.value_label || item.valueLabel,
                      color: item.color,
                    };
                  });

                  // 조인된 테이블의 경우 "테이블명.컬럼명" 형태로 저장
                  const mappingKey = tableName === rightTableName ? columnName : `${tableName}.${columnName}`;
                  mappings[mappingKey] = valueMap;

                  // 🆕 컬럼명만으로도 접근할 수 있도록 추가 저장 (모든 테이블)
                  // 기존 매핑이 있으면 병합, 없으면 새로 생성
                  mappings[columnName] = { ...(mappings[columnName] || {}), ...valueMap };

                  console.log(`✅ 우측 카테고리 매핑 로드 [${mappingKey}]:`, valueMap);
                  console.log(`✅ 우측 카테고리 매핑 (컬럼명만) [${columnName}]:`, mappings[columnName]);
                }
              } catch (error) {
                console.error(`우측 카테고리 값 조회 실패 [${tableName}.${columnName}]:`, error);
              }
            }
          } catch (error) {
            console.error(`테이블 ${tableName} 컬럼 정보 조회 실패:`, error);
          }
        }

        setRightCategoryMappings(mappings);
      } catch (error) {
        console.error("우측 카테고리 매핑 로드 실패:", error);
      }
    };

    loadRightCategoryMappings();
  }, [componentConfig.rightPanel?.tableName, componentConfig.rightPanel?.columns, componentConfig.rightPanel?.additionalTabs, isDesignMode]);

  // 항목 펼치기/접기 토글
  const toggleExpand = useCallback((itemId: any) => {
    setExpandedItems((prev) => {
      const newSet = new Set(prev);
      if (newSet.has(itemId)) {
        newSet.delete(itemId);
      } else {
        newSet.add(itemId);
      }
      return newSet;
    });
  }, []);

  // 추가 버튼 핸들러
  const handleAddClick = useCallback(
    (panel: "left" | "right") => {
      // 좌측 패널 추가 시, addButton 모달 모드 확인
      if (panel === "left") {
        const addButtonConfig = componentConfig.leftPanel?.addButton;
        if (addButtonConfig?.mode === "modal" && addButtonConfig?.modalScreenId) {
          const leftTableName = componentConfig.leftPanel?.tableName || "";

          // ScreenModal 열기 이벤트 발생
          window.dispatchEvent(
            new CustomEvent("openScreenModal", {
              detail: {
                screenId: addButtonConfig.modalScreenId,
                urlParams: {
                  mode: "add",
                  tableName: leftTableName,
                },
              },
            }),
          );

          console.log("✅ [SplitPanel] 좌측 추가 모달 화면 열기:", {
            screenId: addButtonConfig.modalScreenId,
            tableName: leftTableName,
          });
          return;
        }
      }

      // 우측 패널 추가 시, addButton 모달 모드 확인
      if (panel === "right") {
        const addButtonConfig =
          activeTabIndex === 0
            ? componentConfig.rightPanel?.addButton
            : (componentConfig.rightPanel?.additionalTabs?.[activeTabIndex - 1] as any)?.addButton;

        if (addButtonConfig?.mode === "modal" && addButtonConfig?.modalScreenId) {
          if (!selectedLeftItem) {
            toast({
              title: "항목을 선택해주세요",
              description: "좌측 패널에서 항목을 먼저 선택한 후 추가해주세요.",
              variant: "destructive",
            });
            return;
          }

          const currentTableName =
            activeTabIndex === 0
              ? componentConfig.rightPanel?.tableName || ""
              : (componentConfig.rightPanel?.additionalTabs?.[activeTabIndex - 1] as any)?.tableName || "";

          // 좌측 선택 데이터를 modalDataStore에 저장
          if (selectedLeftItem && componentConfig.leftPanel?.tableName) {
            import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
              useModalDataStore.getState().setData(componentConfig.leftPanel!.tableName!, [selectedLeftItem]);
            });
          }

          // relation.keys에서 FK 데이터 추출
          const parentData: Record<string, any> = {};
          const relation = activeTabIndex === 0
            ? componentConfig.rightPanel?.relation
            : (componentConfig.rightPanel?.additionalTabs?.[activeTabIndex - 1] as any)?.relation;

          if (relation?.keys && Array.isArray(relation.keys)) {
            for (const key of relation.keys) {
              if (key.leftColumn && key.rightColumn && selectedLeftItem[key.leftColumn] != null) {
                parentData[key.rightColumn] = selectedLeftItem[key.leftColumn];
              }
            }
          } else if (relation) {
            const leftColumn = relation.leftColumn;
            const rightColumn = relation.foreignKey || relation.rightColumn;
            if (leftColumn && rightColumn && selectedLeftItem[leftColumn] != null) {
              parentData[rightColumn] = selectedLeftItem[leftColumn];
            }
          }

          window.dispatchEvent(
            new CustomEvent("openScreenModal", {
              detail: {
                screenId: addButtonConfig.modalScreenId,
                urlParams: {
                  mode: "add",
                  tableName: currentTableName,
                },
                splitPanelParentData: parentData,
              },
            }),
          );

          console.log("✅ [SplitPanel] 추가 모달 화면 열기:", {
            screenId: addButtonConfig.modalScreenId,
            tableName: currentTableName,
            parentData,
          });
          return;
        }
      }

      // 기존 내장 추가 모달 로직
      setAddModalPanel(panel);

      // 우측 패널 추가 시, 좌측에서 선택된 항목의 조인 컬럼 값을 자동으로 채움
      if (
        panel === "right" &&
        selectedLeftItem &&
        componentConfig.leftPanel?.leftColumn &&
        componentConfig.rightPanel?.rightColumn
      ) {
        const leftColumnValue = selectedLeftItem[componentConfig.leftPanel.leftColumn];
        setAddModalFormData({
          [componentConfig.rightPanel.rightColumn]: leftColumnValue,
        });
      } else {
        setAddModalFormData({});
      }

      setShowAddModal(true);
    },
    [selectedLeftItem, componentConfig, activeTabIndex],
  );

  // 수정 버튼 핸들러
  const handleEditClick = useCallback(
    (panel: "left" | "right", item: any) => {
      // 좌측 패널 수정 버튼 설정 확인 (모달 모드)
      if (panel === "left") {
        const editButtonConfig = componentConfig.leftPanel?.editButton;
        if (editButtonConfig?.mode === "modal" && editButtonConfig?.modalScreenId) {
          const leftTableName = componentConfig.leftPanel?.tableName || "";

          // Primary Key 찾기 - 실제 DB의 id 컬럼 값을 우선 사용
          let primaryKeyValue = item.id || item.ID;

          if (primaryKeyValue === undefined || primaryKeyValue === null) {
            // id가 없으면 sourceColumn 시도, 마지막으로 첫 번째 키
            const sourceColumn = componentConfig.leftPanel?.itemAddConfig?.sourceColumn || "id";
            primaryKeyValue = item[sourceColumn];
            if (primaryKeyValue === undefined || primaryKeyValue === null) {
              const firstKey = Object.keys(item)[0];
              primaryKeyValue = item[firstKey];
            }
          }

          // modalDataStore에 저장
          import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
            useModalDataStore.getState().setData(leftTableName, [item]);
          });

          // ScreenModal 열기 이벤트 발생
          window.dispatchEvent(
            new CustomEvent("openScreenModal", {
              detail: {
                screenId: editButtonConfig.modalScreenId,
                urlParams: {
                  mode: "edit",
                  editId: primaryKeyValue,
                  tableName: leftTableName,
                },
              },
            }),
          );

          console.log("✅ [SplitPanel] 좌측 수정 모달 화면 열기:", {
            screenId: editButtonConfig.modalScreenId,
            tableName: leftTableName,
            primaryKeyValue,
          });
          return;
        }
      }

      // 우측 패널 수정 버튼 설정 확인 (탭별 설정 지원)
      if (panel === "right") {
        const editButtonConfig =
          activeTabIndex === 0
            ? componentConfig.rightPanel?.editButton
            : (componentConfig.rightPanel?.additionalTabs?.[activeTabIndex - 1] as any)?.editButton;

        const currentTableName =
          activeTabIndex === 0
            ? componentConfig.rightPanel?.tableName || ""
            : (componentConfig.rightPanel?.additionalTabs?.[activeTabIndex - 1] as any)?.tableName || "";

        if (editButtonConfig?.mode === "modal") {
          const modalScreenId = editButtonConfig?.modalScreenId;

          if (modalScreenId) {
            // 커스텀 모달 화면 열기
            const rightTableName = currentTableName;

          // Primary Key 찾기 (우선순위: id > ID > user_id > {table}_id > 첫 번째 필드)
          let primaryKeyName = "id";
          let primaryKeyValue: any;

          if (item.id !== undefined && item.id !== null) {
            primaryKeyName = "id";
            primaryKeyValue = item.id;
          } else if (item.ID !== undefined && item.ID !== null) {
            primaryKeyName = "ID";
            primaryKeyValue = item.ID;
          } else if (item.user_id !== undefined && item.user_id !== null) {
            // user_info 테이블 등 user_id를 Primary Key로 사용하는 경우
            primaryKeyName = "user_id";
            primaryKeyValue = item.user_id;
          } else {
            // 테이블명_id 패턴 확인 (예: dept_id, item_id 등)
            const tableIdKey = rightTableName ? `${rightTableName.replace(/_info$/, "")}_id` : "";
            if (tableIdKey && item[tableIdKey] !== undefined && item[tableIdKey] !== null) {
              primaryKeyName = tableIdKey;
              primaryKeyValue = item[tableIdKey];
            } else {
              // 마지막으로 첫 번째 필드를 Primary Key로 간주
              const firstKey = Object.keys(item)[0];
              primaryKeyName = firstKey;
              primaryKeyValue = item[firstKey];
            }
          }

          console.log("✅ 수정 모달 열기:", {
            tableName: rightTableName,
            primaryKeyName,
            primaryKeyValue,
            screenId: modalScreenId,
            fullItem: item,
          });

          // modalDataStore에도 저장 (호환성 유지)
          import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
            useModalDataStore.getState().setData(rightTableName, [item]);
          });

          // 🆕 groupByColumns 추출
          const groupByColumns = componentConfig.rightPanel?.editButton?.groupByColumns || [];

          console.log("🔧 [SplitPanel] 수정 버튼 클릭 - groupByColumns 확인:", {
            groupByColumns,
            editButtonConfig: componentConfig.rightPanel?.editButton,
            hasGroupByColumns: groupByColumns.length > 0,
          });

          // ScreenModal 열기 이벤트 발생 (URL 파라미터로 ID + groupByColumns 전달)
          window.dispatchEvent(
            new CustomEvent("openScreenModal", {
              detail: {
                screenId: modalScreenId,
                urlParams: {
                  mode: "edit",
                  editId: primaryKeyValue,
                  tableName: rightTableName,
                  ...(groupByColumns.length > 0 && {
                    groupByColumns: JSON.stringify(groupByColumns),
                  }),
                },
              },
            }),
          );

          console.log("✅ [SplitPanel] openScreenModal 이벤트 발생:", {
            screenId: modalScreenId,
            editId: primaryKeyValue,
            tableName: rightTableName,
            groupByColumns: groupByColumns.length > 0 ? JSON.stringify(groupByColumns) : "없음",
          });

            return;
          }
        }
      }

      // 기존 자동 편집 모드 (인라인 편집 모달)
      setEditModalPanel(panel);
      setEditModalItem(item);
      setEditModalFormData({ ...item });
      setShowEditModal(true);
    },
    [componentConfig, activeTabIndex],
  );

  // 커스텀 모드 우측 패널 저장 (인라인 편집 데이터)
  const handleCustomRightSave = useCallback(async () => {
    if (!selectedLeftItem || !customLeftSelectedData || Object.keys(customLeftSelectedData).length === 0) {
      toast({
        title: "저장 실패",
        description: "저장할 데이터가 없습니다. 좌측에서 항목을 선택해주세요.",
        variant: "destructive",
      });
      return;
    }

    const tableName = componentConfig.rightPanel?.tableName || componentConfig.leftPanel?.tableName;
    if (!tableName) {
      toast({
        title: "저장 실패",
        description: "테이블 정보가 없습니다.",
        variant: "destructive",
      });
      return;
    }

    // Primary Key 찾기 - 실제 DB의 id 컬럼 값을 사용 (sourceColumn은 관계 연결용이므로 PK로 사용하지 않음)
    const primaryKey = selectedLeftItem.id || selectedLeftItem.ID;

    if (!primaryKey) {
      toast({
        title: "저장 실패",
        description: "Primary Key를 찾을 수 없습니다.",
        variant: "destructive",
      });
      return;
    }

    try {
      // 프론트엔드 전용 필드 제거
      const cleanData = { ...customLeftSelectedData };
      delete cleanData.children;
      delete cleanData.level;
      delete cleanData._originalItems;

      // company_code 자동 추가
      if (companyCode && !cleanData.company_code) {
        cleanData.company_code = companyCode;
      }

      console.log("📝 [SplitPanel] 커스텀 우측 패널 저장:", { tableName, primaryKey, data: cleanData });

      const response = await dataApi.updateRecord(tableName, primaryKey, cleanData);

      if (response.success) {
        toast({
          title: "저장 완료",
          description: "데이터가 저장되었습니다.",
        });
        // 좌측 데이터 새로고침 (변경된 항목 반영)
        loadLeftData();
        // selectedLeftItem도 업데이트
        setSelectedLeftItem(customLeftSelectedData);
      } else {
        toast({
          title: "저장 실패",
          description: response.error || "데이터 저장에 실패했습니다.",
          variant: "destructive",
        });
      }
    } catch (error) {
      console.error("커스텀 우측 패널 저장 오류:", error);
      toast({
        title: "저장 오류",
        description: "데이터 저장 중 오류가 발생했습니다.",
        variant: "destructive",
      });
    }
  }, [selectedLeftItem, customLeftSelectedData, componentConfig, companyCode, toast, loadLeftData]);

  // 수정 모달 저장
  const handleEditModalSave = useCallback(async () => {
    const tableName =
      editModalPanel === "left" ? componentConfig.leftPanel?.tableName : componentConfig.rightPanel?.tableName;

    const sourceColumn = componentConfig.leftPanel?.itemAddConfig?.sourceColumn || "id";
    const primaryKey = editModalItem[sourceColumn] || editModalItem.id || editModalItem.ID;

    if (!tableName || !primaryKey) {
      toast({
        title: "수정 오류",
        description: "테이블명 또는 Primary Key가 없습니다.",
        variant: "destructive",
      });
      return;
    }

    try {
      console.log("📝 데이터 수정:", { tableName, primaryKey, data: editModalFormData });

      // 프론트엔드 전용 필드 제거 (children, level 등)
      const cleanData = { ...editModalFormData };
      delete cleanData.children;
      delete cleanData.level;

      // 좌측 패널 수정 시, 조인 관계 정보 포함
      const updatePayload: any = cleanData;

      if (editModalPanel === "left" && componentConfig.rightPanel?.relation?.type === "join") {
        // 조인 관계가 있는 경우, 관계 정보를 페이로드에 추가
        updatePayload._relationInfo = {
          rightTable: componentConfig.rightPanel.tableName,
          leftColumn: componentConfig.rightPanel.relation.leftColumn,
          rightColumn: componentConfig.rightPanel.relation.rightColumn,
          oldLeftValue: editModalItem[componentConfig.rightPanel.relation.leftColumn],
        };
        console.log("🔗 조인 관계 정보 추가:", updatePayload._relationInfo);
      }

      const result = await dataApi.updateRecord(tableName, primaryKey, updatePayload);

      if (result.success) {
        toast({
          title: "성공",
          description: "데이터가 성공적으로 수정되었습니다.",
        });

        // 모달 닫기
        setShowEditModal(false);
        setEditModalFormData({});
        setEditModalItem(null);

        // 데이터 새로고침
        if (editModalPanel === "left") {
          loadLeftData();
          // 우측 패널도 새로고침 (FK가 변경되었을 수 있음)
          loadRightData(selectedLeftItem);
        } else if (editModalPanel === "right") {
          loadRightData(selectedLeftItem);
        }
      } else {
        toast({
          title: "수정 실패",
          description: result.message || "데이터 수정에 실패했습니다.",
          variant: "destructive",
        });
      }
    } catch (error: any) {
      console.error("데이터 수정 오류:", error);
      toast({
        title: "오류",
        description: error?.response?.data?.message || "데이터 수정 중 오류가 발생했습니다.",
        variant: "destructive",
      });
    }
  }, [
    editModalPanel,
    componentConfig,
    editModalItem,
    editModalFormData,
    toast,
    selectedLeftItem,
    loadLeftData,
    loadRightData,
  ]);

  // 삭제 버튼 핸들러 (tableName: 추가 탭 등 특정 테이블 지정 시 사용)
  const handleDeleteClick = useCallback((panel: "left" | "right", item: any, tableName?: string) => {
    setDeleteModalPanel(panel);
    setDeleteModalItem(item);
    setDeleteModalTableName(tableName || null);
    setShowDeleteModal(true);
  }, []);

  // 삭제 확인
  const handleDeleteConfirm = useCallback(async () => {
    // 1. 테이블명 결정: deleteModalTableName이 있으면 우선 사용 (추가 탭 등)
    let tableName = deleteModalTableName;

    if (!tableName) {
      tableName =
        deleteModalPanel === "left" ? componentConfig.leftPanel?.tableName : componentConfig.rightPanel?.tableName;

      // 우측 패널 + 중계 테이블 모드인 경우
      if (deleteModalPanel === "right" && componentConfig.rightPanel?.addConfig?.targetTable) {
        tableName = componentConfig.rightPanel.addConfig.targetTable;
        console.log("🔗 중계 테이블 모드: 삭제 대상 테이블 =", tableName);
      }
    }

    // 2. Primary Key 추출: id 필드를 우선 사용, 없으면 전체 객체 전달 (복합키)
    let primaryKey: any = deleteModalItem?.id || deleteModalItem?.ID;

    if (!primaryKey && deleteModalItem && typeof deleteModalItem === "object") {
      // id가 없는 경우에만 전체 객체 전달 (복합키 테이블)
      primaryKey = deleteModalItem;
      console.log("🔑 복합키: 전체 객체 전달", Object.keys(primaryKey));
    } else {
      console.log("🔑 단일키 삭제: id =", primaryKey, "테이블 =", tableName);
    }

    if (!tableName || !primaryKey) {
      toast({
        title: "삭제 오류",
        description: "테이블명 또는 Primary Key가 없습니다.",
        variant: "destructive",
      });
      return;
    }

    try {
      console.log("🗑️ 데이터 삭제:", { tableName, primaryKey });

      // 🔍 중복 제거 설정 디버깅
      console.log("🔍 중복 제거 디버깅:", {
        panel: deleteModalPanel,
        dataFilter: componentConfig.rightPanel?.dataFilter,
        deduplication: componentConfig.rightPanel?.dataFilter?.deduplication,
        enabled: componentConfig.rightPanel?.dataFilter?.deduplication?.enabled,
      });

      let result;

      // 🔧 중복 제거가 활성화된 경우, groupByColumn 기준으로 모든 관련 레코드 삭제
      if (deleteModalPanel === "right" && componentConfig.rightPanel?.dataFilter?.deduplication?.enabled) {
        const deduplication = componentConfig.rightPanel.dataFilter.deduplication;
        const groupByColumn = deduplication.groupByColumn;

        if (groupByColumn && deleteModalItem[groupByColumn]) {
          const groupValue = deleteModalItem[groupByColumn];
          console.log(`🔗 중복 제거 활성화: ${groupByColumn} = ${groupValue} 기준으로 모든 레코드 삭제`);

          // groupByColumn 값으로 필터링하여 삭제
          const filterConditions: Record<string, any> = {
            [groupByColumn]: groupValue,
          };

          // 좌측 패널의 선택된 항목 정보도 포함 (customer_id 등)
          if (selectedLeftItem && componentConfig.rightPanel?.mode === "join") {
            const leftColumn = componentConfig.rightPanel.join.leftColumn;
            const rightColumn = componentConfig.rightPanel.join.rightColumn;
            filterConditions[rightColumn] = selectedLeftItem[leftColumn];
          }

          console.log("🗑️ 그룹 삭제 조건:", filterConditions);

          // 그룹 삭제 API 호출
          result = await dataApi.deleteGroupRecords(tableName, filterConditions);
        } else {
          // 단일 레코드 삭제
          result = await dataApi.deleteRecord(tableName, primaryKey);
        }
      } else {
        // 단일 레코드 삭제
        result = await dataApi.deleteRecord(tableName, primaryKey);
      }

      if (result.success) {
        toast({
          title: "성공",
          description: "데이터가 성공적으로 삭제되었습니다.",
        });

        // 모달 닫기
        setShowDeleteModal(false);
        setDeleteModalItem(null);
        setDeleteModalTableName(null);

        // 데이터 새로고침
        if (deleteModalPanel === "left") {
          loadLeftData();
          // 삭제된 항목이 선택되어 있었으면 선택 해제
          const deletedId = deleteModalItem?.id || deleteModalItem?.ID;
          if (selectedLeftItem && (selectedLeftItem.id === deletedId || selectedLeftItem.ID === deletedId)) {
            setSelectedLeftItem(null);
            setRightData(null);
          }
        } else if (deleteModalPanel === "right") {
          // 추가 탭에서 삭제한 경우 해당 탭 데이터 리로드
          if (deleteModalTableName && activeTabIndex > 0) {
            loadTabData(activeTabIndex, selectedLeftItem);
          } else {
            loadRightData(selectedLeftItem);
          }
        }
      } else {
        toast({
          title: "삭제 실패",
          description: result.message || "데이터 삭제에 실패했습니다.",
          variant: "destructive",
        });
      }
    } catch (error: any) {
      console.error("데이터 삭제 오류:", error);

      // 외래키 제약조건 에러 처리
      let errorMessage = "데이터 삭제 중 오류가 발생했습니다.";
      if (error?.response?.data?.error?.includes("foreign key")) {
        errorMessage = "이 데이터를 참조하는 다른 데이터가 있어 삭제할 수 없습니다.";
      }

      toast({
        title: "오류",
        description: errorMessage,
        variant: "destructive",
      });
    }
  }, [deleteModalPanel, deleteModalTableName, componentConfig, deleteModalItem, toast, selectedLeftItem, loadLeftData, loadRightData, loadTabData, activeTabIndex]);

  // 항목별 추가 버튼 핸들러 (좌측 항목의 + 버튼 - 하위 항목 추가)
  const handleItemAddClick = useCallback(
    (item: any) => {
      const itemAddConfig = componentConfig.leftPanel?.itemAddConfig;

      if (!itemAddConfig) {
        toast({
          title: "설정 오류",
          description: "하위 항목 추가 설정이 없습니다.",
          variant: "destructive",
        });
        return;
      }

      const { sourceColumn, parentColumn } = itemAddConfig;

      if (!sourceColumn || !parentColumn) {
        toast({
          title: "설정 오류",
          description: "현재 항목 ID 컬럼과 상위 항목 저장 컬럼을 설정해주세요.",
          variant: "destructive",
        });
        return;
      }

      // 선택된 항목의 sourceColumn 값을 가져와서 parentColumn에 매핑
      const sourceValue = item[sourceColumn];

      if (!sourceValue) {
        toast({
          title: "데이터 오류",
          description: `선택한 항목의 ${sourceColumn} 값이 없습니다.`,
          variant: "destructive",
        });
        return;
      }

      // 좌측 패널 추가 모달 열기 (parentColumn 값 미리 채우기)
      setAddModalPanel("left-item");
      setAddModalFormData({ [parentColumn]: sourceValue });
      setShowAddModal(true);
    },
    [componentConfig, toast],
  );

  // 추가 모달 저장
  const handleAddModalSave = useCallback(async () => {
    // 테이블명과 모달 컬럼 결정
    let tableName: string | undefined;
    let modalColumns: Array<{ name: string; label: string; required?: boolean }> | undefined;
    const finalData = { ...addModalFormData };

    if (addModalPanel === "left") {
      tableName = componentConfig.leftPanel?.tableName;
      modalColumns = componentConfig.leftPanel?.addModalColumns;
    } else if (addModalPanel === "right") {
      // 우측 패널: 중계 테이블 설정이 있는지 확인
      const addConfig = componentConfig.rightPanel?.addConfig;

      if (addConfig?.targetTable) {
        // 중계 테이블 모드
        tableName = addConfig.targetTable;
        modalColumns = componentConfig.rightPanel?.addModalColumns;

        // 좌측 패널에서 선택된 값 자동 채우기
        if (addConfig.leftPanelColumn && addConfig.targetColumn && selectedLeftItem) {
          const leftValue = selectedLeftItem[addConfig.leftPanelColumn];
          finalData[addConfig.targetColumn] = leftValue;
          console.log(`🔗 좌측 패널 값 자동 채움: ${addConfig.targetColumn} = ${leftValue}`);
        }

        // 자동 채움 컬럼 추가
        if (addConfig.autoFillColumns) {
          Object.entries(addConfig.autoFillColumns).forEach(([key, value]) => {
            finalData[key] = value;
          });
          console.log("🔧 자동 채움 컬럼:", addConfig.autoFillColumns);
        }
      } else {
        // 일반 테이블 모드
        tableName = componentConfig.rightPanel?.tableName;
        modalColumns = componentConfig.rightPanel?.addModalColumns;
      }
    } else if (addModalPanel === "left-item") {
      // 하위 항목 추가 (좌측 테이블에 추가)
      tableName = componentConfig.leftPanel?.tableName;
      modalColumns = componentConfig.leftPanel?.itemAddConfig?.addModalColumns;
    }

    if (!tableName) {
      toast({
        title: "테이블 오류",
        description: "테이블명이 설정되지 않았습니다.",
        variant: "destructive",
      });
      return;
    }

    // 필수 필드 검증
    const requiredFields = (modalColumns || []).filter((col) => col.required);
    for (const field of requiredFields) {
      if (!addModalFormData[field.name]) {
        toast({
          title: "입력 오류",
          description: `${field.label}은(는) 필수 입력 항목입니다.`,
          variant: "destructive",
        });
        return;
      }
    }

    try {
      console.log("📝 데이터 추가:", { tableName, data: finalData });

      const result = await dataApi.createRecord(tableName, finalData);

      if (result.success) {
        toast({
          title: "성공",
          description: "데이터가 성공적으로 추가되었습니다.",
        });

        // 모달 닫기
        setShowAddModal(false);
        setAddModalFormData({});

        // 데이터 새로고침
        if (addModalPanel === "left" || addModalPanel === "left-item") {
          // 좌측 패널 데이터 새로고침 (일반 추가 또는 하위 항목 추가)
          loadLeftData();
        } else if (addModalPanel === "right") {
          // 우측 패널 데이터 새로고침
          loadRightData(selectedLeftItem);
        }
      } else {
        toast({
          title: "저장 실패",
          description: result.message || "데이터 추가에 실패했습니다.",
          variant: "destructive",
        });
      }
    } catch (error: any) {
      console.error("데이터 추가 오류:", error);

      // 에러 메시지 추출
      let errorMessage = "데이터 추가 중 오류가 발생했습니다.";

      if (error?.response?.data) {
        const responseData = error.response.data;

        // 백엔드에서 반환한 에러 메시지 확인
        if (responseData.error) {
          // 중복 키 에러 처리
          if (responseData.error.includes("duplicate key")) {
            errorMessage = "이미 존재하는 값입니다. 다른 값을 입력해주세요.";
          }
          // NOT NULL 제약조건 에러
          else if (responseData.error.includes("null value")) {
            const match = responseData.error.match(/column "(\w+)"/);
            const columnName = match ? match[1] : "필수";
            errorMessage = `${columnName} 필드는 필수 입력 항목입니다.`;
          }
          // 외래키 제약조건 에러
          else if (responseData.error.includes("foreign key")) {
            errorMessage = "참조하는 데이터가 존재하지 않습니다.";
          }
          // 기타 에러
          else {
            errorMessage = responseData.message || responseData.error;
          }
        } else if (responseData.message) {
          errorMessage = responseData.message;
        }
      }

      toast({
        title: "오류",
        description: errorMessage,
        variant: "destructive",
      });
    }
  }, [addModalPanel, componentConfig, addModalFormData, toast, selectedLeftItem, loadLeftData, loadRightData]);

  // 🔧 좌측 컬럼 가시성 설정 저장 및 불러오기
  useEffect(() => {
    const leftTableName = componentConfig.leftPanel?.tableName;
    if (leftTableName && currentUserId) {
      // localStorage에서 저장된 설정 불러오기
      const storageKey = `table_column_visibility_${leftTableName}_${currentUserId}`;
      const savedSettings = localStorage.getItem(storageKey);

      if (savedSettings) {
        try {
          const parsed = JSON.parse(savedSettings) as ColumnVisibility[];
          setLeftColumnVisibility(parsed);
        } catch (error) {
          console.error("저장된 컬럼 설정 불러오기 실패:", error);
        }
      }
    }
  }, [componentConfig.leftPanel?.tableName, currentUserId]);

  // 🔧 컬럼 가시성 변경 시 localStorage에 저장 및 순서 업데이트
  useEffect(() => {
    const leftTableName = componentConfig.leftPanel?.tableName;

    if (leftColumnVisibility.length > 0 && leftTableName && currentUserId) {
      // 순서 업데이트
      const newOrder = leftColumnVisibility.map((cv) => cv.columnName).filter((name) => name !== "__checkbox__"); // 체크박스 제외

      setLeftColumnOrder(newOrder);

      // localStorage에 저장
      const storageKey = `table_column_visibility_${leftTableName}_${currentUserId}`;
      localStorage.setItem(storageKey, JSON.stringify(leftColumnVisibility));
    }
  }, [leftColumnVisibility, componentConfig.leftPanel?.tableName, currentUserId]);

  // 초기 데이터 로드 (좌측 + 우측 전체 데이터)
  useEffect(() => {
    if (!isDesignMode && componentConfig.autoLoad !== false) {
      loadLeftData();
      // 좌측 미선택 상태에서 우측 전체 데이터 기본 로드
      // join 모드: 초기 전체 로드 / detail 모드: 초기 로드 안 함
      const relationshipType = componentConfig.rightPanel?.relation?.type || "detail";
      if (relationshipType === "join") {
        loadRightData(null);
      }
      // 추가 탭: 메인 패널이 "detail"(선택 시 표시)이면 추가 탭도 초기 로드하지 않음
      if (relationshipType !== "detail") {
        const tabs = componentConfig.rightPanel?.additionalTabs;
        if (tabs && tabs.length > 0) {
          tabs.forEach((tab: any, idx: number) => {
            const tabRelType = tab.relation?.type || "join";
            if (tabRelType === "join") {
              loadTabData(idx + 1, null);
            }
          });
        }
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isDesignMode, componentConfig.autoLoad]);

  // 🔄 필터 변경 시 데이터 다시 로드
  useEffect(() => {
    if (!isDesignMode && componentConfig.autoLoad !== false) {
      loadLeftData();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [leftFilters]);

  // 전역 테이블 새로고침 이벤트 리스너
  useEffect(() => {
    const handleRefreshTable = () => {
      if (!isDesignMode) {
        console.log("🔄 [SplitPanel] refreshTable 이벤트 수신 - 데이터 새로고침");
        loadLeftData();
        // 현재 활성 탭 데이터 새로고침 (좌측 미선택 시에도 전체 데이터 로드)
        if (activeTabIndex === 0) {
          loadRightData(selectedLeftItem);
        } else {
          loadTabData(activeTabIndex, selectedLeftItem);
        }
      }
    };

    window.addEventListener("refreshTable", handleRefreshTable);

    return () => {
      window.removeEventListener("refreshTable", handleRefreshTable);
    };
  }, [isDesignMode, loadLeftData, loadRightData, loadTabData, activeTabIndex, selectedLeftItem]);

  // 리사이저 드래그 핸들러
  const handleMouseDown = (e: React.MouseEvent) => {
    if (!resizable) return;
    setIsDragging(true);
    e.preventDefault();
  };

  const handleMouseMove = useCallback(
    (e: MouseEvent) => {
      if (!isDragging || !containerRef.current) return;

      const containerRect = containerRef.current.getBoundingClientRect();
      const containerWidth = containerRect.width;
      const relativeX = e.clientX - containerRect.left;
      const newLeftWidth = (relativeX / containerWidth) * 100;

      // 최소/최대 너비 제한 (20% ~ 80%)
      if (newLeftWidth >= 20 && newLeftWidth <= 80) {
        setLeftWidth(newLeftWidth);
      }
    },
    [isDragging],
  );

  const handleMouseUp = useCallback(() => {
    setIsDragging(false);
  }, []);

  React.useEffect(() => {
    if (isDragging) {
      // 드래그 중에는 텍스트 선택 방지
      document.body.style.userSelect = "none";
      document.body.style.cursor = "col-resize";

      document.addEventListener("mousemove", handleMouseMove);
      document.addEventListener("mouseup", handleMouseUp);

      return () => {
        document.body.style.userSelect = "";
        document.body.style.cursor = "";
        document.removeEventListener("mousemove", handleMouseMove);
        document.removeEventListener("mouseup", handleMouseUp);
      };
    }
  }, [isDragging, handleMouseMove, handleMouseUp]);

  return (
    <div
      ref={containerRef}
      style={{
        ...componentStyle,
        display: "flex",
        flexDirection: "row",
      }}
      onClick={(e) => {
        if (isDesignMode) {
          e.stopPropagation();
          onClick?.(e);
        }
      }}
      className="w-full overflow-hidden rounded-lg bg-white shadow-sm"
    >
      {/* 좌측 패널 */}
      <div
        style={{ width: `${leftWidth}%`, minWidth: isDesignMode ? `${minLeftWidth}px` : "0", height: "100%" }}
        className="border-border flex flex-col border-r"
      >
        <Card className="flex flex-col border-0 shadow-none" style={{ height: "100%" }}>
          <CardHeader
            className="flex-shrink-0 border-b"
            style={{
              height: componentConfig.leftPanel?.panelHeaderHeight || 48,
              minHeight: componentConfig.leftPanel?.panelHeaderHeight || 48,
              padding: "0 1rem",
              display: "flex",
              alignItems: "center",
            }}
          >
            <div className="flex w-full items-center justify-between">
              <CardTitle className="text-base font-semibold">
                {componentConfig.leftPanel?.title || "좌측 패널"}
              </CardTitle>
              <div className="flex items-center gap-1">
                {!isDesignMode && (componentConfig.leftPanel as any)?.showBomExcelUpload && (
                  <Button size="sm" variant="outline" onClick={() => setBomExcelUploadOpen(true)}>
                    <FileSpreadsheet className="mr-1 h-4 w-4" />
                    엑셀
                  </Button>
                )}
                {!isDesignMode && componentConfig.leftPanel?.showAdd && (
                  <Button size="sm" variant="outline" onClick={() => handleAddClick("left")}>
                    <Plus className="mr-1 h-4 w-4" />
                    추가
                  </Button>
                )}
              </div>
            </div>
          </CardHeader>
          {componentConfig.leftPanel?.showSearch && (
            <div className="flex-shrink-0 border-b p-2">
              <div className="relative">
                <Search className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
                <Input
                  placeholder="검색..."
                  value={leftSearchQuery}
                  onChange={(e) => setLeftSearchQuery(e.target.value)}
                  className="pl-9"
                />
              </div>
            </div>
          )}
          <CardContent 
            className="flex-1 overflow-auto p-4"
            {...(isDesignMode ? {
              "data-split-panel-container": "true",
              "data-component-id": component.id,
              "data-panel-side": "left",
            } : {})}
          >
            {/* 좌측 데이터 목록/테이블/커스텀 */}
            {componentConfig.leftPanel?.displayMode === "custom" ? (
              // 🆕 커스텀 모드: 패널 안에 자유롭게 컴포넌트 배치
              <div 
                className="relative h-full w-full"
                data-split-panel-container="true"
                data-component-id={component.id}
                data-panel-side="left"
              >
                {/* 커스텀 모드: 디자인/실행 모드 분기 렌더링 */}
                {componentConfig.leftPanel?.components && componentConfig.leftPanel.components.length > 0 ? (
                  !isDesignMode ? (
                    <ScaledCustomPanel
                      components={componentConfig.leftPanel!.components}
                      formData={{}}
                      onFormDataChange={(data: any) => {
                        if (data?.selectedRowsData && data.selectedRowsData.length > 0) {
                          setCustomLeftSelectedData(data.selectedRowsData[0]);
                          setSelectedLeftItem(data.selectedRowsData[0]);
                        } else if (data?.selectedRowsData && data.selectedRowsData.length === 0) {
                          setCustomLeftSelectedData({});
                          setSelectedLeftItem(null);
                        }
                      }}
                      tableName={componentConfig.leftPanel?.tableName}
                      menuObjid={(props as any).menuObjid}
                      screenId={(props as any).screenId}
                      userId={(props as any).userId}
                      userName={(props as any).userName}
                      companyCode={companyCode}
                      allComponents={(props as any).allComponents}
                      selectedRowsData={localSelectedRowsData}
                      onSelectedRowsChange={handleLocalSelectedRowsChange}
                    />
                  ) : (
                  <div className="relative h-full w-full" style={{ minHeight: "100%", minWidth: "100%" }}>
                    {componentConfig.leftPanel.components.map((comp: PanelInlineComponent) => {
                      const isSelectedComp = selectedPanelComponentId === comp.id;
                      const isDraggingComp = draggingCompId === comp.id;
                      const isResizingComp = resizingCompId === comp.id;
                      
                      const displayX = isDraggingComp && dragPosition ? dragPosition.x : (comp.position?.x || 0);
                      const displayY = isDraggingComp && dragPosition ? dragPosition.y : (comp.position?.y || 0);
                      const displayWidth = isResizingComp && resizeSize ? resizeSize.width : (comp.size?.width || 200);
                      const displayHeight = isResizingComp && resizeSize ? resizeSize.height : (comp.size?.height || 100);
                      
                      const componentData = {
                        id: comp.id,
                        type: "component" as const,
                        componentType: comp.componentType,
                        label: comp.label,
                        position: comp.position || { x: 0, y: 0 },
                        size: { width: displayWidth, height: displayHeight },
                        componentConfig: comp.componentConfig || {},
                        style: comp.style || {},
                        tableName: comp.componentConfig?.tableName,
                        columnName: comp.componentConfig?.columnName,
                        webType: comp.componentConfig?.webType,
                        inputType: (comp as any).inputType || comp.componentConfig?.inputType,
                      };

                      return (
                          <div
                            key={comp.id}
                            data-panel-comp-id={comp.id}
                            className="absolute"
                            style={{
                              left: displayX,
                              top: displayY,
                              zIndex: isDraggingComp ? 100 : isSelectedComp ? 10 : 1,
                            }}
                            onClick={(e) => {
                              e.stopPropagation();
                              if (comp.componentType !== "v2-tabs-widget") {
                                setNestedTabSelectedCompId(undefined);
                              }
                              setInternalSelectedCompId(comp.id);
                              onSelectPanelComponent?.("left", comp.id, comp);
                            }}
                          >
                            {/* 드래그 핸들 - 컴포넌트 외부 상단 */}
                            <div
                              className={cn(
                                "flex h-4 cursor-move items-center justify-between rounded-t border border-b-0 bg-muted px-1",
                                isSelectedComp ? "border-primary" : "border-border"
                              )}
                              style={{ width: displayWidth }}
                              onMouseDown={(e) => handlePanelDragStart(e, "left", comp)}
                            >
                              <div className="flex items-center gap-0.5">
                                <Move className="h-2.5 w-2.5 text-muted-foreground/70" />
                                <span className="max-w-[100px] truncate text-[9px] text-muted-foreground">
                                  {comp.label || comp.componentType}
                                </span>
                              </div>
                              <div className="flex items-center">
                                <button
                                  className="rounded p-0.5 hover:bg-muted/80"
                                  onClick={(e) => {
                                    e.stopPropagation();
                                    onSelectPanelComponent?.("left", comp.id, comp);
                                  }}
                                  title="설정"
                                >
                                  <Settings className="h-2.5 w-2.5 text-muted-foreground" />
                                </button>
                                <button
                                  className="rounded p-0.5 hover:bg-destructive/10"
                                  onClick={(e) => {
                                    e.stopPropagation();
                                    handleRemovePanelComponent("left", comp.id);
                                  }}
                                  title="삭제"
                                >
                                  <Trash2 className="h-2.5 w-2.5 text-destructive" />
                                </button>
                              </div>
                            </div>

                            {/* 실제 컴포넌트 렌더링 - 핸들 아래에 별도 영역 */}
                            <div 
                              className={cn(
                                "relative overflow-hidden rounded-b border bg-white shadow-sm",
                                isSelectedComp
                                  ? "border-primary ring-2 ring-primary/30"
                                  : "border-border",
                                (isDraggingComp || isResizingComp) && "opacity-80 shadow-lg",
                                !(isDraggingComp || isResizingComp) && "transition-all"
                              )}
                              style={{
                                width: displayWidth,
                                height: displayHeight,
                              }}
                            >
                              {/* 🆕 컨테이너 컴포넌트(탭, 분할 패널)는 드롭 이벤트를 받을 수 있어야 함 */}
                              <div className={cn(
                                "h-full w-full",
                                // 탭/분할 패널 같은 컨테이너 컴포넌트는 pointer-events 활성화
                                (comp.componentType === "v2-tabs-widget" || 
                                 comp.componentType === "tabs-widget" ||
                                 comp.componentType === "v2-split-panel-layout" ||
                                 comp.componentType === "split-panel-layout")
                                  ? "" 
                                  : "pointer-events-none"
                              )}>
                                <DynamicComponentRenderer
                                  component={componentData as any}
                                  isDesignMode={true}
                                  formData={{}}
                                  // 🆕 중첩된 컴포넌트 업데이트 핸들러 전달
                                  onUpdateComponent={(updatedComp: any) => {
                                    handleNestedComponentUpdate("left", comp.id, updatedComp);
                                  }}
                                  // 🆕 중첩된 탭 내부 컴포넌트 선택 핸들러 - 부모 분할 패널 정보 포함
                                  onSelectTabComponent={(tabId: string, compId: string, tabComp: any) => {
                                    console.log("🔍 [SplitPanel-Left] onSelectTabComponent 호출:", { tabId, compId, tabComp, parentSplitPanelId: component.id });
                                    // 탭 내 컴포넌트 선택 상태 업데이트
                                    setNestedTabSelectedCompId(compId);
                                    // 부모 분할 패널 정보와 함께 전역 이벤트 발생
                                    const event = new CustomEvent("nested-tab-component-select", {
                                      detail: {
                                        tabsComponentId: comp.id,
                                        tabId,
                                        componentId: compId,
                                        component: tabComp,
                                        parentSplitPanelId: component.id,
                                        parentPanelSide: "left",
                                      },
                                    });
                                    window.dispatchEvent(event);
                                  }}
                                  selectedTabComponentId={nestedTabSelectedCompId}
                                />
                              </div>
                              
                              {/* 리사이즈 가장자리 영역 - 선택된 컴포넌트에만 표시 */}
                              {isSelectedComp && (
                                <>
                                  {/* 오른쪽 가장자리 (너비 조절) */}
                                  <div
                                    className="pointer-events-auto absolute right-0 top-0 z-10 h-full w-2 cursor-ew-resize hover:bg-primary/10"
                                    onMouseDown={(e) => handlePanelResizeStart(e, "left", comp, "e")}
                                  />
                                  {/* 아래 가장자리 (높이 조절) */}
                                  <div
                                    className="pointer-events-auto absolute bottom-0 left-0 z-10 h-2 w-full cursor-ns-resize hover:bg-primary/10"
                                    onMouseDown={(e) => handlePanelResizeStart(e, "left", comp, "s")}
                                  />
                                  {/* 오른쪽 아래 모서리 (너비+높이 조절) */}
                                  <div
                                    className="pointer-events-auto absolute bottom-0 right-0 z-20 h-3 w-3 cursor-nwse-resize hover:bg-primary/20"
                                    onMouseDown={(e) => handlePanelResizeStart(e, "left", comp, "se")}
                                  />
                                </>
                              )}
                            </div>
                          </div>
                        );
                    })}
                  </div>
                  )
                ) : (
                  // 컴포넌트가 없을 때 드롭 영역 표시
                  <div className="flex h-full w-full flex-col items-center justify-center rounded border-2 border-dashed border-input bg-muted/50">
                    <Plus className="mb-2 h-8 w-8 text-muted-foreground/70" />
                    <p className="text-sm font-medium text-muted-foreground">
                      커스텀 모드
                    </p>
                    <p className="mt-1 text-xs text-muted-foreground/70">
                      {isDesignMode ? "컴포넌트를 드래그하여 배치하세요" : "배치된 컴포넌트가 없습니다"}
                    </p>
                  </div>
                )}
              </div>
            ) : componentConfig.leftPanel?.displayMode === "table" ? (
              // 테이블 모드
              <div className="w-full">
                {isDesignMode ? (
                  // 디자인 모드: 샘플 테이블
                  <div className="overflow-auto">
                    <table className="min-w-full divide-y divide-border">
                      <thead className="bg-muted">
                        <tr>
                          <th className="px-3 py-2 text-left text-xs font-medium text-muted-foreground uppercase">컬럼 1</th>
                          <th className="px-3 py-2 text-left text-xs font-medium text-muted-foreground uppercase">컬럼 2</th>
                          <th className="px-3 py-2 text-left text-xs font-medium text-muted-foreground uppercase">컬럼 3</th>
                        </tr>
                      </thead>
                      <tbody className="divide-y divide-border bg-white">
                        <tr className="cursor-pointer hover:bg-muted">
                          <td className="px-3 py-2 text-sm whitespace-nowrap">데이터 1-1</td>
                          <td className="px-3 py-2 text-sm whitespace-nowrap">데이터 1-2</td>
                          <td className="px-3 py-2 text-sm whitespace-nowrap">데이터 1-3</td>
                        </tr>
                        <tr className="cursor-pointer hover:bg-muted">
                          <td className="px-3 py-2 text-sm whitespace-nowrap">데이터 2-1</td>
                          <td className="px-3 py-2 text-sm whitespace-nowrap">데이터 2-2</td>
                          <td className="px-3 py-2 text-sm whitespace-nowrap">데이터 2-3</td>
                        </tr>
                      </tbody>
                    </table>
                  </div>
                ) : isLoadingLeft ? (
                  <div className="flex items-center justify-center py-8">
                    <Loader2 className="text-primary h-6 w-6 animate-spin" />
                    <span className="text-muted-foreground ml-2 text-sm">데이터를 불러오는 중...</span>
                  </div>
                ) : (
                  (() => {
                    // 🆕 그룹별 합산된 데이터 사용
                    const dataSource = summedLeftData;
                    console.log(
                      "🔍 [테이블모드 렌더링] dataSource 개수:",
                      dataSource.length,
                      "leftGroupSumConfig:",
                      leftGroupSumConfig,
                    );

                    // 🔧 로컬 검색 필터 적용
                    const filteredData = leftSearchQuery
                      ? dataSource.filter((item) => {
                          const searchLower = leftSearchQuery.toLowerCase();
                          return Object.entries(item).some(([key, value]) => {
                            if (value === null || value === undefined) return false;
                            return String(value).toLowerCase().includes(searchLower);
                          });
                        })
                      : dataSource;

                    // 🔧 가시성 처리된 컬럼 사용
                    const columnsToShow =
                      visibleLeftColumns.length > 0
                        ? visibleLeftColumns.map((col: any) => {
                            const colName = typeof col === "string" ? col : col.name || col.columnName;
                            return {
                              name: colName,
                              label:
                                leftColumnLabels[colName] || (typeof col === "object" ? col.label : null) || colName,
                              width: typeof col === "object" ? col.width : 150,
                              align: (typeof col === "object" ? col.align : "left") as "left" | "center" | "right",
                              format: typeof col === "object" ? col.format : undefined, // 🆕 포맷 설정 포함
                            };
                          })
                        : Object.keys(filteredData[0] || {})
                            .filter((key) => key !== "children" && key !== "level")
                            .slice(0, 5)
                            .map((key) => ({
                              name: key,
                              label: leftColumnLabels[key] || key,
                              width: 150,
                              align: "left" as const,
                              format: undefined, // 🆕 기본값
                            }));

                    // 컬럼 너비 합계 계산 (작업 컬럼 제외, 100% 초과 시 스크롤)
                    const leftTotalColWidth = columnsToShow.reduce((sum, col) => {
                      const w = col.width && col.width <= 100 ? col.width : 0;
                      return sum + w;
                    }, 0);

                    // 🔧 그룹화된 데이터 렌더링
                    const hasGroupedLeftActions = !isDesignMode && (
                      (componentConfig.leftPanel?.showEdit !== false) ||
                      (componentConfig.leftPanel?.showDelete !== false)
                    );
                    if (groupedLeftData.length > 0) {
                      return (
                        <div className="overflow-auto">
                          {groupedLeftData.map((group, groupIdx) => (
                            <div key={groupIdx} className="mb-4">
                              <div className="bg-muted px-3 py-2 text-sm font-semibold">
                                {group.groupKey} ({group.count}개)
                              </div>
                              <table className="divide-y divide-border table-fixed" style={{ width: leftTotalColWidth > 100 ? `${leftTotalColWidth}%` : '100%' }}>
                                <thead className="bg-muted">
                                  <tr>
                                    {columnsToShow.map((col, idx) => (
                                      <th
                                        key={idx}
                                        className="px-3 py-2 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase whitespace-nowrap"
                                        style={{
                                          width: col.width && col.width <= 100 ? `${col.width}%` : "auto",
                                          textAlign: col.align || "left",
                                        }}
                                      >
                                        {col.label}
                                      </th>
                                    ))}
                                    {hasGroupedLeftActions && (
                                      <th className="bg-muted sticky right-0 z-10 px-3 py-2 text-right text-xs font-medium tracking-wider text-muted-foreground uppercase whitespace-nowrap" style={{ width: "80px" }}>
                                      </th>
                                    )}
                                  </tr>
                                </thead>
                                <tbody className="divide-y divide-border bg-white">
                                  {group.items.map((item, idx) => {
                                    const sourceColumn = componentConfig.leftPanel?.itemAddConfig?.sourceColumn || "id";
                                    const itemId = item[sourceColumn] || item.id || item.ID || idx;
                                    const isSelected =
                                      selectedLeftItem &&
                                      (selectedLeftItem[sourceColumn] === itemId || selectedLeftItem === item);

                                    return (
                                      <tr
                                        key={itemId}
                                        onClick={() => handleLeftItemSelect(item)}
                                        className={`group hover:bg-accent cursor-pointer transition-colors ${
                                          isSelected ? "bg-primary/10" : ""
                                        }`}
                                      >
                                        {columnsToShow.map((col, colIdx) => (
                                          <td
                                            key={colIdx}
                                            className="px-3 py-2 text-sm whitespace-nowrap text-foreground"
                                            style={{ textAlign: col.align || "left", overflow: "hidden", textOverflow: "ellipsis" }}
                                          >
                                            {formatCellValue(
                                              col.name,
                                              getEntityJoinValue(item, col.name),
                                              leftCategoryMappings,
                                              col.format,
                                            )}
                                          </td>
                                        ))}
                                        {hasGroupedLeftActions && (
                                          <td className="bg-card sticky right-0 z-10 px-3 py-2 text-right group-hover:bg-accent">
                                            <div className="flex items-center justify-end gap-1 opacity-0 transition-opacity group-hover:opacity-100">
                                              {(componentConfig.leftPanel?.showEdit !== false) && (
                                                <button
                                                  onClick={(e) => {
                                                    e.stopPropagation();
                                                    handleEditClick("left", item);
                                                  }}
                                                  className="rounded p-1 transition-colors hover:bg-muted/80"
                                                >
                                                  <Pencil className="h-3.5 w-3.5 text-muted-foreground" />
                                                </button>
                                              )}
                                              {(componentConfig.leftPanel?.showDelete !== false) && (
                                                <button
                                                  onClick={(e) => {
                                                    e.stopPropagation();
                                                    handleDeleteClick("left", item);
                                                  }}
                                                  className="rounded p-1 transition-colors hover:bg-destructive/10"
                                                >
                                                  <Trash2 className="h-3.5 w-3.5 text-destructive" />
                                                </button>
                                              )}
                                            </div>
                                          </td>
                                        )}
                                      </tr>
                                    );
                                  })}
                                </tbody>
                              </table>
                            </div>
                          ))}
                        </div>
                      );
                    }

                    // 🔧 일반 테이블 렌더링 (그룹화 없음)
                    const hasLeftTableActions = !isDesignMode && (
                      (componentConfig.leftPanel?.showEdit !== false) ||
                      (componentConfig.leftPanel?.showDelete !== false)
                    );
                    return (
                      <div className="overflow-auto">
                        <table className="divide-y divide-border table-fixed" style={{ width: leftTotalColWidth > 100 ? `${leftTotalColWidth}%` : '100%' }}>
                          <thead className="sticky top-0 z-10 bg-muted">
                            <tr>
                              {columnsToShow.map((col, idx) => (
                                <th
                                  key={idx}
                                  className="px-3 py-2 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase whitespace-nowrap"
                                  style={{
                                    width: col.width && col.width <= 100 ? `${col.width}%` : "auto",
                                    textAlign: col.align || "left",
                                  }}
                                >
                                  {col.label}
                                </th>
                              ))}
                              {hasLeftTableActions && (
                                <th className="bg-muted sticky right-0 z-10 px-3 py-2 text-right text-xs font-medium tracking-wider text-muted-foreground uppercase whitespace-nowrap" style={{ width: "80px" }}>
                                </th>
                              )}
                            </tr>
                          </thead>
                          <tbody className="divide-y divide-border bg-white">
                            {filteredData.map((item, idx) => {
                              const sourceColumn = componentConfig.leftPanel?.itemAddConfig?.sourceColumn || "id";
                              const itemId = item[sourceColumn] || item.id || item.ID || idx;
                              const isSelected =
                                selectedLeftItem &&
                                (selectedLeftItem[sourceColumn] === itemId || selectedLeftItem === item);

                              return (
                                <tr
                                  key={itemId}
                                  onClick={() => handleLeftItemSelect(item)}
                                  className={`group hover:bg-accent cursor-pointer transition-colors ${
                                    isSelected ? "bg-primary/10" : ""
                                  }`}
                                >
                                  {columnsToShow.map((col, colIdx) => (
                                    <td
                                      key={colIdx}
                                      className="px-3 py-2 text-sm whitespace-nowrap text-foreground"
                                      style={{ textAlign: col.align || "left", overflow: "hidden", textOverflow: "ellipsis" }}
                                    >
                                      {formatCellValue(
                                        col.name,
                                        getEntityJoinValue(item, col.name),
                                        leftCategoryMappings,
                                        col.format,
                                      )}
                                    </td>
                                  ))}
                                  {hasLeftTableActions && (
                                    <td className="bg-card sticky right-0 z-10 px-3 py-2 text-right group-hover:bg-accent">
                                      <div className="flex items-center justify-end gap-1 opacity-0 transition-opacity group-hover:opacity-100">
                                        {(componentConfig.leftPanel?.showEdit !== false) && (
                                          <button
                                            onClick={(e) => {
                                              e.stopPropagation();
                                              handleEditClick("left", item);
                                            }}
                                            className="rounded p-1 transition-colors hover:bg-muted/80"
                                          >
                                            <Pencil className="h-3.5 w-3.5 text-muted-foreground" />
                                          </button>
                                        )}
                                        {(componentConfig.leftPanel?.showDelete !== false) && (
                                          <button
                                            onClick={(e) => {
                                              e.stopPropagation();
                                              handleDeleteClick("left", item);
                                            }}
                                            className="rounded p-1 transition-colors hover:bg-destructive/10"
                                          >
                                            <Trash2 className="h-3.5 w-3.5 text-destructive" />
                                          </button>
                                        )}
                                      </div>
                                    </td>
                                  )}
                                </tr>
                              );
                            })}
                          </tbody>
                        </table>
                      </div>
                    );
                  })()
                )}
              </div>
            ) : (
              // 목록 모드 (기존)
              <div className="space-y-1">
                {isDesignMode ? (
                  // 디자인 모드: 샘플 데이터
                  <>
                    <div
                      onClick={() => handleLeftItemSelect({ id: 1, name: "항목 1" })}
                      className={`hover:bg-accent cursor-pointer rounded-md p-3 transition-colors ${
                        selectedLeftItem?.id === 1 ? "bg-primary/10 text-primary" : ""
                      }`}
                    >
                      <div className="font-medium">항목 1</div>
                      <div className="text-muted-foreground text-xs">설명 텍스트</div>
                    </div>
                    <div
                      onClick={() => handleLeftItemSelect({ id: 2, name: "항목 2" })}
                      className={`hover:bg-accent cursor-pointer rounded-md p-3 transition-colors ${
                        selectedLeftItem?.id === 2 ? "bg-primary/10 text-primary" : ""
                      }`}
                    >
                      <div className="font-medium">항목 2</div>
                      <div className="text-muted-foreground text-xs">설명 텍스트</div>
                    </div>
                    <div
                      onClick={() => handleLeftItemSelect({ id: 3, name: "항목 3" })}
                      className={`hover:bg-accent cursor-pointer rounded-md p-3 transition-colors ${
                        selectedLeftItem?.id === 3 ? "bg-primary/10 text-primary" : ""
                      }`}
                    >
                      <div className="font-medium">항목 3</div>
                      <div className="text-muted-foreground text-xs">설명 텍스트</div>
                    </div>
                  </>
                ) : isLoadingLeft ? (
                  // 로딩 중
                  <div className="flex items-center justify-center py-8">
                    <Loader2 className="text-primary h-6 w-6 animate-spin" />
                    <span className="text-muted-foreground ml-2 text-sm">데이터를 불러오는 중...</span>
                  </div>
                ) : (
                  (() => {
                    // 🆕 그룹별 합산된 데이터 사용
                    const dataToDisplay = summedLeftData;
                    console.log(
                      "🔍 [렌더링] dataToDisplay 개수:",
                      dataToDisplay.length,
                      "leftGroupSumConfig:",
                      leftGroupSumConfig,
                    );

                    // 검색 필터링 (클라이언트 사이드)
                    const filteredLeftData = leftSearchQuery
                      ? dataToDisplay.filter((item) => {
                          const searchLower = leftSearchQuery.toLowerCase();
                          return Object.entries(item).some(([key, value]) => {
                            if (value === null || value === undefined) return false;
                            return String(value).toLowerCase().includes(searchLower);
                          });
                        })
                      : dataToDisplay;

                    // 재귀 렌더링 함수
                    const renderTreeItem = (item: any, index: number): React.ReactNode => {
                      const sourceColumn = componentConfig.leftPanel?.itemAddConfig?.sourceColumn || "id";
                      const itemId = item[sourceColumn] || item.id || item.ID || index;
                      const isSelected =
                        selectedLeftItem && (selectedLeftItem[sourceColumn] === itemId || selectedLeftItem === item);
                      const hasChildren = item.children && item.children.length > 0;
                      const isExpanded = expandedItems.has(itemId);
                      const level = item.level || 0;

                      // 🔧 수정: "표시할 컬럼 선택"에서 설정한 컬럼을 우선 사용
                      const configuredColumns = componentConfig.leftPanel?.columns || [];
                      let displayFields: { label: string; value: any }[] = [];

                      // 디버그 로그
                      if (index === 0) {
                        console.log("🔍 좌측 패널 표시 로직:");
                        console.log("  - 설정된 표시 컬럼:", configuredColumns);
                        console.log("  - item keys:", Object.keys(item));
                      }

                      if (configuredColumns.length > 0) {
                        // 🔧 "표시할 컬럼 선택"에서 설정한 컬럼 사용
                        displayFields = configuredColumns.slice(0, 2).map((col: any) => {
                          const colName = typeof col === "string" ? col : col.name || col.columnName;
                          const colLabel = typeof col === "object" ? col.label : leftColumnLabels[colName] || colName;
                          const rawValue = getEntityJoinValue(item, colName);
                          // 카테고리 매핑이 있으면 라벨로 변환
                          let displayValue = rawValue;
                          if (rawValue != null && rawValue !== "") {
                            const strVal = String(rawValue);
                            let mapping = leftCategoryMappings[colName];
                            if (!mapping && colName.includes(".")) {
                              mapping = leftCategoryMappings[colName.split(".").pop() || colName];
                            }
                            if (mapping && mapping[strVal]) {
                              displayValue = mapping[strVal].label;
                            }
                          }
                          return {
                            label: colLabel,
                            value: displayValue,
                          };
                        });

                        if (index === 0) {
                          console.log("  ✅ 설정된 컬럼 기반 표시:", displayFields);
                        }
                      } else {
                        // 설정된 컬럼이 없으면 자동으로 첫 2개 필드 표시
                        const keys = Object.keys(item).filter(
                          (k) => k !== "id" && k !== "ID" && k !== "children" && k !== "level" && shouldShowField(k),
                        );
                        displayFields = keys.slice(0, 2).map((key) => {
                          const rawValue = item[key];
                          let displayValue = rawValue;
                          if (rawValue != null && rawValue !== "") {
                            const strVal = String(rawValue);
                            const mapping = leftCategoryMappings[key];
                            if (mapping && mapping[strVal]) {
                              displayValue = mapping[strVal].label;
                            }
                          }
                          return {
                            label: leftColumnLabels[key] || key,
                            value: displayValue,
                          };
                        });

                        if (index === 0) {
                          console.log("  ⚠️ 설정된 컬럼 없음, 자동 선택:", displayFields);
                        }
                      }

                      const displayTitle = displayFields[0]?.value || item.name || item.title || `항목 ${index + 1}`;
                      const displaySubtitle = displayFields[1]?.value || null;

                      return (
                        <React.Fragment key={itemId}>
                          {/* 현재 항목 */}
                          <div
                            className={`group hover:bg-muted relative cursor-pointer rounded-md p-3 transition-colors ${
                              isSelected ? "bg-primary/10 text-primary" : "text-foreground"
                            }`}
                            style={{ paddingLeft: `${12 + level * 24}px` }}
                          >
                            <div
                              className="flex items-center gap-2"
                              onClick={() => {
                                handleLeftItemSelect(item);
                                if (hasChildren) {
                                  toggleExpand(itemId);
                                }
                              }}
                            >
                              {/* 펼치기/접기 아이콘 */}
                              {hasChildren ? (
                                <div className="flex-shrink-0">
                                  {isExpanded ? (
                                    <ChevronDown className="h-4 w-4 text-muted-foreground" />
                                  ) : (
                                    <ChevronRight className="h-4 w-4 text-muted-foreground" />
                                  )}
                                </div>
                              ) : (
                                <div className="w-5" />
                              )}

                              {/* 항목 내용 */}
                              <div className="min-w-0 flex-1">
                                <div className="truncate font-medium">{displayTitle}</div>
                                {displaySubtitle && (
                                  <div className="text-muted-foreground truncate text-xs">{displaySubtitle}</div>
                                )}
                              </div>

                              {/* 항목별 버튼들 */}
                              {!isDesignMode && (
                                <div className="flex flex-shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100">
                                  {/* 수정 버튼 (showEdit 활성화 시에만 표시) */}
                                  {(componentConfig.leftPanel?.showEdit !== false) && (
                                    <button
                                      onClick={(e) => {
                                        e.stopPropagation();
                                        handleEditClick("left", item);
                                      }}
                                      className="rounded p-1 transition-colors hover:bg-muted/80"
                                      title="수정"
                                    >
                                      <Pencil className="h-4 w-4 text-muted-foreground" />
                                    </button>
                                  )}

                                  {/* 삭제 버튼 (showDelete 활성화 시에만 표시) */}
                                  {(componentConfig.leftPanel?.showDelete !== false) && (
                                    <button
                                      onClick={(e) => {
                                        e.stopPropagation();
                                        handleDeleteClick("left", item);
                                      }}
                                      className="rounded p-1 transition-colors hover:bg-destructive/10"
                                      title="삭제"
                                    >
                                      <Trash2 className="h-4 w-4 text-destructive" />
                                    </button>
                                  )}

                                  {/* 항목별 추가 버튼 */}
                                  {componentConfig.leftPanel?.showItemAddButton && (
                                    <button
                                      onClick={(e) => {
                                        e.stopPropagation();
                                        handleItemAddClick(item);
                                      }}
                                      className="rounded p-1 transition-colors hover:bg-muted/80"
                                      title="하위 항목 추가"
                                    >
                                      <Plus className="h-4 w-4 text-muted-foreground" />
                                    </button>
                                  )}
                                </div>
                              )}
                            </div>
                          </div>

                          {/* 자식 항목들 (접혀있으면 표시 안함) */}
                          {hasChildren &&
                            isExpanded &&
                            item.children.map((child: any, childIndex: number) => renderTreeItem(child, childIndex))}
                        </React.Fragment>
                      );
                    };

                    return filteredLeftData.length > 0 ? (
                      // 실제 데이터 표시
                      filteredLeftData.map((item, index) => renderTreeItem(item, index))
                    ) : (
                      // 검색 결과 없음
                      <div className="text-muted-foreground py-8 text-center text-sm">
                        {leftSearchQuery ? (
                          <>
                            <p>검색 결과가 없습니다.</p>
                            <p className="text-muted-foreground/70 mt-1 text-xs">다른 검색어를 입력해보세요.</p>
                          </>
                        ) : (
                          "데이터가 없습니다."
                        )}
                      </div>
                    );
                  })()
                )}
              </div>
            )}
          </CardContent>
        </Card>
      </div>

      {/* 리사이저 */}
      {resizable && (
        <div
          onMouseDown={handleMouseDown}
          className="group bg-border hover:bg-primary flex w-1 cursor-col-resize items-center justify-center transition-colors"
        >
          <GripVertical className="text-muted-foreground group-hover:text-primary-foreground h-4 w-4" />
        </div>
      )}

      {/* 우측 패널 */}
      <div
        style={{ width: `${100 - leftWidth}%`, minWidth: isDesignMode ? `${minRightWidth}px` : "0", height: "100%" }}
        className="flex flex-col border-l border-border/60 bg-muted/5"
      >
        <Card className="flex flex-col border-0 bg-transparent shadow-none" style={{ height: "100%" }}>
          <CardHeader
            className="flex-shrink-0 border-b bg-muted/30"
            style={{
              height: componentConfig.rightPanel?.panelHeaderHeight || 48,
              minHeight: componentConfig.rightPanel?.panelHeaderHeight || 48,
              padding: "0 0.75rem",
              display: "flex",
              alignItems: "center",
            }}
          >
            <div className="flex w-full items-center justify-between">
              <div className="flex items-center gap-0">
                {/* 탭이 없으면 제목만, 있으면 탭으로 전환 */}
                {(componentConfig.rightPanel?.additionalTabs?.length || 0) > 0 ? (
                  <div className="flex items-center gap-0">
                    <button
                      onClick={() => handleTabChange(0)}
                      className={cn(
                        "px-3 py-1 text-sm font-medium transition-colors",
                        activeTabIndex === 0
                          ? "text-foreground border-b-2 border-primary"
                          : "text-muted-foreground hover:text-foreground"
                      )}
                    >
                      {componentConfig.rightPanel?.title || "기본"}
                    </button>
                    {componentConfig.rightPanel?.additionalTabs?.map((tab: any, index: number) => (
                      <button
                        key={tab.tabId || `tab-${index}`}
                        onClick={() => handleTabChange(index + 1)}
                        className={cn(
                          "px-3 py-1 text-sm font-medium transition-colors",
                          activeTabIndex === index + 1
                            ? "text-foreground border-b-2 border-primary"
                            : "text-muted-foreground hover:text-foreground"
                        )}
                      >
                        {tab.label || `탭 ${index + 1}`}
                      </button>
                    ))}
                  </div>
                ) : (
                  <CardTitle className="text-base font-semibold">
                    {componentConfig.rightPanel?.title || "우측 패널"}
                  </CardTitle>
                )}
              </div>
              {!isDesignMode && (
                <div className="flex items-center gap-2">
                  {/* 커스텀 모드 기본정보 탭: 저장 버튼 */}
                  {activeTabIndex === 0 && componentConfig.rightPanel?.displayMode === "custom" && selectedLeftItem && (
                    <Button size="sm" variant="default" onClick={handleCustomRightSave}>
                      <Save className="mr-1 h-4 w-4" />
                      저장
                    </Button>
                  )}
                  {activeTabIndex === 0
                    ? componentConfig.rightPanel?.showAdd && (
                        <Button size="sm" variant="outline" onClick={() => handleAddClick("right")}>
                          <Plus className="mr-1 h-4 w-4" />
                          추가
                        </Button>
                      )
                    : (componentConfig.rightPanel?.additionalTabs?.[activeTabIndex - 1] as any)?.showAdd && (
                        <Button size="sm" variant="outline" onClick={() => handleAddClick("right")}>
                          <Plus className="mr-1 h-4 w-4" />
                          추가
                        </Button>
                      )}
                </div>
              )}
            </div>
          </CardHeader>
          {componentConfig.rightPanel?.showSearch && (
            <div className="flex-shrink-0 border-b p-2">
              <div className="relative">
                <Search className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
                <Input
                  placeholder="검색..."
                  value={rightSearchQuery}
                  onChange={(e) => setRightSearchQuery(e.target.value)}
                  className="pl-9"
                />
              </div>
            </div>
          )}
          <CardContent 
            className="flex-1 overflow-auto p-4"
            {...(isDesignMode ? {
              "data-split-panel-container": "true",
              "data-component-id": component.id,
              "data-panel-side": "right",
            } : {})}
          >
            {/* 추가 탭 컨텐츠 */}
            {activeTabIndex > 0 ? (
              (() => {
                const currentTabConfig = componentConfig.rightPanel?.additionalTabs?.[activeTabIndex - 1] as any;
                const currentTabData = tabsData[activeTabIndex] || [];
                const isTabLoading = tabsLoading[activeTabIndex];

                // 메인 패널이 "detail"(선택 시 표시)이면 좌측 미선택 시 안내 메시지
                const mainRelationType = componentConfig.rightPanel?.relation?.type || "detail";
                if (mainRelationType === "detail" && !selectedLeftItem && !isDesignMode) {
                  return (
                    <div className="text-muted-foreground flex h-full flex-col items-center justify-center gap-2 py-12 text-sm">
                      <p>좌측에서 항목을 선택하세요</p>
                      <p className="text-xs">선택한 항목의 관련 데이터가 여기에 표시됩니다</p>
                    </div>
                  );
                }

                if (isTabLoading) {
                  return (
                    <div className="flex h-32 items-center justify-center">
                      <Loader2 className="text-muted-foreground h-6 w-6 animate-spin" />
                    </div>
                  );
                }

                if (currentTabData.length === 0 && !isTabLoading) {
                  return (
                    <div className="text-muted-foreground flex h-full flex-col items-center justify-center gap-2 py-12 text-sm">
                      <p>관련 데이터가 없습니다.</p>
                    </div>
                  );
                }

                // 탭 컬럼 설정
                const tabColumns = currentTabConfig?.columns || [];

                // 테이블 모드로 표시 (행 클릭 시 상세 정보 펼치기)
                if (currentTabConfig?.displayMode === "table") {
                  const hasTabActions = currentTabConfig?.showEdit || currentTabConfig?.showDelete;
                  // showInSummary가 false가 아닌 것만 메인 테이블에 표시
                  const tabSummaryColumns = tabColumns.filter((col: any) => col.showInSummary !== false);
                  return (
                    <div className="h-full overflow-auto">
                      <table className="w-full text-sm">
                        <thead className="sticky top-0 z-10 bg-background">
                          <tr className="border-b-2 border-border/60">
                            {tabSummaryColumns.map((col: any) => (
                              <th key={col.name} className="text-muted-foreground px-3 py-2 text-left text-xs font-semibold">
                                {col.label || col.name}
                              </th>
                            ))}
                            {hasTabActions && (
                              <th className="text-muted-foreground px-3 py-2 text-right text-xs font-semibold">작업</th>
                            )}
                          </tr>
                        </thead>
                        <tbody>
                          {currentTabData.map((item: any, idx: number) => {
                            const tabItemId = item.id || item.ID || idx;
                            const isTabExpanded = expandedRightItems.has(`tab_${activeTabIndex}_${tabItemId}`);

                            // 상세 정보용 전체 값 목록 (showInDetail이 false가 아닌 것만)
                            const tabDetailColumns = tabColumns.filter((col: any) => col.showInDetail !== false);
                            const tabAllValues: [string, any, string][] = tabDetailColumns.length > 0
                              ? tabDetailColumns.map((col: any) => [col.name, getEntityJoinValue(item, col.name), col.label || col.name] as [string, any, string])
                              : Object.entries(item)
                                  .filter(([, v]) => v !== null && v !== undefined && v !== "")
                                  .map(([k, v]) => [k, v, ""] as [string, any, string]);

                            return (
                              <React.Fragment key={tabItemId}>
                                <tr
                                  className={cn(
                                    "cursor-pointer border-b border-border/40 transition-colors",
                                    isTabExpanded ? "bg-primary/5" : idx % 2 === 1 ? "bg-muted/10 hover:bg-muted/30" : "hover:bg-muted/30",
                                  )}
                                  onClick={() => toggleRightItemExpansion(`tab_${activeTabIndex}_${tabItemId}`)}
                                >
                                  {tabSummaryColumns.map((col: any) => (
                                    <td key={col.name} className="px-3 py-2 text-xs" style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                                      {col.type === "progress"
                                        ? renderProgressCell(col, item, selectedLeftItem)
                                        : formatCellValue(
                                            col.name,
                                            getEntityJoinValue(item, col.name),
                                            rightCategoryMappings,
                                            col.format,
                                          )}
                                    </td>
                                  ))}
                                  {hasTabActions && (
                                    <td className="px-3 py-2 text-right">
                                      <div className="flex items-center justify-end gap-1">
                                        {currentTabConfig?.showEdit && (
                                          <Button size="sm" variant="ghost" className="h-7 px-2 text-xs"
                                            onClick={(e) => {
                                              e.stopPropagation();
                                              handleEditClick("right", item);
                                            }}
                                          >
                                            <Pencil className="h-3 w-3" />
                                          </Button>
                                        )}
                                        {currentTabConfig?.showDelete && (
                                          <Button size="sm" variant="ghost" className="text-destructive h-7 px-2 text-xs"
                                            onClick={(e) => {
                                              e.stopPropagation();
                                              handleDeleteClick("right", item, currentTabConfig?.tableName);
                                            }}
                                          >
                                            <Trash2 className="h-3 w-3" />
                                          </Button>
                                        )}
                                      </div>
                                    </td>
                                  )}
                                </tr>
                                {/* 상세 정보 (행 클릭 시 펼쳐짐) */}
                                {isTabExpanded && (
                                  <tr>
                                    <td colSpan={tabSummaryColumns.length + (hasTabActions ? 1 : 0)} className="bg-muted/30 px-3 py-2">
                                      <div className="mb-1 text-xs font-semibold">상세 정보</div>
                                      <div className="bg-card overflow-auto rounded-md border">
                                        <table className="w-full text-sm">
                                          <tbody className="divide-border divide-y">
                                            {tabAllValues.map(([key, value, label]) => {
                                              const displayValue = (value === null || value === undefined || value === "")
                                                ? "-"
                                                : formatCellValue(key, value, rightCategoryMappings);
                                              return (
                                                <tr key={key} className="hover:bg-muted">
                                                  <td className="text-muted-foreground px-3 py-1.5 text-xs font-medium whitespace-nowrap">
                                                    {label || getColumnLabel(key)}
                                                  </td>
                                                  <td className="text-foreground px-3 py-1.5 text-xs break-all">{displayValue}</td>
                                                </tr>
                                              );
                                            })}
                                          </tbody>
                                        </table>
                                      </div>
                                    </td>
                                  </tr>
                                )}
                              </React.Fragment>
                            );
                          })}
                        </tbody>
                      </table>
                    </div>
                  );
                }

                // 리스트 모드도 테이블형으로 통일 (행 클릭 시 상세 정보 표시)
                {
                  const hasTabActions = currentTabConfig?.showEdit || currentTabConfig?.showDelete;
                  // showInSummary가 false가 아닌 것만 메인 테이블에 표시
                  const listSummaryColumns = tabColumns.filter((col: any) => col.showInSummary !== false);
                  return (
                    <div className="h-full overflow-auto">
                      <table className="w-full text-sm">
                        <thead className="sticky top-0 z-10 bg-background">
                          <tr className="border-b-2 border-border/60">
                            {listSummaryColumns.map((col: any) => (
                              <th key={col.name} className="text-muted-foreground px-3 py-2 text-left text-xs font-semibold">
                                {col.label || col.name}
                              </th>
                            ))}
                            {hasTabActions && (
                              <th className="text-muted-foreground px-3 py-2 text-right text-xs font-semibold">작업</th>
                            )}
                          </tr>
                        </thead>
                        <tbody>
                          {currentTabData.map((item: any, idx: number) => {
                            const tabItemId = item.id || item.ID || idx;
                            const isTabExpanded = expandedRightItems.has(`tab_${activeTabIndex}_${tabItemId}`);
                            // showInDetail이 false가 아닌 것만 상세에 표시
                            const listDetailColumns = tabColumns.filter((col: any) => col.showInDetail !== false);
                            const tabAllValues: [string, any, string][] = listDetailColumns.length > 0
                              ? listDetailColumns.map((col: any) => [col.name, getEntityJoinValue(item, col.name), col.label || col.name] as [string, any, string])
                              : Object.entries(item)
                                  .filter(([, v]) => v !== null && v !== undefined && v !== "")
                                  .map(([k, v]) => [k, v, ""] as [string, any, string]);

                            return (
                              <React.Fragment key={tabItemId}>
                                <tr
                                  className={cn(
                                    "cursor-pointer border-b border-border/40 transition-colors",
                                    isTabExpanded ? "bg-primary/5" : idx % 2 === 1 ? "bg-muted/10 hover:bg-muted/30" : "hover:bg-muted/30",
                                  )}
                                  onClick={() => toggleRightItemExpansion(`tab_${activeTabIndex}_${tabItemId}`)}
                                >
                                  {listSummaryColumns.map((col: any) => (
                                    <td key={col.name} className="px-3 py-2 text-xs" style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                                      {col.type === "progress"
                                        ? renderProgressCell(col, item, selectedLeftItem)
                                        : formatCellValue(
                                            col.name,
                                            getEntityJoinValue(item, col.name),
                                            rightCategoryMappings,
                                            col.format,
                                          )}
                                    </td>
                                  ))}
                                  {hasTabActions && (
                                    <td className="px-3 py-2 text-right">
                                      <div className="flex items-center justify-end gap-1">
                                        {currentTabConfig?.showEdit && (
                                          <Button size="sm" variant="ghost" className="h-7 px-2 text-xs"
                                            onClick={(e) => { e.stopPropagation(); handleEditClick("right", item); }}
                                          >
                                            <Pencil className="h-3 w-3" />
                                          </Button>
                                        )}
                                        {currentTabConfig?.showDelete && (
                                          <Button size="sm" variant="ghost" className="text-destructive h-7 px-2 text-xs"
                                            onClick={(e) => { e.stopPropagation(); handleDeleteClick("right", item, currentTabConfig?.tableName); }}
                                          >
                                            <Trash2 className="h-3 w-3" />
                                          </Button>
                                        )}
                                      </div>
                                    </td>
                                  )}
                                </tr>
                                {isTabExpanded && (
                                  <tr>
                                    <td colSpan={listSummaryColumns.length + (hasTabActions ? 1 : 0)} className="bg-muted/30 px-3 py-2">
                                      <div className="mb-1 text-xs font-semibold">상세 정보</div>
                                      <div className="bg-card overflow-auto rounded-md border">
                                        <table className="w-full text-sm">
                                          <tbody className="divide-border divide-y">
                                            {tabAllValues.map(([key, value, label]) => {
                                              const displayValue = (value === null || value === undefined || value === "")
                                                ? "-" : formatCellValue(key, value, rightCategoryMappings);
                                              return (
                                                <tr key={key} className="hover:bg-muted">
                                                  <td className="text-muted-foreground px-3 py-1.5 text-xs font-medium whitespace-nowrap">
                                                    {label || getColumnLabel(key)}
                                                  </td>
                                                  <td className="text-foreground px-3 py-1.5 text-xs break-all">{displayValue}</td>
                                                </tr>
                                              );
                                            })}
                                          </tbody>
                                        </table>
                                      </div>
                                    </td>
                                  </tr>
                                )}
                              </React.Fragment>
                            );
                          })}
                        </tbody>
                      </table>
                    </div>
                  );
                }
              })()
            ) : componentConfig.rightPanel?.displayMode === "custom" ? (
              // 커스텀 모드: alwaysShow가 아닌 경우에만 좌측 선택 필요
              !isDesignMode && !selectedLeftItem && !componentConfig.rightPanel?.alwaysShow ? (
                <div className="flex h-full items-center justify-center">
                  <div className="text-muted-foreground text-center text-sm">
                    <p className="mb-2">좌측에서 항목을 선택하세요</p>
                    <p className="text-xs">선택한 항목의 상세 정보가 여기에 표시됩니다</p>
                  </div>
                </div>
              ) : (
              <div 
                className="relative h-full w-full"
                data-split-panel-container="true"
                data-component-id={component.id}
                data-panel-side="right"
              >
                {/* 커스텀 모드: 디자인/실행 모드 분기 렌더링 */}
                {componentConfig.rightPanel?.components && componentConfig.rightPanel.components.length > 0 ? (
                  !isDesignMode ? (
                    <ScaledCustomPanel
                      components={componentConfig.rightPanel!.components}
                      formData={customLeftSelectedData}
                      onFormDataChange={(fieldName: string, value: any) => {
                        setCustomLeftSelectedData((prev: Record<string, any>) => ({ ...prev, [fieldName]: value }));
                      }}
                      tableName={componentConfig.rightPanel?.tableName || componentConfig.leftPanel?.tableName}
                      menuObjid={(props as any).menuObjid}
                      screenId={(props as any).screenId}
                      userId={(props as any).userId}
                      userName={(props as any).userName}
                      companyCode={companyCode}
                      allComponents={(props as any).allComponents}
                      selectedRowsData={localSelectedRowsData}
                      onSelectedRowsChange={handleLocalSelectedRowsChange}
                    />
                  ) : (
                  <div className="relative h-full w-full" style={{ minHeight: "100%", minWidth: "100%" }}>
                    {componentConfig.rightPanel.components.map((comp: PanelInlineComponent) => {
                      const isSelectedComp = selectedPanelComponentId === comp.id;
                      const isDraggingComp = draggingCompId === comp.id;
                      const isResizingComp = resizingCompId === comp.id;
                      
                      const displayX = isDraggingComp && dragPosition ? dragPosition.x : (comp.position?.x || 0);
                      const displayY = isDraggingComp && dragPosition ? dragPosition.y : (comp.position?.y || 0);
                      const displayWidth = isResizingComp && resizeSize ? resizeSize.width : (comp.size?.width || 200);
                      const displayHeight = isResizingComp && resizeSize ? resizeSize.height : (comp.size?.height || 100);
                      
                      const componentData = {
                        id: comp.id,
                        type: "component" as const,
                        componentType: comp.componentType,
                        label: comp.label,
                        position: comp.position || { x: 0, y: 0 },
                        size: { width: displayWidth, height: displayHeight },
                        componentConfig: comp.componentConfig || {},
                        style: comp.style || {},
                        tableName: comp.componentConfig?.tableName,
                        columnName: comp.componentConfig?.columnName,
                        webType: comp.componentConfig?.webType,
                        inputType: (comp as any).inputType || comp.componentConfig?.inputType,
                      };

                      return (
                          <div
                            key={comp.id}
                            data-panel-comp-id={comp.id}
                            className="absolute"
                            style={{
                              left: displayX,
                              top: displayY,
                              zIndex: isDraggingComp ? 100 : isSelectedComp ? 10 : 1,
                            }}
                            onClick={(e) => {
                              e.stopPropagation();
                              if (comp.componentType !== "v2-tabs-widget") {
                                setNestedTabSelectedCompId(undefined);
                              }
                              setInternalSelectedCompId(comp.id);
                              onSelectPanelComponent?.("right", comp.id, comp);
                            }}
                          >
                            <div
                              className={cn(
                                "flex h-4 cursor-move items-center justify-between rounded-t border border-b-0 bg-muted px-1",
                                isSelectedComp ? "border-primary" : "border-border"
                              )}
                              style={{ width: displayWidth }}
                              onMouseDown={(e) => handlePanelDragStart(e, "right", comp)}
                            >
                              <div className="flex items-center gap-0.5">
                                <Move className="h-2.5 w-2.5 text-muted-foreground/70" />
                                <span className="max-w-[100px] truncate text-[9px] text-muted-foreground">
                                  {comp.label || comp.componentType}
                                </span>
                              </div>
                              <div className="flex items-center">
                                <button
                                  className="rounded p-0.5 hover:bg-muted/80"
                                  onClick={(e) => {
                                    e.stopPropagation();
                                    onSelectPanelComponent?.("right", comp.id, comp);
                                  }}
                                  title="설정"
                                >
                                  <Settings className="h-2.5 w-2.5 text-muted-foreground" />
                                </button>
                                <button
                                  className="rounded p-0.5 hover:bg-destructive/10"
                                  onClick={(e) => {
                                    e.stopPropagation();
                                    handleRemovePanelComponent("right", comp.id);
                                  }}
                                  title="삭제"
                                >
                                  <Trash2 className="h-2.5 w-2.5 text-destructive" />
                                </button>
                              </div>
                            </div>

                            <div 
                              className={cn(
                                "relative overflow-hidden rounded-b border bg-white shadow-sm",
                                isSelectedComp
                                  ? "border-primary ring-2 ring-primary/30"
                                  : "border-border",
                                (isDraggingComp || isResizingComp) && "opacity-80 shadow-lg",
                                !(isDraggingComp || isResizingComp) && "transition-all"
                              )}
                              style={{
                                width: displayWidth,
                                height: displayHeight,
                              }}
                            >
                              <div className={cn(
                                "h-full w-full",
                                (comp.componentType === "v2-tabs-widget" || 
                                 comp.componentType === "tabs-widget" ||
                                 comp.componentType === "v2-split-panel-layout" ||
                                 comp.componentType === "split-panel-layout")
                                  ? "" 
                                  : "pointer-events-none"
                              )}>
                                <DynamicComponentRenderer
                                  component={componentData as any}
                                  isDesignMode={true}
                                  formData={{}}
                                  onUpdateComponent={(updatedComp: any) => {
                                    handleNestedComponentUpdate("right", comp.id, updatedComp);
                                  }}
                                  onSelectTabComponent={(tabId: string, compId: string, tabComp: any) => {
                                    setNestedTabSelectedCompId(compId);
                                    const event = new CustomEvent("nested-tab-component-select", {
                                      detail: {
                                        tabsComponentId: comp.id,
                                        tabId,
                                        componentId: compId,
                                        component: tabComp,
                                        parentSplitPanelId: component.id,
                                        parentPanelSide: "right",
                                      },
                                    });
                                    window.dispatchEvent(event);
                                  }}
                                  selectedTabComponentId={nestedTabSelectedCompId}
                                />
                              </div>
                              
                              {isSelectedComp && (
                                <>
                                  <div
                                    className="pointer-events-auto absolute right-0 top-0 z-10 h-full w-2 cursor-ew-resize hover:bg-primary/10"
                                    onMouseDown={(e) => handlePanelResizeStart(e, "right", comp, "e")}
                                  />
                                  <div
                                    className="pointer-events-auto absolute bottom-0 left-0 z-10 h-2 w-full cursor-ns-resize hover:bg-primary/10"
                                    onMouseDown={(e) => handlePanelResizeStart(e, "right", comp, "s")}
                                  />
                                  <div
                                    className="pointer-events-auto absolute bottom-0 right-0 z-20 h-3 w-3 cursor-nwse-resize hover:bg-primary/20"
                                    onMouseDown={(e) => handlePanelResizeStart(e, "right", comp, "se")}
                                  />
                                </>
                              )}
                            </div>
                          </div>
                        );
                    })}
                  </div>
                  )
                ) : (
                  // 컴포넌트가 없을 때 드롭 영역 표시
                  <div className="flex h-full w-full flex-col items-center justify-center rounded border-2 border-dashed border-input bg-muted/50">
                    <Plus className="mb-2 h-8 w-8 text-muted-foreground/70" />
                    <p className="text-sm font-medium text-muted-foreground">
                      커스텀 모드
                    </p>
                    <p className="mt-1 text-xs text-muted-foreground/70">
                      {isDesignMode ? "컴포넌트를 드래그하여 배치하세요" : "배치된 컴포넌트가 없습니다"}
                    </p>
                  </div>
                )}
              </div>
              )
            ) : isLoadingRight ? (
              // 로딩 중
              <div className="flex h-full items-center justify-center">
                <div className="text-center">
                  <Loader2 className="text-primary mx-auto h-8 w-8 animate-spin" />
                  <p className="text-muted-foreground mt-2 text-sm">데이터를 불러오는 중...</p>
                </div>
              </div>
            ) : rightData ? (
              // 실제 데이터 표시
              Array.isArray(rightData) ? (
                // 조인 모드: 여러 데이터를 테이블/리스트로 표시
                (() => {
                  // 검색 필터링
                  const filteredData = rightSearchQuery
                    ? rightData.filter((item) => {
                        const searchLower = rightSearchQuery.toLowerCase();
                        return Object.entries(item).some(([key, value]) => {
                          if (value === null || value === undefined) return false;
                          return String(value).toLowerCase().includes(searchLower);
                        });
                      })
                    : rightData;

                  // 테이블 모드 체크
                  const isTableMode = componentConfig.rightPanel?.displayMode === "table";

                  if (isTableMode) {
                    // 테이블 모드 렌더링
                    const displayColumns = componentConfig.rightPanel?.columns || [];

                    // 🆕 그룹 합산 모드일 때: 복합키 컬럼을 우선 표시
                    const relationKeys = componentConfig.rightPanel?.relation?.keys || [];
                    const keyColumns = relationKeys.map((k: any) => k.leftColumn).filter(Boolean);
                    const isGroupedMode = selectedLeftItem?._originalItems?.length > 0;

                    let columnsToShow: any[] = [];

                    if (displayColumns.length > 0) {
                      // 설정된 컬럼 사용 (showInSummary가 false가 아닌 것만 테이블에 표시)
                      columnsToShow = displayColumns
                        .filter((col) => col.showInSummary !== false)
                        .map((col) => ({
                          ...col,
                          label: rightColumnLabels[col.name] || col.label || col.name,
                          format: col.format,
                        }));

                      // 🆕 그룹 합산 모드이고, 키 컬럼이 표시 목록에 없으면 맨 앞에 추가
                      if (isGroupedMode && keyColumns.length > 0) {
                        const existingColNames = columnsToShow.map((c) => c.name);
                        const missingKeyColumns = keyColumns.filter((k: string) => !existingColNames.includes(k));

                        if (missingKeyColumns.length > 0) {
                          const keyColsToAdd = missingKeyColumns.map((colName: string) => ({
                            name: colName,
                            label: rightColumnLabels[colName] || colName,
                            width: 120,
                            align: "left" as const,
                            format: undefined,
                            _isKeyColumn: true, // 구분용 플래그
                          }));
                          columnsToShow = [...keyColsToAdd, ...columnsToShow];
                          console.log("🔗 [우측패널] 그룹모드 - 키 컬럼 추가:", missingKeyColumns);
                        }
                      }
                    } else {
                      // 기본 컬럼 자동 생성
                      columnsToShow = Object.keys(filteredData[0] || {})
                        .filter((key) => shouldShowField(key))
                        .slice(0, 5)
                        .map((key) => ({
                          name: key,
                          label: rightColumnLabels[key] || key,
                          width: 150,
                          align: "left" as const,
                          format: undefined,
                        }));
                    }

                    // 컬럼 너비 합계 계산 (작업 컬럼 제외, 100% 초과 시 스크롤)
                    const rightTotalColWidth = columnsToShow.reduce((sum, col) => {
                      const w = col.width && col.width <= 100 ? col.width : 0;
                      return sum + w;
                    }, 0);

                    return (
                      <div className="flex h-full w-full flex-col">
                        <div className="min-h-0 flex-1 overflow-auto">
                          <table className="table-fixed" style={{ width: rightTotalColWidth > 100 ? `${rightTotalColWidth}%` : '100%' }}>
                            <thead className="sticky top-0 z-10">
                              <tr className="border-b-2 border-border/60">
                                {columnsToShow.map((col, idx) => (
                                  <th
                                    key={idx}
                                    className="text-muted-foreground px-3 py-2 text-left text-xs font-semibold whitespace-nowrap"
                                    style={{
                                      width: col.width && col.width <= 100 ? `${col.width}%` : "auto",
                                      textAlign: col.align || "left",
                                    }}
                                  >
                                    {col.label}
                                  </th>
                                ))}
                                {/* 수정 또는 삭제 버튼이 하나라도 활성화되어 있을 때만 작업 컬럼 표시 */}
                                {!isDesignMode &&
                                  ((componentConfig.rightPanel?.editButton?.enabled ?? true) ||
                                    (componentConfig.rightPanel?.deleteButton?.enabled ?? true)) && (
                                    <th className="text-muted-foreground px-3 py-2 text-right text-xs font-semibold" style={{ width: '80px' }}>
                                      작업
                                    </th>
                                  )}
                              </tr>
                            </thead>
                            <tbody>
                              {filteredData.map((item, idx) => {
                                const itemId = item.id || item.ID || idx;

                                return (
                                  <tr key={itemId} className={cn("border-b border-border/40 transition-colors hover:bg-muted/30", idx % 2 === 1 && "bg-muted/10")}>
                                    {columnsToShow.map((col, colIdx) => (
                                      <td
                                        key={colIdx}
                                        className="px-3 py-2 text-xs"
                                        style={{ textAlign: col.align || "left", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}
                                      >
                                        {col.type === "progress"
                                          ? renderProgressCell(col, item, selectedLeftItem)
                                          : formatCellValue(
                                              col.name,
                                              getEntityJoinValue(item, col.name),
                                              rightCategoryMappings,
                                              col.format,
                                            )}
                                      </td>
                                    ))}
                                    {/* 수정 또는 삭제 버튼이 하나라도 활성화되어 있을 때만 작업 셀 표시 */}
                                    {!isDesignMode &&
                                      ((componentConfig.rightPanel?.editButton?.enabled ?? true) ||
                                        (componentConfig.rightPanel?.deleteButton?.enabled ?? true)) && (
                                        <td className="px-3 py-2 text-right text-sm whitespace-nowrap">
                                          <div className="flex justify-end gap-1">
                                            {(componentConfig.rightPanel?.editButton?.enabled ?? true) && (
                                              <Button
                                                variant={
                                                  componentConfig.rightPanel?.editButton?.buttonVariant || "outline"
                                                }
                                                size="sm"
                                                onClick={(e) => {
                                                  e.stopPropagation();
                                                  handleEditClick("right", item);
                                                }}
                                                className="h-7"
                                              >
                                                <Pencil className="mr-1 h-3 w-3" />
                                                {componentConfig.rightPanel?.editButton?.buttonLabel || "수정"}
                                              </Button>
                                            )}
                                            {(componentConfig.rightPanel?.deleteButton?.enabled ?? true) && (
                                              <button
                                                onClick={(e) => {
                                                  e.stopPropagation();
                                                  handleDeleteClick("right", item);
                                                }}
                                                className="rounded p-1 transition-colors hover:bg-destructive/10"
                                                title={componentConfig.rightPanel?.deleteButton?.buttonLabel || "삭제"}
                                              >
                                                <Trash2 className="h-4 w-4 text-destructive" />
                                              </button>
                                            )}
                                          </div>
                                        </td>
                                      )}
                                  </tr>
                                );
                              })}
                            </tbody>
                          </table>
                        </div>
                      </div>
                    );
                  }

                  // 목록 모드 - 테이블형 디자인 (행 클릭 시 상세 정보 표시)
                  {
                    // 표시 컬럼 결정
                    const rightColumns = componentConfig.rightPanel?.columns;
                    let columnsToDisplay: { name: string; label: string; format?: string; bold?: boolean; width?: number }[] = [];

                    if (rightColumns && rightColumns.length > 0) {
                      // showInSummary가 false가 아닌 것만 메인 테이블에 표시
                      columnsToDisplay = rightColumns
                        .filter((col) => col.showInSummary !== false)
                        .map((col) => ({
                          name: col.name,
                          label: rightColumnLabels[col.name] || col.label || col.name,
                          format: col.format,
                          bold: col.bold,
                          width: col.width,
                        }));
                    } else if (filteredData.length > 0) {
                      columnsToDisplay = Object.keys(filteredData[0])
                        .filter((key) => shouldShowField(key))
                        .slice(0, 6)
                        .map((key) => ({
                          name: key,
                          label: rightColumnLabels[key] || key,
                        }));
                    }

                    // 컬럼 너비 합계 계산 (작업 컬럼 제외, 100% 초과 시 스크롤)
                    const displayTotalColWidth = columnsToDisplay.reduce((sum, col) => {
                      const w = col.width && col.width <= 100 ? col.width : 0;
                      return sum + w;
                    }, 0);

                    const hasEditButton = !isDesignMode && (componentConfig.rightPanel?.editButton?.enabled ?? true);
                    const hasDeleteButton = !isDesignMode && (componentConfig.rightPanel?.deleteButton?.enabled ?? true);
                    const hasActions = hasEditButton || hasDeleteButton;

                    return filteredData.length > 0 ? (
                      <div className="flex h-full w-full flex-col">
                        <div className="min-h-0 flex-1 overflow-auto">
                          <table className="table-fixed text-sm" style={{ width: displayTotalColWidth > 100 ? `${displayTotalColWidth}%` : '100%' }}>
                            <thead className="sticky top-0 z-10 bg-background">
                              <tr className="border-b-2 border-border/60">
                                {columnsToDisplay.map((col) => (
                                  <th
                                    key={col.name}
                                    className="text-muted-foreground px-3 py-2 text-left text-xs font-semibold whitespace-nowrap"
                                    style={{ width: col.width && col.width <= 100 ? `${col.width}%` : "auto" }}
                                  >
                                    {col.label}
                                  </th>
                                ))}
                                {hasActions && (
                                  <th className="text-muted-foreground px-3 py-2 text-right text-xs font-semibold" style={{ width: '80px' }}>작업</th>
                                )}
                              </tr>
                            </thead>
                            <tbody>
                              {filteredData.map((item, idx) => {
                                const itemId = item.id || item.ID || idx;
                                const isExpanded = expandedRightItems.has(itemId);

                                // 상세 정보용 전체 값 목록 (showInDetail이 false가 아닌 것만 표시)
                                let allValues: [string, any, string][] = [];
                                if (rightColumns && rightColumns.length > 0) {
                                  allValues = rightColumns
                                    .filter((col) => col.showInDetail !== false)
                                    .map((col) => {
                                      const value = getEntityJoinValue(item, col.name);
                                      return [col.name, value, col.label] as [string, any, string];
                                    });
                                } else {
                                  allValues = Object.entries(item)
                                    .filter(([, value]) => value !== null && value !== undefined && value !== "")
                                    .map(([key, value]) => [key, value, ""] as [string, any, string]);
                                }

                                return (
                                  <React.Fragment key={itemId}>
                                    <tr
                                      className={cn(
                                        "cursor-pointer border-b border-border/40 transition-colors",
                                        isExpanded ? "bg-primary/5" : idx % 2 === 1 ? "bg-muted/10 hover:bg-muted/30" : "hover:bg-muted/30",
                                      )}
                                      onClick={() => toggleRightItemExpansion(itemId)}
                                    >
                                      {columnsToDisplay.map((col) => (
                                        <td key={col.name} className="px-3 py-2 text-xs" style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                                          {formatCellValue(
                                            col.name,
                                            getEntityJoinValue(item, col.name),
                                            rightCategoryMappings,
                                            col.format,
                                          )}
                                        </td>
                                      ))}
                                      {hasActions && (
                                        <td className="px-3 py-2 text-right">
                                          <div className="flex items-center justify-end gap-1">
                                            {hasEditButton && (
                                              <Button size="sm" variant="ghost" className="h-7 px-2 text-xs"
                                                onClick={(e) => {
                                                  e.stopPropagation();
                                                  handleEditClick("right", item);
                                                }}
                                              >
                                                <Pencil className="h-3 w-3" />
                                              </Button>
                                            )}
                                            {hasDeleteButton && (
                                              <Button size="sm" variant="ghost" className="text-destructive h-7 px-2 text-xs"
                                                onClick={(e) => {
                                                  e.stopPropagation();
                                                  handleDeleteClick("right", item);
                                                }}
                                              >
                                                <Trash2 className="h-3 w-3" />
                                              </Button>
                                            )}
                                          </div>
                                        </td>
                                      )}
                                    </tr>
                                    {/* 상세 정보 (행 클릭 시 펼쳐짐) */}
                                    {isExpanded && (
                                      <tr>
                                        <td colSpan={columnsToDisplay.length + (hasActions ? 1 : 0)} className="bg-muted/30 px-3 py-2">
                                          <div className="mb-1 text-xs font-semibold">상세 정보</div>
                                          <div className="bg-card overflow-auto rounded-md border">
                                            <table className="w-full text-sm">
                                              <tbody className="divide-border divide-y">
                                                {allValues.map(([key, value, label]) => {
                                                  const colConfig = rightColumns?.find((c) => c.name === key);
                                                  const format = colConfig?.format;
                                                  const displayValue = (value === null || value === undefined || value === "")
                                                    ? "-"
                                                    : formatCellValue(key, value, rightCategoryMappings, format);
                                                  return (
                                                    <tr key={key} className="hover:bg-muted">
                                                      <td className="text-muted-foreground px-3 py-1.5 text-xs font-medium whitespace-nowrap">
                                                        {label || getColumnLabel(key)}
                                                      </td>
                                                      <td className="text-foreground px-3 py-1.5 text-xs break-all">{displayValue}</td>
                                                    </tr>
                                                  );
                                                })}
                                              </tbody>
                                            </table>
                                          </div>
                                        </td>
                                      </tr>
                                    )}
                                  </React.Fragment>
                                );
                              })}
                            </tbody>
                          </table>
                        </div>
                      </div>
                    ) : (
                    <div className="text-muted-foreground py-8 text-center text-sm">
                      {rightSearchQuery ? (
                        <>
                          <p>검색 결과가 없습니다.</p>
                          <p className="text-muted-foreground/70 mt-1 text-xs">다른 검색어를 입력해보세요.</p>
                        </>
                      ) : (
                        "관련 데이터가 없습니다."
                      )}
                    </div>
                  );
                  }
                })()
              ) : (
                // 상세 모드: 단일 객체를 상세 정보로 표시
                (() => {
                  const rightColumns = componentConfig.rightPanel?.columns;
                  let displayEntries: [string, any, string][] = [];

                  if (rightColumns && rightColumns.length > 0) {
                    console.log("🔍 [디버깅] 상세 모드 표시 로직:");
                    console.log("  📋 rightData 전체:", rightData);
                    console.log("  📋 rightData keys:", Object.keys(rightData));
                    console.log(
                      "  ⚙️ 설정된 컬럼:",
                      rightColumns.map((c) => `${c.name} (${c.label})`),
                    );

                    // 설정된 컬럼만 표시 (showInDetail이 false가 아닌 것만)
                    displayEntries = rightColumns
                      .filter((col) => col.showInDetail !== false)
                      .map((col) => {
                        // 🆕 엔티티 조인 컬럼 처리 (예: item_info.item_name → item_name)
                        let value = rightData[col.name];
                        console.log(`  🔎 컬럼 "${col.name}": 직접 접근 = ${value}`);

                        if (value === undefined && col.name.includes(".")) {
                          const columnName = col.name.split(".").pop();
                          value = rightData[columnName || ""];
                          console.log(`    → 변환 후 "${columnName}" 접근 = ${value}`);
                        }

                        return [col.name, value, col.label] as [string, any, string];
                      })
; // 설정된 컬럼은 null/empty여도 항상 표시

                    console.log("  ✅ 최종 표시할 항목:", displayEntries.length, "개");
                  } else {
                    // 설정 없으면 모든 컬럼 표시
                    displayEntries = Object.entries(rightData)
                      .filter(([_, value]) => value !== null && value !== undefined && value !== "")
                      .map(([key, value]) => [key, value, ""] as [string, any, string]);
                    console.log("  ⚠️ 컬럼 설정 없음, 모든 컬럼 표시");
                  }

                  return (
                    <div className="space-y-2">
                      {displayEntries.map(([key, value, label]) => (
                        <div key={key} className="bg-card rounded-lg border p-4 shadow-sm">
                          <div className="text-muted-foreground mb-1 text-xs font-semibold tracking-wide uppercase">
                            {label || getColumnLabel(key)}
                          </div>
                          <div className="text-sm">
                            {(value === null || value === undefined || value === "") ? <span className="text-muted-foreground">-</span> : String(value)}
                          </div>
                        </div>
                      ))}
                    </div>
                  );
                })()
              )
            ) : selectedLeftItem && isDesignMode ? (
              // 디자인 모드: 샘플 데이터
              <div className="space-y-4">
                <div className="rounded-lg border p-4">
                  <h3 className="mb-2 font-medium">{selectedLeftItem.name} 상세 정보</h3>
                  <div className="space-y-2 text-sm">
                    <div className="flex justify-between">
                      <span className="text-muted-foreground">항목 1:</span>
                      <span className="font-medium">값 1</span>
                    </div>
                    <div className="flex justify-between">
                      <span className="text-muted-foreground">항목 2:</span>
                      <span className="font-medium">값 2</span>
                    </div>
                    <div className="flex justify-between">
                      <span className="text-muted-foreground">항목 3:</span>
                      <span className="font-medium">값 3</span>
                    </div>
                  </div>
                </div>
              </div>
            ) : (
              // 데이터 없음 또는 초기 로딩 대기
              <div className="flex h-full items-center justify-center">
                <div className="text-muted-foreground text-center text-sm">
                  {componentConfig.rightPanel?.relation?.type === "join" ? (
                    <>
                      <Loader2 className="text-muted-foreground mx-auto h-6 w-6 animate-spin" />
                      <p className="mt-2">데이터를 불러오는 중...</p>
                    </>
                  ) : (
                    <>
                      <p className="mb-2">좌측에서 항목을 선택하세요</p>
                      <p className="text-xs">선택한 항목의 관련 데이터가 여기에 표시됩니다</p>
                    </>
                  )}
                </div>
              </div>
            )}
          </CardContent>
        </Card>
      </div>

      {/* 추가 모달 */}
      <Dialog open={showAddModal} onOpenChange={setShowAddModal}>
        <DialogContent className="max-w-[95vw] sm:max-w-[500px]">
          <DialogHeader>
            <DialogTitle className="text-base sm:text-lg">
              {addModalPanel === "left"
                ? `${componentConfig.leftPanel?.title} 추가`
                : addModalPanel === "right"
                  ? `${componentConfig.rightPanel?.title} 추가`
                  : `하위 ${componentConfig.leftPanel?.title} 추가`}
            </DialogTitle>
            <DialogDescription className="text-xs sm:text-sm">
              {addModalPanel === "left-item"
                ? "선택한 항목의 하위 항목을 추가합니다. 필수 항목을 입력해주세요."
                : "새로운 데이터를 추가합니다. 필수 항목을 입력해주세요."}
            </DialogDescription>
          </DialogHeader>

          <div className="space-y-3 sm:space-y-4">
            {(() => {
              // 어떤 컬럼들을 표시할지 결정
              let modalColumns: Array<{ name: string; label: string; required?: boolean }> | undefined;

              if (addModalPanel === "left") {
                modalColumns = componentConfig.leftPanel?.addModalColumns;
              } else if (addModalPanel === "right") {
                modalColumns = componentConfig.rightPanel?.addModalColumns;
              } else if (addModalPanel === "left-item") {
                modalColumns = componentConfig.leftPanel?.itemAddConfig?.addModalColumns;
              }

              return modalColumns?.map((col, index) => {
                // 항목별 추가 버튼으로 열렸을 때, parentColumn은 미리 채워져 있고 수정 불가
                const isItemAddPreFilled =
                  addModalPanel === "left-item" &&
                  componentConfig.leftPanel?.itemAddConfig?.parentColumn === col.name &&
                  addModalFormData[col.name];

                // 우측 패널 추가 시, 조인 컬럼(rightColumn)은 미리 채워져 있고 수정 불가
                const isRightJoinPreFilled =
                  addModalPanel === "right" &&
                  componentConfig.rightPanel?.rightColumn === col.name &&
                  addModalFormData[col.name];

                const isPreFilled = isItemAddPreFilled || isRightJoinPreFilled;

                return (
                  <div key={index}>
                    <Label htmlFor={col.name} className="text-xs sm:text-sm">
                      {col.label} {col.required && <span className="text-destructive">*</span>}
                      {isPreFilled && <span className="ml-2 text-[10px] text-primary">(자동 설정됨)</span>}
                    </Label>
                    <Input
                      id={col.name}
                      value={addModalFormData[col.name] || ""}
                      onChange={(e) => {
                        setAddModalFormData((prev) => ({
                          ...prev,
                          [col.name]: e.target.value,
                        }));
                      }}
                      placeholder={`${col.label} 입력`}
                      className="h-8 text-xs sm:h-10 sm:text-sm"
                      required={col.required}
                      disabled={isPreFilled}
                    />
                  </div>
                );
              });
            })()}
          </div>

          <DialogFooter className="gap-2 sm:gap-0">
            <Button
              variant="outline"
              onClick={() => setShowAddModal(false)}
              className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
            >
              취소
            </Button>
            <Button onClick={handleAddModalSave} className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm">
              <Save className="mr-1 h-3 w-3 sm:h-4 sm:w-4" />
              저장
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* 수정 모달 */}
      <Dialog open={showEditModal} onOpenChange={setShowEditModal}>
        <DialogContent className="max-w-[95vw] sm:max-w-[500px]">
          <DialogHeader>
            <DialogTitle className="text-base sm:text-lg">
              {editModalPanel === "left"
                ? `${componentConfig.leftPanel?.title} 수정`
                : `${componentConfig.rightPanel?.title} 수정`}
            </DialogTitle>
            <DialogDescription className="text-xs sm:text-sm">
              데이터를 수정합니다. 필요한 항목을 변경해주세요.
            </DialogDescription>
          </DialogHeader>

          <div className="space-y-3 sm:space-y-4">
            {editModalItem &&
              (() => {
                // 좌측 패널 수정: leftColumn만 수정 가능
                if (editModalPanel === "left") {
                  const leftColumn = componentConfig.rightPanel?.relation?.leftColumn;

                  // leftColumn만 표시
                  if (!leftColumn || editModalFormData[leftColumn] === undefined) {
                    return <p className="text-muted-foreground text-sm">수정 가능한 컬럼이 없습니다.</p>;
                  }

                  return (
                    <div>
                      <Label htmlFor={`edit-${leftColumn}`} className="text-xs sm:text-sm">
                        {leftColumn}
                      </Label>
                      <Input
                        id={`edit-${leftColumn}`}
                        value={editModalFormData[leftColumn] || ""}
                        onChange={(e) => {
                          setEditModalFormData((prev) => ({
                            ...prev,
                            [leftColumn]: e.target.value,
                          }));
                        }}
                        placeholder={`${leftColumn} 입력`}
                        className="h-8 text-xs sm:h-10 sm:text-sm"
                      />
                    </div>
                  );
                }

                // 우측 패널 수정: 우측 패널에 설정된 표시 컬럼들만
                if (editModalPanel === "right") {
                  const rightColumns = componentConfig.rightPanel?.columns;

                  if (rightColumns && rightColumns.length > 0) {
                    // 설정된 컬럼만 표시
                    return rightColumns.map((col) => (
                      <div key={col.name}>
                        <Label htmlFor={`edit-${col.name}`} className="text-xs sm:text-sm">
                          {col.label || col.name}
                        </Label>
                        <Input
                          id={`edit-${col.name}`}
                          value={editModalFormData[col.name] || ""}
                          onChange={(e) => {
                            setEditModalFormData((prev) => ({
                              ...prev,
                              [col.name]: e.target.value,
                            }));
                          }}
                          placeholder={`${col.label || col.name} 입력`}
                          className="h-8 text-xs sm:h-10 sm:text-sm"
                        />
                      </div>
                    ));
                  } else {
                    // 설정이 없으면 모든 컬럼 표시 (민감한 필드 제외)
                    return Object.entries(editModalFormData)
                      .filter(([key]) => shouldShowField(key))
                      .map(([key, value]) => (
                        <div key={key}>
                          <Label htmlFor={`edit-${key}`} className="text-xs sm:text-sm">
                            {key}
                          </Label>
                          <Input
                            id={`edit-${key}`}
                            value={editModalFormData[key] || ""}
                            onChange={(e) => {
                              setEditModalFormData((prev) => ({
                                ...prev,
                                [key]: e.target.value,
                              }));
                            }}
                            placeholder={`${key} 입력`}
                            className="h-8 text-xs sm:h-10 sm:text-sm"
                          />
                        </div>
                      ));
                  }
                }

                return null;
              })()}
          </div>

          <DialogFooter className="gap-2 sm:gap-0">
            <Button
              variant="outline"
              onClick={() => setShowEditModal(false)}
              className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
            >
              취소
            </Button>
            <Button onClick={handleEditModalSave} className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm">
              <Save className="mr-1 h-3 w-3 sm:h-4 sm:w-4" />
              저장
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {/* 삭제 확인 모달 */}
      <Dialog open={showDeleteModal} onOpenChange={setShowDeleteModal}>
        <DialogContent className="max-w-[95vw] sm:max-w-[500px]">
          <DialogHeader>
            <DialogTitle className="text-base sm:text-lg">삭제 확인</DialogTitle>
            <DialogDescription className="text-xs sm:text-sm">
              정말로 이 데이터를 삭제하시겠습니까?
              <br />이 작업은 되돌릴 수 없습니다.
            </DialogDescription>
          </DialogHeader>

          <DialogFooter className="gap-2 sm:gap-0">
            <Button
              variant="outline"
              onClick={() => setShowDeleteModal(false)}
              className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
            >
              취소
            </Button>
            <Button
              variant="destructive"
              onClick={handleDeleteConfirm}
              className="h-8 flex-1 text-xs sm:h-10 sm:flex-none sm:text-sm"
            >
              <Trash2 className="mr-1 h-3 w-3 sm:h-4 sm:w-4" />
              삭제
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {(componentConfig.leftPanel as any)?.showBomExcelUpload && (
        <BomExcelUploadModal
          open={bomExcelUploadOpen}
          onOpenChange={setBomExcelUploadOpen}
          onSuccess={() => {
            loadLeftData();
          }}
        />
      )}

    </div>
  );
};

/**
 * SplitPanelLayout 래퍼 컴포넌트
 */
export const SplitPanelLayoutWrapper: React.FC<SplitPanelLayoutComponentProps> = (props) => {
  return <SplitPanelLayoutComponent {...props} />;
};
