"use client"; import React, { useState, useEffect } from "react"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Checkbox } from "@/components/ui/checkbox"; import { Switch } from "@/components/ui/switch"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Button } from "@/components/ui/button"; import { Link2, ExternalLink } from "lucide-react"; import Link from "next/link"; import { SelectBasicConfig } from "./types"; import { cascadingRelationApi, CascadingRelation } from "@/lib/api/cascadingRelation"; import { categoryValueCascadingApi, CategoryValueCascadingGroup } from "@/lib/api/categoryValueCascading"; export interface SelectBasicConfigPanelProps { config: SelectBasicConfig; onChange: (config: Partial) => void; /** 현재 화면의 모든 컴포넌트 목록 (부모 필드 자동 감지용) */ allComponents?: any[]; /** 현재 컴포넌트 정보 */ currentComponent?: any; } /** * SelectBasic 설정 패널 * 컴포넌트의 설정값들을 편집할 수 있는 UI 제공 */ export const SelectBasicConfigPanel: React.FC = ({ config, onChange, allComponents = [], currentComponent, }) => { // 연쇄 드롭다운 관련 상태 const [cascadingEnabled, setCascadingEnabled] = useState(!!config.cascadingRelationCode); const [relationList, setRelationList] = useState([]); const [loadingRelations, setLoadingRelations] = useState(false); // 🆕 카테고리 값 연쇄관계 상태 const [categoryRelationEnabled, setCategoryRelationEnabled] = useState(!!(config as any).categoryRelationCode); const [categoryRelationList, setCategoryRelationList] = useState([]); const [loadingCategoryRelations, setLoadingCategoryRelations] = useState(false); // 연쇄 관계 목록 로드 useEffect(() => { if (cascadingEnabled && relationList.length === 0) { loadRelationList(); } }, [cascadingEnabled]); // 🆕 카테고리 값 연쇄관계 목록 로드 useEffect(() => { if (categoryRelationEnabled && categoryRelationList.length === 0) { loadCategoryRelationList(); } }, [categoryRelationEnabled]); // config 변경 시 상태 동기화 useEffect(() => { setCascadingEnabled(!!config.cascadingRelationCode); setCategoryRelationEnabled(!!(config as any).categoryRelationCode); }, [config.cascadingRelationCode, (config as any).categoryRelationCode]); const loadRelationList = async () => { setLoadingRelations(true); try { const response = await cascadingRelationApi.getList("Y"); if (response.success && response.data) { setRelationList(response.data); } } catch (error) { console.error("연쇄 관계 목록 로드 실패:", error); } finally { setLoadingRelations(false); } }; // 🆕 카테고리 값 연쇄관계 목록 로드 const loadCategoryRelationList = async () => { setLoadingCategoryRelations(true); try { const response = await categoryValueCascadingApi.getGroups("Y"); if (response.success && response.data) { setCategoryRelationList(response.data); } } catch (error) { console.error("카테고리 값 연쇄관계 목록 로드 실패:", error); } finally { setLoadingCategoryRelations(false); } }; const handleChange = (key: keyof SelectBasicConfig, value: any) => { // 기존 config와 병합하여 전체 객체 전달 (다른 속성 보호) const newConfig = { ...config, [key]: value }; onChange(newConfig); }; // 연쇄 드롭다운 토글 const handleCascadingToggle = (enabled: boolean) => { setCascadingEnabled(enabled); if (!enabled) { // 비활성화 시 관계 설정 제거 const newConfig = { ...config, cascadingRelationCode: undefined, cascadingRole: undefined, cascadingParentField: undefined, }; onChange(newConfig); } else { loadRelationList(); // 카테고리 값 연쇄관계 비활성화 (둘 중 하나만 사용) if (categoryRelationEnabled) { setCategoryRelationEnabled(false); onChange({ ...config, categoryRelationCode: undefined } as any); } } }; // 🆕 카테고리 값 연쇄관계 토글 const handleCategoryRelationToggle = (enabled: boolean) => { setCategoryRelationEnabled(enabled); if (!enabled) { // 비활성화 시 관계 설정 제거 const newConfig = { ...config, categoryRelationCode: undefined, cascadingRole: undefined, cascadingParentField: undefined, } as any; onChange(newConfig); } else { loadCategoryRelationList(); // 일반 연쇄관계 비활성화 (둘 중 하나만 사용) if (cascadingEnabled) { setCascadingEnabled(false); onChange({ ...config, cascadingRelationCode: undefined }); } } }; // 🆕 같은 연쇄 관계의 부모 역할 컴포넌트 찾기 const findParentComponent = (relationCode: string) => { console.log("🔍 findParentComponent 호출:", { relationCode, allComponentsLength: allComponents?.length, currentComponentId: currentComponent?.id, }); if (!allComponents || allComponents.length === 0) { console.log("❌ allComponents가 비어있음"); return null; } // 모든 컴포넌트의 cascading 설정 확인 allComponents.forEach((comp: any) => { const compConfig = comp.componentConfig || {}; if (compConfig.cascadingRelationCode) { console.log("📦 컴포넌트 cascading 설정:", { id: comp.id, columnName: comp.columnName, cascadingRelationCode: compConfig.cascadingRelationCode, cascadingRole: compConfig.cascadingRole, }); } }); const found = allComponents.find((comp: any) => { const compConfig = comp.componentConfig || {}; return ( comp.id !== currentComponent?.id && // 자기 자신 제외 compConfig.cascadingRelationCode === relationCode && compConfig.cascadingRole === "parent" ); }); console.log("🔍 찾은 부모 컴포넌트:", found); return found; }; // 역할 변경 시 부모 필드 자동 감지 const handleRoleChange = (role: "parent" | "child") => { let parentField = config.cascadingParentField; // 자식 역할 선택 시 부모 필드 자동 감지 if (role === "child" && config.cascadingRelationCode) { const parentComp = findParentComponent(config.cascadingRelationCode); if (parentComp) { parentField = parentComp.columnName; console.log("🔗 부모 필드 자동 감지:", parentField); } } const newConfig = { ...config, cascadingRole: role, // 부모 역할일 때는 부모 필드 불필요, 자식일 때는 자동 감지된 값 또는 기존 값 cascadingParentField: role === "parent" ? undefined : parentField, }; onChange(newConfig); }; // 선택된 관계 정보 const selectedRelation = relationList.find((r) => r.relation_code === config.cascadingRelationCode); return (
select-basic 설정
{/* select 관련 설정 */}
handleChange("placeholder", e.target.value)} />
{/* 공통 설정 */}
handleChange("disabled", checked)} />
handleChange("required", checked)} />
handleChange("readonly", checked)} />
handleChange("multiple", checked)} />
{/* 연쇄 드롭다운 설정 */}

