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

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

@@ -184,52 +184,118 @@ export function AutocompleteSearchInputConfigPanel({
</Popover>
</div>
{/* 2. 표시 필드 선택 */}
{/* 2. 표시 필드 선택 (다중 선택 가능) */}
<div className="space-y-2">
<Label className="text-xs font-semibold sm:text-sm">2. *</Label>
<Popover open={openDisplayFieldCombo} onOpenChange={setOpenDisplayFieldCombo}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={openDisplayFieldCombo}
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 ? "로딩 중..." : "사용자에게 보여줄 필드"}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0" style={{ width: "var(--radix-popover-trigger-width)" }} align="start">
<Command>
<CommandInput placeholder="필드 검색..." className="text-xs sm:text-sm" />
<CommandList>
<CommandEmpty className="text-xs sm:text-sm"> .</CommandEmpty>
<CommandGroup>
{sourceTableColumns.map((column) => (
<CommandItem
key={column.columnName}
value={column.columnName}
onSelect={() => {
updateConfig({ displayField: column.columnName });
setOpenDisplayFieldCombo(false);
<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="text-xs sm:text-sm"
className="hover:text-destructive"
>
<Check className={cn("mr-2 h-4 w-4", localConfig.displayField === column.columnName ? "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>
<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
variant="outline"
role="combobox"
aria-expanded={openDisplayFieldCombo}
className="h-8 w-full justify-between text-xs sm:h-10 sm:text-sm"
disabled={!localConfig.tableName || isLoadingSourceColumns}
>
{isLoadingSourceColumns ? "로딩 중..." : "필드 추가..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="p-0" style={{ width: "var(--radix-popover-trigger-width)" }} align="start">
<Command>
<CommandInput placeholder="필드 검색..." className="text-xs sm:text-sm" />
<CommandList>
<CommandEmpty className="text-xs sm:text-sm"> .</CommandEmpty>
<CommandGroup>
{sourceTableColumns.map((column) => {
const isSelected = localConfig.displayFields?.includes(column.columnName);
return (
<CommandItem
key={column.columnName}
value={column.columnName}
onSelect={() => {
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", 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}