Files
vexplor/frontend/lib/registry/pop-components/pop-shared/TableCombobox.tsx
SeongHyun Kim e3ae8d273c feat(pop): 컴포넌트 연결 단순화 + 상태 변경 규칙 UI 개선 + 조회 키 설정
컴포넌트 연결 단순화
- ConnectionEditor: 이벤트 연결 시 "어디로" Select 1개로 단순화
- useConnectionResolver: 호환 이벤트 자동 라우팅 (_auto 모드)
- connectionMeta에 category(event/filter/data) 필드 추가

상태 변경 규칙 UI 개선
- StatusChangeRule 타입 통합, 모든 버튼 프리셋에서 사용 가능
- TableCombobox/ColumnCombobox 공용 컴포넌트 추출 (pop-shared/)
- 테이블/컬럼 드롭다운, 고정값/조건부 값 설정 UI
- 입고확정 API 신규 (popActionRoutes.ts, 동적 상태 변경 처리)

조회 키 자동/수동 설정
- 대상 테이블 기반 자동 판단 (cart_items -> id, 그 외 -> row_key -> PK)
- 수동 모드: 카드 항목 필드와 대상 PK 컬럼을 직접 지정 가능
- PK 컬럼명 동적 표시 (isPrimaryKey 정보 활용)
2026-03-03 15:31:13 +09:00

117 lines
3.4 KiB
TypeScript

"use client";
import React, { useState, useMemo } from "react";
import { Check, ChevronsUpDown } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import type { TableInfo } from "../pop-dashboard/utils/dataFetcher";
interface TableComboboxProps {
tables: TableInfo[];
value: string;
onSelect: (tableName: string) => void;
placeholder?: string;
}
export function TableCombobox({
tables,
value,
onSelect,
placeholder = "테이블을 선택하세요",
}: TableComboboxProps) {
const [open, setOpen] = useState(false);
const [search, setSearch] = useState("");
const selectedLabel = useMemo(() => {
const found = tables.find((t) => t.tableName === value);
return found ? (found.displayName || found.tableName) : "";
}, [tables, value]);
const filtered = useMemo(() => {
if (!search) return tables;
const q = search.toLowerCase();
return tables.filter(
(t) =>
t.tableName.toLowerCase().includes(q) ||
(t.displayName && t.displayName.toLowerCase().includes(q))
);
}, [tables, search]);
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="mt-1 h-8 w-full justify-between text-xs"
>
{value ? selectedLabel : placeholder}
<ChevronsUpDown className="ml-2 h-3.5 w-3.5 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
className="p-0"
style={{ width: "var(--radix-popover-trigger-width)" }}
align="start"
>
<Command shouldFilter={false}>
<CommandInput
placeholder="테이블명 또는 한글명 검색..."
className="text-xs"
value={search}
onValueChange={setSearch}
/>
<CommandList>
<CommandEmpty className="py-4 text-center text-xs">
.
</CommandEmpty>
<CommandGroup>
{filtered.map((table) => (
<CommandItem
key={table.tableName}
value={table.tableName}
onSelect={() => {
onSelect(table.tableName);
setOpen(false);
setSearch("");
}}
className="text-xs"
>
<Check
className={cn(
"mr-2 h-3.5 w-3.5",
value === table.tableName ? "opacity-100" : "opacity-0"
)}
/>
<div className="flex flex-col">
<span>{table.displayName || table.tableName}</span>
{table.displayName && (
<span className="text-[10px] text-muted-foreground">
{table.tableName}
</span>
)}
</div>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}