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(),