feat: implement seeker dashboard#227
Conversation
📝 WalkthroughWalkthroughThis PR introduces seeker and expert dashboard components, adds supporting type definitions and mock data, integrates the seeker dashboard into the main dashboard page, and updates the wallet connection modal to support Freighter and Albedo wallets. ChangesSeeker and Expert Dashboard
Wallet Modal Connection Update
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed due to a network error. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/components/dashboard/SeekerOverview.tsx (1)
48-99: ⚡ Quick winHoist
SessionCardout of the render body.Defining
SessionCardinsideSeekerOverviewcreates a new component type on every render, causing React to unmount/remount the subtree instead of updating it. Move it to module scope (passingroutervia prop or keeping the navigation in the parent) so its identity is stable.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/dashboard/SeekerOverview.tsx` around lines 48 - 99, SessionCard is declared inside the SeekerOverview render which recreates the component type on every render causing remounts; move SessionCard to module scope as a standalone component (export or local) and pass any needed values from SeekerOverview via props (e.g., pass router or a onViewDetails(sessionId) callback and session data) so SessionCard no longer closes over SeekerOverview's router and has a stable identity.src/components/dashboard/AvailabilityToggle.tsx (1)
34-46: ⚡ Quick winExpose toggle state to assistive tech.
The button visually conveys on/off but screen readers only hear a static label. Add
role="switch"andaria-checkedso the state is announced.♿ Proposed a11y fix
<button onClick={handleToggle} + role="switch" + aria-checked={isAvailable} className={`relative inline-flex h-8 w-14 items-center rounded-full transition-colors ${ isAvailable ? "bg-green-500/30" : "bg-slate-700/30" }`} aria-label="Toggle availability" >🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/dashboard/AvailabilityToggle.tsx` around lines 34 - 46, The toggle button in AvailabilityToggle.tsx uses handleToggle and isAvailable but lacks accessibility state semantics; update the <button> element to include role="switch" and set aria-checked={isAvailable} so assistive tech announces on/off state (keep the existing aria-label and click handler intact).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/CreateWalletModal.tsx`:
- Around line 45-49: The connection timer in CreateWalletModal uses setTimeout
that can fire after the modal has closed; store the timeout ID (e.g., in a ref
or local variable) when you call setTimeout for the wallet connection simulation
and call clearTimeout(timeoutId) in the component cleanup or whenever the modal
is closed/restarted so the old callback cannot call setState; update the logic
around the setTimeout (the block that checks wallet === "Freighter" /
Math.random) to clear any existing timer before starting a new one and to clear
it on unmount (useEffect cleanup) to prevent stale state transitions.
In `@src/components/dashboard/ExpertDashboard.tsx`:
- Around line 213-216: The Session Completion percentage can produce NaN when
totalSessions === 0; inside the ExpertDashboard component compute a guarded
completion value (e.g. derive a completionRate variable) that checks
totalSessions > 0 before doing Math.round((completedSessions / totalSessions) *
100) and falls back to a safe value like 0 (or '--') and then render that
variable in the JSX instead of the raw expression; update the JSX block that
currently contains Math.round((completedSessions / totalSessions) * 100) to use
the new guarded completionRate.
---
Nitpick comments:
In `@src/components/dashboard/AvailabilityToggle.tsx`:
- Around line 34-46: The toggle button in AvailabilityToggle.tsx uses
handleToggle and isAvailable but lacks accessibility state semantics; update the
<button> element to include role="switch" and set aria-checked={isAvailable} so
assistive tech announces on/off state (keep the existing aria-label and click
handler intact).
In `@src/components/dashboard/SeekerOverview.tsx`:
- Around line 48-99: SessionCard is declared inside the SeekerOverview render
which recreates the component type on every render causing remounts; move
SessionCard to module scope as a standalone component (export or local) and pass
any needed values from SeekerOverview via props (e.g., pass router or a
onViewDetails(sessionId) callback and session data) so SessionCard no longer
closes over SeekerOverview's router and has a stable identity.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 47a8f106-1671-4bd8-b2c5-36d75fae9ba7
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (7)
src/app/dashboard/page.tsxsrc/components/CreateWalletModal.tsxsrc/components/dashboard/AvailabilityToggle.tsxsrc/components/dashboard/ExpertDashboard.tsxsrc/components/dashboard/SeekerOverview.tsxutils/data/mock-data.tsutils/types/types.ts
| setTimeout(() => { | ||
| // deterministic success for MetaMask, error for Coinbase, random for WalletConnect | ||
| if (wallet === "MetaMask") setState("connected"); | ||
| else if (wallet === "Coinbase Wallet") setState("error"); | ||
| // deterministic success for Freighter, random for Albedo | ||
| if (wallet === "Freighter") setState("connected"); | ||
| else setState(Math.random() > 0.4 ? "connected" : "error"); | ||
| }, 1200); |
There was a problem hiding this comment.
Clear pending connection timeout to avoid stale state transitions.
On Line 45, the timer is not canceled. If the modal closes/reopens before it fires, the old callback can flip state in a later session.
Suggested fix
export default function CreateWalletModal({ open, onClose }: Props) {
const [state, setState] = useState<ModalState>("idle");
const [method, setMethod] = useState<string | null>(null);
const backdropRef = useRef<HTMLDivElement | null>(null);
+ const connectTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
// reset when opened
useEffect(() => {
if (open) {
setState("idle");
setMethod(null);
}
+ return () => {
+ if (connectTimerRef.current) {
+ clearTimeout(connectTimerRef.current);
+ connectTimerRef.current = null;
+ }
+ };
}, [open]);
function simulateConnect(wallet: string) {
+ if (connectTimerRef.current) clearTimeout(connectTimerRef.current);
setMethod(wallet);
setState("connecting");
- setTimeout(() => {
+ connectTimerRef.current = setTimeout(() => {
// deterministic success for Freighter, random for Albedo
if (wallet === "Freighter") setState("connected");
else setState(Math.random() > 0.4 ? "connected" : "error");
}, 1200);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| setTimeout(() => { | |
| // deterministic success for MetaMask, error for Coinbase, random for WalletConnect | |
| if (wallet === "MetaMask") setState("connected"); | |
| else if (wallet === "Coinbase Wallet") setState("error"); | |
| // deterministic success for Freighter, random for Albedo | |
| if (wallet === "Freighter") setState("connected"); | |
| else setState(Math.random() > 0.4 ? "connected" : "error"); | |
| }, 1200); | |
| export default function CreateWalletModal({ open, onClose }: Props) { | |
| const [state, setState] = useState<ModalState>("idle"); | |
| const [method, setMethod] = useState<string | null>(null); | |
| const backdropRef = useRef<HTMLDivElement | null>(null); | |
| const connectTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null); | |
| // reset when opened | |
| useEffect(() => { | |
| if (open) { | |
| setState("idle"); | |
| setMethod(null); | |
| } | |
| return () => { | |
| if (connectTimerRef.current) { | |
| clearTimeout(connectTimerRef.current); | |
| connectTimerRef.current = null; | |
| } | |
| }; | |
| }, [open]); | |
| function simulateConnect(wallet: string) { | |
| if (connectTimerRef.current) clearTimeout(connectTimerRef.current); | |
| setMethod(wallet); | |
| setState("connecting"); | |
| connectTimerRef.current = setTimeout(() => { | |
| // deterministic success for Freighter, random for Albedo | |
| if (wallet === "Freighter") setState("connected"); | |
| else setState(Math.random() > 0.4 ? "connected" : "error"); | |
| }, 1200); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/CreateWalletModal.tsx` around lines 45 - 49, The connection
timer in CreateWalletModal uses setTimeout that can fire after the modal has
closed; store the timeout ID (e.g., in a ref or local variable) when you call
setTimeout for the wallet connection simulation and call clearTimeout(timeoutId)
in the component cleanup or whenever the modal is closed/restarted so the old
callback cannot call setState; update the logic around the setTimeout (the block
that checks wallet === "Freighter" / Math.random) to clear any existing timer
before starting a new one and to clear it on unmount (useEffect cleanup) to
prevent stale state transitions.
| <div> | ||
| <p className="text-xs text-slate-400 mb-2">Session Completion</p> | ||
| <p className="text-xl font-bold text-white">{Math.round((completedSessions / totalSessions) * 100)}%</p> | ||
| </div> |
There was a problem hiding this comment.
Guard against division by zero.
completedSessions / totalSessions yields NaN when totalSessions === 0, rendering "NaN%". This card always renders (unlike Recent Sessions which is length-guarded), so add a fallback.
🛡️ Proposed guard
- <p className="text-xl font-bold text-white">{Math.round((completedSessions / totalSessions) * 100)}%</p>
+ <p className="text-xl font-bold text-white">{totalSessions > 0 ? Math.round((completedSessions / totalSessions) * 100) : 0}%</p>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div> | |
| <p className="text-xs text-slate-400 mb-2">Session Completion</p> | |
| <p className="text-xl font-bold text-white">{Math.round((completedSessions / totalSessions) * 100)}%</p> | |
| </div> | |
| <div> | |
| <p className="text-xs text-slate-400 mb-2">Session Completion</p> | |
| <p className="text-xl font-bold text-white">{totalSessions > 0 ? Math.round((completedSessions / totalSessions) * 100) : 0}%</p> | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/dashboard/ExpertDashboard.tsx` around lines 213 - 216, The
Session Completion percentage can produce NaN when totalSessions === 0; inside
the ExpertDashboard component compute a guarded completion value (e.g. derive a
completionRate variable) that checks totalSessions > 0 before doing
Math.round((completedSessions / totalSessions) * 100) and falls back to a safe
value like 0 (or '--') and then render that variable in the JSX instead of the
raw expression; update the JSX block that currently contains
Math.round((completedSessions / totalSessions) * 100) to use the new guarded
completionRate.
Overview
Successfully implemented all three assigned open-source issues:
Close : #220 - Create Wallet Connection Modal
Closes: #221 - Build Seeker Dashboard Overview
Closes: #222 - Build Expert Dashboard & Earning Stats
Issue #220: Create Wallet Connection Modal
Changes Made
File:
src/components/CreateWalletModal.tsxUpdated wallet options to support Stellar ecosystem wallets:
Features
Acceptance Criteria Met
✅ Clean, accessible modal matching the design system
✅ Options for Freighter, Albedo (Stellar wallets)
Issue #221: Build Seeker Dashboard Overview ✅
New Components Created
File:
src/components/dashboard/SeekerOverview.tsxFeatures Implemented
Quick Stats Section
Session Management
Quick Actions
Session Card Details
Empty State
Data Structure
Types Added (
utils/types/types.ts):Mock Data (
utils/data/mock-data.ts):Dashboard Page Update
File:
src/app/dashboard/page.tsxChanged from expert-focused view (courses, students) to seeker-focused view using SeekerOverview component.
Acceptance Criteria Met
Fetch and display session data (mocked)
Quick actions: "Find Expert", "Fund Session"
Display active, upcoming, and past sessions
Issue #222: Build Expert Dashboard & Earning Stats ✅
New Components Created
1. AvailabilityToggle Component
File:
src/components/dashboard/AvailabilityToggle.tsxFeatures:
2. ExpertDashboard Component
File:
src/components/dashboard/ExpertDashboard.tsxKey Metrics Displayed
1. Total Earnings Card
2. Staked Amount Card
3. Average Rating Card
4. Total Sessions Card
Additional Features
Availability Toggle
Quick Actions
Recent Sessions
Performance Summary
Data Structure
Types Added (
utils/types/types.ts):Mock Data (
utils/data/mock-data.ts):Acceptance Criteria Met
Display total earnings
Display current staked amount
Display average rating
Toggle switch for "Available Now" status
Component Import Test
All imports verified - no compilation errors:
Summary by CodeRabbit