다른 필드의 값에 따라 옵션이 동적으로 변경됩니다.

{cascadingEnabled && (
{/* 관계 선택 */}
{/* 역할 선택 */} {config.cascadingRelationCode && (

{config.cascadingRole === "parent" ? "이 필드가 상위 선택 역할을 합니다. (예: 창고 선택)" : config.cascadingRole === "child" ? "이 필드는 상위 필드 값에 따라 옵션이 변경됩니다. (예: 위치 선택)" : "이 필드의 역할을 선택하세요."}

)} {/* 부모 필드 설정 (자식 역할일 때만) */} {config.cascadingRelationCode && config.cascadingRole === "child" && (() => { // 선택된 관계에서 부모 값 컬럼 가져오기 const expectedParentColumn = selectedRelation?.parent_value_column; // 부모 역할에 맞는 컴포넌트만 필터링 const parentFieldCandidates = allComponents.filter((comp) => { // 현재 컴포넌트 제외 if (currentComponent && comp.id === currentComponent.id) return false; // 관계에서 지정한 부모 컬럼명과 일치하는 컴포넌트만 if (expectedParentColumn && comp.columnName !== expectedParentColumn) return false; // columnName이 있어야 함 return !!comp.columnName; }); return (
{expectedParentColumn && (

관계에서 지정된 부모 컬럼: {expectedParentColumn}

)}

상위 값을 제공할 필드를 선택하세요.

); })()} {/* 선택된 관계 정보 표시 */} {selectedRelation && config.cascadingRole && (
{config.cascadingRole === "parent" ? ( <>
부모 역할 (상위 선택)
데이터 소스:{" "} {selectedRelation.parent_table}
저장 값:{" "} {selectedRelation.parent_value_column}
) : ( <>
자식 역할 (하위 선택)
데이터 소스:{" "} {selectedRelation.child_table}
필터 기준:{" "} {selectedRelation.child_filter_column}
저장 값:{" "} {selectedRelation.child_value_column}
)}
)} {/* 관계 관리 페이지 링크 */}
)}
{/* 🆕 카테고리 값 연쇄관계 설정 */}

