Redesign the groups page

This commit is contained in:
Sebastien Castiel
2023-12-07 20:54:11 -05:00
parent 6f077f141e
commit 0e7b879aa8
4 changed files with 86 additions and 24 deletions

View File

@@ -1,5 +1,7 @@
import { RecentGroupList } from '@/app/groups/recent-group-list' import { RecentGroupList } from '@/app/groups/recent-group-list'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { getGroups } from '@/lib/api'
import { Plus } from 'lucide-react'
import { Metadata } from 'next' import { Metadata } from 'next'
import Link from 'next/link' import Link from 'next/link'
@@ -8,15 +10,25 @@ export const metadata: Metadata = {
} }
export default async function GroupsPage() { export default async function GroupsPage() {
async function getGroupsAction(groupIds: string[]) {
'use server'
return getGroups(groupIds)
}
return ( return (
<main> <main>
<h1 className="font-bold text-2xl mb-4"> <div className="flex flex-col sm:flex-row sm:justify-between sm:items-baseline mb-2">
<Link href="/groups">Recently visited groups</Link> <h1 className="font-bold text-2xl mb-4">
</h1> <Link href="/groups">Recently visited groups</Link>
<Button asChild> </h1>
<Link href="/groups/create">New group</Link> <Button asChild>
</Button> <Link href="/groups/create">
<RecentGroupList /> <Plus className="w-4 h-4 mr-2" />
Create group
</Link>
</Button>
</div>
<RecentGroupList getGroupsAction={getGroupsAction} />
</main> </main>
) )
} }

View File

@@ -1,6 +1,8 @@
'use client' 'use client'
import { getRecentGroups } from '@/app/groups/recent-groups-helpers' import { getRecentGroups } from '@/app/groups/recent-groups-helpers'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { getGroups } from '@/lib/api'
import { Loader2 } from 'lucide-react'
import Link from 'next/link' import Link from 'next/link'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { z } from 'zod' import { z } from 'zod'
@@ -13,31 +15,69 @@ const recentGroupsSchema = z.array(
) )
type RecentGroups = z.infer<typeof recentGroupsSchema> type RecentGroups = z.infer<typeof recentGroupsSchema>
type State = { status: 'pending' } | { status: 'success'; groups: RecentGroups } type State =
| { status: 'pending' }
| { status: 'partial'; groups: RecentGroups }
| {
status: 'complete'
groups: RecentGroups
groupsDetails: Awaited<ReturnType<typeof getGroups>>
}
export function RecentGroupList() { type Props = {
getGroupsAction: (groupIds: string[]) => ReturnType<typeof getGroups>
}
export function RecentGroupList({ getGroupsAction }: Props) {
const [state, setState] = useState<State>({ status: 'pending' }) const [state, setState] = useState<State>({ status: 'pending' })
useEffect(() => { useEffect(() => {
const groupsInStorage = getRecentGroups() const groupsInStorage = getRecentGroups()
setState({ status: 'success', groups: groupsInStorage }) setState({ status: 'partial', groups: groupsInStorage })
}, []) getGroupsAction(groupsInStorage.map((g) => g.id)).then((groupsDetails) => {
setState({ status: 'complete', groups: groupsInStorage, groupsDetails })
})
}, [getGroupsAction])
if (state.status === 'pending')
return (
<p>
<Loader2 className="w-4 m-4 mr-2 inline animate-spin" /> Loading recent
groups
</p>
)
return ( return (
<ul className="flex flex-col gap-2 mt-2"> <ul className="grid grid-cols-1 gap-2 mt-2 sm:grid-cols-3">
{state.status === 'pending' ? ( {state.groups.map((group) => {
<li> const details =
<em>Loading recent groups</em> state.status === 'complete'
</li> ? state.groupsDetails.find((d) => d.id === group.id)
) : ( : null
state.groups.map(({ id, name }) => ( return (
<li key={id}> <li key={group.id}>
<Button asChild variant="outline"> <Button variant="outline" className="h-fit w-full py-3" asChild>
<Link href={`/groups/${id}`}>{name}</Link> <Link href={`/groups/${group.id}`} className="text-base">
<div className="w-full flex flex-col items-start gap-1">
<div className="text-base">{group.name}</div>
<div className="text-muted-foreground font-normal text-xs">
{details ? (
<>
{details._count.participants} participants ·{' '}
{details.currency}
</>
) : (
<>
<Loader2 className="w-3 h-3 mr-1 inline animate-spin" />
</>
)}
</div>
</div>
</Link>
</Button> </Button>
</li> </li>
)) )
)} })}
</ul> </ul>
) )
} }

View File

@@ -128,7 +128,7 @@ export function GroupForm({
<CardContent> <CardContent>
<ul className="flex flex-col gap-2"> <ul className="flex flex-col gap-2">
{fields.map((item, index) => ( {fields.map((item, index) => (
<li key={item.id}> <li key={item.key}>
<FormField <FormField
control={form.control} control={form.control}
name={`participants.${index}.name`} name={`participants.${index}.name`}

View File

@@ -1,5 +1,6 @@
import { getPrisma } from '@/lib/prisma' import { getPrisma } from '@/lib/prisma'
import { ExpenseFormValues, GroupFormValues } from '@/lib/schemas' import { ExpenseFormValues, GroupFormValues } from '@/lib/schemas'
import { delay } from '@/lib/utils'
import { Expense } from '@prisma/client' import { Expense } from '@prisma/client'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
@@ -78,6 +79,15 @@ export async function getGroupExpensesParticipants(groupId: string) {
) )
} }
export async function getGroups(groupIds: string[]) {
await delay(2000)
const prisma = await getPrisma()
return prisma.group.findMany({
where: { id: { in: groupIds } },
include: { _count: { select: { participants: true } } },
})
}
export async function updateExpense( export async function updateExpense(
groupId: string, groupId: string,
expenseId: string, expenseId: string,