diff --git a/apps/dashboard/app/(authenticated)/sales/vehicles/page.tsx b/apps/dashboard/app/(authenticated)/sales/vehicles/page.tsx index c1316fe..d32cccb 100644 --- a/apps/dashboard/app/(authenticated)/sales/vehicles/page.tsx +++ b/apps/dashboard/app/(authenticated)/sales/vehicles/page.tsx @@ -3,11 +3,13 @@ import { useRouter } from 'next/navigation' import { ResourcePage } from '@/shared/data-view/resource-page' import { ColumnHeader } from '@/shared/data-view/table-view' +import type { ColumnDef } from '@tanstack/react-table' import FormDialog from '@/shared/components/form-dialog' import { ImportDataButton } from '@/shared/components/import-data-button' import { ExportDataButton } from '@/shared/components/export-data-button' import { DownloadSampleButton } from '@/shared/components/download-sample-button' import { useAuthApi } from '@/shared/useApi' +import type { CrudResourceColumnHelpers, ResourceItem } from '@/shared/data-view/resource-page' import { VehicleForm } from '@/modules/vehicles/vehicle-form' import { VEHICLE_ROUTES } from '@garage/api' import type { VehiclesClient } from '@garage/api' @@ -49,9 +51,11 @@ export default function VehiclesPage() { ), })} - columns={({ actionsColumn }) => [ + + columns={({ actionsColumn })=> [ { - accessorKey: "name", + id: "name", + header: ({ column }) => , cell: ({ row }) => { const r = row.original as any @@ -117,6 +121,14 @@ export default function VehiclesPage() { return val ? new Date(val).toLocaleDateString() : "—" }, }, + { + accessorKey: "customer", + header: ({ column }) => , + cell: ({ row }) => { + const val = (row.original as any).customer + return val ? val.name : "—" + }, + }, actionsColumn(), ]} /> diff --git a/apps/dashboard/modules/job-cards/job-card-form.tsx b/apps/dashboard/modules/job-cards/job-card-form.tsx index bbaf5e5..e03f828 100644 --- a/apps/dashboard/modules/job-cards/job-card-form.tsx +++ b/apps/dashboard/modules/job-cards/job-card-form.tsx @@ -252,7 +252,7 @@ export function JobCardForm({ resourceId, initialData, onSuccess }: JobCardFormP
- +
diff --git a/apps/dashboard/modules/vehicles/rhf-vehicle-identity-field.tsx b/apps/dashboard/modules/vehicles/rhf-vehicle-identity-field.tsx index 8c3bf7d..cd6a9df 100644 --- a/apps/dashboard/modules/vehicles/rhf-vehicle-identity-field.tsx +++ b/apps/dashboard/modules/vehicles/rhf-vehicle-identity-field.tsx @@ -18,8 +18,14 @@ import type { VehicleFormValues } from "./vehicle.schema" // ── Helpers ── -function unique(values: (string | undefined)[]): string[] { - return [...new Set(values.filter((v): v is string => !!v))].sort() +function toOptionalString(value: unknown): string | undefined { + if (typeof value === "string") return value || undefined + if (typeof value === "number") return String(value) + return undefined +} + +function unique(values: unknown[]): string[] { + return [...new Set(values.map(toOptionalString).filter((v): v is string => !!v))].sort() } // ── Internal combobox for a single free-text + suggestion field ── @@ -60,8 +66,9 @@ function VehicleCombobox({ }, [value]) // Client-side filtering of suggestions based on what's been typed + const normalizedInput = inputText.toLowerCase() const filtered = options.filter((opt) => - !inputText || opt.toLowerCase().includes(inputText.toLowerCase()), + !normalizedInput || opt.toLowerCase().includes(normalizedInput), ) return ( @@ -130,7 +137,7 @@ export function RhfVehicleIdentityField() { const { control, setValue } = useFormContext() // Read shop_type to pass as shop_type_id filter to the API - const shopType = useWatch({ control, name: "shop_type" }) + const shopType = useWatch({ control, name: "shop_type_id" }) const shopTypeId = shopType?.value const makeCtrl = useController({ name: "make", control }) @@ -158,11 +165,26 @@ export function RhfVehicleIdentityField() { drivetrain?: string } + function normalizeRecord(record: unknown): MakeAndModelRecord { + const value = record as Record + + return { + id: typeof value.id === "number" ? value.id : undefined, + shop_type_id: typeof value.shop_type_id === "number" ? value.shop_type_id : undefined, + make: toOptionalString(value.make), + model: toOptionalString(value.model), + year: toOptionalString(value.year), + sub_model: toOptionalString(value.sub_model), + engine_size: toOptionalString(value.engine_size), + drivetrain: toOptionalString(value.drivetrain), + } + } + function extractRecords(response: unknown): MakeAndModelRecord[] { const obj = response as any - if (Array.isArray(obj?.data)) return obj.data - if (Array.isArray(obj?.data?.data)) return obj.data.data - if (Array.isArray(obj)) return obj + if (Array.isArray(obj?.data)) return obj.data.map(normalizeRecord) + if (Array.isArray(obj?.data?.data)) return obj.data.data.map(normalizeRecord) + if (Array.isArray(obj)) return obj.map(normalizeRecord) return [] } diff --git a/apps/dashboard/modules/vehicles/vehicle-form.tsx b/apps/dashboard/modules/vehicles/vehicle-form.tsx index 4f3da99..e772fc2 100644 --- a/apps/dashboard/modules/vehicles/vehicle-form.tsx +++ b/apps/dashboard/modules/vehicles/vehicle-form.tsx @@ -26,6 +26,8 @@ import { toRelation, toId } from "@/shared/lib/utils" import { formatUppercase } from "@/shared/utils/formatters" import { vehicleFormSchema, type VehicleFormValues } from "./vehicle.schema" +import { CustomerForm } from "../customers/customer-form" +import { getFullName } from "@/shared/utils/getFullName" // ── Props ── @@ -43,6 +45,7 @@ const DEFAULT_VALUES: VehicleFormValues = { vehicle_fuel_type_id: null, vehicle_transmission_id: null, vehicle_color_id: null, + customer_id: null, make: "", model: "", year: "", @@ -74,6 +77,7 @@ function mapToFormValues(data: unknown): VehicleFormValues { vehicle_fuel_type_id: toRelation(d.vehicle_fuel_type_id, d.vehicle_fuel_type?.title), vehicle_transmission_id: toRelation(d.vehicle_transmission_id, d.vehicle_transmission?.title), vehicle_color_id: toRelation(d.vehicle_color_id, d.vehicle_color?.title), + customer_id: toRelation(d.customer_id, d.customer?.name), make: d.make || "", model: d.model || "", year: d.year || "", @@ -95,6 +99,8 @@ function mapToPayload(values: VehicleFormValues) { vehicle_fuel_type_id: toId(values.vehicle_fuel_type_id), vehicle_transmission_id: toId(values.vehicle_transmission_id), vehicle_color_id: toId(values.vehicle_color_id), + + customer_id: toId(values.customer_id), make: values.make, model: values.model, year: values.year, @@ -222,6 +228,20 @@ export function VehicleForm({ resourceId, initialData, onSuccess }: VehicleFormP /> +
+ api.customers.list()} + mapOption={(op:any)=> ({ value: String(op.id), label: getFullName(op) })} + createForm={(props) => } + createLabel="Customer" + + {...STORE_OBJECT} + /> +
{/* License & identifiers */}
diff --git a/apps/dashboard/modules/vehicles/vehicle.schema.ts b/apps/dashboard/modules/vehicles/vehicle.schema.ts index 1a7830c..7db8a5e 100644 --- a/apps/dashboard/modules/vehicles/vehicle.schema.ts +++ b/apps/dashboard/modules/vehicles/vehicle.schema.ts @@ -11,7 +11,7 @@ export const vehicleFormSchema = z.object({ vehicle_fuel_type_id: relationFieldSchema, vehicle_transmission_id: relationFieldSchema, vehicle_color_id: relationFieldSchema, - + customer_id: relationFieldSchema, // ── Vehicle identity ── make: z.string().optional(), model: z.string().optional(),