부모 카테고리 값 선택에 따라 자식 카테고리 옵션이 변경됩니다.
예: 검사유형 선택 시 해당 유형에 맞는 적용대상만 표시

{categoryRelationEnabled && (
{/* 관계 선택 */}
{/* 역할 선택 */} {(config as any).categoryRelationCode && (

{config.cascadingRole === "parent" ? "이 필드가 상위 카테고리 선택 역할을 합니다. (예: 검사유형)" : config.cascadingRole === "child" ? "이 필드는 상위 카테고리 값에 따라 옵션이 변경됩니다. (예: 적용대상)" : "이 필드의 역할을 선택하세요."}

)} {/* 부모 필드 설정 (자식 역할일 때만) */} {(config as any).categoryRelationCode && config.cascadingRole === "child" && (() => { // 선택된 관계 정보 가져오기 const selectedRelation = categoryRelationList.find( (r) => r.relation_code === (config as any).categoryRelationCode, ); const expectedParentColumn = selectedRelation?.parent_column_name; // 부모 역할에 맞는 컴포넌트만 필터링 const parentFieldCandidates = allComponents.filter((comp) => { // 현재 컴포넌트 제외 if (currentComponent && comp.id === currentComponent.id) return false; // 관계에서 지정한 부모 컬럼명과 일치하는 컴포넌트만 if (expectedParentColumn && comp.columnName !== expectedParentColumn) return false; // columnName이 있어야 함 return !!comp.columnName; }); return (
{expectedParentColumn && (

관계에서 지정된 부모 컬럼: {expectedParentColumn}

)}

상위 카테고리 값을 제공할 필드를 선택하세요.

); })()} {/* 관계 관리 페이지 링크 */}
)}
{/* 계층구조 코드 설정 */}
공통코드에 계층구조(depth 2 이상)가 있으면 자동으로 대분류 → 중분류 → 소분류 셀렉트박스로 표시됩니다.
{/* 상세 설정 (항상 표시, 계층구조가 있을 때 적용됨) */}

계층구조 코드가 감지되면 아래 설정이 적용됩니다.

{/* 코드 카테고리 선택 안내 */} {!config.codeCategory && (
먼저 상단에서 코드 카테고리를 선택해주세요.
)} {/* 최대 깊이 선택 */}

표시할 계층의 최대 깊이를 선택하세요.

{/* 1단계 라벨 */}
{ const newLabels: [string, string?, string?] = [ e.target.value, config.hierarchicalLabels?.[1], config.hierarchicalLabels?.[2], ]; handleChange("hierarchicalLabels", newLabels); }} placeholder="예: 대분류" className="h-8 text-xs" />
{/* 2단계 라벨 */} {(config.hierarchicalMaxDepth || 3) >= 2 && (
{ const newLabels: [string, string?, string?] = [ config.hierarchicalLabels?.[0] || "대분류", e.target.value, config.hierarchicalLabels?.[2], ]; handleChange("hierarchicalLabels", newLabels); }} placeholder="예: 중분류" className="h-8 text-xs" />
)} {/* 3단계 라벨 */} {(config.hierarchicalMaxDepth || 3) >= 3 && (
{ const newLabels: [string, string?, string?] = [ config.hierarchicalLabels?.[0] || "대분류", config.hierarchicalLabels?.[1] || "중분류", e.target.value, ]; handleChange("hierarchicalLabels", newLabels); }} placeholder="예: 소분류" className="h-8 text-xs" />
)} {/* 인라인 표시 */}
handleChange("hierarchicalInline", checked)} />

셀렉트박스를 가로로 나란히 표시합니다.

); };