Skip to content

Commit 09cd433

Browse files
feat(web): use aggregated dashboard endpoint
1 parent 5b073e9 commit 09cd433

1 file changed

Lines changed: 50 additions & 52 deletions

File tree

apps/web/app/dashboard/page.tsx

Lines changed: 50 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Link from 'next/link';
2-
import { ActivityDto, RecurringExpenseDto, SimplifySuggestionDto } from '@fairshare/shared-types';
2+
import { ActivityDto, GroupDashboardDto } from '@fairshare/shared-types';
33
import {
44
ArrowUpRight,
55
CreditCard,
@@ -17,14 +17,6 @@ import { GlassCard } from '../../components/ui/GlassCard';
1717

1818
type Group = { id: string; name: string; currency: string };
1919

20-
type AttentionItem = {
21-
groupId: string;
22-
groupName: string;
23-
currency: string;
24-
settlementCount: number;
25-
dueRecurringCount: number;
26-
};
27-
2820
function formatMoney(cents: string | null, currency: string | null): string {
2921
if (!cents || !currency) {
3022
return '$0.00';
@@ -34,41 +26,16 @@ function formatMoney(cents: string | null, currency: string | null): string {
3426
return amount.toLocaleString(undefined, { style: 'currency', currency });
3527
}
3628

37-
function isRecurringDue(nextOccurrenceAt: string): boolean {
38-
return new Date(nextOccurrenceAt).getTime() <= Date.now();
39-
}
40-
4129
export default async function DashboardPage() {
42-
const [groups, recentActivityData, summary] = await Promise.all([
43-
backendFetch<Group[]>('/groups'),
30+
const [dashboard, recentActivityData] = await Promise.all([
31+
backendFetch<GroupDashboardDto>('/groups/dashboard'),
4432
backendFetch<{ items: ActivityDto[] }>('/activity'),
45-
backendFetch<{ totalBalanceCents: string }>('/groups/summary'),
4633
]);
4734

48-
const attentionCandidates = await Promise.all(
49-
groups.slice(0, 6).map(async (group) => {
50-
const [suggestions, recurringExpenses] = await Promise.all([
51-
backendFetch<SimplifySuggestionDto[]>(`/groups/${group.id}/simplify`).catch(() => []),
52-
backendFetch<RecurringExpenseDto[]>(`/groups/${group.id}/recurring-expenses`).catch(() => []),
53-
]);
54-
55-
return {
56-
groupId: group.id,
57-
groupName: group.name,
58-
currency: group.currency,
59-
settlementCount: suggestions.length,
60-
dueRecurringCount: recurringExpenses.filter((item) => isRecurringDue(item.nextOccurrenceAt)).length,
61-
} satisfies AttentionItem;
62-
}),
63-
);
64-
65-
const attentionItems = attentionCandidates
66-
.filter((item) => item.settlementCount > 0 || item.dueRecurringCount > 0)
67-
.sort((a, b) => (b.settlementCount + b.dueRecurringCount) - (a.settlementCount + a.dueRecurringCount))
68-
.slice(0, 4);
69-
35+
const groups: Group[] = dashboard.groups;
36+
const attentionItems = dashboard.attentionItems.slice(0, 4);
7037
const recentActivity = recentActivityData.items || [];
71-
const totalBalanceCents = summary.totalBalanceCents;
38+
const totalBalanceCents = dashboard.totalBalanceCents;
7239
const isPositive = Number(totalBalanceCents) >= 0;
7340

7441
return (
@@ -85,7 +52,7 @@ export default async function DashboardPage() {
8552
/>
8653
<SummaryCard
8754
title="Active Crews"
88-
value={groups.length.toString()}
55+
value={dashboard.activeGroupCount.toString()}
8956
icon="users"
9057
change="Live"
9158
trend="neutral"
@@ -113,20 +80,31 @@ export default async function DashboardPage() {
11380
<GlassCard className="p-5 sm:p-6 border-white/5 bg-white/[0.01]">
11481
<div className="flex items-start justify-between gap-4 mb-5">
11582
<div>
116-
<p className="text-[10px] font-bold tracking-widest text-zinc-500 uppercase">Needs Attention</p>
117-
<h2 className="mt-1 text-xl font-black italic tracking-tight text-white uppercase">Resolve the next blockers</h2>
83+
<p className="text-[10px] font-bold tracking-widest text-zinc-500 uppercase">
84+
Needs Attention
85+
</p>
86+
<h2 className="mt-1 text-xl font-black italic tracking-tight text-white uppercase">
87+
Resolve the next blockers
88+
</h2>
11889
</div>
11990
<div className="h-10 w-10 rounded-2xl border border-amber-500/20 bg-amber-500/10 flex items-center justify-center text-amber-400">
12091
<AlertTriangle size={18} />
12192
</div>
12293
</div>
12394
<div className="grid gap-3 md:grid-cols-2">
12495
{attentionItems.map((item) => (
125-
<div key={item.groupId} className="rounded-2xl border border-white/5 bg-white/5 p-4">
96+
<div
97+
key={item.groupId}
98+
className="rounded-2xl border border-white/5 bg-white/5 p-4"
99+
>
126100
<div className="flex items-start justify-between gap-3">
127101
<div>
128-
<p className="text-sm font-black tracking-tight text-white uppercase">{item.groupName}</p>
129-
<p className="mt-1 text-[11px] font-bold uppercase tracking-[0.16em] text-zinc-500">{item.currency}</p>
102+
<p className="text-sm font-black tracking-tight text-white uppercase">
103+
{item.groupName}
104+
</p>
105+
<p className="mt-1 text-[11px] font-bold uppercase tracking-[0.16em] text-zinc-500">
106+
{item.currency} · {item.memberCount} members
107+
</p>
130108
</div>
131109
<Link
132110
href={`/dashboard/groups/${item.groupId}`}
@@ -154,6 +132,9 @@ export default async function DashboardPage() {
154132
{item.dueRecurringCount} due recurring
155133
</Link>
156134
) : null}
135+
<span className="inline-flex items-center gap-2 rounded-full bg-white/5 px-3 py-1.5 text-[11px] font-bold uppercase tracking-[0.12em] text-zinc-300">
136+
{formatMoney(item.netBalanceCents, item.currency)}
137+
</span>
157138
</div>
158139
</div>
159140
))}
@@ -176,8 +157,12 @@ export default async function DashboardPage() {
176157
<GlassCard className="p-5 sm:p-8 border-white/5 bg-white/[0.01]">
177158
<div className="flex items-center justify-between mb-8">
178159
<div>
179-
<h2 className="text-sm font-black italic tracking-tight text-white uppercase">Crews & Nodes</h2>
180-
<p className="text-[10px] font-bold tracking-widest text-zinc-600 uppercase mt-0.5">Distributed spending groups</p>
160+
<h2 className="text-sm font-black italic tracking-tight text-white uppercase">
161+
Crews & Nodes
162+
</h2>
163+
<p className="text-[10px] font-bold tracking-widest text-zinc-600 uppercase mt-0.5">
164+
Distributed spending groups
165+
</p>
181166
</div>
182167
<button
183168
title="Create New Crew"
@@ -189,22 +174,35 @@ export default async function DashboardPage() {
189174

190175
<div className="grid gap-3 grid-cols-1 sm:grid-cols-2">
191176
{groups.map((group) => (
192-
<Link href={`/dashboard/groups/${group.id}`} key={group.id} className="flex items-center justify-between p-4 rounded-xl border border-white/5 bg-white/5 hover:bg-white/10 transition-all cursor-pointer group">
177+
<Link
178+
href={`/dashboard/groups/${group.id}`}
179+
key={group.id}
180+
className="flex items-center justify-between p-4 rounded-xl border border-white/5 bg-white/5 hover:bg-white/10 transition-all cursor-pointer group"
181+
>
193182
<div className="flex items-center gap-3">
194183
<div className="h-8 w-8 rounded-lg bg-zinc-900 flex items-center justify-center text-zinc-500 group-hover:text-purple-400 transition-colors">
195184
<CreditCard size={14} />
196185
</div>
197-
<span className="text-xs font-black tracking-tight text-white uppercase">{group.name}</span>
186+
<span className="text-xs font-black tracking-tight text-white uppercase">
187+
{group.name}
188+
</span>
198189
</div>
199190
<div className="flex items-center gap-2">
200-
<span className="text-[10px] font-bold text-zinc-500 uppercase tracking-widest">{group.currency}</span>
201-
<ArrowUpRight size={12} className="text-zinc-800 group-hover:text-purple-500 transition-colors" />
191+
<span className="text-[10px] font-bold text-zinc-500 uppercase tracking-widest">
192+
{group.currency}
193+
</span>
194+
<ArrowUpRight
195+
size={12}
196+
className="text-zinc-800 group-hover:text-purple-500 transition-colors"
197+
/>
202198
</div>
203199
</Link>
204200
))}
205201
{groups.length === 0 && (
206202
<div className="col-span-full py-12 text-center rounded-2xl border border-dashed border-white/5">
207-
<p className="text-[10px] font-black uppercase tracking-[0.2em] text-zinc-700">NO_ACTIVE_GROUPS_LOCATED</p>
203+
<p className="text-[10px] font-black uppercase tracking-[0.2em] text-zinc-700">
204+
NO_ACTIVE_GROUPS_LOCATED
205+
</p>
208206
</div>
209207
)}
210208
</div>

0 commit comments

Comments
 (0)