자동완성 검색 입력 컴포넌트 다중 컬럼 표시 기능추가

This commit is contained in:
kjs 2025-12-04 16:02:00 +09:00
parent 2cddb42255
commit 93d9937343
3 changed files with 134 additions and 47 deletions

View File

@ -42,10 +42,26 @@ export function AutocompleteSearchInputComponent({
// config prop 우선, 없으면 개별 prop 사용
const tableName = config?.tableName || propTableName || "";
const displayField = config?.displayField || propDisplayField || "";
const displayFields = config?.displayFields || (displayField ? [displayField] : []); // 다중 표시 필드
const displaySeparator = config?.displaySeparator || " → "; // 구분자
const valueField = config?.valueField || propValueField || "";
const searchFields = config?.searchFields || propSearchFields || [displayField];
const searchFields = config?.searchFields || propSearchFields || displayFields; // 검색 필드도 다중 표시 필드 사용
const placeholder = config?.placeholder || propPlaceholder || "검색...";
// 다중 필드 값을 조합하여 표시 문자열 생성
const getDisplayValue = (item: EntitySearchResult): string => {
if (displayFields.length > 1) {
// 여러 필드를 구분자로 조합
const values = displayFields
.map((field) => item[field])
.filter((v) => v !== null && v !== undefined && v !== "")
.map((v) => String(v));
return values.join(displaySeparator);
}
// 단일 필드
return item[displayField] || "";
};
const [inputValue, setInputValue] = useState("");
const [isOpen, setIsOpen] = useState(false);
const [selectedData, setSelectedData] = useState<EntitySearchResult | null>(null);
@ -115,7 +131,7 @@ export function AutocompleteSearchInputComponent({
const handleSelect = (item: EntitySearchResult) => {
setSelectedData(item);
setInputValue(item[displayField] || "");
setInputValue(getDisplayValue(item));
console.log("🔍 AutocompleteSearchInput handleSelect:", {
item,
@ -239,7 +255,7 @@ export function AutocompleteSearchInputComponent({
onClick={() => handleSelect(item)}
className="w-full px-3 py-2 text-left text-xs transition-colors hover:bg-accent sm:text-sm"
>
<div className="font-medium">{item[displayField]}</div>
<div className="font-medium">{getDisplayValue(item)}</div>
</button>
))}
</div>

View File

@ -184,9 +184,45 @@ export function AutocompleteSearchInputConfigPanel({
</Popover>
</div>
{/* 2. 표시 필드 선택 */}
{/* 2. 표시 필드 선택 (다중 선택 가능) */}
<div className="space-y-2">
<Label className="text-xs font-semibold sm:text-sm">2. *</Label>
<Label className="text-xs font-semibold sm:text-sm">2. * ( )</Label>
<div className="space-y-2">
{/* 선택된 필드 표시 */}
{(localConfig.displayFields && localConfig.displayFields.length > 0) ? (
<div className="flex flex-wrap gap-1 rounded-md border p-2 min-h-[40px]">
{localConfig.displayFields.map((fieldName) => {
const col = sourceTableColumns.find((c) => c.columnName === fieldName);
return (
<span
key={fieldName}
className="inline-flex items-center gap-1 rounded-md bg-primary/10 px-2 py-1 text-xs"
>
{col?.displayName || fieldName}
<button
type="button"
onClick={() => {
const newFields = localConfig.displayFields?.filter((f) => f !== fieldName) || [];
updateConfig({
displayFields: newFields,
displayField: newFields[0] || "", // 첫 번째 필드를 기본 displayField로
});
}}
className="hover:text-destructive"
>
<X className="h-3 w-3" />
</button>
</span>
);
})}
</div>
) : (
<div className="rounded-md border border-dashed p-2 text-center text-xs text-muted-foreground">
</div>
)}
{/* 필드 선택 드롭다운 */}
<Popover open={openDisplayFieldCombo} onOpenChange={setOpenDisplayFieldCombo}>
<PopoverTrigger asChild>
<Button
@ -196,9 +232,7 @@ export function AutocompleteSearchInputConfigPanel({
className="h-8 w-full justify-between text-xs sm:h-10 sm:text-sm"
disabled={!localConfig.tableName || isLoadingSourceColumns}
>
{localConfig.displayField
? sourceTableColumns.find((c) => c.columnName === localConfig.displayField)?.displayName || localConfig.displayField
: isLoadingSourceColumns ? "로딩 중..." : "사용자에게 보여줄 필드"}
{isLoadingSourceColumns ? "로딩 중..." : "필드 추가..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
@ -208,28 +242,60 @@ export function AutocompleteSearchInputConfigPanel({
<CommandList>
<CommandEmpty className="text-xs sm:text-sm"> .</CommandEmpty>
<CommandGroup>
{sourceTableColumns.map((column) => (
{sourceTableColumns.map((column) => {
const isSelected = localConfig.displayFields?.includes(column.columnName);
return (
<CommandItem
key={column.columnName}
value={column.columnName}
onSelect={() => {
updateConfig({ displayField: column.columnName });
setOpenDisplayFieldCombo(false);
const currentFields = localConfig.displayFields || [];
let newFields: string[];
if (isSelected) {
newFields = currentFields.filter((f) => f !== column.columnName);
} else {
newFields = [...currentFields, column.columnName];
}
updateConfig({
displayFields: newFields,
displayField: newFields[0] || "", // 첫 번째 필드를 기본 displayField로
});
}}
className="text-xs sm:text-sm"
>
<Check className={cn("mr-2 h-4 w-4", localConfig.displayField === column.columnName ? "opacity-100" : "opacity-0")} />
<Check className={cn("mr-2 h-4 w-4", isSelected ? "opacity-100" : "opacity-0")} />
<div className="flex flex-col">
<span className="font-medium">{column.displayName || column.columnName}</span>
{column.displayName && <span className="text-[10px] text-gray-500">{column.columnName}</span>}
</div>
</CommandItem>
))}
);
})}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
{/* 구분자 설정 */}
{localConfig.displayFields && localConfig.displayFields.length > 1 && (
<div className="flex items-center gap-2">
<Label className="text-xs whitespace-nowrap">:</Label>
<Input
value={localConfig.displaySeparator || " → "}
onChange={(e) => updateConfig({ displaySeparator: e.target.value })}
placeholder=" → "
className="h-7 w-20 text-xs text-center"
/>
<span className="text-xs text-muted-foreground">
: {localConfig.displayFields.map((f) => {
const col = sourceTableColumns.find((c) => c.columnName === f);
return col?.displayName || f;
}).join(localConfig.displaySeparator || " → ")}
</span>
</div>
)}
</div>
</div>
{/* 3. 저장 대상 테이블 선택 */}
@ -419,7 +485,9 @@ export function AutocompleteSearchInputConfigPanel({
<strong> :</strong> {localConfig.tableName}
</p>
<p>
<strong> :</strong> {localConfig.displayField}
<strong> :</strong> {localConfig.displayFields?.length
? localConfig.displayFields.join(localConfig.displaySeparator || " → ")
: localConfig.displayField}
</p>
<p>
<strong> :</strong> {localConfig.targetTable}

View File

@ -29,5 +29,8 @@ export interface AutocompleteSearchInputConfig {
fieldMappings?: FieldMapping[]; // 매핑할 필드 목록
// 저장 대상 테이블 (간소화 버전)
targetTable?: string;
// 🆕 다중 표시 필드 설정 (여러 컬럼 조합)
displayFields?: string[]; // 여러 컬럼을 조합하여 표시
displaySeparator?: string; // 구분자 (기본값: " - ")
}