fix(dashboard): resolve tutorial QA notes (clips 2–6)

- settings/company: show success toast on save (2.1)
- inspection templates: add explicit Save button (2.6)
- parts: label price fields with AED currency (3.1)
- service groups: add parts/services picker to packages (3.3)
- vehicles: add insurance (has_insurance + insurance type) field (4.2)
- appointments: add calendar view with list/calendar toggle (5.1)
- job card check-in: require department (5.4)
- job card payments: refresh job card/invoice queries so PAID shows (5.6)
- vendors: add phone + address fields (6.1)
- bill payments: surface real vendor/employee validation errors (6.4)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 11:39:07 +04:00
co-authored by Claude Opus 4.8
parent c71b5a19ed
commit fdce301495
14 changed files with 562 additions and 39 deletions
@@ -10,6 +10,7 @@ import {
RhfTextField,
RhfTextareaField,
RhfAsyncSelectField,
RhfCheckboxField,
RhfImageField,
} from "@/shared/components/form"
import { ShopTypeInlineForm } from "./inline-forms/shop-type-inline-form"
@@ -24,7 +25,7 @@ import { useResourceForm } from "@/shared/hooks/use-resource-form"
import { useFormMutation } from "@/shared/hooks/use-form-mutation"
import { toRelation, toId } from "@/shared/lib/utils"
import { formatUppercase } from "@/shared/utils/formatters"
import { VEHICLE_ROUTES } from "@garage/api"
import { VEHICLE_ROUTES, INSURANCE_TYPE_ROUTES } from "@garage/api"
import { vehicleFormSchema, type VehicleFormValues } from "./vehicle.schema"
import { CustomerForm } from "../customers/customer-form"
@@ -57,6 +58,8 @@ const DEFAULT_VALUES: VehicleFormValues = {
drivetrain: "",
mileage: "",
note: "",
has_insurance: false,
insurance_type: null,
image: null,
}
@@ -92,6 +95,8 @@ function mapToFormValues(data: unknown): VehicleFormValues {
drivetrain: toFormString(d.drivetrain),
mileage: toFormString(d.mileage),
note: toFormString(d.note),
has_insurance: Boolean(d.has_insurance),
insurance_type: toRelation(d.insurance_type_id, d.insurance_type?.title),
image: null,
}
}
@@ -115,6 +120,8 @@ function mapToPayload(values: VehicleFormValues) {
drivetrain: values.drivetrain || undefined,
mileage: values.mileage || undefined,
note: values.note || undefined,
has_insurance: values.has_insurance ?? false,
insurance_type_id: values.has_insurance ? toId(values.insurance_type) : undefined,
image: values.image instanceof File ? values.image : undefined,
}
}
@@ -137,6 +144,8 @@ export function VehicleForm({ resourceId, initialData, onSuccess }: VehicleFormP
const existingImageUrl: string | null =
(resolvedData as any)?.data?.image_url ?? (resolvedData as any)?.image_url ?? null
const hasInsurance = form.watch("has_insurance")
const { mutate, error, isPending } = useFormMutation(form, {
mutationFn: (values: VehicleFormValues) => {
const payload = mapToPayload(values)
@@ -254,6 +263,24 @@ export function VehicleForm({ resourceId, initialData, onSuccess }: VehicleFormP
/>
</div>
{/* Insurance */}
<div className="border-t pt-4">
<RhfCheckboxField name="has_insurance" label="Has Insurance?" />
{hasInsurance && (
<div className="mt-4">
<RhfAsyncSelectField
name="insurance_type"
label="Insurance Type"
placeholder="Select insurance type (e.g. Comprehensive)"
queryKey={[INSURANCE_TYPE_ROUTES.INDEX]}
listFn={() => api.insuranceTypes.list()}
mapOption={mapLookupOption}
{...STORE_OBJECT}
/>
</div>
)}
</div>
{/* License & identifiers */}
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<RhfTextField
@@ -31,6 +31,10 @@ export const vehicleFormSchema = z.object({
// ── Notes ──
note: z.string().optional(),
// ── Insurance ──
has_insurance: z.boolean().optional(),
insurance_type: relationFieldSchema.optional(),
// ── Image ──
image: z.any().optional(),
})