diff options
| author | Sebastien Castiel <sebastien@castiel.me> | 2023-12-07 16:44:01 -0500 |
|---|---|---|
| committer | Sebastien Castiel <sebastien@castiel.me> | 2023-12-07 16:44:01 -0500 |
| commit | 0b27f90fb76a2492c17a958b6dd807e6fbbf8030 (patch) | |
| tree | bfc9c0e533eb28b43bcded9c53020dc15bca1955 /src | |
| parent | 6611e3a187e5398f686449938b7cf1ddbfcb5392 (diff) | |
Titles and navigation
Diffstat (limited to 'src')
| -rw-r--r-- | src/app/apple-icon.png | bin | 0 -> 19143 bytes | |||
| -rw-r--r-- | src/app/favicon.ico | bin | 25931 -> 15086 bytes | |||
| -rw-r--r-- | src/app/groups/[groupId]/balances/page.tsx | 5 | ||||
| -rw-r--r-- | src/app/groups/[groupId]/edit/page.tsx | 5 | ||||
| -rw-r--r-- | src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx | 5 | ||||
| -rw-r--r-- | src/app/groups/[groupId]/expenses/create/page.tsx | 5 | ||||
| -rw-r--r-- | src/app/groups/[groupId]/expenses/expense-list.tsx | 2 | ||||
| -rw-r--r-- | src/app/groups/[groupId]/expenses/page.tsx | 5 | ||||
| -rw-r--r-- | src/app/groups/[groupId]/layout.tsx | 34 | ||||
| -rw-r--r-- | src/app/groups/create/page.tsx | 16 | ||||
| -rw-r--r-- | src/app/groups/not-found.tsx | 15 | ||||
| -rw-r--r-- | src/app/groups/page.tsx | 8 | ||||
| -rw-r--r-- | src/app/icon.svg | 1 | ||||
| -rw-r--r-- | src/app/layout.tsx | 45 | ||||
| -rw-r--r-- | src/app/page.tsx | 11 | ||||
| -rw-r--r-- | src/components/group-form.tsx | 10 | ||||
| -rw-r--r-- | src/components/progress-bar.tsx | 2 | ||||
| -rw-r--r-- | src/lib/env.ts | 9 |
18 files changed, 133 insertions, 45 deletions
diff --git a/src/app/apple-icon.png b/src/app/apple-icon.png Binary files differnew file mode 100644 index 0000000..1083802 --- /dev/null +++ b/src/app/apple-icon.png diff --git a/src/app/favicon.ico b/src/app/favicon.ico Binary files differindex 718d6fe..dd2aca1 100644 --- a/src/app/favicon.ico +++ b/src/app/favicon.ico diff --git a/src/app/groups/[groupId]/balances/page.tsx b/src/app/groups/[groupId]/balances/page.tsx index ab2c582..31dbf63 100644 --- a/src/app/groups/[groupId]/balances/page.tsx +++ b/src/app/groups/[groupId]/balances/page.tsx @@ -9,8 +9,13 @@ import { } from '@/components/ui/card' import { getGroup, getGroupExpenses } from '@/lib/api' import { getBalances, getSuggestedReimbursements } from '@/lib/balances' +import { Metadata } from 'next' import { notFound } from 'next/navigation' +export const metadata: Metadata = { + title: 'Balances', +} + export default async function GroupPage({ params: { groupId }, }: { diff --git a/src/app/groups/[groupId]/edit/page.tsx b/src/app/groups/[groupId]/edit/page.tsx index f902428..1fcf225 100644 --- a/src/app/groups/[groupId]/edit/page.tsx +++ b/src/app/groups/[groupId]/edit/page.tsx @@ -1,8 +1,13 @@ import { GroupForm } from '@/components/group-form' import { getGroup, updateGroup } from '@/lib/api' import { groupFormSchema } from '@/lib/schemas' +import { Metadata } from 'next' import { notFound, redirect } from 'next/navigation' +export const metadata: Metadata = { + title: 'Settings', +} + export default async function EditGroupPage({ params: { groupId }, }: { diff --git a/src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx b/src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx index 0623f50..188d08f 100644 --- a/src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx +++ b/src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx @@ -1,8 +1,13 @@ import { ExpenseForm } from '@/components/expense-form' import { deleteExpense, getExpense, getGroup, updateExpense } from '@/lib/api' import { expenseFormSchema } from '@/lib/schemas' +import { Metadata } from 'next' import { notFound, redirect } from 'next/navigation' +export const metadata: Metadata = { + title: 'Edit expense', +} + export default async function EditExpensePage({ params: { groupId, expenseId }, }: { diff --git a/src/app/groups/[groupId]/expenses/create/page.tsx b/src/app/groups/[groupId]/expenses/create/page.tsx index 3a730d5..e603e59 100644 --- a/src/app/groups/[groupId]/expenses/create/page.tsx +++ b/src/app/groups/[groupId]/expenses/create/page.tsx @@ -1,8 +1,13 @@ import { ExpenseForm } from '@/components/expense-form' import { createExpense, getGroup } from '@/lib/api' import { expenseFormSchema } from '@/lib/schemas' +import { Metadata } from 'next' import { notFound, redirect } from 'next/navigation' +export const metadata: Metadata = { + title: 'Create expense', +} + export default async function ExpensePage({ params: { groupId }, }: { diff --git a/src/app/groups/[groupId]/expenses/expense-list.tsx b/src/app/groups/[groupId]/expenses/expense-list.tsx index 1ba7df7..907a37a 100644 --- a/src/app/groups/[groupId]/expenses/expense-list.tsx +++ b/src/app/groups/[groupId]/expenses/expense-list.tsx @@ -66,7 +66,7 @@ export function ExpenseList({ ) : ( <p className="px-6 text-sm py-6"> Your group doesn’t contain any expense yet.{' '} - <Button variant="link" asChild className="-m-3"> + <Button variant="link" asChild className="-m-4"> <Link href={`/groups/${groupId}/expenses/create`}> Create the first one </Link> diff --git a/src/app/groups/[groupId]/expenses/page.tsx b/src/app/groups/[groupId]/expenses/page.tsx index 48f355b..0ec5086 100644 --- a/src/app/groups/[groupId]/expenses/page.tsx +++ b/src/app/groups/[groupId]/expenses/page.tsx @@ -9,9 +9,14 @@ import { } from '@/components/ui/card' import { getGroup, getGroupExpenses } from '@/lib/api' import { Plus } from 'lucide-react' +import { Metadata } from 'next' import Link from 'next/link' import { notFound } from 'next/navigation' +export const metadata: Metadata = { + title: 'Expenses', +} + export default async function GroupExpensesPage({ params: { groupId }, }: { diff --git a/src/app/groups/[groupId]/layout.tsx b/src/app/groups/[groupId]/layout.tsx index 5b99dd2..5fbd7cb 100644 --- a/src/app/groups/[groupId]/layout.tsx +++ b/src/app/groups/[groupId]/layout.tsx @@ -1,31 +1,39 @@ import { GroupTabs } from '@/app/groups/[groupId]/group-tabs' import { SaveGroupLocally } from '@/app/groups/[groupId]/save-recent-group' -import { Button } from '@/components/ui/button' import { getGroup } from '@/lib/api' -import { ChevronLeft } from 'lucide-react' +import { Metadata } from 'next' import Link from 'next/link' import { notFound } from 'next/navigation' import { PropsWithChildren } from 'react' +type Props = { + params: { + groupId: string + } +} + +export async function generateMetadata({ + params: { groupId }, +}: Props): Promise<Metadata> { + const group = await getGroup(groupId) + + return { + title: { + default: group?.name ?? '', + template: `%s · ${group?.name} · Spliit`, + }, + } +} + export default async function GroupLayout({ children, params: { groupId }, -}: PropsWithChildren<{ - params: { groupId: string } -}>) { +}: PropsWithChildren<Props>) { const group = await getGroup(groupId) if (!group) notFound() return ( <> - <div className="mb-4 flex justify-between"> - <Button variant="ghost" asChild> - <Link href="/groups"> - <ChevronLeft className="w-4 h-4 mr-2" /> Back to recent groups - </Link> - </Button> - </div> - <h1 className="font-bold text-2xl mb-4"> <Link href={`/groups/${groupId}`}>{group.name}</Link> </h1> diff --git a/src/app/groups/create/page.tsx b/src/app/groups/create/page.tsx index 4f220d2..82047a5 100644 --- a/src/app/groups/create/page.tsx +++ b/src/app/groups/create/page.tsx @@ -1,9 +1,6 @@ import { GroupForm } from '@/components/group-form' -import { Button } from '@/components/ui/button' import { createGroup } from '@/lib/api' import { groupFormSchema } from '@/lib/schemas' -import { ChevronLeft } from 'lucide-react' -import Link from 'next/link' import { redirect } from 'next/navigation' export default function CreateGroupPage() { @@ -14,16 +11,5 @@ export default function CreateGroupPage() { redirect(`/groups/${group.id}`) } - return ( - <main> - <div className="mb-4"> - <Button variant="ghost" asChild> - <Link href="/groups"> - <ChevronLeft className="w-4 h-4 mr-2" /> Back to recent groups - </Link> - </Button> - </div> - <GroupForm onSubmit={createGroupAction} /> - </main> - ) + return <GroupForm onSubmit={createGroupAction} /> } diff --git a/src/app/groups/not-found.tsx b/src/app/groups/not-found.tsx new file mode 100644 index 0000000..d4532e1 --- /dev/null +++ b/src/app/groups/not-found.tsx @@ -0,0 +1,15 @@ +import { Button } from '@/components/ui/button' +import Link from 'next/link' + +export default function NotFound() { + return ( + <div className="flex flex-col gap-2"> + <p>This group does not exist.</p> + <p> + <Button asChild variant="secondary"> + <Link href="/groups">Go to recently visited groups</Link> + </Button> + </p> + </div> + ) +} diff --git a/src/app/groups/page.tsx b/src/app/groups/page.tsx index 7e4f484..86c63ac 100644 --- a/src/app/groups/page.tsx +++ b/src/app/groups/page.tsx @@ -1,10 +1,18 @@ import { RecentGroupList } from '@/app/groups/recent-group-list' import { Button } from '@/components/ui/button' +import { Metadata } from 'next' import Link from 'next/link' +export const metadata: Metadata = { + title: 'Recently visited groups', +} + export default async function GroupsPage() { return ( <main> + <h1 className="font-bold text-2xl mb-4"> + <Link href="/groups">Recently visited groups</Link> + </h1> <Button asChild> <Link href="/groups/create">New group</Link> </Button> diff --git a/src/app/icon.svg b/src/app/icon.svg new file mode 100644 index 0000000..b94b8c4 --- /dev/null +++ b/src/app/icon.svg @@ -0,0 +1 @@ +<?xml version="1.0" ?><svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><defs><style>.cls-1{fill:#ccc;}.cls-2{fill:#ed5167;}.cls-3{fill:#72d3b8;}.cls-4{fill:#55bc9c;}.cls-5{fill:#e8e8e8;}.cls-6{fill:#333538;}</style></defs><title/><g data-name="1 funnel" id="_1_funnel"><path class="cls-1" d="M261.25,134.9h0a76.62,76.62,0,0,1,76.62,76.62V423.22a0,0,0,0,1,0,0H261.25a0,0,0,0,1,0,0V134.9a0,0,0,0,1,0,0Z" transform="translate(285.06 -130.08) rotate(45)"/><path class="cls-2" d="M161.87,470.93l75.07-75.08a17.06,17.06,0,0,0,0-24.14l-40.19-40.19a17.06,17.06,0,0,0-24.14,0l-10.8,10.8a91,91,0,0,0-129.58.92c-34.17,35.14-34.17,91.69,0,126.83a91,91,0,0,0,129.58.92ZM57,447.11a57.21,57.21,0,1,1,80.9,0A57.22,57.22,0,0,1,57,447.11Z"/><rect class="cls-3" height="253.49" rx="47.61" transform="translate(511.66 282.22) rotate(180)" width="344.78" x="83.44" y="14.36"/><path class="cls-4" d="M126,184.86V97.36a40.46,40.46,0,0,0,40.46-40.45H345.23a40.44,40.44,0,0,0,40.44,40.45v87.5a40.44,40.44,0,0,0-40.44,40.44H166.44A40.46,40.46,0,0,0,126,184.86Z"/><circle class="cls-3" cx="255.83" cy="141.11" r="60.11"/><circle class="cls-3" cx="349.72" cy="141.11" r="21.88"/><circle class="cls-3" cx="161.94" cy="141.11" r="21.88"/><path class="cls-5" d="M174.13,134.9h76.62a0,0,0,0,1,0,0V346.61a76.62,76.62,0,0,1-76.62,76.62h0a0,0,0,0,1,0,0V134.9a0,0,0,0,1,0,0Z" transform="translate(559.99 326.17) rotate(135)"/><path class="cls-2" d="M350.19,471a91,91,0,0,0,129.58-.92c34.17-35.14,34.17-91.69,0-126.83a91,91,0,0,0-129.58-.92l-10.8-10.8a17.06,17.06,0,0,0-24.14,0l-40.19,40.19a17.06,17.06,0,0,0,0,24.14l75.07,75.08Zm23.89-23.88a57.21,57.21,0,1,1,80.9,0A57.2,57.2,0,0,1,374.08,447.11Z"/><circle class="cls-6" cx="256" cy="322.62" r="20.01"/></g></svg>
\ No newline at end of file diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 7c5afbe..894e2d0 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,10 +1,36 @@ import { ProgressBar } from '@/components/progress-bar' +import { Button } from '@/components/ui/button' +import { env } from '@/lib/env' import type { Metadata } from 'next' +import Image from 'next/image' +import Link from 'next/link' import './globals.css' export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app', + metadataBase: new URL(env.NEXT_PUBLIC_BASE_URL), + title: { + default: 'Spliit · Share expenses with friends and family', + template: '%s · Spliit', + }, + description: + 'Spliit is a minimalist web application to share expenses with friends and family. No ads, no account, no problem.', + openGraph: { + title: 'Spliit · Share expenses with friends and family', + description: + 'Spliit is a minimalist web application to share expenses with friends and family. No ads, no account, no problem.', + images: `/banner.png`, + type: 'website', + url: '/', + }, + twitter: { + card: 'summary_large_image', + creator: '@scastiel', + site: '@scastiel', + images: `/banner.png`, + title: 'Spliit · Share expenses with friends and family', + description: + 'Spliit is a minimalist web application to share expenses with friends and family. No ads, no account, no problem.', + }, } export default function RootLayout({ @@ -16,7 +42,20 @@ export default function RootLayout({ <html lang="en"> <body className=""> <ProgressBar /> - <div className="max-w-screen-md mx-auto p-4">{children}</div> + <header className="fixed top-0 left-0 right-0 flex justify-between bg-white bg-opacity-50 p-2 border-b backdrop-blur-sm"> + <Link className="flex items-center gap-2" href="/"> + <Image src="/logo.svg" width={30} height={30} alt="" /> + <h1 className="font-bold text-primary">Spliit</h1> + </Link> + <ul role="nav" className="flex items-center text-sm"> + <li> + <Button variant="link" asChild className="-my-3"> + <Link href="/groups">Groups</Link> + </Button> + </li> + </ul> + </header> + <div className="max-w-screen-md mx-auto p-4 pt-16">{children}</div> </body> </html> ) diff --git a/src/app/page.tsx b/src/app/page.tsx index a94e2d5..dedeb98 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,12 +1,3 @@ -import { Button } from '@/components/ui/button' -import Link from 'next/link' - export default function HomePage() { - return ( - <main> - <Button asChild variant="link"> - <Link href="/groups">My groups</Link> - </Button> - </main> - ) + return <main></main> } diff --git a/src/components/group-form.tsx b/src/components/group-form.tsx index 39edd72..d2140f0 100644 --- a/src/components/group-form.tsx +++ b/src/components/group-form.tsx @@ -22,7 +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 { Save, Trash2 } from 'lucide-react' import { useFieldArray, useForm } from 'react-hook-form' export type Props = { @@ -106,6 +106,7 @@ export function GroupForm({ group, onSubmit }: Props) { /> </CardContent> </Card> + <Card className="mb-4"> <CardHeader> <CardTitle>Participants</CardTitle> @@ -160,7 +161,12 @@ export function GroupForm({ group, onSubmit }: Props) { </CardFooter> </Card> - <SubmitButton loadingContent="Submitting…">Submit</SubmitButton> + <SubmitButton + size="lg" + loadingContent={group ? 'Saving…' : 'Creating…'} + > + <Save className="w-4 h-4 mr-2" /> {group ? <>Save</> : <> Create</>} + </SubmitButton> </form> </Form> ) diff --git a/src/components/progress-bar.tsx b/src/components/progress-bar.tsx index cbf068e..f776c97 100644 --- a/src/components/progress-bar.tsx +++ b/src/components/progress-bar.tsx @@ -6,7 +6,7 @@ export function ProgressBar() { <Next13ProgressBar height="2px" color="#64748b" - options={{ showSpinner: true }} + options={{ showSpinner: false }} showOnShallow /> ) diff --git a/src/lib/env.ts b/src/lib/env.ts new file mode 100644 index 0000000..af5616c --- /dev/null +++ b/src/lib/env.ts @@ -0,0 +1,9 @@ +import { z } from 'zod' + +const envSchema = z.object({ + NEXT_PUBLIC_BASE_URL: z.string().url(), + POSTGRES_URL_NON_POOLING: z.string().url(), + POSTGRES_PRISMA_URL: z.string().url(), +}) + +export const env = envSchema.parse(process.env) |
