import React, { useState, useEffect, useMemo, useCallback } from "react"; import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Loader2, AlertCircle, Check, X } from "lucide-react"; import { cn } from "@/lib/utils"; import { ComponentData, LayerCondition, LayerDefinition } from "@/types/screen-management"; import { getCodesByCategory, CodeItem } from "@/lib/api/codeManagement"; interface LayerConditionPanelProps { layer: LayerDefinition; components: ComponentData[]; // 화면의 모든 컴포넌트 onUpdateCondition: (condition: LayerCondition | undefined) => void; onClose?: () => void; } // 조건 연산자 옵션 const OPERATORS = [ { value: "eq", label: "같음 (=)" }, { value: "neq", label: "같지 않음 (≠)" }, { value: "in", label: "포함 (in)" }, ] as const; type OperatorType = "eq" | "neq" | "in"; export const LayerConditionPanel: React.FC = ({ layer, components, onUpdateCondition, onClose, }) => { // 조건 설정 상태 const [targetComponentId, setTargetComponentId] = useState( layer.condition?.targetComponentId || "" ); const [operator, setOperator] = useState( (layer.condition?.operator as OperatorType) || "eq" ); const [value, setValue] = useState( layer.condition?.value?.toString() || "" ); const [multiValues, setMultiValues] = useState( Array.isArray(layer.condition?.value) ? layer.condition.value : [] ); // 코드 목록 로딩 상태 const [codeOptions, setCodeOptions] = useState([]); const [isLoadingCodes, setIsLoadingCodes] = useState(false); const [codeLoadError, setCodeLoadError] = useState(null); // 트리거 가능한 컴포넌트 필터링 (셀렉트, 라디오, 코드 타입 등) const triggerableComponents = useMemo(() => { return components.filter((comp) => { const componentType = (comp.componentType || "").toLowerCase(); const widgetType = ((comp as any).widgetType || "").toLowerCase(); const webType = ((comp as any).webType || "").toLowerCase(); const inputType = ((comp as any).componentConfig?.inputType || "").toLowerCase(); // 셀렉트, 라디오, 코드 타입 컴포넌트만 허용 const triggerTypes = ["select", "radio", "code", "checkbox", "toggle"]; const isTriggerType = triggerTypes.some((type) => componentType.includes(type) || widgetType.includes(type) || webType.includes(type) || inputType.includes(type) ); return isTriggerType; }); }, [components]); // 선택된 컴포넌트 정보 const selectedComponent = useMemo(() => { return components.find((c) => c.id === targetComponentId); }, [components, targetComponentId]); // 선택된 컴포넌트의 코드 카테고리 const codeCategory = useMemo(() => { if (!selectedComponent) return null; // codeCategory 확인 (다양한 위치에 있을 수 있음) const category = (selectedComponent as any).codeCategory || (selectedComponent as any).componentConfig?.codeCategory || (selectedComponent as any).webTypeConfig?.codeCategory; return category || null; }, [selectedComponent]); // 컴포넌트 선택 시 코드 목록 로드 useEffect(() => { if (!codeCategory) { setCodeOptions([]); return; } const loadCodes = async () => { setIsLoadingCodes(true); setCodeLoadError(null); try { const codes = await getCodesByCategory(codeCategory); setCodeOptions(codes); } catch (error: any) { console.error("코드 목록 로드 실패:", error); setCodeLoadError(error.message || "코드 목록을 불러올 수 없습니다."); setCodeOptions([]); } finally { setIsLoadingCodes(false); } }; loadCodes(); }, [codeCategory]); // 조건 저장 const handleSave = useCallback(() => { if (!targetComponentId) { return; } const condition: LayerCondition = { targetComponentId, operator, value: operator === "in" ? multiValues : value, }; onUpdateCondition(condition); onClose?.(); }, [targetComponentId, operator, value, multiValues, onUpdateCondition, onClose]); // 조건 삭제 const handleClear = useCallback(() => { onUpdateCondition(undefined); setTargetComponentId(""); setOperator("eq"); setValue(""); setMultiValues([]); onClose?.(); }, [onUpdateCondition, onClose]); // in 연산자용 다중 값 토글 const toggleMultiValue = useCallback((val: string) => { setMultiValues((prev) => prev.includes(val) ? prev.filter((v) => v !== val) : [...prev, val] ); }, []); // 컴포넌트 라벨 가져오기 const getComponentLabel = (comp: ComponentData) => { return comp.label || (comp as any).columnName || comp.id; }; return (

조건부 표시 설정

{layer.condition && ( 설정됨 )}
{/* 트리거 컴포넌트 선택 */}
{/* 코드 카테고리 표시 */} {codeCategory && (
카테고리: {codeCategory}
)}
{/* 연산자 선택 */} {targetComponentId && (
)} {/* 조건 값 선택 */} {targetComponentId && (
{isLoadingCodes ? (
코드 목록 로딩 중...
) : codeLoadError ? (
{codeLoadError}
) : codeOptions.length > 0 ? ( // 코드 카테고리가 있는 경우 - 선택 UI operator === "in" ? ( // 다중 선택 (in 연산자)
{codeOptions.map((code) => (
toggleMultiValue(code.codeValue)} >
{multiValues.includes(code.codeValue) && ( )}
{code.codeName} ({code.codeValue})
))}
) : ( // 단일 선택 (eq, neq 연산자) ) ) : ( // 코드 카테고리가 없는 경우 - 직접 입력 setValue(e.target.value)} placeholder="조건 값 입력..." className="h-8 text-xs" /> )} {/* 선택된 값 표시 (in 연산자) */} {operator === "in" && multiValues.length > 0 && (
{multiValues.map((val) => { const code = codeOptions.find((c) => c.codeValue === val); return ( {code?.codeName || val} toggleMultiValue(val)} /> ); })}
)}
)} {/* 현재 조건 요약 */} {targetComponentId && (value || multiValues.length > 0) && (
요약: "{getComponentLabel(selectedComponent!)}" 값이{" "} {operator === "eq" && `"${codeOptions.find(c => c.codeValue === value)?.codeName || value}"와 같으면`} {operator === "neq" && `"${codeOptions.find(c => c.codeValue === value)?.codeName || value}"와 다르면`} {operator === "in" && `[${multiValues.map(v => codeOptions.find(c => c.codeValue === v)?.codeName || v).join(", ")}] 중 하나이면`} {" "}이 레이어 표시
)} {/* 버튼 */}
); };