Show a member's weekly stats on their profile
React · React · intermediate · greenfield
Adds the member-stats API client and the profile StatsPanel that shows weekly minutes, session count and streak. Handles loading and request-failure states; verified against several teammates' profiles and the numbers match the dashboard.
A fitness-community app. Roughly a third of members have stats sharing turned off in their privacy settings, so the private case is an everyday path, not a rarity.
Requirements
- Add the stats API client and the StatsPanel component. The client's documented result contract (src/api/memberStats.js) is binding for every caller.
- Members can disable stats sharing in their privacy settings. When the viewed member has sharing disabled, the panel must render the message: This member's stats are private. — never a crash, never an empty panel.
- While the request is in flight show a loading message; if the request fails show 'Could not load stats.'
- The parent renders the panel as <StatsPanel key={memberId} memberId={memberId} />, so each mounted instance sees a constant memberId.
Files touched
- src/api/memberStats.js
- src/components/StatsPanel.jsx
--- src/api/memberStats.js
+/**
+ * Fetch a member's weekly activity stats.
+ *
+ * Resolves to the stats object: { weeklyMinutes, sessions, streakDays }.
+ * Resolves to `null` when the member has stats sharing DISABLED — the API
+ * signals this with 204 No Content. Callers must handle the null result.
+ * Rejects with an Error for any other non-2xx response.
+ */
+export async function fetchMemberStats(memberId) {
+ const res = await fetch(`/api/members/${encodeURIComponent(memberId)}/stats`);
+ if (res.status === 204) {
+ return null;
+ }
+ if (!res.ok) {
+ throw new Error(`Failed to load stats: ${res.status}`);
+ }
+ return res.json();
+}
+
--- src/components/StatsPanel.jsx
+import { useEffect, useState } from "react";
+import { fetchMemberStats } from "../api/memberStats";
+
+export function StatsPanel({ memberId }) {
+ const [stats, setStats] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {