finish bills , po , jobcards relational fields creation

This commit is contained in:
2026-04-13 18:48:15 +03:00
parent 3eb471a604
commit 020ffccfd6
41 changed files with 11973 additions and 467 deletions
@@ -39,7 +39,7 @@ const SALUTATION_OPTIONS = [
export type CustomerFormProps = {
resourceId?: string | null
initialData?: unknown
onSuccess?: () => void
onSuccess?: (data?: any) => void
}
// ── Default values ──
@@ -146,9 +146,9 @@ export function CustomerForm({ resourceId, initialData, onSuccess }: CustomerFor
})
return promise
},
onSuccess: () => {
onSuccess: (data) => {
form.reset()
onSuccess?.()
onSuccess?.(data)
},
})
@@ -2,11 +2,14 @@
import { useRef, useState } from "react"
import { useFormContext, useController, type FieldValues, type FieldPath } from "react-hook-form"
import { useQuery } from "@tanstack/react-query"
import { Building2, Loader2 } from "lucide-react"
import { useQuery, useQueryClient } from "@tanstack/react-query"
import { Building2, Loader2, PlusIcon } from "lucide-react"
import { useAuthApi } from "@/shared/useApi"
import { CUSTOMER_ROUTES } from "@garage/api"
import { FieldShell } from "@/shared/components/form/field-shell"
import { Field, FieldLabel, FieldError, FieldDescription } from "@/shared/components/ui/field"
import { Button } from "@/shared/components/ui/button"
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/shared/components/ui/dialog"
import { ScrollArea } from "@/shared/components/ui/scroll-area"
import {
Combobox,
ComboboxInput,
@@ -15,6 +18,7 @@ import {
ComboboxItem,
ComboboxEmpty,
} from "@/shared/components/ui/combobox"
import { CustomerForm } from "./customer-form"
// ── Customer option type (enriched for display) ──
@@ -91,6 +95,8 @@ export function RhfCustomerSelectField<
const api = useAuthApi()
const anchorRef = useRef<HTMLDivElement>(null)
const [inputValue, setInputValue] = useState("")
const [isCreateOpen, setIsCreateOpen] = useState(false)
const queryClient = useQueryClient()
const { control } = useFormContext<TValues>()
const {
@@ -123,14 +129,17 @@ export function RhfCustomerSelectField<
)
: options
return (
<FieldShell
label={label}
error={error?.message}
description={description}
required={required}
>
<div ref={anchorRef}>
const handleCreateSuccess = (data?: any) => {
const item = data?.data ?? data
if (item?.id) {
field.onChange(buildCustomerOption(item))
}
queryClient.invalidateQueries({ queryKey: [CUSTOMER_ROUTES.INDEX, "customer-select", customerType ?? "all"] })
setIsCreateOpen(false)
}
const combobox = (
<div ref={anchorRef}>
<Combobox
value={field.value}
onValueChange={(val: CustomerOption | CustomerOption[] | null) => {
@@ -200,6 +209,44 @@ export function RhfCustomerSelectField<
</ComboboxContent>
</Combobox>
</div>
</FieldShell>
)
return (
<Field data-invalid={!!error?.message || undefined}>
{label && (
<div className="flex items-center justify-between">
<FieldLabel>
{label}
{required && <span className="text-destructive ms-0.5">*</span>}
</FieldLabel>
<Button
type="button"
size="icon"
variant="ghost"
className="h-5 w-5"
onClick={() => setIsCreateOpen(true)}
title={`Add new ${label}`}
>
<PlusIcon className="h-3.5 w-3.5" />
</Button>
</div>
)}
{combobox}
{description && <FieldDescription>{description}</FieldDescription>}
{error?.message && <FieldError>{error.message}</FieldError>}
<Dialog open={isCreateOpen} onOpenChange={(v) => { if (!v) setIsCreateOpen(false) }}>
<DialogContent className="min-w-xl">
<DialogHeader>
<DialogTitle className="text-2xl font-bold">
Add {label}
</DialogTitle>
</DialogHeader>
<ScrollArea className="max-h-[80vh] px-4">
<CustomerForm onSuccess={handleCreateSuccess} />
</ScrollArea>
</DialogContent>
</Dialog>
</Field>
)
}