Skip to content

Commit 31fd37a

Browse files
CopilotJustinhSE
andauthored
fix: apply all PR reviewer feedback
Agent-Logs-Url: https://github.com/Debugging-Disciples/community-software/sessions/d3f0fef2-78ac-4e1f-b292-1f68e4126a94 Co-authored-by: JustinhSE <84724234+JustinhSE@users.noreply.github.com>
1 parent 327e109 commit 31fd37a

5 files changed

Lines changed: 46 additions & 8 deletions

File tree

‎app/admin/page.tsx‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,19 @@ export default async function AdminPage() {
1010
redirect("/");
1111
}
1212

13-
// TODO: Add role-based authorization once user roles are stored in the session.
14-
// Only users with an "admin" or "Leader" role should be allowed to access this page.
15-
// Example: if (session.user.role !== "admin") redirect("/dashboard");
13+
// Temporary authorization: restrict access to a configured allowlist of admin emails.
14+
// When roles are available on the session (e.g., session.user.role), replace this with a role check.
15+
const adminEmails =
16+
process.env.ADMIN_EMAILS
17+
?.split(",")
18+
.map((email) => email.trim())
19+
.filter(Boolean) ?? [];
20+
21+
const userEmail = session.user?.email;
22+
23+
if (!userEmail || (adminEmails.length > 0 && !adminEmails.includes(userEmail))) {
24+
redirect("/");
25+
}
1626

1727
return <AdminDashboardClient session={session} />;
1828
}

‎app/globals.css‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,11 @@ body {
7171
.engagement-score-value {
7272
animation: counter-up 0.6s ease-out forwards;
7373
}
74+
75+
@media (prefers-reduced-motion: reduce) {
76+
.badge-flame,
77+
.badge-gradient-animated,
78+
.engagement-score-value {
79+
animation: none;
80+
}
81+
}

‎components/AdminDashboardClient.tsx‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ function MiniLineGraph({ data, color }: { data: number[]; color: string }) {
115115

116116
function DonutChart({ segments }: { segments: { label: string; value: number; color: string }[] }) {
117117
const total = segments.reduce((s, seg) => s + seg.value, 0);
118+
119+
if (segments.length === 0 || total <= 0) {
120+
return <p className="text-gray-500 text-sm">No data</p>;
121+
}
122+
118123
const r = 36;
119124
const cx = 50;
120125
const cy = 50;
@@ -160,7 +165,11 @@ function DonutChart({ segments }: { segments: { label: string; value: number; co
160165
}
161166

162167
function StackedBar({ items }: { items: { label: string; value: number; color: string }[] }) {
168+
if (items.length === 0) {
169+
return <p className="text-gray-500 text-sm">No data</p>;
170+
}
163171
const max = Math.max(...items.map((i) => i.value));
172+
const safeMax = max > 0 ? max : 1;
164173
return (
165174
<div className="space-y-2">
166175
{items.map((item) => (
@@ -169,7 +178,7 @@ function StackedBar({ items }: { items: { label: string; value: number; color: s
169178
<div className="flex-1 h-5 rounded bg-white/10 overflow-hidden">
170179
<div
171180
className="h-full rounded transition-all duration-700"
172-
style={{ width: `${(item.value / max) * 100}%`, background: item.color }}
181+
style={{ width: `${(item.value / safeMax) * 100}%`, background: item.color }}
173182
/>
174183
</div>
175184
<span className="text-xs w-5 text-right" style={{ color: item.color }}>

‎components/DashboardClient.tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export function DashboardClient({ session }: DashboardClientProps) {
9898
score={mockStats.engagementScore}
9999
breakdown={mockScoreBreakdown}
100100
monthlyRank={8}
101+
allTimeRank={23}
101102
weeklyTopN={5}
102103
/>
103104
</section>

‎components/EngagementScore.tsx‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ interface EngagementScoreProps {
1313
score: number;
1414
breakdown: ScoreBreakdown;
1515
monthlyRank: number;
16+
allTimeRank?: number;
1617
weeklyTopN?: number;
1718
}
1819

1920
export function EngagementScore({
2021
score,
2122
breakdown,
2223
monthlyRank,
24+
allTimeRank,
2325
weeklyTopN,
2426
}: EngagementScoreProps) {
2527
const [period, setPeriod] = useState<"month" | "alltime">("month");
@@ -74,9 +76,10 @@ export function EngagementScore({
7476
</div>
7577

7678
{/* Period toggle */}
77-
<div className="flex gap-4 text-sm mt-1">
79+
<div className="flex gap-4 text-sm mt-1" role="group" aria-label="Score period">
7880
<button
7981
onClick={() => setPeriod("month")}
82+
aria-pressed={period === "month"}
8083
className={`pb-1 transition-colors ${
8184
period === "month"
8285
? "text-brand-cyan border-b-2 border-brand-cyan font-semibold"
@@ -87,6 +90,7 @@ export function EngagementScore({
8790
</button>
8891
<button
8992
onClick={() => setPeriod("alltime")}
93+
aria-pressed={period === "alltime"}
9094
className={`pb-1 transition-colors ${
9195
period === "alltime"
9296
? "text-brand-cyan border-b-2 border-brand-cyan font-semibold"
@@ -100,9 +104,15 @@ export function EngagementScore({
100104

101105
{/* Leaderboard position */}
102106
<div className="flex flex-wrap items-center gap-3 mb-5">
103-
<span className="text-brand-cyan font-medium text-sm">
104-
You&apos;re ranked #{monthlyRank} this month
105-
</span>
107+
{period === "month" ? (
108+
<span className="text-brand-cyan font-medium text-sm">
109+
You&apos;re ranked #{monthlyRank} this month
110+
</span>
111+
) : allTimeRank !== undefined ? (
112+
<span className="text-brand-cyan font-medium text-sm">
113+
You&apos;re ranked #{allTimeRank} all-time
114+
</span>
115+
) : null}
106116
{weeklyTopN !== undefined && (
107117
<span className="text-brand-purple font-medium text-sm">
108118
Top {weeklyTopN} this week!

0 commit comments

Comments
 (0)