fix many bugs

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-04-30 19:03:31 +03:00
co-authored by Copilot
parent 0c43c8edd2
commit e1ef6fa2ea
24 changed files with 333 additions and 82 deletions
@@ -17,9 +17,13 @@ export function InsuranceTypeCrudDialog() {
resourceLabel="insurance type"
columns={() => [
{
accessorKey: "name",
accessorKey: "title",
header: ({ column }) => <ColumnHeader column={column} title="Name" />,
},
{
accessorKey: "description",
header: ({ column }) => <ColumnHeader column={column} title="Description" />,
},
]}
renderForm={({ resourceId, initialData, onSuccess }) => (
<InsuranceTypeForm
@@ -6,7 +6,7 @@ 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 { Rhform, RhfTextField, RhfTextareaField } from "@/shared/components/form"
import { toast } from "sonner"
import { useAuthApi } from "@/shared/useApi"
import { useEffect } from "react"
@@ -15,6 +15,7 @@ import { useEffect } from "react"
const insuranceTypeSchema = z.object({
name: z.string().min(1, "Name is required"),
description: z.string().min(1, "Description is required"),
})
type InsuranceTypeFormValues = z.infer<typeof insuranceTypeSchema>
@@ -35,22 +36,22 @@ export function InsuranceTypeForm({ resourceId, initialData, onSuccess }: Insura
const form = useForm<InsuranceTypeFormValues>({
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
/>
<RhfTextareaField
name="description"
label="Description"
placeholder="e.g. Comprehensive insurance coverage for vehicles"
rows={3}
required
/>
<Button type="submit" disabled={form.formState.isSubmitting}>
{isEditing ? <Save className="h-4 w-4" /> : <Plus className="h-4 w-4" />}
{form.formState.isSubmitting
@@ -20,7 +20,7 @@ import { Calendar } from "@/shared/components/ui/calendar"
import { toast } from "sonner"
import { Pencil, CalendarIcon, UserCog, UserCheck, Loader2 } from "lucide-react"
import { format } from "date-fns"
import { EmployeeCombobox } from "@/modules/employees/employee-combobox"
import { EmployeeCombobox, type EmployeeOption } from "@/modules/employees/employee-combobox"
type JobCardActionsProps = {
jobCardId: string
@@ -82,6 +82,16 @@ function EmployeePickerDialog({
isPending,
onSelect,
}: EmployeePickerDialogProps) {
const handleSelect = (emp: EmployeeOption | null) => {
console.log('Selected employee object:', emp, 'Value:', emp?.value, 'Type of value:', typeof emp?.value)
if (emp && emp.value) {
const employeeId = Number(emp.value)
console.log('Parsed employee ID:', employeeId, 'Type:', typeof employeeId)
onSelect(employeeId)
onOpenChange(false)
}
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-md">
@@ -91,9 +101,7 @@ function EmployeePickerDialog({
</DialogHeader>
<EmployeeCombobox
value={null}
onValueChange={(emp) => {
if (emp) onSelect(Number(emp.value))
}}
onValueChange={handleSelect}
disabled={isPending}
placeholder="Search employees..."
showClear={false}
@@ -131,7 +139,8 @@ export function JobCardActions({ jobCardId, orderDate, serviceWriterName, salesP
})
const changeServiceWriterMutation = useMutation({
mutationFn: (employeeId: number) => {
mutationFn: async (employeeId: number) => {
console.log('Sending service writer ID:', employeeId, 'Type:', typeof employeeId)
const promise = api.jobCards.changeServiceWriter(jobCardId, { service_writer_id: employeeId })
toast.promise(promise, {
loading: "Updating service writer...",
@@ -147,7 +156,8 @@ export function JobCardActions({ jobCardId, orderDate, serviceWriterName, salesP
})
const changeSalesPersonMutation = useMutation({
mutationFn: (employeeId: number) => {
mutationFn: async (employeeId: number) => {
console.log('Sending sales person ID:', employeeId, 'Type:', typeof employeeId)
const promise = api.jobCards.changeSalesPerson(jobCardId, { sales_person_id: employeeId })
toast.promise(promise, {
loading: "Updating sales person...",
@@ -162,6 +172,23 @@ export function JobCardActions({ jobCardId, orderDate, serviceWriterName, salesP
},
})
const changePrimaryTechnicianMutation = useMutation({
mutationFn: async (employeeId: number) => {
console.log('Sending primary technician ID:', employeeId, 'Type:', typeof employeeId)
const promise = api.jobCards.changeTechnician(jobCardId, { technician_id: employeeId })
toast.promise(promise, {
loading: "Updating primary technician...",
success: "Primary technician updated successfully",
error: "Failed to update primary technician",
})
return promise
},
onSuccess: () => {
setPrimaryTechnicianDialogOpen(false)
router.refresh()
},
})
return (
@@ -226,7 +253,7 @@ export function JobCardActions({ jobCardId, orderDate, serviceWriterName, salesP
icon={UserCheck}
label="Primary Technician"
value={primaryTechnicianName ?? null}
isPending={false}
isPending={changePrimaryTechnicianMutation.isPending}
/>
</button>
@@ -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)
}}
/>
<EmployeePickerDialog
@@ -248,24 +278,22 @@ export function JobCardActions({ jobCardId, orderDate, serviceWriterName, salesP
title="Change Sales Person"
description="Search and select an employee to assign as sales person."
isPending={changeSalesPersonMutation.isPending}
onSelect={(id) => changeSalesPersonMutation.mutate(id)}
/>
<EmployeePickerDialog
open={salesPersonDialogOpen}
onOpenChange={setSalesPersonDialogOpen}
title="Change Sales Person"
description="Search and select an employee to assign as sales person."
isPending={changeSalesPersonMutation.isPending}
onSelect={(id) => changeSalesPersonMutation.mutate(id)}
onSelect={(id) => {
console.log('Dialog onSelect called with:', id, 'Type:', typeof id)
changeSalesPersonMutation.mutate(id)
}}
/>
<EmployeePickerDialog
open={primaryTechnicianDialogOpen}
onOpenChange={setPrimaryTechnicianDialogOpen}
title="Change Primary Technician"
description="Search and select an employee to assign as primary technician."
isPending={false}
onSelect={()=>{}}
// 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)
}}
/>
</div>
@@ -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,
@@ -79,7 +79,7 @@ export function JobCardGeneralInfo({ jobCard }: { jobCard: JobCard }) {
return (
<div className="flex flex-col gap-6">
<JobCardActions
jobCardId={String(jobCard)}
jobCardId={String(jobCard.id )}
orderDate={jobCard.order_date ?? null}
serviceWriterName={jobCard.service_writer?.first_name}
salesPersonName={jobCard.sales_person?.first_name}
@@ -47,6 +47,7 @@ export default function JobCardPaymentsReceived() {
<PaymentReceivedForm
resourceId={resourceId}
defaultJobCard={{ id: jobCard?.id, title: jobCard?.title }}
invoiceCustomer={jobCard?.customer as any}
onSuccess={invalidateQuery}
/>
)}
@@ -83,14 +84,14 @@ export default function JobCardPaymentsReceived() {
},
},
{
accessorKey: "payment_mode_name",
accessorKey: "payment_mode",
header: ({ column }) => <ColumnHeader column={column} title="Payment Mode" />,
cell: ({ row }) => {
const item = row.original as any
return (
<div className="flex items-center gap-2">
<CreditCardIcon className="h-4 w-4 text-muted-foreground" />
<span className="capitalize">{item.payment_mode_name || "—"}</span>
<span className="capitalize">{(item.payment_mode?.title) || "—"}</span>
</div>
)
},