fix estimate table and expense items
This commit is contained in:
parent
cc7dc1bd17
commit
6b356d2855
9
.claude/settings.local.json
Normal file
9
.claude/settings.local.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(pnpm --version)",
|
||||||
|
"Bash(grep -E \"\\\\.\\(tsx?|jsx?\\)$\")",
|
||||||
|
"Bash(grep -E \"\\\\.\\(tsx?\\)$\")"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
2
.env.prod
Normal file
2
.env.prod
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
NIXPACKS_NODE_VERSION=22
|
||||||
|
NEXT_PUBLIC_API_URL=http://reparee.test
|
||||||
@ -16,11 +16,14 @@ export default function ExpenseItemPage() {
|
|||||||
headerProps={({ selectedItem, invalidateQuery }) => ({
|
headerProps={({ selectedItem, invalidateQuery }) => ({
|
||||||
actions: (
|
actions: (
|
||||||
<FormDialog title="Expense Item">
|
<FormDialog title="Expense Item">
|
||||||
{(resourceId) => (
|
{(resourceId, { close }) => (
|
||||||
<ExpenseItemForm
|
<ExpenseItemForm
|
||||||
resourceId={resourceId}
|
resourceId={resourceId}
|
||||||
initialData={selectedItem}
|
initialData={selectedItem}
|
||||||
onSuccess={invalidateQuery}
|
onSuccess={() => {
|
||||||
|
invalidateQuery()
|
||||||
|
close()
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</FormDialog>
|
</FormDialog>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
|
import { useRouter } from 'next/navigation'
|
||||||
import { ResourcePage } from '@/shared/data-view/resource-page'
|
import { ResourcePage } from '@/shared/data-view/resource-page'
|
||||||
import { ColumnHeader } from '@/shared/data-view/table-view'
|
import { ColumnHeader } from '@/shared/data-view/table-view'
|
||||||
import FormDialog from '@/shared/components/form-dialog'
|
import FormDialog from '@/shared/components/form-dialog'
|
||||||
@ -14,11 +15,13 @@ import { getVehicleLabel } from '@/modules/vehicles/utils/getVehicleLabel'
|
|||||||
import { getFullName } from '@/shared/utils/getFullName'
|
import { getFullName } from '@/shared/utils/getFullName'
|
||||||
|
|
||||||
export default function EstimatesPage() {
|
export default function EstimatesPage() {
|
||||||
|
const router = useRouter()
|
||||||
return (
|
return (
|
||||||
<ResourcePage<EstimatesClient>
|
<ResourcePage<EstimatesClient>
|
||||||
pageTitle="Estimates"
|
pageTitle="Estimates"
|
||||||
routeKey={ESTIMATE_ROUTES.INDEX}
|
routeKey={ESTIMATE_ROUTES.INDEX}
|
||||||
getClient={(api) => api.estimates}
|
getClient={(api) => api.estimates}
|
||||||
|
onRowClick={(row) => router.push(`/sales/estimates/${(row as any).id}`)}
|
||||||
headerProps={({ selectedItem, invalidateQuery }) => ({
|
headerProps={({ selectedItem, invalidateQuery }) => ({
|
||||||
actions: (
|
actions: (
|
||||||
<FormDialog title="Estimate">
|
<FormDialog title="Estimate">
|
||||||
@ -39,7 +42,7 @@ export default function EstimatesPage() {
|
|||||||
const item = row.original
|
const item = row.original
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Link href={`/sales/estimates/${item.id}`} className="flex items-center gap-2 hover:underline">
|
<Link href={`/sales/estimates/${item.id}`} className="flex items-center gap-2 hover:underline" onClick={(e) => e.stopPropagation()}>
|
||||||
<FileTextIcon className="text-muted-foreground h-4 w-4" />
|
<FileTextIcon className="text-muted-foreground h-4 w-4" />
|
||||||
<span>{item.title}</span>
|
<span>{item.title}</span>
|
||||||
</Link>
|
</Link>
|
||||||
@ -56,11 +59,13 @@ export default function EstimatesPage() {
|
|||||||
header: ({ column }) => <ColumnHeader column={column} title="Customer" />,
|
header: ({ column }) => <ColumnHeader column={column} title="Customer" />,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const item:any = row.original
|
const item:any = row.original
|
||||||
|
if (!item.customer?.id) return "—"
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<Button variant="outline" asChild size="sm" onClick={(e) => e.stopPropagation()}>
|
||||||
<UserIcon className="h-4 w-4 text-muted-foreground" />
|
<Link href={`/sales/customers/${item.customer.id}`}>
|
||||||
<span>{getFullName(item.customer) || "—"}</span>
|
<UserIcon /> {getFullName(item.customer) || "—"}
|
||||||
</div>
|
</Link>
|
||||||
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -69,7 +74,7 @@ export default function EstimatesPage() {
|
|||||||
header: ({ column }) => <ColumnHeader column={column} title="Vehicle" />,
|
header: ({ column }) => <ColumnHeader column={column} title="Vehicle" />,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const item :any= row.original
|
const item :any= row.original
|
||||||
return <Button variant="outline" asChild size="sm">
|
return <Button variant="outline" asChild size="sm" onClick={(e) => e.stopPropagation()}>
|
||||||
<Link href={`/sales/vehicles/${item.vehicle?.id}`}>
|
<Link href={`/sales/vehicles/${item.vehicle?.id}`}>
|
||||||
<Car/> {getVehicleLabel(item.vehicle as any) || "—"}
|
<Car/> {getVehicleLabel(item.vehicle as any) || "—"}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
|
import Link from "next/link"
|
||||||
|
import { UserIcon } from "lucide-react"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import { ResourcePage } from "@/shared/data-view/resource-page"
|
import { ResourcePage } from "@/shared/data-view/resource-page"
|
||||||
import { ColumnHeader } from "@/shared/data-view/table-view"
|
import { ColumnHeader } from "@/shared/data-view/table-view"
|
||||||
import FormDialog from "@/shared/components/form-dialog"
|
import FormDialog from "@/shared/components/form-dialog"
|
||||||
import { InvoiceForm } from "@/modules/invoices/invoice-form"
|
import { InvoiceForm } from "@/modules/invoices/invoice-form"
|
||||||
import { Badge } from "@/shared/components/ui/badge"
|
import { Badge } from "@/shared/components/ui/badge"
|
||||||
|
import { Button } from "@/shared/components/ui/button"
|
||||||
import { formatCurrency, formatDate, formatEnum } from "@/shared/utils/formatters"
|
import { formatCurrency, formatDate, formatEnum } from "@/shared/utils/formatters"
|
||||||
import { INVOICE_ROUTES } from "@garage/api"
|
import { INVOICE_ROUTES } from "@garage/api"
|
||||||
import type { InvoicesClient } from "@garage/api"
|
import type { InvoicesClient } from "@garage/api"
|
||||||
@ -23,6 +26,7 @@ type InvoiceItem = {
|
|||||||
total?: number | string
|
total?: number | string
|
||||||
balance_due?: number | string
|
balance_due?: number | string
|
||||||
customer?: {
|
customer?: {
|
||||||
|
id?: number | string
|
||||||
first_name?: string
|
first_name?: string
|
||||||
last_name?: string
|
last_name?: string
|
||||||
company_name?: string
|
company_name?: string
|
||||||
@ -134,13 +138,20 @@ export default function InvoicesPage() {
|
|||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const item = row.original as unknown as InvoiceItem
|
const item = row.original as unknown as InvoiceItem
|
||||||
const customerLabel = getCustomerLabel(item)
|
const customerLabel = getCustomerLabel(item)
|
||||||
|
const subline = item.customer?.phone || item.customer?.company_name || "—"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-w-[190px]">
|
<div className="min-w-[190px]">
|
||||||
<p className="font-medium leading-none">{customerLabel}</p>
|
{item.customer?.id ? (
|
||||||
<p className="mt-1 text-xs text-muted-foreground">
|
<Button variant="outline" asChild size="sm" onClick={(e) => e.stopPropagation()}>
|
||||||
{item.customer?.phone || item.customer?.company_name || "—"}
|
<Link href={`/sales/customers/${item.customer.id}`}>
|
||||||
</p>
|
<UserIcon /> {customerLabel}
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<p className="font-medium leading-none">{customerLabel}</p>
|
||||||
|
)}
|
||||||
|
<p className="mt-1 text-xs text-muted-foreground">{subline}</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@ -140,12 +140,13 @@ const DISCOUNT_OPTIONS = DiscountType.map((value) => ({
|
|||||||
export function EstimateForm({ resourceId, initialData, onSuccess }: EstimateFormProps) {
|
export function EstimateForm({ resourceId, initialData, onSuccess }: EstimateFormProps) {
|
||||||
const api = useAuthApi()
|
const api = useAuthApi()
|
||||||
|
|
||||||
const { form, isEditing } = useResourceForm<EstimateFormValues, any>({
|
const { form, isEditing, invalidate } = useResourceForm<EstimateFormValues, any>({
|
||||||
schema: estimateFormSchema,
|
schema: estimateFormSchema,
|
||||||
defaultValues: DEFAULT_VALUES,
|
defaultValues: DEFAULT_VALUES,
|
||||||
resourceId,
|
resourceId,
|
||||||
initialData,
|
initialData,
|
||||||
queryKey: [ESTIMATE_ROUTES.BY_ID, resourceId],
|
queryKey: [ESTIMATE_ROUTES.BY_ID, resourceId],
|
||||||
|
initialize: (id) => api.estimates.show(id),
|
||||||
mapToFormValues,
|
mapToFormValues,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -163,7 +164,7 @@ export function EstimateForm({ resourceId, initialData, onSuccess }: EstimateFor
|
|||||||
return promise
|
return promise
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
form.reset()
|
if (!isEditing) form.reset()
|
||||||
onSuccess?.()
|
onSuccess?.()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -27,9 +27,9 @@ import {
|
|||||||
INVENTORY_CATEGORY_ROUTES,
|
INVENTORY_CATEGORY_ROUTES,
|
||||||
INVENTORY_ROUTES,
|
INVENTORY_ROUTES,
|
||||||
DEPARTMENT_ROUTES,
|
DEPARTMENT_ROUTES,
|
||||||
VENDOR_ROUTES,
|
|
||||||
} from "@garage/api"
|
} from "@garage/api"
|
||||||
import { InventoryCategoryCrudDialog } from "./inventory-category-crud-dialog"
|
import { InventoryCategoryCrudDialog } from "./inventory-category-crud-dialog"
|
||||||
|
import { RhfVendorSelectField } from "@/modules/vendors/rhf-vendor-select-field"
|
||||||
|
|
||||||
// ── Constants ──
|
// ── Constants ──
|
||||||
|
|
||||||
@ -84,15 +84,26 @@ function mapToFormValues(data: unknown): ExpenseItemFormValues {
|
|||||||
sku: d.sku || "",
|
sku: d.sku || "",
|
||||||
item_code: d.item_code || "",
|
item_code: d.item_code || "",
|
||||||
description: d.description || "",
|
description: d.description || "",
|
||||||
category: toRelation(d.category_id, d.category_title ?? d.category_name),
|
category: toRelation(
|
||||||
unit_type: toRelation(d.unit_type_id, d.unit_type_title ?? d.unit_type_name),
|
d.category_id ?? d.category?.id,
|
||||||
department: toRelation(d.department_id, d.department_name ?? d.department_title),
|
d.category_title ?? d.category_name ?? d.category?.title ?? d.category?.name,
|
||||||
|
),
|
||||||
|
unit_type: toRelation(
|
||||||
|
d.unit_type_id ?? d.unit_type?.id,
|
||||||
|
d.unit_type_title ?? d.unit_type_name ?? d.unit_type?.title ?? d.unit_type?.name,
|
||||||
|
),
|
||||||
|
department: toRelation(
|
||||||
|
d.department_id ?? d.department?.id,
|
||||||
|
d.department_name ?? d.department_title ?? d.department?.name ?? d.department?.title,
|
||||||
|
),
|
||||||
purchase_information: d.purchase_information ?? true,
|
purchase_information: d.purchase_information ?? true,
|
||||||
purchase_price: d.purchase_price ?? undefined,
|
purchase_price: d.purchase_price ?? undefined,
|
||||||
purchase_chart_of_account: d.purchase_chart_of_account || "",
|
purchase_chart_of_account: d.purchase_chart_of_account || "",
|
||||||
purchase_preferred_vendor: toRelation(
|
purchase_preferred_vendor: toRelation(
|
||||||
d.purchase_preferred_vendor_id,
|
d.purchase_preferred_vendor_id ?? d.purchase_preferred_vendor?.id,
|
||||||
d.purchase_preferred_vendor_name,
|
d.purchase_preferred_vendor_name
|
||||||
|
?? d.purchase_preferred_vendor?.company_name
|
||||||
|
?? [d.purchase_preferred_vendor?.first_name, d.purchase_preferred_vendor?.last_name].filter(Boolean).join(" "),
|
||||||
),
|
),
|
||||||
sales_information: d.sales_information ?? false,
|
sales_information: d.sales_information ?? false,
|
||||||
selling_price: d.selling_price ?? undefined,
|
selling_price: d.selling_price ?? undefined,
|
||||||
@ -127,7 +138,7 @@ function mapFormToPayload(values: ExpenseItemFormValues) {
|
|||||||
export function ExpenseItemForm({ resourceId, initialData, onSuccess }: ExpenseItemFormProps) {
|
export function ExpenseItemForm({ resourceId, initialData, onSuccess }: ExpenseItemFormProps) {
|
||||||
const api = useAuthApi()
|
const api = useAuthApi()
|
||||||
|
|
||||||
const { form, isEditing } = useResourceForm<ExpenseItemFormValues, any>({
|
const { form, isEditing, invalidate } = useResourceForm<ExpenseItemFormValues, any>({
|
||||||
schema: expenseItemFormSchema,
|
schema: expenseItemFormSchema,
|
||||||
defaultValues: DEFAULT_VALUES,
|
defaultValues: DEFAULT_VALUES,
|
||||||
resourceId,
|
resourceId,
|
||||||
@ -150,7 +161,7 @@ export function ExpenseItemForm({ resourceId, initialData, onSuccess }: ExpenseI
|
|||||||
return promise
|
return promise
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
form.reset()
|
if (!isEditing) form.reset()
|
||||||
onSuccess?.()
|
onSuccess?.()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -247,7 +258,7 @@ export function ExpenseItemForm({ resourceId, initialData, onSuccess }: ExpenseI
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Purchase Information */}
|
{/* Purchase Information */}
|
||||||
{/* <RhfCheckboxField
|
<RhfCheckboxField
|
||||||
name="purchase_information"
|
name="purchase_information"
|
||||||
label="Purchase Information"
|
label="Purchase Information"
|
||||||
/>
|
/>
|
||||||
@ -259,22 +270,21 @@ export function ExpenseItemForm({ resourceId, initialData, onSuccess }: ExpenseI
|
|||||||
placeholder="0.00"
|
placeholder="0.00"
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
|
{/* TODO(phase-2): wire Purchase Chart of Account to the chart-of-accounts module (currently disabled, marked "Coming soon"). */}
|
||||||
<RhfTextField
|
<RhfTextField
|
||||||
name="purchase_chart_of_account"
|
name="purchase_chart_of_account"
|
||||||
label="Purchase Chart of Account"
|
label="Purchase Chart of Account"
|
||||||
placeholder="e.g. Expenses"
|
placeholder="e.g. Expenses"
|
||||||
|
description="Coming soon"
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<RhfAsyncSelectField
|
<RhfVendorSelectField
|
||||||
name="purchase_preferred_vendor"
|
name="purchase_preferred_vendor"
|
||||||
label="Preferred Vendor"
|
label="Preferred Vendor"
|
||||||
placeholder="Select vendor"
|
placeholder="Select vendor"
|
||||||
queryKey={[VENDOR_ROUTES.INDEX]}
|
/>
|
||||||
listFn={() => api.vendors.list()}
|
|
||||||
mapOption={(item: any) => ({ value: String(item.id), label: item.name ?? String(item.id) })}
|
|
||||||
{...STORE_OBJECT}
|
|
||||||
/> */}
|
|
||||||
|
|
||||||
{/* Sales Information */}
|
{/* Sales Information */}
|
||||||
{/* <RhfCheckboxField
|
{/* <RhfCheckboxField
|
||||||
|
|||||||
@ -251,12 +251,13 @@ function TransactionDiscountField() {
|
|||||||
export function InvoiceForm({ resourceId, initialData, onSuccess }: InvoiceFormProps) {
|
export function InvoiceForm({ resourceId, initialData, onSuccess }: InvoiceFormProps) {
|
||||||
const api = useAuthApi()
|
const api = useAuthApi()
|
||||||
|
|
||||||
const { form, isEditing } = useResourceForm<InvoiceFormValues, any>({
|
const { form, isEditing, invalidate } = useResourceForm<InvoiceFormValues, any>({
|
||||||
schema: invoiceFormSchema,
|
schema: invoiceFormSchema,
|
||||||
defaultValues: DEFAULT_VALUES,
|
defaultValues: DEFAULT_VALUES,
|
||||||
resourceId,
|
resourceId,
|
||||||
initialData,
|
initialData,
|
||||||
queryKey: [INVOICE_ROUTES.BY_ID, resourceId],
|
queryKey: [INVOICE_ROUTES.BY_ID, resourceId],
|
||||||
|
initialize: (id) => api.invoices.show(id),
|
||||||
mapToFormValues,
|
mapToFormValues,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -277,7 +278,7 @@ export function InvoiceForm({ resourceId, initialData, onSuccess }: InvoiceFormP
|
|||||||
return promise
|
return promise
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
form.reset()
|
if (!isEditing) form.reset()
|
||||||
onSuccess?.()
|
onSuccess?.()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -231,12 +231,13 @@ export function JobCardForm({ resourceId, initialData, onSuccess }: JobCardFormP
|
|||||||
const api = useAuthApi()
|
const api = useAuthApi()
|
||||||
const [isCheckInDialogOpen, setIsCheckInDialogOpen] = useState(false)
|
const [isCheckInDialogOpen, setIsCheckInDialogOpen] = useState(false)
|
||||||
|
|
||||||
const { form, isEditing } = useResourceForm<JobCardFormValues, any>({
|
const { form, isEditing, invalidate } = useResourceForm<JobCardFormValues, any>({
|
||||||
schema: jobCardFormSchema,
|
schema: jobCardFormSchema,
|
||||||
defaultValues: DEFAULT_VALUES,
|
defaultValues: DEFAULT_VALUES,
|
||||||
resourceId,
|
resourceId,
|
||||||
initialData,
|
initialData,
|
||||||
queryKey: [JOB_CARD_ROUTES.BY_ID, resourceId],
|
queryKey: [JOB_CARD_ROUTES.BY_ID, resourceId],
|
||||||
|
initialize: (id) => api.jobCards.show(id),
|
||||||
mapToFormValues,
|
mapToFormValues,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -260,7 +261,7 @@ export function JobCardForm({ resourceId, initialData, onSuccess }: JobCardFormP
|
|||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setIsCheckInDialogOpen(false)
|
setIsCheckInDialogOpen(false)
|
||||||
form.reset()
|
if (!isEditing) form.reset()
|
||||||
onSuccess?.()
|
onSuccess?.()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { useMutation } from "@tanstack/react-query"
|
import { useMutation } from "@tanstack/react-query"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
import { confirm } from "@/shared/components/confirm-dialog"
|
import { confirm } from "@/shared/components/confirm-dialog"
|
||||||
@ -42,6 +42,10 @@ export function useResourcePage<TClient extends ResourcePageClient>({
|
|||||||
const { open: openDialog, close: closeDialog, isOpen, resourceId } = useFormDialog(paramKey)
|
const { open: openDialog, close: closeDialog, isOpen, resourceId } = useFormDialog(paramKey)
|
||||||
const [selectedItem, setSelectedItem] = useState<TItem | null>(null)
|
const [selectedItem, setSelectedItem] = useState<TItem | null>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!resourceId) setSelectedItem(null)
|
||||||
|
}, [resourceId])
|
||||||
|
|
||||||
const tableQuery = useDataTableQuery({
|
const tableQuery = useDataTableQuery({
|
||||||
queryKey: [routeKey],
|
queryKey: [routeKey],
|
||||||
client,
|
client,
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
import { useForm, type DefaultValues, type FieldValues, type UseFormReturn } from "react-hook-form"
|
import { useForm, type DefaultValues, type FieldValues, type UseFormReturn } from "react-hook-form"
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { useQuery, type QueryKey } from "@tanstack/react-query"
|
import { useQuery, useQueryClient, type QueryKey } from "@tanstack/react-query"
|
||||||
import type { ZodType } from "zod"
|
import type { ZodType } from "zod"
|
||||||
|
|
||||||
type UseResourceFormOptions<TFormValues extends FieldValues, TApiData = unknown> = {
|
type UseResourceFormOptions<TFormValues extends FieldValues, TApiData = unknown> = {
|
||||||
@ -20,6 +20,7 @@ type UseResourceFormReturn<TFormValues extends FieldValues> = {
|
|||||||
form: UseFormReturn<TFormValues>
|
form: UseFormReturn<TFormValues>
|
||||||
isEditing: boolean
|
isEditing: boolean
|
||||||
isInitializing: boolean
|
isInitializing: boolean
|
||||||
|
invalidate: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useResourceForm<TFormValues extends FieldValues, TApiData = unknown>({
|
export function useResourceForm<TFormValues extends FieldValues, TApiData = unknown>({
|
||||||
@ -32,11 +33,15 @@ export function useResourceForm<TFormValues extends FieldValues, TApiData = unkn
|
|||||||
queryKey,
|
queryKey,
|
||||||
}: UseResourceFormOptions<TFormValues, TApiData>): UseResourceFormReturn<TFormValues> {
|
}: UseResourceFormOptions<TFormValues, TApiData>): UseResourceFormReturn<TFormValues> {
|
||||||
const isEditing = !!resourceId
|
const isEditing = !!resourceId
|
||||||
|
const queryClient = useQueryClient()
|
||||||
|
const resolvedQueryKey = queryKey ?? ["resource", resourceId]
|
||||||
|
|
||||||
const { data: queriedData, isLoading: isQueryLoading } = useQuery<TApiData>({
|
const { data: queriedData, isLoading: isQueryLoading } = useQuery<TApiData>({
|
||||||
queryKey: queryKey ?? ["resource", resourceId],
|
queryKey: resolvedQueryKey,
|
||||||
queryFn: () => initialize!(resourceId!),
|
queryFn: () => initialize!(resourceId!),
|
||||||
enabled: isEditing && !!initialize,
|
enabled: isEditing && !!initialize,
|
||||||
|
staleTime: 0,
|
||||||
|
refetchOnMount: "always",
|
||||||
})
|
})
|
||||||
|
|
||||||
const resolvedData = queriedData ?? (isEditing ? initialData : undefined)
|
const resolvedData = queriedData ?? (isEditing ? initialData : undefined)
|
||||||
@ -49,7 +54,7 @@ export function useResourceForm<TFormValues extends FieldValues, TApiData = unkn
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isEditing) {
|
if (!isEditing) {
|
||||||
if (initialData) {
|
if (initialData) {
|
||||||
form.reset({ ...defaultValues, ...initialData } as any)
|
form.reset({ ...defaultValues, ...mapToFormValues(initialData) } as any)
|
||||||
} else {
|
} else {
|
||||||
form.reset(defaultValues)
|
form.reset(defaultValues)
|
||||||
}
|
}
|
||||||
@ -61,5 +66,9 @@ export function useResourceForm<TFormValues extends FieldValues, TApiData = unkn
|
|||||||
}
|
}
|
||||||
}, [isEditing, resolvedData, initialData]) // eslint-disable-line react-hooks/exhaustive-deps
|
}, [isEditing, resolvedData, initialData]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
return { form, isEditing, isInitializing: isEditing && !!initialize && isQueryLoading }
|
const invalidate = () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: resolvedQueryKey })
|
||||||
|
}
|
||||||
|
|
||||||
|
return { form, isEditing, isInitializing: isEditing && !!initialize && isQueryLoading, invalidate }
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user