mirror of
https://github.com/spliit-app/spliit.git
synced 2026-03-07 12:56:12 +01:00
Redesign the groups page
This commit is contained in:
@@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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`}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user