From e1ef6fa2ea973e5325f0c6c4f2cd76fcf392281b Mon Sep 17 00:00:00 2001 From: Mohammad Khyata Date: Thu, 30 Apr 2026 19:03:31 +0300 Subject: [PATCH] fix many bugs Co-authored-by: Copilot --- .../(authenticated)/sales/estimates/page.tsx | 21 +++-- .../sales/payment-received/page.tsx | 19 ++-- .../modules/appointments/appointment-form.tsx | 9 +- .../modules/customers/customer-form.tsx | 34 +++++--- .../modules/customers/customer.schema.ts | 2 +- .../customers/payment-term-crud-dialog.tsx | 33 +++++++ .../modules/customers/payment-term-form.tsx | 86 +++++++++++++++++++ .../invoices/invoice-payments-section.tsx | 10 ++- .../job-cards/insurance-type-crud-dialog.tsx | 6 +- .../modules/job-cards/insurance-type-form.tsx | 18 ++-- .../modules/job-cards/job-card-actions.tsx | 68 ++++++++++----- .../modules/job-cards/job-card-form.tsx | 5 +- .../job-cards/job-card-general-info.tsx | 2 +- .../job-cards/job-card-payments-received.tsx | 5 +- .../payment-received-form.tsx | 10 +-- .../vehicles/rhf-vehicle-select-field.tsx | 4 +- .../modules/vehicles/vehicle-form.tsx | 8 +- .../components/crud-dialog/crud-dialog.tsx | 2 +- .../components/crud-dialog/use-crud-dialog.ts | 52 +++++++++-- .../form/controls/text-input-field.tsx | 4 +- .../form/controls/time-picker-field.tsx | 2 +- .../components/form/fields/rhf-time-field.tsx | 2 +- apps/dashboard/shared/utils/formatters.ts | 8 ++ packages/api/src/clients/job-cards.ts | 5 ++ 24 files changed, 333 insertions(+), 82 deletions(-) create mode 100644 apps/dashboard/modules/customers/payment-term-crud-dialog.tsx create mode 100644 apps/dashboard/modules/customers/payment-term-form.tsx diff --git a/apps/dashboard/app/(authenticated)/sales/estimates/page.tsx b/apps/dashboard/app/(authenticated)/sales/estimates/page.tsx index 2fa2273..ab2504f 100644 --- a/apps/dashboard/app/(authenticated)/sales/estimates/page.tsx +++ b/apps/dashboard/app/(authenticated)/sales/estimates/page.tsx @@ -6,10 +6,12 @@ import FormDialog from '@/shared/components/form-dialog' import { EstimateForm } from '@/modules/estimates/estimate-form' import { ESTIMATE_ROUTES } from '@garage/api' import type { EstimatesClient } from '@garage/api' -import { Car, FileTextIcon } from 'lucide-react' +import { Car, FileTextIcon, UserIcon } from 'lucide-react' import { Button } from '@/shared/components/ui/button' import Link from 'next/link' import { formatDate } from '@/shared/utils/formatters' +import { getVehicleLabel } from '@/modules/vehicles/utils/getVehicleLabel' +import { getFullName } from '@/shared/utils/getFullName' export default function EstimatesPage() { return ( @@ -52,15 +54,24 @@ export default function EstimatesPage() { { accessorKey: "customer_name", header: ({ column }) => , + cell: ({ row }) => { + const item:any = row.original + return ( +
+ + {getFullName(item.customer) || "—"} +
+ ) + } }, { - accessorKey: "vehicle_id", + accessorKey: "vehicle", header: ({ column }) => , cell: ({ row }) => { - const item = row.original + const item :any= row.original return } diff --git a/apps/dashboard/app/(authenticated)/sales/payment-received/page.tsx b/apps/dashboard/app/(authenticated)/sales/payment-received/page.tsx index 6a1182d..9d852c8 100644 --- a/apps/dashboard/app/(authenticated)/sales/payment-received/page.tsx +++ b/apps/dashboard/app/(authenticated)/sales/payment-received/page.tsx @@ -13,6 +13,7 @@ import { UserIcon, ClipboardListIcon, } from "lucide-react" +import { getFullName } from "@/shared/utils/getFullName" type PaymentReceivedItem = { id: number @@ -63,24 +64,24 @@ export default function PaymentReceivedPage() { }, }, { - accessorKey: "customer_name", + accessorKey: "customer", header: ({ column }) => , cell: ({ row }) => { - const item = row.original as unknown as PaymentReceivedItem + const item:any = row.original as unknown as PaymentReceivedItem return (
- {item.customer_name || "—"} + {getFullName(item.customer) || "—"}
) }, }, { - accessorKey: "job_card_name", + accessorKey: "job_card", header: ({ column }) => , cell: ({ row }) => { - const item = row.original as unknown as PaymentReceivedItem - const label = item.job_card_number || item.job_card_name + const item:any = row.original as unknown as PaymentReceivedItem + const label = item.job_card?.title return (
@@ -111,14 +112,14 @@ export default function PaymentReceivedPage() { }, }, { - accessorKey: "payment_mode_name", + accessorKey: "payment_mode", header: ({ column }) => , cell: ({ row }) => { - const item = row.original as unknown as PaymentReceivedItem + const item:any = row.original as unknown as PaymentReceivedItem return (
- {item.payment_mode_name || "—"} + {item.payment_mode?.title || "—"}
) }, diff --git a/apps/dashboard/modules/appointments/appointment-form.tsx b/apps/dashboard/modules/appointments/appointment-form.tsx index e567687..71262cb 100644 --- a/apps/dashboard/modules/appointments/appointment-form.tsx +++ b/apps/dashboard/modules/appointments/appointment-form.tsx @@ -11,6 +11,7 @@ import { RhfTextareaField, RhfSelectField, RhfAsyncSelectField, + RhfTimeField, } from "@/shared/components/form" import { toast } from "sonner" import { useAuthApi } from "@/shared/useApi" @@ -40,8 +41,8 @@ export type AppointmentFormProps = { const DEFAULT_VALUES: AppointmentFormValues = { title: "", date: "", - from_time: "", - to_time: "", + from_time: "00:00:00", + to_time: "00:00:00", customer: null, vehicle: null, service_writer: null, @@ -157,8 +158,8 @@ export function AppointmentForm({ resourceId, initialData, onSuccess }: Appointm />
- - + +
diff --git a/apps/dashboard/modules/customers/customer-form.tsx b/apps/dashboard/modules/customers/customer-form.tsx index 463782c..0f6249c 100644 --- a/apps/dashboard/modules/customers/customer-form.tsx +++ b/apps/dashboard/modules/customers/customer-form.tsx @@ -21,7 +21,8 @@ import { customerFormSchema, type CustomerFormValues, } from "./customer.schema" -import { CUSTOMER_ROUTES } from "@garage/api" +import { CUSTOMER_ROUTES, PAYMENT_TERM_ROUTES } from "@garage/api" +import { PaymentTermCrudDialog } from "./payment-term-crud-dialog" // ── Constants ── @@ -69,7 +70,7 @@ function mapCustomerToFormValues(data: unknown): CustomerFormValues { const c = (data as any)?.data ?? data ?? {} return { - customer_type: toRelation(c.customer_type_id, c.customer_type_name), + customer_type: toRelation(c.customer_type_id, c.customer_type?.name ?? c.customer_type_name), referral_source: toRelation(c.referral_source_id, c.referral_source_name), payment_terms: toRelation(c.payment_terms_id, c.payment_terms_name), country: toRelation(c.country_id, c.country_name), @@ -113,10 +114,10 @@ function mapFormToPayload(values: CustomerFormValues) { const mapLookupOption = (item: any) => ({ value: String(item.id), - label: item.name, + label: item.title||item.name || `#${item.id}`, }) -const STORE_OBJECT = { getOptionValue: (o: any) => o, getOptionLabel: (o: any) => o.label } +const STORE_OBJECT = { getOptionValue: (o: any) => o, getOptionLabel: (o: any) => o.label || o.name || `#${o.id}` } // ── Component ── @@ -173,6 +174,7 @@ export function CustomerForm({ resourceId, initialData, onSuccess }: CustomerFor options={SALUTATION_OPTIONS} /> - api.paymentTerms.list()} - mapOption={mapLookupOption} - {...STORE_OBJECT} - /> +
+
+ Payment Terms + +
+ api.paymentTerms.list()} + mapOption={mapLookupOption} + {...STORE_OBJECT} + /> +
{/* Address */} diff --git a/apps/dashboard/modules/customers/customer.schema.ts b/apps/dashboard/modules/customers/customer.schema.ts index b17b582..f820569 100644 --- a/apps/dashboard/modules/customers/customer.schema.ts +++ b/apps/dashboard/modules/customers/customer.schema.ts @@ -12,7 +12,7 @@ type RelationField = z.infer const customerFormSchema = z.object({ // ── Relations (stored as objects, mapped to IDs on submit) ── - customer_type: relationFieldSchema, + customer_type: relationFieldSchema.refine((val) => !!val?.value, "Customer type is required"), referral_source: relationFieldSchema, payment_terms: relationFieldSchema, country: relationFieldSchema, diff --git a/apps/dashboard/modules/customers/payment-term-crud-dialog.tsx b/apps/dashboard/modules/customers/payment-term-crud-dialog.tsx new file mode 100644 index 0000000..8a43e1e --- /dev/null +++ b/apps/dashboard/modules/customers/payment-term-crud-dialog.tsx @@ -0,0 +1,33 @@ +"use client" + +import { CrudDialog } from "@/shared/components/crud-dialog" +import { ColumnHeader } from "@/shared/data-view/table-view" +import { useAuthApi } from "@/shared/useApi" +import { PAYMENT_TERM_ROUTES } from "@garage/api" +import { PaymentTermForm } from "./payment-term-form" + +export function PaymentTermCrudDialog() { + const api = useAuthApi() + + return ( + api.paymentTerms} + resourceLabel="payment term" + columns={() => [ + { + accessorKey: "title", + header: ({ column }) => , + }, + ]} + renderForm={({ resourceId, initialData, onSuccess }) => ( + + )} + /> + ) +} diff --git a/apps/dashboard/modules/customers/payment-term-form.tsx b/apps/dashboard/modules/customers/payment-term-form.tsx new file mode 100644 index 0000000..4cb120f --- /dev/null +++ b/apps/dashboard/modules/customers/payment-term-form.tsx @@ -0,0 +1,86 @@ +"use client" + +import { z } from "zod" +import { useForm } from "react-hook-form" +import { zodResolver } from "@hookform/resolvers/zod" +import { Plus, Save } from "lucide-react" +import { Button } from "@/shared/components/ui/button" +import { FieldGroup } from "@/shared/components/ui/field" +import { Rhform, RhfTextField } from "@/shared/components/form" +import { toast } from "sonner" +import { useAuthApi } from "@/shared/useApi" +import { useEffect } from "react" + +// ── Schema ── + +const paymentTermSchema = z.object({ + name: z.string().min(1, "Name is required"), +}) + +type PaymentTermFormValues = z.infer + +// ── Props ── + +type PaymentTermFormProps = { + resourceId?: string | null + initialData?: any + onSuccess?: () => void +} + +// ── Component ── + +export function PaymentTermForm({ resourceId, initialData, onSuccess }: PaymentTermFormProps) { + const api = useAuthApi() + const isEditing = !!resourceId + + const form = useForm({ + resolver: zodResolver(paymentTermSchema), + defaultValues: { name: "" }, + }) + + useEffect(() => { + if (initialData) { + const d = initialData?.data ?? initialData + form.reset({ name: d.title ?? "" }) + } + }, [initialData, form]) + + const handleSubmit = async (values: PaymentTermFormValues) => { + try { + const promise = isEditing + ? api.paymentTerms.update(resourceId!, { title: values.name } as any) + : api.paymentTerms.create({ title: values.name } as any) + + toast.promise(promise, { + loading: isEditing ? "Updating..." : "Creating...", + success: isEditing ? "Updated successfully" : "Created successfully", + error: isEditing ? "Failed to update" : "Failed to create", + }) + + await promise + form.reset() + onSuccess?.() + } catch { + // toast already shown + } + } + + return ( + + + + + + + ) +} diff --git a/apps/dashboard/modules/invoices/invoice-payments-section.tsx b/apps/dashboard/modules/invoices/invoice-payments-section.tsx index 15923f1..12a6ac1 100644 --- a/apps/dashboard/modules/invoices/invoice-payments-section.tsx +++ b/apps/dashboard/modules/invoices/invoice-payments-section.tsx @@ -31,7 +31,15 @@ export default function InvoicePaymentsSection() { {router.refresh(); invalidateQuery()}} diff --git a/apps/dashboard/modules/job-cards/insurance-type-crud-dialog.tsx b/apps/dashboard/modules/job-cards/insurance-type-crud-dialog.tsx index 180bc2d..8d2a130 100644 --- a/apps/dashboard/modules/job-cards/insurance-type-crud-dialog.tsx +++ b/apps/dashboard/modules/job-cards/insurance-type-crud-dialog.tsx @@ -17,9 +17,13 @@ export function InsuranceTypeCrudDialog() { resourceLabel="insurance type" columns={() => [ { - accessorKey: "name", + accessorKey: "title", header: ({ column }) => , }, + { + accessorKey: "description", + header: ({ column }) => , + }, ]} renderForm={({ resourceId, initialData, onSuccess }) => ( @@ -35,22 +36,22 @@ export function InsuranceTypeForm({ resourceId, initialData, onSuccess }: Insura const form = useForm({ resolver: zodResolver(insuranceTypeSchema), - defaultValues: { name: "" }, + defaultValues: { name: "", description: "" }, }) // Pre-fill when editing useEffect(() => { if (initialData) { const d = initialData?.data ?? initialData - form.reset({ name: d.name ?? "" }) + form.reset({ name: d.title ?? "", description: d.description ?? "" }) } }, [initialData, form]) const handleSubmit = async (values: InsuranceTypeFormValues) => { try { const promise = isEditing - ? api.insuranceTypes.update(resourceId!, { title: values.name } as any) - : api.insuranceTypes.create({ title: values.name } as any) + ? api.insuranceTypes.update(resourceId!, { title: values.name, description: values.description } as any) + : api.insuranceTypes.create({ title: values.name, description: values.description } as any) toast.promise(promise, { loading: isEditing ? "Updating..." : "Creating...", @@ -75,6 +76,13 @@ export function InsuranceTypeForm({ resourceId, initialData, onSuccess }: Insura placeholder="e.g. Comprehensive" required /> + @@ -239,7 +266,10 @@ export function JobCardActions({ jobCardId, orderDate, serviceWriterName, salesP title="Change Service Writer" description="Search and select an employee to assign as service writer." isPending={changeServiceWriterMutation.isPending} - onSelect={(id) => changeServiceWriterMutation.mutate(id)} + onSelect={(id) => { + console.log('Service Writer dialog onSelect called with:', id, 'Type:', typeof id) + changeServiceWriterMutation.mutate(id) + }} /> changeSalesPersonMutation.mutate(id)} - /> - changeSalesPersonMutation.mutate(id)} + onSelect={(id) => { + console.log('Dialog onSelect called with:', id, 'Type:', typeof id) + changeSalesPersonMutation.mutate(id) + }} /> + {}} - // onSelect={(id) => changePrimaryTechnicianMutation.mutate(id)} + isPending={changePrimaryTechnicianMutation.isPending} + onSelect={(id) => { + console.log('Primary Technician dialog onSelect called with:', id, 'Type:', typeof id) + changePrimaryTechnicianMutation.mutate(id) + }} /> diff --git a/apps/dashboard/modules/job-cards/job-card-form.tsx b/apps/dashboard/modules/job-cards/job-card-form.tsx index f92d39c..bbaf5e5 100644 --- a/apps/dashboard/modules/job-cards/job-card-form.tsx +++ b/apps/dashboard/modules/job-cards/job-card-form.tsx @@ -69,9 +69,10 @@ const DEFAULT_VALUES: JobCardFormValues = { check_in_date: "", check_in_time: "", start_date: "", - start_time: "", + // Must be initialized with 00:00:00 + start_time: "00:00:00", delivery_date: "", - delivery_time: "", + delivery_time: "00:00:00", km_in: "", fuel_level: "", has_insurance: false, diff --git a/apps/dashboard/modules/job-cards/job-card-general-info.tsx b/apps/dashboard/modules/job-cards/job-card-general-info.tsx index 88e8edd..7d03ba1 100644 --- a/apps/dashboard/modules/job-cards/job-card-general-info.tsx +++ b/apps/dashboard/modules/job-cards/job-card-general-info.tsx @@ -79,7 +79,7 @@ export function JobCardGeneralInfo({ jobCard }: { jobCard: JobCard }) { return (
)} @@ -83,14 +84,14 @@ export default function JobCardPaymentsReceived() { }, }, { - accessorKey: "payment_mode_name", + accessorKey: "payment_mode", header: ({ column }) => , cell: ({ row }) => { const item = row.original as any return (
- {item.payment_mode_name || "—"} + {(item.payment_mode?.title) || "—"}
) }, diff --git a/apps/dashboard/modules/payment-received/payment-received-form.tsx b/apps/dashboard/modules/payment-received/payment-received-form.tsx index ffa6062..312a75d 100644 --- a/apps/dashboard/modules/payment-received/payment-received-form.tsx +++ b/apps/dashboard/modules/payment-received/payment-received-form.tsx @@ -100,12 +100,10 @@ export function PaymentReceivedForm({ resourceId, initialData, onSuccess, defaul base.job_card = toRelation(defaultJobCard.id, defaultJobCard.title ?? undefined) } if (invoiceCustomer?.id != null) { - base.customer = toRelation( - invoiceCustomer.id, - invoiceCustomer.first_name - ? `${invoiceCustomer.first_name} ${invoiceCustomer.last_name || ""}`.trim() - : undefined - ) + const customerLabel = invoiceCustomer.first_name + ? `${invoiceCustomer.first_name} ${invoiceCustomer.last_name || ""}`.trim() + : (invoiceCustomer as any).company_name || (invoiceCustomer as any).name || undefined + base.customer = toRelation(invoiceCustomer.id, customerLabel) } if (invoiceAmount != null && invoiceAmount !== "") { base.amount_received = String(invoiceAmount) diff --git a/apps/dashboard/modules/vehicles/rhf-vehicle-select-field.tsx b/apps/dashboard/modules/vehicles/rhf-vehicle-select-field.tsx index ddd3e99..efd6d57 100644 --- a/apps/dashboard/modules/vehicles/rhf-vehicle-select-field.tsx +++ b/apps/dashboard/modules/vehicles/rhf-vehicle-select-field.tsx @@ -131,7 +131,7 @@ export function RhfVehicleSelectField< const combobox = (
- { const single = Array.isArray(val) ? val[0] ?? null : val @@ -246,7 +246,7 @@ export function RhfVehicleSelectField< Add {label} - + diff --git a/apps/dashboard/modules/vehicles/vehicle-form.tsx b/apps/dashboard/modules/vehicles/vehicle-form.tsx index 59e90dc..dd94bd1 100644 --- a/apps/dashboard/modules/vehicles/vehicle-form.tsx +++ b/apps/dashboard/modules/vehicles/vehicle-form.tsx @@ -23,6 +23,7 @@ import { useAuthApi } from "@/shared/useApi" 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 { vehicleFormSchema, type VehicleFormValues } from "./vehicle.schema" import { VEHICLE_ROUTES } from "@garage/api" @@ -225,7 +226,12 @@ export function VehicleForm({ resourceId, initialData, onSuccess }: VehicleFormP {/* License & identifiers */}
- +
diff --git a/apps/dashboard/shared/components/crud-dialog/crud-dialog.tsx b/apps/dashboard/shared/components/crud-dialog/crud-dialog.tsx index 03e631d..b07f52c 100644 --- a/apps/dashboard/shared/components/crud-dialog/crud-dialog.tsx +++ b/apps/dashboard/shared/components/crud-dialog/crud-dialog.tsx @@ -131,7 +131,7 @@ export function CrudDialog({ Add {title}
- = { resourceLabel?: string } +type CrudListShape = { + data?: unknown + meta?: { + last_page?: number + total?: number + } + pagination?: { + last_page?: number + total?: number + } +} + +function normalizeCrudListResponse(response: unknown): { + items: any[] + meta?: { last_page?: number; total?: number } +} { + const root = (response ?? {}) as CrudListShape + const directData = root.data + + if (Array.isArray(directData)) { + return { items: directData, meta: root.meta ?? root.pagination } + } + + if (directData && typeof directData === "object") { + const nested = directData as CrudListShape + if (Array.isArray(nested.data)) { + return { + items: nested.data, + meta: nested.meta ?? nested.pagination ?? root.meta ?? root.pagination, + } + } + } + + return { items: [], meta: root.meta ?? root.pagination } +} + // ── Hook ── export function useCrudDialog({ @@ -46,17 +82,23 @@ export function useCrudDialog({ const { data, isLoading } = useQuery({ queryKey: fullQueryKey, - queryFn: () => { + queryFn: async () => { const params: Record = { page, per_page: pageSize } if (sortBy) params.sort_by = sortBy if (sortOrder) params.sort_order = sortOrder - return client.list(params) + + try { + return await client.list(params) + } catch { + // Some endpoints ignore/reject pagination params; retry without params. + return client.list() + } }, }) - const responseData = (data as any)?.data ?? [] - const items = Array.isArray(responseData) ? responseData : [] - const meta = (data as any)?.meta + const normalized = normalizeCrudListResponse(data) + const items = normalized.items + const meta = normalized.meta const pagination: DataViewPaginationState = { page, diff --git a/apps/dashboard/shared/components/form/controls/text-input-field.tsx b/apps/dashboard/shared/components/form/controls/text-input-field.tsx index 83d6b4b..fa89ba8 100644 --- a/apps/dashboard/shared/components/form/controls/text-input-field.tsx +++ b/apps/dashboard/shared/components/form/controls/text-input-field.tsx @@ -5,6 +5,7 @@ export type TextInputFieldProps = BaseFieldControlProps & { placeholder?: string type?: React.HTMLInputTypeAttribute step?: React.InputHTMLAttributes["step"] + formatter?: (value: string) => string } export function TextInputField({ @@ -17,11 +18,12 @@ export function TextInputField({ placeholder, type = "text", step, + formatter, }: TextInputFieldProps) { return ( onChange(e.target.value)} + onChange={(e) => onChange(formatter ? formatter(e.target.value) : e.target.value)} onBlur={onBlur} name={name} disabled={disabled} diff --git a/apps/dashboard/shared/components/form/controls/time-picker-field.tsx b/apps/dashboard/shared/components/form/controls/time-picker-field.tsx index 8ccc874..190117c 100644 --- a/apps/dashboard/shared/components/form/controls/time-picker-field.tsx +++ b/apps/dashboard/shared/components/form/controls/time-picker-field.tsx @@ -38,7 +38,7 @@ export function TimePickerField({ disabled, invalid, placeholder = "Pick a time", - withSeconds = true, + withSeconds = false, }: TimePickerFieldProps) { const { hours, minutes, seconds } = parseTime(value ?? "") const hasValue = !!value diff --git a/apps/dashboard/shared/components/form/fields/rhf-time-field.tsx b/apps/dashboard/shared/components/form/fields/rhf-time-field.tsx index 455c633..8a4c74a 100644 --- a/apps/dashboard/shared/components/form/fields/rhf-time-field.tsx +++ b/apps/dashboard/shared/components/form/fields/rhf-time-field.tsx @@ -20,5 +20,5 @@ export function RhfTimeField< TValues extends FieldValues, TName extends FieldPath, >(props: RhfTimeFieldProps) { - return + return } diff --git a/apps/dashboard/shared/utils/formatters.ts b/apps/dashboard/shared/utils/formatters.ts index aed158c..fce0a55 100644 --- a/apps/dashboard/shared/utils/formatters.ts +++ b/apps/dashboard/shared/utils/formatters.ts @@ -99,3 +99,11 @@ export function formatCurrency( if (isNaN(num)) return "—" return new Intl.NumberFormat(locale, { style: "currency", currency }).format(num) } + +/** + * Format text to uppercase. + */ +export function formatUppercase(value?: string | null): string { + if (value == null) return "" + return value.toUpperCase() +} diff --git a/packages/api/src/clients/job-cards.ts b/packages/api/src/clients/job-cards.ts index 19bf810..4abdbd9 100644 --- a/packages/api/src/clients/job-cards.ts +++ b/packages/api/src/clients/job-cards.ts @@ -20,6 +20,7 @@ export const JOB_CARD_ROUTES = { DELETE_ATTACHMENT: "/api/job-cards/{id}/delete-attachment", CHANGE_SERVICE_WRITER: "/api/job-cards/{id}/change-service-writer-id", CHANGE_SALES_PERSON: "/api/job-cards/{id}/change-sales-person-id", + CHANGE_TECHNICIAN: "/api/job-cards/{id}/change-technician-id", GET_PARTS: "/api/job-cards/{id}/get-parts", ADD_PART: "/api/job-cards/{id}/add-part", UPDATE_PART: "/api/job-cards/{id}/update-part", @@ -118,6 +119,10 @@ export class JobCardsClient extends CrudClient< return this.post(JOB_CARD_ROUTES.CHANGE_SALES_PERSON, payload, { params: { id } }) } + async changeTechnician(id: string, payload: ApiRequestBody) { + return this.post(JOB_CARD_ROUTES.CHANGE_TECHNICIAN, payload, { params: { id } }) + } + async getParts(id: string, params?: Record) { return this.get(JOB_CARD_ROUTES.GET_PARTS, { params: { id }, query: params as any }) }