"use client" import { useRef, useState } from "react" import { useFormContext, useController, type FieldValues, type FieldPath } from "react-hook-form" import { useQuery, useQueryClient } from "@tanstack/react-query" import { Building2, Loader2, PlusIcon } from "lucide-react" import { useAuthApi } from "@/shared/useApi" import { VENDOR_ROUTES } from "@garage/api" import { Field, FieldLabel, FieldError, FieldDescription } from "@/shared/components/ui/field" import { Button } from "@/shared/components/ui/button" import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/shared/components/ui/dialog" import { ScrollArea } from "@/shared/components/ui/scroll-area" import { Combobox, ComboboxInput, ComboboxContent, ComboboxList, ComboboxItem, ComboboxEmpty, } from "@/shared/components/ui/combobox" import { VendorForm } from "./vendor-form" // ── Vendor option type ── type VendorOption = { value: string label: string first_name?: string last_name?: string company_name?: string email?: string phone?: string } function buildVendorOption(item: any): VendorOption { const name = [item.first_name, item.last_name].filter(Boolean).join(" ") const label = item.company_name || name || `Vendor #${item.id}` return { value: String(item.id), label, first_name: item.first_name, last_name: item.last_name, company_name: item.company_name, email: item.email, phone: item.phone, } } 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: VendorOption): string { if (opt.company_name) return opt.company_name[0].toUpperCase() if (opt.first_name || opt.last_name) { return [opt.first_name?.[0], opt.last_name?.[0]].filter(Boolean).join("").toUpperCase() } return "V" } // ── Props ── export type RhfVendorSelectFieldProps< TValues extends FieldValues, TName extends FieldPath, > = { name: TName label?: string description?: string required?: boolean disabled?: boolean placeholder?: string } // ── Component ── export function RhfVendorSelectField< TValues extends FieldValues, TName extends FieldPath, >({ name, label = "Vendor", description, required, disabled, placeholder = "Search by company name, name, or phone...", }: RhfVendorSelectFieldProps) { const api = useAuthApi() const anchorRef = useRef(null) const [inputValue, setInputValue] = useState("") const [isCreateOpen, setIsCreateOpen] = useState(false) const queryClient = useQueryClient() const { control } = useFormContext() const { field, fieldState: { error }, } = useController({ name, control, disabled }) const { data: options = [], isLoading } = useQuery({ queryKey: [VENDOR_ROUTES.INDEX, "vendor-select"], queryFn: async () => { const res = await api.vendors.list() const items = extractItems(res) return items.map(buildVendorOption) }, staleTime: 5 * 60 * 1000, }) const filtered = inputValue ? options.filter((v) => [v.company_name, v.first_name, v.last_name, v.email, v.phone] .filter(Boolean) .join(" ") .toLowerCase() .includes(inputValue.toLowerCase()), ) : options const handleCreateSuccess = (data?: any) => { const item = data?.data ?? data if (item?.id) { field.onChange(buildVendorOption(item)) } queryClient.invalidateQueries({ queryKey: [VENDOR_ROUTES.INDEX, "vendor-select"] }) setIsCreateOpen(false) } return ( {label && (
{label} {required && *}
)}
{ const single = Array.isArray(val) ? val[0] ?? null : val field.onChange( single ? { value: single.value, label: single.label } : null, ) }} disabled={field.disabled} onInputValueChange={(val: string, { reason }: { reason: string }) => { if (reason === "input-change") setInputValue(val) }} isItemEqualToValue={(item: VendorOption, val: any) => item?.value === val?.value } > {isLoading && (
)} {!isLoading && filtered.map((opt) => (
{getInitials(opt)}
{opt.label}
{opt.company_name && opt.label !== opt.company_name && ( {opt.company_name} )} {opt.phone && ( {opt.phone} )}
))} {!isLoading && filtered.length === 0 && ( No vendors found )}
{description && {description}} {error?.message && {error.message}} { if (!v) setIsCreateOpen(false) }}> Add Vendor
) }