fixes
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
parent
cdd1cbc31a
commit
1fda8d8d7b
@ -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() {
|
||||
</div>
|
||||
),
|
||||
})}
|
||||
columns={({ actionsColumn }) => [
|
||||
|
||||
columns={({ actionsColumn })=> [
|
||||
{
|
||||
accessorKey: "name",
|
||||
id: "name",
|
||||
|
||||
header: ({ column }) => <ColumnHeader column={column} title="Vehicle" />,
|
||||
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 }) => <ColumnHeader column={column} title="Customer" />,
|
||||
cell: ({ row }) => {
|
||||
const val = (row.original as any).customer
|
||||
return val ? val.name : "—"
|
||||
},
|
||||
},
|
||||
actionsColumn(),
|
||||
]}
|
||||
/>
|
||||
|
||||
@ -252,7 +252,7 @@ export function JobCardForm({ resourceId, initialData, onSuccess }: JobCardFormP
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<RhfCustomerSelectField name="customer" />
|
||||
<RhfVehicleSelectField name="vehicle" customer_id={customer?.value} />
|
||||
<RhfVehicleSelectField name="vehicle" disabled={!customer?.value} customer_id={customer?.value} />
|
||||
</div>
|
||||
|
||||
<RhfCheckboxField name="has_insurance" label="Has Insurance Work?" />
|
||||
|
||||
@ -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<VehicleFormValues>()
|
||||
|
||||
// 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<string, unknown>
|
||||
|
||||
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 []
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
/>
|
||||
<RhfTextField name="vin_number" label="VIN Number" placeholder="e.g. 1HGBH41JXMN109186" />
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<RhfAsyncSelectField
|
||||
name="customer_id"
|
||||
label="Customer"
|
||||
placeholder="Select customer"
|
||||
queryKey={["customers"]}
|
||||
listFn={() => api.customers.list()}
|
||||
mapOption={(op:any)=> ({ value: String(op.id), label: getFullName(op) })}
|
||||
createForm={(props) => <CustomerForm {...props} />}
|
||||
createLabel="Customer"
|
||||
|
||||
{...STORE_OBJECT}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* License & identifiers */}
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
|
||||
@ -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(),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user