"use client"; import React, { useState, useEffect } from "react"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Search, X, Check, ChevronsUpDown } from "lucide-react"; import { EntitySearchModal } from "./EntitySearchModal"; import { EntitySearchInputProps, EntitySearchResult } from "./types"; import { cn } from "@/lib/utils"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command"; import { dynamicFormApi } from "@/lib/api/dynamicForm"; export function EntitySearchInputComponent({ tableName, displayField, valueField, searchFields = [displayField], mode: modeProp, uiMode, // EntityConfigPanel에서 저장되는 값 placeholder = "검색...", disabled = false, filterCondition = {}, value, onChange, modalTitle = "검색", modalColumns = [], showAdditionalInfo = false, additionalFields = [], className, style, // 🆕 추가 props component, isInteractive, onFormDataChange, }: EntitySearchInputProps & { uiMode?: string; component?: any; isInteractive?: boolean; onFormDataChange?: (fieldName: string, value: any) => void; }) { // uiMode가 있으면 우선 사용, 없으면 modeProp 사용, 기본값 "combo" const mode = (uiMode || modeProp || "combo") as "select" | "modal" | "combo" | "autocomplete"; const [modalOpen, setModalOpen] = useState(false); const [selectOpen, setSelectOpen] = useState(false); const [displayValue, setDisplayValue] = useState(""); const [selectedData, setSelectedData] = useState(null); const [options, setOptions] = useState([]); const [isLoadingOptions, setIsLoadingOptions] = useState(false); const [optionsLoaded, setOptionsLoaded] = useState(false); // filterCondition을 문자열로 변환하여 비교 (객체 참조 문제 해결) const filterConditionKey = JSON.stringify(filterCondition || {}); // select 모드일 때 옵션 로드 (한 번만) useEffect(() => { if (mode === "select" && tableName && !optionsLoaded) { loadOptions(); setOptionsLoaded(true); } }, [mode, tableName, filterConditionKey, optionsLoaded]); const loadOptions = async () => { if (!tableName) return; setIsLoadingOptions(true); try { const response = await dynamicFormApi.getTableData(tableName, { page: 1, pageSize: 100, // 최대 100개까지 로드 filters: filterCondition, }); if (response.success && response.data) { setOptions(response.data); } } catch (error) { console.error("옵션 로드 실패:", error); } finally { setIsLoadingOptions(false); } }; // value가 변경되면 표시값 업데이트 useEffect(() => { if (value && selectedData) { setDisplayValue(selectedData[displayField] || ""); } else if (value && mode === "select" && options.length > 0) { // select 모드에서 value가 있고 options가 로드된 경우 const found = options.find(opt => opt[valueField] === value); if (found) { setSelectedData(found); setDisplayValue(found[displayField] || ""); } } else if (!value) { setDisplayValue(""); setSelectedData(null); } }, [value, displayField, options, mode, valueField]); const handleSelect = (newValue: any, fullData: EntitySearchResult) => { setSelectedData(fullData); setDisplayValue(fullData[displayField] || ""); onChange?.(newValue, fullData); // 🆕 onFormDataChange 호출 (formData에 값 저장) if (isInteractive && onFormDataChange && component?.columnName) { onFormDataChange(component.columnName, newValue); console.log("📤 EntitySearchInput -> onFormDataChange:", component.columnName, newValue); } }; const handleClear = () => { setDisplayValue(""); setSelectedData(null); onChange?.(null, null); // 🆕 onFormDataChange 호출 (formData에서 값 제거) if (isInteractive && onFormDataChange && component?.columnName) { onFormDataChange(component.columnName, null); console.log("📤 EntitySearchInput -> onFormDataChange (clear):", component.columnName, null); } }; const handleOpenModal = () => { if (!disabled) { setModalOpen(true); } }; const handleSelectOption = (option: EntitySearchResult) => { handleSelect(option[valueField], option); setSelectOpen(false); }; // 높이 계산 (style에서 height가 있으면 사용, 없으면 기본값) const componentHeight = style?.height; const inputStyle: React.CSSProperties = componentHeight ? { height: componentHeight } : {}; // select 모드: 검색 가능한 드롭다운 if (mode === "select") { return (
항목을 찾을 수 없습니다. {options.map((option, index) => ( handleSelectOption(option)} className="text-xs sm:text-sm" >
{option[displayField]} {valueField !== displayField && ( {option[valueField]} )}
))}
{/* 추가 정보 표시 */} {showAdditionalInfo && selectedData && additionalFields.length > 0 && (
{additionalFields.map((field) => (
{field}: {selectedData[field] || "-"}
))}
)}
); } // modal, combo, autocomplete 모드 return (
{/* 입력 필드 */}
setDisplayValue(e.target.value)} placeholder={placeholder} disabled={disabled} readOnly={mode === "modal" || mode === "combo"} className={cn("w-full pr-8", !componentHeight && "h-8 text-xs sm:h-10 sm:text-sm")} style={inputStyle} /> {displayValue && !disabled && ( )}
{/* 모달 버튼: modal 또는 combo 모드일 때만 표시 */} {(mode === "modal" || mode === "combo") && ( )}
{/* 추가 정보 표시 */} {showAdditionalInfo && selectedData && additionalFields.length > 0 && (
{additionalFields.map((field) => (
{field}: {selectedData[field] || "-"}
))}
)} {/* 검색 모달: modal 또는 combo 모드일 때만 렌더링 */} {(mode === "modal" || mode === "combo") && ( )}
); }