feat: add logo field to settings schema and update settings client to handle file uploads
feat: integrate dialog close context in vendor select field and CRUD dialog components feat: enhance vendor general info to format status using utility function feat: implement form dialog context for managing dialog close actions feat: add async select field dialog close context for better form handling fix: update form mutation hook to close dialog on successful submission feat: extend document print types to include expense and credit note feat: add settings update payload type to include logo and other fields feat: create employee attendance and work history pages with resource management feat: implement payment made and received detail pages with actions feat: add quick shortcuts component for easy navigation in the dashboard feat: create actions for payment made and received with print and delete options feat: implement dialog close context for better dialog management feat: add error parsing utility for improved error handling in API responses
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
import { ScrollArea } from "@/shared/components/ui/scroll-area"
|
||||
import { DataTable } from "@/shared/data-view/table-view"
|
||||
import { createActionsColumn } from "@/shared/data-view/table-view"
|
||||
import { DialogCloseContext } from "@/shared/hooks/use-dialog-close"
|
||||
import { useCrudDialog, type CrudDialogClient, type UseCrudDialogOptions } from "./use-crud-dialog"
|
||||
|
||||
// ── Types ──
|
||||
@@ -89,6 +90,11 @@ export function CrudDialog<TClient extends CrudDialogClient>({
|
||||
|
||||
<Dialog open={isOpen} onOpenChange={(v) => { if (!v) handleClose() }}>
|
||||
<DialogContent className="min-w-2xl max-w-3xl">
|
||||
{/* Inner forms manage their own close via `onSuccess` → */}
|
||||
{/* `crud.handleFormSuccess`. Shadow the parent dialog's */}
|
||||
{/* close so a CrudDialog opened from inside a FormDialog */}
|
||||
{/* doesn't dismiss the outer dialog on every save. */}
|
||||
<DialogCloseContext.Provider value={crud.closeForm}>
|
||||
<DialogHeader>
|
||||
<div className="flex items-center gap-2">
|
||||
{crud.isFormOpen && (
|
||||
@@ -142,6 +148,7 @@ export function CrudDialog<TClient extends CrudDialogClient>({
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</DialogCloseContext.Provider>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/shared/compo
|
||||
import { ScrollArea } from '@/shared/components/ui/scroll-area'
|
||||
import { Plus } from 'lucide-react'
|
||||
import { cn } from '../lib/utils'
|
||||
import { DialogCloseContext } from '@/shared/hooks/use-dialog-close'
|
||||
|
||||
export const formDialogParams = {
|
||||
dialog: parseAsBoolean.withDefault(false),
|
||||
@@ -87,7 +88,9 @@ export default function FormDialog(props: {
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<ScrollArea className={`max-h-[80vh] px-4 ${cn(props.classNames?.scrollArea)}`}>
|
||||
{props.children(resourceId, { open, close , isOpen})}
|
||||
<DialogCloseContext.Provider value={close}>
|
||||
{props.children(resourceId, { open, close, isOpen })}
|
||||
</DialogCloseContext.Provider>
|
||||
</ScrollArea>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
} from "@/shared/components/ui/dialog"
|
||||
import { ScrollArea } from "@/shared/components/ui/scroll-area"
|
||||
import { PlusIcon } from "lucide-react"
|
||||
import { DialogCloseContext } from "@/shared/hooks/use-dialog-close"
|
||||
|
||||
// ── Inline create types ──
|
||||
|
||||
@@ -199,7 +200,9 @@ export function RhfAsyncSelectField<
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<ScrollArea className="max-h-[80vh] px-4">
|
||||
{createForm({ onSuccess: handleCreateSuccess })}
|
||||
<DialogCloseContext.Provider value={() => setIsCreateOpen(false)}>
|
||||
{createForm({ onSuccess: handleCreateSuccess })}
|
||||
</DialogCloseContext.Provider>
|
||||
</ScrollArea>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
"use client"
|
||||
|
||||
import { createContext, useContext } from "react"
|
||||
|
||||
// Closes the nearest dialog/sheet wrapper. `null` means the form is not
|
||||
// inside a dialog that should auto-close (or a parent has explicitly
|
||||
// shadowed the context, e.g. CrudDialog).
|
||||
export const DialogCloseContext = createContext<(() => void) | null>(null)
|
||||
|
||||
export function useDialogClose(): (() => void) | null {
|
||||
return useContext(DialogCloseContext)
|
||||
}
|
||||
@@ -3,14 +3,25 @@
|
||||
import { useMutation, type UseMutationOptions } from "@tanstack/react-query"
|
||||
import type { FieldValues, UseFormReturn } from "react-hook-form"
|
||||
import { ApiError } from "@garage/api"
|
||||
import { useDialogClose } from "./use-dialog-close"
|
||||
|
||||
export function useFormMutation<TValues extends FieldValues, TResponse = unknown>(
|
||||
form: UseFormReturn<TValues>,
|
||||
options: UseMutationOptions<TResponse, Error, TValues>,
|
||||
) {
|
||||
const closeDialog = useDialogClose()
|
||||
|
||||
return useMutation<TResponse, Error, TValues>({
|
||||
...options,
|
||||
onError: (err, vars,values, ctx) => {
|
||||
onSuccess: async (data, vars, onMutateResult, ctx) => {
|
||||
await options.onSuccess?.(data, vars, onMutateResult, ctx)
|
||||
// If this form is rendered inside a dialog wrapper that registers
|
||||
// a close handler (e.g. FormDialog), dismiss the dialog after a
|
||||
// successful submit. Plain pages render the form without the
|
||||
// provider, so this is a no-op there.
|
||||
closeDialog?.()
|
||||
},
|
||||
onError: (err, vars, values, ctx) => {
|
||||
if (err instanceof ApiError && err.validationErrors) {
|
||||
Object.entries(err.validationErrors).forEach(([field, msgs]) => {
|
||||
form.setError(field as any, { message: msgs[0] })
|
||||
|
||||
Reference in New Issue
Block a user