humam kerdiah 4f0a2f790f 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
2026-05-19 17:56:39 +04:00

100 lines
3.4 KiB
TypeScript

import React from 'react'
import { useQueryStates, parseAsBoolean, parseAsString } from 'nuqs'
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 { Plus } from 'lucide-react'
import { cn } from '../lib/utils'
import { DialogCloseContext } from '@/shared/hooks/use-dialog-close'
export const formDialogParams = {
dialog: parseAsBoolean.withDefault(false),
resourceId: parseAsString,
}
export function useFormDialog(paramKey?: string) {
// Default (no paramKey) uses the standard `dialog` and `resourceId` params
const defaultState = useQueryStates(formDialogParams)
// When a paramKey is provided, use prefixed params to avoid URL collisions
const prefixedState = useQueryStates({
[`${paramKey ?? "_"}_dialog`]: parseAsBoolean.withDefault(false),
[`${paramKey ?? "_"}_resourceId`]: parseAsString,
})
if (paramKey) {
const [params, setParams] = prefixedState
const dialogKey = `${paramKey}_dialog`
const resourceIdKey = `${paramKey}_resourceId`
const open = (resourceId?: string) => {
setParams({ [dialogKey]: true, [resourceIdKey]: resourceId ?? null })
}
const close = () => {
setParams({ [dialogKey]: false, [resourceIdKey]: null })
}
return {
isOpen: (params as Record<string, unknown>)[dialogKey] as boolean,
resourceId: (params as Record<string, unknown>)[resourceIdKey] as string | null,
open,
close,
}
}
const [params, setParams] = defaultState
const open = (resourceId?: string) => {
setParams({ dialog: true, resourceId: resourceId ?? null })
}
const close = () => {
setParams({ dialog: false, resourceId: null })
}
return {
isOpen: params.dialog,
resourceId: params.resourceId,
open,
close,
}
}
export default function FormDialog(props: {
children: (resourceId: string | null,
ctx: { open: (resourceId?: string) => void, close: () => void,isOpen:boolean }
) => React.ReactNode
title: string
paramKey?: string
classNames?: {
trigger?: string
dialogContent?: string
scrollArea?: string
}
}) {
const { isOpen, resourceId, open, close } = useFormDialog(props.paramKey)
return (
<>
<Button size='sm' className={cn(props.classNames?.trigger)} onClick={() => open()}>
<Plus />
{props.title}
</Button>
<Dialog open={isOpen} onOpenChange={(v) => { if (!v) close() }}>
<DialogContent className={`min-w-xl ${cn(props.classNames?.dialogContent)}`}>
<DialogHeader>
<DialogTitle className='text-2xl font-bold'>
{props.title}
</DialogTitle>
</DialogHeader>
<ScrollArea className={`max-h-[80vh] px-4 ${cn(props.classNames?.scrollArea)}`}>
<DialogCloseContext.Provider value={close}>
{props.children(resourceId, { open, close, isOpen })}
</DialogCloseContext.Provider>
</ScrollArea>
</DialogContent>
</Dialog>
</>
)
}