diff options
| author | Chris Johnston <christopher_johnston@me.com> | 2024-01-11 21:38:30 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-11 16:38:30 -0500 |
| commit | 45ee9cdba4e6e7c9ac918859b89d2b2ed38d4571 (patch) | |
| tree | fab43518bdb1dce24bb9731b6f41f7f73a193282 /src/components | |
| parent | 057f3e9c533704d9f1d695e8df9c4779c983c337 (diff) | |
Assign categories to expenses (#28)
* add expense categories
* set category to Payment for reimbursements
* Insert categories as part of the migration
* Display category groups
---------
Co-authored-by: Sebastien Castiel <sebastien@castiel.me>
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/expense-form.tsx | 64 |
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"> |
