"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 { Car, Loader2, PlusIcon } from "lucide-react"
import { useAuthApi } from "@/shared/useApi"
import { VEHICLE_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 Image from "next/image"
import { VehicleForm } from "./vehicle-form"
// ── Vehicle option type (enriched for display) ──
type VehicleOption = {
value: string
label: string
make?: string
model?: string
year?: string
sub_model?: string
license_plate?: string
image_url?: string
mileage?: string
}
function buildVehicleOption(item: any): VehicleOption {
const label =
[item.year, item.make, item.model].filter(Boolean).join(" ") ||
`Vehicle #${item.id}`
return {
value: String(item.id),
label,
make: item.make,
model: item.model,
year: item.year,
sub_model: item.sub_model,
license_plate: item.license_plate,
image_url: item.image_url,
mileage: item.mileage,
}
}
function VehicleThumb({ src }: { src?: string }) {
const [broken, setBroken] = useState(false)
if (!src || broken) {
return (
)
}
return (
setBroken(true)}
className="size-9 shrink-0 rounded-md object-cover border border-border"
/>
)
}
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 []
}
// ── Props ──
export type RhfVehicleSelectFieldProps<
TValues extends FieldValues,
TName extends FieldPath,
> = {
name: TName
label?: string
description?: string
required?: boolean
disabled?: boolean
placeholder?: string
customer_id?: string | null
}
// ── Component ──
export function RhfVehicleSelectField<
TValues extends FieldValues,
TName extends FieldPath,
>({
name,
label = "Vehicle",
description,
required,
disabled,
placeholder = "Search by make, model, year, or plate...",
customer_id,
}: RhfVehicleSelectFieldProps) {
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: [VEHICLE_ROUTES.INDEX, "vehicle-select", customer_id],
queryFn: async () => {
const res = await api.vehicles.list({ customer_id: customer_id || undefined })
return extractItems(res).map(buildVehicleOption)
},
staleTime: 5 * 60 * 1000,
})
const filtered = inputValue
? options.filter((v) =>
[v.year, v.make, v.model, v.sub_model, v.license_plate]
.filter(Boolean)
.join(" ")
.toLowerCase()
.includes(inputValue.toLowerCase()),
)
: options
const handleCreateSuccess = (data?: any) => {
const item = data?.data ?? data
if (item?.id) {
field.onChange(buildVehicleOption(item))
}
queryClient.invalidateQueries({ queryKey: [VEHICLE_ROUTES.INDEX, "vehicle-select"] })
setIsCreateOpen(false)
}
const combobox = (
{
const single = Array.isArray(val) ? val[0] ?? null : val
// Store only { value, label } to stay compatible with relationFieldSchema
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)
}}
// Compare by id string so the selected item highlights correctly
// even when the stored form value has fewer fields than VehicleOption
isItemEqualToValue={(item: VehicleOption, val: any) =>
item?.value === val?.value
}
>
{isLoading && (
)}
{!isLoading &&
filtered.map((opt) => (
{/* Identity */}
{[opt.year, opt.make, opt.model]
.filter(Boolean)
.join(" ")}
{opt.sub_model && (
{opt.sub_model}
)}
{opt.license_plate && (
{opt.license_plate}
)}
))}
{!isLoading && filtered.length === 0 && (
No vehicles found
)}
)
return (
{label && (
{label}
{required && * }
setIsCreateOpen(true)}
title={`Add new ${label}`}
>
)}
{combobox}
{description && {description} }
{error?.message && {error.message} }
{ if (!v) setIsCreateOpen(false) }}>
Add {label}
)
}