diff options
Diffstat (limited to 'src/components/expense-form.tsx')
| -rw-r--r-- | src/components/expense-form.tsx | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/src/components/expense-form.tsx b/src/components/expense-form.tsx new file mode 100644 index 0000000..056d25e --- /dev/null +++ b/src/components/expense-form.tsx @@ -0,0 +1,186 @@ +'use client' +import { Button } from '@/components/ui/button' +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card' +import { Checkbox } from '@/components/ui/checkbox' +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form' +import { Input } from '@/components/ui/input' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { getExpense, getGroup } from '@/lib/api' +import { ExpenseFormValues, expenseFormSchema } from '@/lib/schemas' +import { zodResolver } from '@hookform/resolvers/zod' +import { useForm } from 'react-hook-form' + +export type Props = { + group: NonNullable<Awaited<ReturnType<typeof getGroup>>> + expense?: NonNullable<Awaited<ReturnType<typeof getExpense>>> + onSubmit: (values: ExpenseFormValues) => void +} + +export function ExpenseForm({ group, expense, onSubmit }: Props) { + const form = useForm<ExpenseFormValues>({ + resolver: zodResolver(expenseFormSchema), + defaultValues: expense + ? { + title: expense.title, + amount: expense.amount, + paidBy: expense.paidById, + paidFor: expense.paidFor.map(({ participantId }) => participantId), + } + : { title: '', amount: 0, paidFor: [] }, + }) + + return ( + <Form {...form}> + <form onSubmit={form.handleSubmit((values) => onSubmit(values))}> + <Card> + <CardHeader> + <CardTitle>Expense information</CardTitle> + </CardHeader> + <CardContent className="grid grid-cols-2 gap-6"> + <FormField + control={form.control} + name="title" + render={({ field }) => ( + <FormItem> + <FormLabel>Expense title</FormLabel> + <FormControl> + <Input placeholder="Monday evening restaurant" {...field} /> + </FormControl> + <FormDescription> + Enter a description for the expense. + </FormDescription> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="paidBy" + render={({ field }) => ( + <FormItem> + <FormLabel>Paid by</FormLabel> + <Select + onValueChange={field.onChange} + defaultValue={field.value} + > + <SelectTrigger> + <SelectValue placeholder="Select a participant" /> + </SelectTrigger> + <SelectContent> + {group.participants.map(({ id, name }) => ( + <SelectItem key={id} value={id}> + {name} + </SelectItem> + ))} + </SelectContent> + </Select> + <FormDescription> + Select the participant who paid the expense. + </FormDescription> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="amount" + render={({ field }) => ( + <FormItem> + <FormLabel>Amount</FormLabel> + <FormControl> + <Input + type="number" + min={0.01} + step={0.01} + placeholder="0.00" + {...field} + /> + </FormControl> + <FormDescription>Enter the expense amount.</FormDescription> + <FormMessage /> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="paidFor" + render={() => ( + <FormItem> + <div className="mb-4"> + <FormLabel>Paid for</FormLabel> + <FormDescription> + Select who the expense was paid for. + </FormDescription> + </div> + {group.participants.map(({ id, name }) => ( + <FormField + key={id} + control={form.control} + name="paidFor" + render={({ field }) => { + return ( + <FormItem + key={id} + className="flex flex-row items-start space-x-3 space-y-0" + > + <FormControl> + <Checkbox + checked={field.value?.includes(id)} + onCheckedChange={(checked) => { + return checked + ? field.onChange([...field.value, id]) + : field.onChange( + field.value?.filter( + (value) => value !== id, + ), + ) + }} + /> + </FormControl> + <FormLabel className="text-sm font-normal"> + {name} + </FormLabel> + </FormItem> + ) + }} + /> + ))} + <FormMessage /> + </FormItem> + )} + /> + </CardContent> + + <CardFooter> + <Button variant="secondary" type="submit"> + Submit + </Button> + </CardFooter> + </Card> + </form> + </Form> + ) +} |
