"use client" import { useRef, useState } from "react" import { useQuery } from "@tanstack/react-query" import { Loader2 } from "lucide-react" import { useAuthApi } from "@/shared/useApi" import { EMPLOYEE_ROUTES } from "@garage/api" import { Badge } from "@/shared/components/ui/badge" import { Combobox, ComboboxInput, ComboboxContent, ComboboxList, ComboboxItem, ComboboxEmpty, } from "@/shared/components/ui/combobox" // ── Types ── export type EmployeeOption = { value: string label: string first_name?: string last_name?: string email?: string type?: string status?: string } // ── Helpers ── function buildEmployeeOption(item: any): EmployeeOption { const label = [item.first_name, item.last_name].filter(Boolean).join(" ") || `Employee #${item.id}` return { value: String(item.id), label, first_name: item.first_name, last_name: item.last_name, email: item.email, type: item.type, status: item.status, } } function extractItems(response: unknown): any[] { if (Array.isArray(response)) return response const obj = response as any if (Array.isArray(obj?.data?.data)) return obj.data.data if (Array.isArray(obj?.data)) return obj.data return [] } function getInitials(opt: EmployeeOption): string { return ( [opt.first_name?.[0], opt.last_name?.[0]].filter(Boolean).join("").toUpperCase() || "E" ) } // ── Props ── type EmployeeComboboxProps = { value?: EmployeeOption | null onValueChange: (employee: EmployeeOption | null) => void disabled?: boolean placeholder?: string showClear?: boolean onBlur?: () => void "aria-invalid"?: boolean } // ── Component ── export function EmployeeCombobox({ value, onValueChange, disabled, placeholder = "Search by name or email...", showClear, onBlur, "aria-invalid": ariaInvalid, }: EmployeeComboboxProps) { const api = useAuthApi() const anchorRef = useRef(null) const [inputValue, setInputValue] = useState("") const { data: options = [], isLoading } = useQuery({ queryKey: [EMPLOYEE_ROUTES.INDEX, "employee-select"], queryFn: async () => { const res = await api.employees.list() return extractItems(res).map(buildEmployeeOption) }, staleTime: 5 * 60 * 1000, }) const filtered = inputValue ? options.filter((e) => [e.first_name, e.last_name, e.email, e.type] .filter(Boolean) .join(" ") .toLowerCase() .includes(inputValue.toLowerCase()), ) : options return (
{ const single = Array.isArray(val) ? val[0] ?? null : val onValueChange(single) }} disabled={disabled} onInputValueChange={(val: string, { reason }: { reason: string }) => { if (reason === "input-change") setInputValue(val) }} isItemEqualToValue={(item: EmployeeOption, val: any) => item?.value === val?.value } > {isLoading && (
)} {!isLoading && filtered.map((opt) => (
{getInitials(opt)}
{opt.label}
{opt.type && ( {opt.type} )} {opt.status && ( {opt.status} )}
))} {!isLoading && filtered.length === 0 && ( No employees found )}
) }