Skip to content

Commit

Permalink
feat(filter): transaction filter amount range
Browse files Browse the repository at this point in the history
  • Loading branch information
iammursal committed Mar 3, 2024
1 parent 0c91729 commit 5c51ff3
Show file tree
Hide file tree
Showing 12 changed files with 393 additions and 263 deletions.
10 changes: 1 addition & 9 deletions app/(common)/components/HeroSection/HeroSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function HeroSection() {
'text-success': totalBalance > 0,
})}
>
{totalBalance.toFixed(2)}
{totalBalance?.toFixed(2)}
</h2>
)}
</div>
Expand All @@ -71,14 +71,6 @@ export function HeroSection() {
</span>
</Link>
</div>
{/* <div className="flex justify-center pt-4">
<button
className="m-4 text-center px-4 py-2 border-2 border-white"
onClick={() => clearAllTransaction()}
>
Clear Transactions
</button>
</div> */}
</section>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,101 @@ import Field from '@/components/form/Field'
import { Button } from '@/components/ui/button'
import { DialogTrigger } from '@/components/ui/dialog'
import { Form } from '@/components/ui/form'
import { Label } from '@/components/ui/label'
import { Slider } from "@/components/ui/slider"
import { isSettledOptions } from './constants'
import { useFilterForm, useFilterFormState } from './hooks'


export const FilterForm = () => {
const { form, onReset, onSubmit } = useFilterForm()
const { usersOptions } = useFilterFormState()
const { form, onReset, onSubmit } = useFilterForm()
const { usersOptions } = useFilterFormState()
const amount_range = form.watch('amount_range')
const arDefault = [amount_range?.from || 0, amount_range?.to || 100]

console.log("🚀 ~ FilterForm ~ amount_range:", amount_range)

return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 mt-6 text-start"
>
<Field
form={form}
label="Is Settled"
type="select"
name="is_settled"
options={isSettledOptions}
required
/>
<Field
form={form}
label="User"
type="select"
name="user_id"
placeholder="Select a user"
options={usersOptions}
isSearchable={true}
/>
<Field
form={form}
label="Date from"
type="date-range"
name="date_range"
mode="range"
/>
<DialogTrigger className="w-full flex gap-4 pt-6">
<Button
type="submit"
variant="success"
className="w-1/2 w-md-auto"
>
Submit
</Button>
<Button
type="button"
onClick={onReset}
variant="destructive"
className="w-1/2 w-md-auto"
>
Reset
</Button>
</DialogTrigger>
</form>
</Form>
)
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 mt-6 text-start"
>
<Field
form={form}
label="Is Settled"
type="select"
name="is_settled"
options={isSettledOptions}
required
/>
<Field
form={form}
label="User"
type="select"
name="user_id"
placeholder="Select a user"
options={usersOptions}
isSearchable={true}
/>
<Field
form={form}
label="Date from"
type="date-range"
name="date_range"
mode="range"
/>
<div className="grid grid-cols-2 gap-2">
<Label className='col-span-2 mb-2'>Amount Range</Label>
<Field
form={form}
inputMode='numeric'
type="text"
name="amount_range[from]"
placeholder='From'
/>
<Field
form={form}
inputMode='numeric'
type="text"
name="amount_range[to]"
placeholder='To'
/>
<div className="col-span-2">
<Slider
key={amount_range?.from}
defaultValue={arDefault}
min={1}
max={100}
step={3}
minStepsBetweenThumbs={1}
onValueCommit={(value) => {
// @ts-ignore
form.setValue('amount_range[from]', value?.[0]);
// @ts-ignore
form.setValue('amount_range[to]', value?.[1]);
}}
/>
</div>
</div>
<DialogTrigger className="w-full flex gap-4 pt-6">
<Button
type="submit"
variant="success"
className="w-1/2 w-md-auto"
>
Submit
</Button>
<Button
type="button"
onClick={onReset}
variant="destructive"
className="w-1/2 w-md-auto"
>
Reset
</Button>
</DialogTrigger>
</form>
</Form>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import { useContext, useMemo } from 'react'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { FormSchema } from './schema'
import { merge } from 'lodash-es'

export const useFilterForm = () => {
const { filters, setFilters } = useContext(TransactionFilterContext)
console.log("🚀 ~ useFilterForm ~ filters:", filters)

const defaultValues = {
date_range:
Expand All @@ -30,6 +32,11 @@ export const useFilterForm = () => {
? '1'
: '0'
: 'undefined',

amount_range: {
from: filters?.whereBetween?.amount?.[0] || '',
to: filters?.whereBetween?.amount?.[1] || '',
},
} as z.infer<typeof FormSchema>

const form = useForm<z.infer<typeof FormSchema>>({
Expand Down Expand Up @@ -74,6 +81,17 @@ export const useFilterForm = () => {
user_id: value,
},
}
} else if (
key === 'amount_range' &&
typeof value === 'object' &&
typeof value?.from === 'number' &&
typeof value?.to === 'number'
) {
filters = merge({}, filters, {
whereBetween: {
amount: [value.from, value.to],
}
})
} else if (
key === 'date_range' &&
typeof value === 'object' &&
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { z } from 'zod'

export const FormSchema = z.object({
// enum of 0, 1, undefined
is_settled: z
.string()
.refine((val) => ['0', '1', 'undefined'].includes(val)),
user_id: z.coerce.number().optional(),
date_range: z
.object({
from: z.date().optional(),
to: z.date().optional(),
})
.optional(),
// enum of 0, 1, undefined
is_settled: z
.string()
.refine((val) => ['0', '1', 'undefined'].includes(val)),
user_id: z.coerce.number().optional(),
date_range: z
.object({
from: z.date().optional(),
to: z.date().optional(),
})
.optional(),
amount_range: z
.object({
from: z.coerce.number().optional(),
to: z.coerce.number().optional(),
})
.optional(),
})
57 changes: 27 additions & 30 deletions components/form/Field/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,36 @@ import { FieldType } from './FieldType'
import { FieldProps } from './types'

const Field: FC<Omit<FieldProps, 'renderProps'>> = (props) => {
const {
label,
name,
type,
required,
options,
form,
isClearable = true,
placeholder = '',
...otherProps
} = props
const {
formState: { errors },
} = form
const {
name,
type,
required,
options,
form,
label = '',
isClearable = true,
placeholder = '',
...otherProps
} = props
const {
formState: { errors },
} = form

return (
<FormField
control={form?.control}
render={(renderProps) => (
<FormItem>
<FormLabel className='d-block'>{label}</FormLabel>
return (
<FormField
control={form?.control}
render={(renderProps) => (
<FormItem>
{label && <FormLabel className='d-block'>{label}</FormLabel>}

<FieldType renderProps={renderProps} {...props} />
<FieldType renderProps={renderProps} {...props} />

<FormDescription className="text-destructive">
<ErrorMessage errors={errors} name={name} />
</FormDescription>
<FormMessage />
</FormItem>
)}
{...props}
/>
)
<FormMessage />
</FormItem>
)}
{...props}
/>
)
}

export default Field
66 changes: 33 additions & 33 deletions components/form/Field/types.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
import { InputHTMLAttributes } from 'react'
import {
ControllerFieldState,
ControllerRenderProps,
UseFormReturn,
UseFormStateReturn,
ControllerFieldState,
ControllerRenderProps,
UseFormReturn,
UseFormStateReturn,
} from 'react-hook-form'

export type FieldProps = Omit<
InputHTMLAttributes<HTMLInputElement>,
'form'
InputHTMLAttributes<HTMLInputElement>,
'form'
> & {
form: UseFormReturn<any, any, any>
renderProps: {
field: ControllerRenderProps<any, string>
fieldState: ControllerFieldState
formState: UseFormStateReturn<any>
}
label: string
name: string
type:
| 'text'
| 'select'
| 'search-select'
| 'date'
| 'date-range'
| 'number'
| 'checkbox'
| 'radio'
| 'textarea'
| 'datetime-local'
| 'time'
isClearable?: boolean
options?: {
label: string
value: string
}[]
error?: string
isSearchable?: boolean
form: UseFormReturn<any, any, any>
renderProps: {
field: ControllerRenderProps<any, string>
fieldState: ControllerFieldState
formState: UseFormStateReturn<any>
}
label?: string
name: string
type:
| 'text'
| 'select'
| 'search-select'
| 'date'
| 'date-range'
| 'number'
| 'checkbox'
| 'radio'
| 'textarea'
| 'datetime-local'
| 'time'
isClearable?: boolean
options?: {
label: string
value: string
}[]
error?: string
isSearchable?: boolean
mode?: 'single' | 'range'
}
Loading

0 comments on commit 5c51ff3

Please sign in to comment.