Skip to content

Commit 8e0ba59

Browse files
authored
M6 dashboard: reconnect card list (#38)
* feat(dashboard): M6 reconnect card list - New `ReconnectCard` renders avatar, name, ring badge, and time-since-last-connected subtitle for each active reminder from `/api/dashboard/reconnect`. - Dashboard route now renders a `Reconnect` section under the stat cards: loading state, list of cards, or empty state with copy from design lock #21 (`You're all caught up. / Check back tomorrow or add someone new.`). - Fix typo in `RingBadge` `acquaintances` variant (`bg-tieracquaintances-bg` was missing a hyphen, so acquaintance pills had no background). Piece 2 of 3 for M6 Dashboard. Next piece wires expand-to-log interactions (We connected / snooze pill row) onto each card. * chore(deps): bump net-imap to 0.6.4 Patches GHSA-hm49-wcqc-g2xg (command injection via raw arguments) and GHSA-75xq-5h9v-w6px (command injection via unvalidated Symbol inputs). Both surfaced in CI on the M6 reconnect-card PR; bumping here to unblock that branch.
1 parent 094609f commit 8e0ba59

4 files changed

Lines changed: 59 additions & 2 deletions

File tree

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ GEM
158158
drb (~> 2.0)
159159
prism (~> 1.5)
160160
msgpack (1.8.0)
161-
net-imap (0.6.3)
161+
net-imap (0.6.4)
162162
date
163163
net-protocol
164164
net-pop (0.1.2)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Avatar } from "./Avatar";
2+
import { RingBadge } from "./RingBadge";
3+
import { relativeDays } from "@/lib/time";
4+
import type { ReconnectReminder } from "@/api/dashboard";
5+
6+
export function ReconnectCard({ reminder }: { reminder: ReconnectReminder }) {
7+
const { person } = reminder;
8+
const lastConnected = person.last_connected_at
9+
? relativeDays(person.last_connected_at)
10+
: "Not yet connected";
11+
12+
return (
13+
<article className="rounded-lg border bg-card">
14+
<div className="grid grid-cols-[auto_1fr] gap-3 px-3.5 py-3">
15+
<Avatar name={person.name} />
16+
<div className="min-w-0">
17+
<div className="flex items-start justify-between gap-2">
18+
<div className="min-w-0">
19+
<p className="truncate text-name font-medium">{person.name}</p>
20+
<p className="mt-0.5 text-label text-muted-foreground">
21+
{lastConnected}
22+
</p>
23+
</div>
24+
<div className="flex shrink-0 items-conter gap-1.5">
25+
<RingBadge ring={person.ring} />
26+
</div>
27+
</div>
28+
</div>
29+
</div>
30+
</article>
31+
);
32+
}

client/src/components/RingBadge.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const VARIANTS: Record<Ring, string> = {
1313
inner_circle: "bg-tier-inner-bg text-tier-inner-fg",
1414
network: "bg-tier-network-bg text-tier-network-fg",
1515
community: "bg-tier-community-bg text-tier-community-fg",
16-
acquaintances: "bg-tieracquaintances-bg text-tier-acquaintances-fg",
16+
acquaintances: "bg-tier-acquaintances-bg text-tier-acquaintances-fg",
1717
};
1818

1919
export function RingBadge({

client/src/routes/dashboard.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Sparkles } from "lucide-react";
12
import { useCurrentUser } from "@/auth/hooks";
23
import {
34
useReconnectReminders,
@@ -7,6 +8,8 @@ import {
78
import { useDocumentTitle } from "@/lib/use-document-title";
89
import { getGreeting } from "@/lib/greeting";
910
import { StatCard } from "@/components/StatCard";
11+
import { ReconnectCard } from "@/components/ReconnectCard";
12+
import { EmptyState } from "@/components/EmptyState";
1013

1114
function displayNameFromEmail(email: string | undefined): string {
1215
if (!email) return "";
@@ -43,6 +46,28 @@ export default function DashboardPage() {
4346
<StatCard label="Coming up soon" value={upcomingCount} />
4447
<StatCard label="In your cirle" value={peopleCount} />
4548
</div>
49+
50+
<section className="space-y-2.5">
51+
<h2 className="text-label font-medium uppercase tracking-wider text-muted-foreground">
52+
Reconnect
53+
</h2>
54+
55+
{reconnectQuery.isPending ? (
56+
<p className="text-meta text-muted-foreground">Loading...</p>
57+
) : reconnectQuery.data && reconnectQuery.data.length > 0 ? (
58+
<div className="flex flex-col gap-2.5">
59+
{reconnectQuery.data.map((reminder) => (
60+
<ReconnectCard key={reminder.id} reminder={reminder} />
61+
))}
62+
</div>
63+
) : (
64+
<EmptyState
65+
icon={Sparkles}
66+
headline="You're all caught up."
67+
subtext="Check back tomorrow or add someone new."
68+
/>
69+
)}
70+
</section>
4671
</div>
4772
);
4873
}

0 commit comments

Comments
 (0)