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.tsx64
1 files changed, 61 insertions, 3 deletions
diff --git a/src/components/expense-form.tsx b/src/components/expense-form.tsx
index d14d72e..d00211a 100644
--- a/src/components/expense-form.tsx
+++ b/src/components/expense-form.tsx
@@ -28,14 +28,17 @@ import { Input } from '@/components/ui/input'
import {
Select,
SelectContent,
+ SelectGroup,
SelectItem,
+ SelectLabel,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
-import { getExpense, getGroup } from '@/lib/api'
+import { getCategories, getExpense, getGroup } from '@/lib/api'
import { ExpenseFormValues, expenseFormSchema } from '@/lib/schemas'
import { cn } from '@/lib/utils'
import { zodResolver } from '@hookform/resolvers/zod'
+import { Category } from '@prisma/client'
import { Save, Trash2 } from 'lucide-react'
import { useSearchParams } from 'next/navigation'
import { useForm } from 'react-hook-form'
@@ -44,11 +47,18 @@ import { match } from 'ts-pattern'
export type Props = {
group: NonNullable<Awaited<ReturnType<typeof getGroup>>>
expense?: NonNullable<Awaited<ReturnType<typeof getExpense>>>
+ categories: NonNullable<Awaited<ReturnType<typeof getCategories>>>
onSubmit: (values: ExpenseFormValues) => Promise<void>
onDelete?: () => Promise<void>
}
-export function ExpenseForm({ group, expense, onSubmit, onDelete }: Props) {
+export function ExpenseForm({
+ group,
+ expense,
+ categories,
+ onSubmit,
+ onDelete,
+}: Props) {
const isCreate = expense === undefined
const searchParams = useSearchParams()
const getSelectedPayer = (field?: { value: string }) => {
@@ -67,6 +77,7 @@ export function ExpenseForm({ group, expense, onSubmit, onDelete }: Props) {
title: expense.title,
expenseDate: expense.expenseDate ?? new Date(),
amount: String(expense.amount / 100) as unknown as number, // hack
+ category: expense.categoryId,
paidBy: expense.paidById,
paidFor: expense.paidFor.map(({ participantId, shares }) => ({
participant: participantId,
@@ -82,6 +93,7 @@ export function ExpenseForm({ group, expense, onSubmit, onDelete }: Props) {
amount: String(
(Number(searchParams.get('amount')) || 0) / 100,
) as unknown as number, // hack
+ category: 1, // category with Id 1 is Payment
paidBy: searchParams.get('from') ?? undefined,
paidFor: [
searchParams.get('to')
@@ -95,6 +107,7 @@ export function ExpenseForm({ group, expense, onSubmit, onDelete }: Props) {
title: '',
expenseDate: new Date(),
amount: 0,
+ category: 0, // category with Id 0 is General
paidFor: [],
paidBy: getSelectedPayer(),
isReimbursement: false,
@@ -102,6 +115,14 @@ export function ExpenseForm({ group, expense, onSubmit, onDelete }: Props) {
},
})
+ const categoriesByGroup = categories.reduce<Record<string, Category[]>>(
+ (acc, category) => ({
+ ...acc,
+ [category.grouping]: [...(acc[category.grouping] ?? []), category],
+ }),
+ {},
+ )
+
return (
<Form {...form}>
<form onSubmit={form.handleSubmit((values) => onSubmit(values))}>
@@ -138,7 +159,7 @@ export function ExpenseForm({ group, expense, onSubmit, onDelete }: Props) {
name="expenseDate"
render={({ field }) => (
<FormItem className="sm:order-1">
- <FormLabel>Expense Date</FormLabel>
+ <FormLabel>Expense date</FormLabel>
<FormControl>
<Input
className="date-base"
@@ -203,6 +224,43 @@ export function ExpenseForm({ group, expense, onSubmit, onDelete }: Props) {
<FormField
control={form.control}
+ name="category"
+ render={({ field }) => (
+ <FormItem className="order-3 sm:order-2">
+ <FormLabel>Category</FormLabel>
+ <Select
+ onValueChange={field.onChange}
+ defaultValue={field.value.toString()}
+ >
+ <SelectTrigger>
+ <SelectValue placeholder="Select a category" />
+ </SelectTrigger>
+ <SelectContent>
+ {Object.keys(categoriesByGroup).map((group) => (
+ <SelectGroup key={group}>
+ <SelectLabel className="-ml-6">{group}</SelectLabel>
+ {categoriesByGroup[group].map(({ id, name }) => (
+ <SelectItem
+ key={id.toString()}
+ value={id.toString()}
+ >
+ {name}
+ </SelectItem>
+ ))}
+ </SelectGroup>
+ ))}
+ </SelectContent>
+ </Select>
+ <FormDescription>
+ Select the expense category.
+ </FormDescription>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ <FormField
+ control={form.control}
name="paidBy"
render={({ field }) => (
<FormItem className="sm:order-5">