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 (
)
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))
+}