diff --git a/src/app/groups/[groupId]/expenses/[expenseId]/edit/loading.tsx b/src/app/groups/[groupId]/expenses/[expenseId]/edit/loading.tsx new file mode 100644 index 0000000..2b6ca56 --- /dev/null +++ b/src/app/groups/[groupId]/expenses/[expenseId]/edit/loading.tsx @@ -0,0 +1,3 @@ +export default function Loading() { + return

Loading…

+} diff --git a/src/app/groups/[groupId]/layout.tsx b/src/app/groups/[groupId]/layout.tsx new file mode 100644 index 0000000..f8030c6 --- /dev/null +++ b/src/app/groups/[groupId]/layout.tsx @@ -0,0 +1,37 @@ +import { Button } from '@/components/ui/button' +import { getGroup } from '@/lib/api' +import { ChevronLeft, Edit } from 'lucide-react' +import Link from 'next/link' +import { notFound } from 'next/navigation' +import { PropsWithChildren } from 'react' + +export default async function GroupLayout({ + children, + params: { groupId }, +}: PropsWithChildren<{ + params: { groupId: string } +}>) { + const group = await getGroup(groupId) + if (!group) notFound() + + return ( +
+
+ + +
+ +

{group.name}

+ + {children} +
+ ) +} diff --git a/src/app/groups/[groupId]/loading.tsx b/src/app/groups/[groupId]/loading.tsx new file mode 100644 index 0000000..2b6ca56 --- /dev/null +++ b/src/app/groups/[groupId]/loading.tsx @@ -0,0 +1,3 @@ +export default function Loading() { + return

Loading…

+} diff --git a/src/app/groups/[groupId]/page.tsx b/src/app/groups/[groupId]/page.tsx index 1577d7a..9547fcc 100644 --- a/src/app/groups/[groupId]/page.tsx +++ b/src/app/groups/[groupId]/page.tsx @@ -17,7 +17,7 @@ import { TableRow, } from '@/components/ui/table' import { getGroup, getGroupExpenses } from '@/lib/api' -import { ChevronLeft, ChevronRight, Edit, Plus } from 'lucide-react' +import { ChevronRight, Plus } from 'lucide-react' import Link from 'next/link' import { notFound } from 'next/navigation' @@ -32,22 +32,7 @@ export default async function GroupPage({ const expenses = await getGroupExpenses(groupId) return ( -
-
- - -
- -

{group.name}

- + <>
@@ -136,6 +121,6 @@ export default async function GroupPage({ -
+ ) } diff --git a/src/app/groups/loading.tsx b/src/app/groups/loading.tsx new file mode 100644 index 0000000..2b6ca56 --- /dev/null +++ b/src/app/groups/loading.tsx @@ -0,0 +1,3 @@ +export default function Loading() { + return

Loading…

+} diff --git a/src/app/loading.tsx b/src/app/loading.tsx new file mode 100644 index 0000000..2b6ca56 --- /dev/null +++ b/src/app/loading.tsx @@ -0,0 +1,3 @@ +export default function Loading() { + return

Loading…

+} diff --git a/src/components/expense-form.tsx b/src/components/expense-form.tsx index 056d25e..f7826c6 100644 --- a/src/components/expense-form.tsx +++ b/src/components/expense-form.tsx @@ -1,5 +1,5 @@ 'use client' -import { Button } from '@/components/ui/button' +import { SubmitButton } from '@/components/submit-button' import { Card, CardContent, @@ -175,9 +175,7 @@ export function ExpenseForm({ group, expense, onSubmit }: Props) { - + Submit diff --git a/src/components/group-form.tsx b/src/components/group-form.tsx index 0b6e560..9841cc5 100644 --- a/src/components/group-form.tsx +++ b/src/components/group-form.tsx @@ -1,4 +1,5 @@ 'use client' +import { SubmitButton } from '@/components/submit-button' import { Button } from '@/components/ui/button' import { Card, @@ -25,7 +26,7 @@ import { useFieldArray, useForm } from 'react-hook-form' export type Props = { group?: NonNullable>> - onSubmit: (groupFormValues: GroupFormValues) => void + onSubmit: (groupFormValues: GroupFormValues) => Promise } export function GroupForm({ group, onSubmit }: Props) { @@ -49,8 +50,8 @@ export function GroupForm({ group, onSubmit }: Props) { return (
{ - onSubmit(values) + onSubmit={form.handleSubmit(async (values) => { + await onSubmit(values) })} className="space-y-8" > @@ -129,7 +130,7 @@ export function GroupForm({ group, onSubmit }: Props) { - + Submit
) diff --git a/src/components/submit-button.tsx b/src/components/submit-button.tsx new file mode 100644 index 0000000..7489c6c --- /dev/null +++ b/src/components/submit-button.tsx @@ -0,0 +1,23 @@ +import { Button } from '@/components/ui/button' +import { Loader2 } from 'lucide-react' +import { PropsWithChildren, ReactNode } from 'react' +import { useFormState } from 'react-hook-form' + +type Props = PropsWithChildren<{ + loadingContent: ReactNode +}> + +export function SubmitButton({ children, loadingContent }: Props) { + const { isSubmitting } = useFormState() + return ( + + ) +} diff --git a/src/lib/api.ts b/src/lib/api.ts index 6ea26f4..10f0b3d 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -4,7 +4,8 @@ import { Expense } from '@prisma/client' import { v4 as uuidv4 } from 'uuid' export async function createGroup(groupFormValues: GroupFormValues) { - return getPrisma().group.create({ + const prisma = await getPrisma() + return prisma.group.create({ data: { id: uuidv4(), name: groupFormValues.name, @@ -36,7 +37,8 @@ export async function createExpense( throw new Error(`Invalid participant ID: ${participant}`) } - return getPrisma().expense.create({ + const prisma = await getPrisma() + return prisma.expense.create({ data: { id: uuidv4(), groupId, @@ -73,7 +75,8 @@ export async function updateExpense( throw new Error(`Invalid participant ID: ${participant}`) } - return getPrisma().expense.update({ + const prisma = await getPrisma() + return prisma.expense.update({ where: { id: expenseId }, data: { amount: expenseFormValues.amount, @@ -104,7 +107,8 @@ export async function updateGroup( const existingGroup = await getGroup(groupId) if (!existingGroup) throw new Error('Invalid group ID') - return getPrisma().group.update({ + const prisma = await getPrisma() + return prisma.group.update({ where: { id: groupId }, data: { name: groupFormValues.name, @@ -134,21 +138,24 @@ export async function updateGroup( } export async function getGroup(groupId: string) { - return getPrisma().group.findUnique({ + const prisma = await getPrisma() + return prisma.group.findUnique({ where: { id: groupId }, include: { participants: true }, }) } export async function getGroupExpenses(groupId: string) { - return getPrisma().expense.findMany({ + const prisma = await getPrisma() + return prisma.expense.findMany({ where: { groupId }, include: { paidFor: { include: { participant: true } }, paidBy: true }, }) } export async function getExpense(groupId: string, expenseId: string) { - return getPrisma().expense.findUnique({ + const prisma = await getPrisma() + return prisma.expense.findUnique({ where: { id: expenseId }, include: { paidBy: true, paidFor: true }, }) diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 773547e..210e691 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -2,7 +2,7 @@ import { PrismaClient } from '@prisma/client' let prisma: PrismaClient -export function getPrisma() { +export async function getPrisma() { if (!prisma) { if (process.env.NODE_ENV === 'production') { prisma = new PrismaClient() diff --git a/src/lib/utils.ts b/src/lib/utils.ts index ec79801..502daf3 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,10 @@ -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" - +import { clsx, type ClassValue } from 'clsx' +import { twMerge } from 'tailwind-merge' + export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } + +export function delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)) +}