ERP-node/frontend/lib/registry/components/entity-search-input/EntitySearchInputComponent.tsx

127 lines
3.6 KiB
TypeScript

"use client";
import React, { useState, useEffect } from "react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Search, X } from "lucide-react";
import { EntitySearchModal } from "./EntitySearchModal";
import { EntitySearchInputProps, EntitySearchResult } from "./types";
import { cn } from "@/lib/utils";
export function EntitySearchInputComponent({
tableName,
displayField,
valueField,
searchFields = [displayField],
mode = "combo",
placeholder = "검색...",
disabled = false,
filterCondition = {},
value,
onChange,
modalTitle = "검색",
modalColumns = [],
showAdditionalInfo = false,
additionalFields = [],
className,
}: EntitySearchInputProps) {
const [modalOpen, setModalOpen] = useState(false);
const [displayValue, setDisplayValue] = useState("");
const [selectedData, setSelectedData] = useState<EntitySearchResult | null>(null);
// value가 변경되면 표시값 업데이트
useEffect(() => {
if (value && selectedData) {
setDisplayValue(selectedData[displayField] || "");
} else {
setDisplayValue("");
setSelectedData(null);
}
}, [value, displayField]);
const handleSelect = (newValue: any, fullData: EntitySearchResult) => {
setSelectedData(fullData);
setDisplayValue(fullData[displayField] || "");
onChange?.(newValue, fullData);
};
const handleClear = () => {
setDisplayValue("");
setSelectedData(null);
onChange?.(null, null);
};
const handleOpenModal = () => {
if (!disabled) {
setModalOpen(true);
}
};
return (
<div className={cn("space-y-2", className)}>
{/* 입력 필드 */}
<div className="flex gap-2">
<div className="relative flex-1">
<Input
value={displayValue}
onChange={(e) => setDisplayValue(e.target.value)}
placeholder={placeholder}
disabled={disabled}
readOnly={mode === "modal" || mode === "combo"}
className="h-8 text-xs sm:h-10 sm:text-sm pr-8"
/>
{displayValue && !disabled && (
<Button
type="button"
variant="ghost"
size="sm"
onClick={handleClear}
className="absolute right-1 top-1/2 -translate-y-1/2 h-6 w-6 p-0"
>
<X className="h-3 w-3" />
</Button>
)}
</div>
{(mode === "modal" || mode === "combo") && (
<Button
type="button"
onClick={handleOpenModal}
disabled={disabled}
className="h-8 text-xs sm:h-10 sm:text-sm"
>
<Search className="h-4 w-4" />
</Button>
)}
</div>
{/* 추가 정보 표시 */}
{showAdditionalInfo && selectedData && additionalFields.length > 0 && (
<div className="text-xs text-muted-foreground space-y-1 px-2">
{additionalFields.map((field) => (
<div key={field} className="flex gap-2">
<span className="font-medium">{field}:</span>
<span>{selectedData[field] || "-"}</span>
</div>
))}
</div>
)}
{/* 검색 모달 */}
<EntitySearchModal
open={modalOpen}
onOpenChange={setModalOpen}
tableName={tableName}
displayField={displayField}
valueField={valueField}
searchFields={searchFields}
filterCondition={filterCondition}
modalTitle={modalTitle}
modalColumns={modalColumns}
onSelect={handleSelect}
/>
</div>
);
}