filters , order inspections , inventory items , credit notes
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import type { FieldValues, UseFormReturn } from "react-hook-form"
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetFooter,
|
||||
} from "@/shared/components/ui/sheet"
|
||||
import { Button } from "@/shared/components/ui/button"
|
||||
import { Badge } from "@/shared/components/ui/badge"
|
||||
import { Rhform } from "@/shared/components/form"
|
||||
import { ScrollArea } from "@/shared/components/ui/scroll-area"
|
||||
import { Filter, FilterIcon, RotateCcwIcon } from "lucide-react"
|
||||
|
||||
type FilterDrawerProps<TFormValues extends FieldValues> = {
|
||||
form: UseFormReturn<TFormValues>
|
||||
isOpen: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
onSubmit: (values: TFormValues) => void
|
||||
onReset: () => void
|
||||
activeFilterCount: number
|
||||
title?: string
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
export function FilterDrawer<TFormValues extends FieldValues>({
|
||||
form,
|
||||
isOpen,
|
||||
onOpenChange,
|
||||
onSubmit,
|
||||
onReset,
|
||||
activeFilterCount,
|
||||
title = "Filters",
|
||||
children,
|
||||
}: FilterDrawerProps<TFormValues>) {
|
||||
return (
|
||||
<Sheet open={isOpen} onOpenChange={onOpenChange}>
|
||||
<SheetContent side="left" className="flex flex-col sm:max-w-md">
|
||||
<SheetHeader>
|
||||
<SheetTitle className="text-lg font-bold">
|
||||
<FilterIcon className="mr-2 inline h-5 w-5" />
|
||||
{title}</SheetTitle>
|
||||
</SheetHeader>
|
||||
<Rhform form={form} onSubmit={onSubmit} className="flex min-h-0 flex-1 flex-col">
|
||||
<ScrollArea className="min-h-0 flex-1 px-4">
|
||||
<div className="space-y-4 pb-4">
|
||||
{children}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
<SheetFooter className="border-t">
|
||||
<div className="flex w-full gap-2">
|
||||
<Button type="button" variant="outline" className="flex-1" onClick={onReset}>
|
||||
<RotateCcwIcon className="mr-2 h-4 w-4" />
|
||||
Reset
|
||||
</Button>
|
||||
<Button type="submit" className="flex-1">
|
||||
Apply Filters
|
||||
{activeFilterCount > 0 && (
|
||||
<Badge variant="secondary" className="ml-2 h-5 min-w-5 rounded-full px-1.5 text-xs">
|
||||
{activeFilterCount}
|
||||
</Badge>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</SheetFooter>
|
||||
</Rhform>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
)
|
||||
}
|
||||
|
||||
export function FilterTrigger({
|
||||
onClick,
|
||||
activeFilterCount,
|
||||
}: {
|
||||
onClick: () => void
|
||||
activeFilterCount: number
|
||||
}) {
|
||||
return (
|
||||
<Button variant="outline" size="sm" onClick={onClick} className="gap-2">
|
||||
<FilterIcon className="h-4 w-4" />
|
||||
Filters
|
||||
{activeFilterCount > 0 && (
|
||||
<Badge variant="secondary" className="h-5 min-w-5 rounded-full px-1.5 text-xs">
|
||||
{activeFilterCount}
|
||||
</Badge>
|
||||
)}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useMemo, useCallback } from "react"
|
||||
import { useForm, type DefaultValues, type FieldValues, type UseFormReturn } from "react-hook-form"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { useQueryStates, useQueryState, parseAsInteger } from "nuqs"
|
||||
import type { ZodType } from "zod"
|
||||
|
||||
export type UseFilterParamsOptions<TFormValues extends FieldValues> = {
|
||||
schema: ZodType<TFormValues, any, any>
|
||||
defaultValues: DefaultValues<TFormValues>
|
||||
paramsParsers: Record<string, any>
|
||||
mapParamsToFormValues: (params: Record<string, any>) => Partial<TFormValues>
|
||||
mapFormValuesToParams: (values: TFormValues) => Record<string, any>
|
||||
}
|
||||
|
||||
export type UseFilterParamsReturn<TFormValues extends FieldValues> = {
|
||||
form: UseFormReturn<TFormValues>
|
||||
isOpen: boolean
|
||||
open: () => void
|
||||
close: () => void
|
||||
onSubmit: (values: TFormValues) => void
|
||||
reset: () => void
|
||||
appliedParams: Record<string, unknown>
|
||||
activeFilterCount: number
|
||||
}
|
||||
|
||||
export function useFilterParams<TFormValues extends FieldValues>({
|
||||
schema,
|
||||
defaultValues,
|
||||
paramsParsers,
|
||||
mapParamsToFormValues,
|
||||
mapFormValuesToParams,
|
||||
}: UseFilterParamsOptions<TFormValues>): UseFilterParamsReturn<TFormValues> {
|
||||
const [params, setParams] = useQueryStates(paramsParsers)
|
||||
const [, setPage] = useQueryState("page", parseAsInteger)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
const form = useForm<TFormValues>({
|
||||
resolver: zodResolver(schema) as any,
|
||||
defaultValues,
|
||||
})
|
||||
|
||||
const open = useCallback(() => {
|
||||
const formValues = mapParamsToFormValues(params)
|
||||
form.reset({ ...defaultValues, ...formValues } as any)
|
||||
setIsOpen(true)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [params])
|
||||
|
||||
const close = useCallback(() => {
|
||||
setIsOpen(false)
|
||||
}, [])
|
||||
|
||||
const onSubmit = useCallback((values: TFormValues) => {
|
||||
const mapped = mapFormValuesToParams(values)
|
||||
const fullParams: Record<string, any> = {}
|
||||
for (const key of Object.keys(paramsParsers)) {
|
||||
fullParams[key] = mapped[key] ?? null
|
||||
}
|
||||
setParams(fullParams)
|
||||
setPage(1)
|
||||
setIsOpen(false)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [mapFormValuesToParams, paramsParsers, setParams, setPage])
|
||||
|
||||
const reset = useCallback(() => {
|
||||
const nullParams: Record<string, null> = {}
|
||||
for (const key of Object.keys(paramsParsers)) {
|
||||
nullParams[key] = null
|
||||
}
|
||||
setParams(nullParams)
|
||||
setPage(1)
|
||||
form.reset(defaultValues)
|
||||
setIsOpen(false)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [paramsParsers, setParams, setPage])
|
||||
|
||||
const appliedParams = useMemo(() => {
|
||||
const result: Record<string, unknown> = {}
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (value === null || value === undefined || value === "") continue
|
||||
if (Array.isArray(value) && value.length === 0) continue
|
||||
if (typeof value === "boolean" && value === false) continue
|
||||
result[key] = value
|
||||
}
|
||||
return result
|
||||
}, [params])
|
||||
|
||||
const activeFilterCount = Object.keys(appliedParams).length
|
||||
|
||||
return {
|
||||
form,
|
||||
isOpen,
|
||||
open,
|
||||
close,
|
||||
onSubmit,
|
||||
reset,
|
||||
appliedParams,
|
||||
activeFilterCount,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user