mirror of
https://github.com/spliit-app/spliit.git
synced 2026-03-07 20:59:05 +01:00
Add tRPC, use it for group expenses, balances and information page (#246)
* Add tRPC, use it for group expense list * Use tRPC for balances * Use tRPC in group information + better loading states
This commit is contained in:
committed by
GitHub
parent
727803ea5c
commit
66e15e419e
@@ -0,0 +1,138 @@
|
||||
'use client'
|
||||
|
||||
import { BalancesList } from '@/app/groups/[groupId]/balances-list'
|
||||
import { ReimbursementList } from '@/app/groups/[groupId]/reimbursement-list'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { getGroup } from '@/lib/api'
|
||||
import { trpc } from '@/trpc/client'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { Fragment, useEffect } from 'react'
|
||||
|
||||
export default function BalancesAndReimbursements({
|
||||
group,
|
||||
}: {
|
||||
group: NonNullable<Awaited<ReturnType<typeof getGroup>>>
|
||||
}) {
|
||||
const utils = trpc.useUtils()
|
||||
|
||||
useEffect(() => {
|
||||
// Until we use tRPC more widely and can invalidate the cache on expense
|
||||
// update, it's easier and safer to invalidate the cache on page load.
|
||||
utils.groups.balances.invalidate()
|
||||
}, [utils])
|
||||
|
||||
const t = useTranslations('Balances')
|
||||
|
||||
const { data, isLoading } = trpc.groups.balances.list.useQuery({
|
||||
groupId: group.id,
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>{t('title')}</CardTitle>
|
||||
<CardDescription>{t('description')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isLoading || !data ? (
|
||||
<BalancesLoading participantCount={group.participants.length} />
|
||||
) : (
|
||||
<BalancesList
|
||||
balances={data.balances}
|
||||
participants={group.participants}
|
||||
currency={group.currency}
|
||||
/>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>{t('Reimbursements.title')}</CardTitle>
|
||||
<CardDescription>{t('Reimbursements.description')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{isLoading || !data ? (
|
||||
<ReimbursementsLoading
|
||||
participantCount={group.participants.length}
|
||||
/>
|
||||
) : (
|
||||
<ReimbursementList
|
||||
reimbursements={data.reimbursements}
|
||||
participants={group.participants}
|
||||
currency={group.currency}
|
||||
groupId={group.id}
|
||||
/>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const ReimbursementsLoading = ({
|
||||
participantCount,
|
||||
}: {
|
||||
participantCount: number
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{Array(participantCount - 1)
|
||||
.fill(undefined)
|
||||
.map((_, index) => (
|
||||
<div key={index} className="flex justify-between py-5">
|
||||
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4">
|
||||
<Skeleton className="h-3 w-32" />
|
||||
<Skeleton className="h-3 w-24" />
|
||||
</div>
|
||||
<Skeleton className="h-3 w-16" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const BalancesLoading = ({
|
||||
participantCount,
|
||||
}: {
|
||||
participantCount: number
|
||||
}) => {
|
||||
return (
|
||||
<div className="grid grid-cols-2 py-1 gap-y-2">
|
||||
{Array(participantCount)
|
||||
.fill(undefined)
|
||||
.map((_, index) =>
|
||||
index % 2 === 0 ? (
|
||||
<Fragment key={index}>
|
||||
<div className="flex items-center justify-end pr-2">
|
||||
<Skeleton className="h-3 w-16" />
|
||||
</div>
|
||||
<div className="self-start">
|
||||
<Skeleton
|
||||
className={`h-7 w-${(index % 3) + 1}/3 rounded-l-none`}
|
||||
/>
|
||||
</div>
|
||||
</Fragment>
|
||||
) : (
|
||||
<Fragment key={index}>
|
||||
<div className="flex items-center justify-end">
|
||||
<Skeleton
|
||||
className={`h-7 w-${(index % 3) + 1}/3 rounded-r-none`}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center pl-2">
|
||||
<Skeleton className="h-3 w-16" />
|
||||
</div>
|
||||
</Fragment>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +1,6 @@
|
||||
import { cached } from '@/app/cached-functions'
|
||||
import { BalancesList } from '@/app/groups/[groupId]/balances-list'
|
||||
import { ReimbursementList } from '@/app/groups/[groupId]/reimbursement-list'
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
import { getGroupExpenses } from '@/lib/api'
|
||||
import {
|
||||
getBalances,
|
||||
getPublicBalances,
|
||||
getSuggestedReimbursements,
|
||||
} from '@/lib/balances'
|
||||
import BalancesAndReimbursements from '@/app/groups/[groupId]/balances/balances-and-reimbursements'
|
||||
import { Metadata } from 'next'
|
||||
import { getTranslations } from 'next-intl/server'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -27,44 +12,8 @@ export default async function GroupPage({
|
||||
}: {
|
||||
params: { groupId: string }
|
||||
}) {
|
||||
const t = await getTranslations('Balances')
|
||||
const group = await cached.getGroup(groupId)
|
||||
if (!group) notFound()
|
||||
|
||||
const expenses = await getGroupExpenses(groupId)
|
||||
const balances = getBalances(expenses)
|
||||
const reimbursements = getSuggestedReimbursements(balances)
|
||||
const publicBalances = getPublicBalances(reimbursements)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>{t('title')}</CardTitle>
|
||||
<CardDescription>{t('description')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<BalancesList
|
||||
balances={publicBalances}
|
||||
participants={group.participants}
|
||||
currency={group.currency}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>{t('Reimbursements.title')}</CardTitle>
|
||||
<CardDescription>{t('Reimbursements.description')}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="p-0">
|
||||
<ReimbursementList
|
||||
reimbursements={reimbursements}
|
||||
participants={group.participants}
|
||||
currency={group.currency}
|
||||
groupId={groupId}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
return <BalancesAndReimbursements group={group} />
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user