aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/expense-form.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/expense-form.tsx')
-rw-r--r--src/components/expense-form.tsx186
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>
+ )
+}