diff options
| author | Sebastien Castiel <sebastien@castiel.me> | 2023-12-06 15:08:52 -0500 |
|---|---|---|
| committer | Sebastien Castiel <sebastien@castiel.me> | 2023-12-06 15:08:52 -0500 |
| commit | 570aa713b1b2becf7c06c89610c75d2cb119b532 (patch) | |
| tree | b6e3ae339bd55a7a00d77ee64532ece9fc43c13f /src/components | |
| parent | fee1963284eafa6db7887103a156799b7610e13d (diff) | |
Delete expense
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/async-button.tsx | 42 | ||||
| -rw-r--r-- | src/components/expense-form.tsx | 27 | ||||
| -rw-r--r-- | src/components/group-form.tsx | 7 | ||||
| -rw-r--r-- | src/components/submit-button.tsx | 12 |
4 files changed, 75 insertions, 13 deletions
diff --git a/src/components/async-button.tsx b/src/components/async-button.tsx new file mode 100644 index 0000000..7d2bf93 --- /dev/null +++ b/src/components/async-button.tsx @@ -0,0 +1,42 @@ +'use client' +import { Button, ButtonProps } from '@/components/ui/button' +import { Loader2 } from 'lucide-react' +import { ReactNode, useState } from 'react' + +type Props = ButtonProps & { + action?: () => Promise<void> + loadingContent?: ReactNode +} + +export function AsyncButton({ + action, + children, + loadingContent, + ...props +}: Props) { + const [loading, setLoading] = useState(false) + return ( + <Button + onClick={async () => { + try { + setLoading(true) + await action?.() + } catch (err) { + console.error(err) + } finally { + setLoading(false) + } + }} + {...props} + > + {loading ? ( + <> + <Loader2 className="w-4 h-4 mr-2 animate-spin" />{' '} + {loadingContent ?? children} + </> + ) : ( + children + )} + </Button> + ) +} diff --git a/src/components/expense-form.tsx b/src/components/expense-form.tsx index 35c6aaf..3202788 100644 --- a/src/components/expense-form.tsx +++ b/src/components/expense-form.tsx @@ -1,4 +1,5 @@ 'use client' +import { AsyncButton } from '@/components/async-button' import { SubmitButton } from '@/components/submit-button' import { Card, @@ -33,10 +34,12 @@ 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 + onSubmit: (values: ExpenseFormValues) => Promise<void> + onDelete?: () => Promise<void> } -export function ExpenseForm({ group, expense, onSubmit }: Props) { +export function ExpenseForm({ group, expense, onSubmit, onDelete }: Props) { + const isCreate = expense === undefined const form = useForm<ExpenseFormValues>({ resolver: zodResolver(expenseFormSchema), defaultValues: expense @@ -54,7 +57,9 @@ export function ExpenseForm({ group, expense, onSubmit }: Props) { <form onSubmit={form.handleSubmit((values) => onSubmit(values))}> <Card> <CardHeader> - <CardTitle>Expense information</CardTitle> + <CardTitle> + {isCreate ? <>Create expense</> : <>Edit expense</>} + </CardTitle> </CardHeader> <CardContent className="grid grid-cols-1 sm:grid-cols-2 gap-6"> <FormField @@ -179,8 +184,20 @@ export function ExpenseForm({ group, expense, onSubmit }: Props) { /> </CardContent> - <CardFooter> - <SubmitButton loadingContent="Submitting…">Submit</SubmitButton> + <CardFooter className="gap-2"> + <SubmitButton loadingContent="Submitting…"> + {isCreate ? <>Create</> : <>Save</>} + </SubmitButton> + {!isCreate && onDelete && ( + <AsyncButton + type="button" + variant="destructive" + loadingContent="Deleting…" + action={onDelete} + > + Delete + </AsyncButton> + )} </CardFooter> </Card> </form> diff --git a/src/components/group-form.tsx b/src/components/group-form.tsx index 5ddc865..2e21aea 100644 --- a/src/components/group-form.tsx +++ b/src/components/group-form.tsx @@ -22,6 +22,7 @@ import { Input } from '@/components/ui/input' import { getGroup } from '@/lib/api' import { GroupFormValues, groupFormSchema } from '@/lib/schemas' import { zodResolver } from '@hookform/resolvers/zod' +import { Trash2 } from 'lucide-react' import { useFieldArray, useForm } from 'react-hook-form' export type Props = { @@ -105,11 +106,13 @@ export function GroupForm({ group, onSubmit }: Props) { <div className="flex gap-2"> <Input className="text-base" {...field} /> <Button - variant="destructive" + variant="ghost" + className="text-destructive" onClick={() => remove(index)} type="button" + size="icon" > - Remove + <Trash2 className="w-4 h-4" /> </Button> </div> </FormControl> diff --git a/src/components/submit-button.tsx b/src/components/submit-button.tsx index 7489c6c..c5215c5 100644 --- a/src/components/submit-button.tsx +++ b/src/components/submit-button.tsx @@ -1,16 +1,16 @@ -import { Button } from '@/components/ui/button' +import { Button, ButtonProps } from '@/components/ui/button' import { Loader2 } from 'lucide-react' -import { PropsWithChildren, ReactNode } from 'react' +import { ReactNode } from 'react' import { useFormState } from 'react-hook-form' -type Props = PropsWithChildren<{ +type Props = { loadingContent: ReactNode -}> +} & ButtonProps -export function SubmitButton({ children, loadingContent }: Props) { +export function SubmitButton({ children, loadingContent, ...props }: Props) { const { isSubmitting } = useFormState() return ( - <Button type="submit" disabled={isSubmitting}> + <Button type="submit" disabled={isSubmitting} {...props}> {isSubmitting ? ( <> <Loader2 className="w-4 h-4 mr-2 animate-spin" /> {loadingContent} |
