자동완성 검색 입력 컴포넌트 다중 컬럼 표시 기능추가
This commit is contained in:
parent
2cddb42255
commit
93d9937343
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -29,5 +29,8 @@ export interface AutocompleteSearchInputConfig {
|
|||
fieldMappings?: FieldMapping[]; // 매핑할 필드 목록
|
||||
// 저장 대상 테이블 (간소화 버전)
|
||||
targetTable?: string;
|
||||
// 🆕 다중 표시 필드 설정 (여러 컬럼 조합)
|
||||
displayFields?: string[]; // 여러 컬럼을 조합하여 표시
|
||||
displaySeparator?: string; // 구분자 (기본값: " - ")
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue