"use client"; /** * 멀티 컬럼 계층구조 선택 컴포넌트 * * 대분류, 중분류, 소분류를 각각 다른 컬럼에 저장하는 계층구조 선택 컴포넌트 * * @example * { * setFormData(prev => ({ ...prev, [columnName]: value })); * }} * /> */ import React, { useState, useEffect, useCallback } from "react"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Label } from "@/components/ui/label"; import { commonCodeApi } from "@/lib/api/commonCode"; import { Loader2 } from "lucide-react"; import type { CodeInfo } from "@/types/commonCode"; export type HierarchyRole = "large" | "medium" | "small"; export interface HierarchyColumnConfig { columnName: string; label?: string; placeholder?: string; } export interface MultiColumnHierarchySelectProps { /** 코드 카테고리 */ categoryCode: string; /** 각 계층별 컬럼 설정 */ columns: { large?: HierarchyColumnConfig; medium?: HierarchyColumnConfig; small?: HierarchyColumnConfig; }; /** 현재 값들 */ values?: { large?: string; medium?: string; small?: string; }; /** 값 변경 핸들러 (역할, 컬럼명, 값) */ onChange?: (role: HierarchyRole, columnName: string, value: string) => void; /** 비활성화 */ disabled?: boolean; /** 메뉴 OBJID */ menuObjid?: number; /** 추가 클래스 */ className?: string; /** 인라인 표시 (가로 배열) */ inline?: boolean; } interface LoadingState { large: boolean; medium: boolean; small: boolean; } export function MultiColumnHierarchySelect({ categoryCode, columns, values = {}, onChange, disabled = false, menuObjid, className = "", inline = false, }: MultiColumnHierarchySelectProps) { // 각 단계별 옵션 const [largeOptions, setLargeOptions] = useState([]); const [mediumOptions, setMediumOptions] = useState([]); const [smallOptions, setSmallOptions] = useState([]); // 로딩 상태 const [loading, setLoading] = useState({ large: false, medium: false, small: false, }); // 대분류 로드 (depth = 1) const loadLargeOptions = useCallback(async () => { if (!categoryCode) return; setLoading(prev => ({ ...prev, large: true })); try { const response = await commonCodeApi.hierarchy.getHierarchicalCodes( categoryCode, null, // 부모 없음 1, // depth = 1 menuObjid ); if (response.success && response.data) { setLargeOptions(response.data); } } catch (error) { console.error("대분류 로드 실패:", error); } finally { setLoading(prev => ({ ...prev, large: false })); } }, [categoryCode, menuObjid]); // 중분류 로드 (대분류 선택 기준) const loadMediumOptions = useCallback(async (parentCodeValue: string) => { if (!categoryCode || !parentCodeValue) { setMediumOptions([]); return; } setLoading(prev => ({ ...prev, medium: true })); try { const response = await commonCodeApi.hierarchy.getHierarchicalCodes( categoryCode, parentCodeValue, undefined, menuObjid ); if (response.success && response.data) { setMediumOptions(response.data); } } catch (error) { console.error("중분류 로드 실패:", error); } finally { setLoading(prev => ({ ...prev, medium: false })); } }, [categoryCode, menuObjid]); // 소분류 로드 (중분류 선택 기준) const loadSmallOptions = useCallback(async (parentCodeValue: string) => { if (!categoryCode || !parentCodeValue) { setSmallOptions([]); return; } setLoading(prev => ({ ...prev, small: true })); try { const response = await commonCodeApi.hierarchy.getHierarchicalCodes( categoryCode, parentCodeValue, undefined, menuObjid ); if (response.success && response.data) { setSmallOptions(response.data); } } catch (error) { console.error("소분류 로드 실패:", error); } finally { setLoading(prev => ({ ...prev, small: false })); } }, [categoryCode, menuObjid]); // 초기 로드 useEffect(() => { loadLargeOptions(); }, [loadLargeOptions]); // 대분류 값이 있으면 중분류 로드 useEffect(() => { if (values.large) { loadMediumOptions(values.large); } else { setMediumOptions([]); setSmallOptions([]); } }, [values.large, loadMediumOptions]); // 중분류 값이 있으면 소분류 로드 useEffect(() => { if (values.medium) { loadSmallOptions(values.medium); } else { setSmallOptions([]); } }, [values.medium, loadSmallOptions]); // 대분류 변경 const handleLargeChange = (codeValue: string) => { const columnName = columns.large?.columnName || ""; if (onChange && columnName) { onChange("large", columnName, codeValue); } // 하위 값 초기화 if (columns.medium?.columnName && onChange) { onChange("medium", columns.medium.columnName, ""); } if (columns.small?.columnName && onChange) { onChange("small", columns.small.columnName, ""); } }; // 중분류 변경 const handleMediumChange = (codeValue: string) => { const columnName = columns.medium?.columnName || ""; if (onChange && columnName) { onChange("medium", columnName, codeValue); } // 하위 값 초기화 if (columns.small?.columnName && onChange) { onChange("small", columns.small.columnName, ""); } }; // 소분류 변경 const handleSmallChange = (codeValue: string) => { const columnName = columns.small?.columnName || ""; if (onChange && columnName) { onChange("small", columnName, codeValue); } }; const containerClass = inline ? "flex flex-wrap gap-4 items-end" : "space-y-4"; const selectItemClass = inline ? "flex-1 min-w-[150px] space-y-1" : "space-y-1"; // 설정된 컬럼만 렌더링 const hasLarge = !!columns.large; const hasMedium = !!columns.medium; const hasSmall = !!columns.small; return (
{/* 대분류 */} {hasLarge && (
)} {/* 중분류 */} {hasMedium && (
)} {/* 소분류 */} {hasSmall && (
)}
); } export default MultiColumnHierarchySelect;