Skip to content

feat: implement seeker dashboard#227

Merged
Luluameh merged 1 commit into
LightForgeHub:mainfrom
JoyLight00:feat/dashboard-implementation-seeker
May 29, 2026
Merged

feat: implement seeker dashboard#227
Luluameh merged 1 commit into
LightForgeHub:mainfrom
JoyLight00:feat/dashboard-implementation-seeker

Conversation

@JoyLight00
Copy link
Copy Markdown
Contributor

@JoyLight00 JoyLight00 commented May 29, 2026

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.tsx

Updated wallet options to support Stellar ecosystem wallets:

  • Freighter Wallet (recommended - marked as primary option)
  • Albedo Wallet (secure alternative)

Features

  • Clean, accessible modal matching design system
  • Status states: idle, connecting, connected, error
  • Visual feedback with loading animations
  • Escape key to close
  • Click outside to close
  • Color-coded wallet options with gradient backgrounds

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.tsx

Features Implemented

  1. Quick Stats Section

    • Active Sessions count with pulsing indicator
    • Upcoming Sessions count
    • Completed Sessions count
  2. Session Management

    • Display active sessions with real-time indicators
    • Display upcoming sessions with countdown
    • Display completed/past sessions
    • Session cards show: title, expert name, date, time, duration, price, status
  3. Quick Actions

    • "Find Expert" button → routes to /explore-experts
    • "Fund Session" button → triggers session funding flow
  4. Session Card Details

    • Expert avatar and name
    • Session timing and duration
    • Price display with $ icon
    • Status badge (active/upcoming/completed/cancelled)
    • "View Details" button
    • "Join Now" button (for upcoming sessions)
  5. Empty State

    • Friendly message when no sessions exist
    • CTA to browse experts

Data Structure

Types Added (utils/types/types.ts):

interface Session {
  id: string
  title: string
  expertName: string
  expertAvatar: string
  date: string
  time: string
  duration: string
  status: "active" | "upcoming" | "completed" | "cancelled"
  price: string
  category: string
}

Mock Data (utils/data/mock-data.ts):

  • 4 mock sessions with varying statuses
  • Realistic data for testing

Dashboard Page Update

File: src/app/dashboard/page.tsx

Changed 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.tsx

Features:

  • Smooth toggle switch UI
  • Visual status indicator
  • Info messages based on state
  • Callback support for state changes
  • Color-coded feedback (green when available, gray when unavailable)

2. ExpertDashboard Component

File: src/components/dashboard/ExpertDashboard.tsx

Key Metrics Displayed

1. Total Earnings Card

  • Large value display with currency
  • Trend indicator (↑ 12% from last month)
  • Cyan color scheme with icon

2. Staked Amount Card

  • Current staked balance
  • "Earning rewards" status
  • Purple color scheme

3. Average Rating Card

  • Rating value out of 5.0
  • Star visualization (filled/empty)
  • Yellow color scheme
  • Real data from expert profile

4. Total Sessions Card

  • Session count
  • Completion count displayed
  • Emerald color scheme

Additional Features

Availability Toggle

  • Integrated AvailabilityToggle component
  • Toggle switch for "Available Now" status
  • Status feedback messages

Quick Actions

  • View Detailed Earnings → /dashboard/earnings
  • View Learners → /dashboard/learners
  • Messages → /dashboard/messages

Recent Sessions

  • Show last 3 sessions
  • Each session displays: title, date, time, status, price, duration
  • "View All" link to see complete list

Performance Summary

  • Response Time (5m average)
  • Session Completion Rate
  • Repeat Clients count
  • Revenue This Month

Data Structure

Types Added (utils/types/types.ts):

interface Expert {
  id: string
  name: string
  avatar: string
  category: string
  rating: number
  reviews: number
  hourlyRate: string
  availability: boolean
}

Mock Data (utils/data/mock-data.ts):

  • 4 expert profiles with complete data
  • Realistic earnings and ratings

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:

  • SeekerOverview.tsx
  • ExpertDashboard.tsx
  • AvailabilityToggle.tsx
  • CreateWalletModal.tsx
  • dashboard/page.tsx

Summary by CodeRabbit

  • New Features
    • Redesigned dashboard with dedicated Seeker and Expert overview pages
    • Session tracking with quick stats dashboard (active, upcoming, completed sessions)
    • Expert performance summary with earnings, ratings, and completion metrics
    • Availability toggle for experts to manage their online status
    • Updated wallet connection options to Freighter and Albedo

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 29, 2026

📝 Walkthrough

Walkthrough

This 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.

Changes

Seeker and Expert Dashboard

Layer / File(s) Summary
Type definitions and mock data foundation
utils/types/types.ts, utils/data/mock-data.ts
Session and Expert interfaces are defined with status, metadata, and profile fields; mockSessions and mockExperts arrays populate the mock dataset.
Reusable AvailabilityToggle component
src/components/dashboard/AvailabilityToggle.tsx
AvailabilityToggle manages internal availability state initialized from props, renders a toggle button with callback support, and conditionally shows available/unavailable UI.
SeekerOverview component for session browsing
src/components/dashboard/SeekerOverview.tsx
SeekerOverview filters mockSessions by status and renders Quick Stats cards, Quick Actions buttons, conditional session sections (active/upcoming/completed), and SessionCard subcomponent with navigation and "Join Now" CTA.
ExpertDashboard component with metrics and availability
src/components/dashboard/ExpertDashboard.tsx
ExpertDashboard computes session statistics and displays Key Metrics (Total Earnings, Staked Amount, Average Rating with star visualization, Total Sessions), integrates AvailabilityToggle, Quick Actions, Recent Sessions list, and Performance Summary card.
Dashboard page integration
src/app/dashboard/page.tsx
DashboardHome page replaces legacy MetricCard and QuickActions with SeekerOverview component as the sole UI output.

Wallet Modal Connection Update

Layer / File(s) Summary
Wallet connection logic and UI
src/components/CreateWalletModal.tsx
simulateConnect now deterministically succeeds only for Freighter; other wallets use randomized success/failure. Wallet option buttons are replaced with Freighter (Recommended) and Albedo (Secure) options.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 A dashboard blooms with new UI dreams,
Sessions flow and metrics gleam,
Freighter stands recommended and true,
While seekers and experts get a brand-new view!
From types to mocks, the foundation is laid,
A dashboard masterpiece, expertly made!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: implement seeker dashboard' directly reflects the main change—adding a seeker-focused dashboard implementation with overview component and related UI elements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/components/dashboard/SeekerOverview.tsx (1)

48-99: ⚡ Quick win

Hoist SessionCard out of the render body.

Defining SessionCard inside SeekerOverview creates a new component type on every render, causing React to unmount/remount the subtree instead of updating it. Move it to module scope (passing router via 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 win

Expose toggle state to assistive tech.

The button visually conveys on/off but screen readers only hear a static label. Add role="switch" and aria-checked so 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

📥 Commits

Reviewing files that changed from the base of the PR and between e0c9642 and c5d3df2.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • src/app/dashboard/page.tsx
  • src/components/CreateWalletModal.tsx
  • src/components/dashboard/AvailabilityToggle.tsx
  • src/components/dashboard/ExpertDashboard.tsx
  • src/components/dashboard/SeekerOverview.tsx
  • utils/data/mock-data.ts
  • utils/types/types.ts

Comment on lines 45 to 49
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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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.

Comment on lines +213 to +216
<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>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Suggested change
<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.

@Luluameh Luluameh merged commit 9b53f6d into LightForgeHub:main May 29, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Build Expert Dashboard & Earning Stats Build Seeker Dashboard Overview Create Wallet Connection Modal

2 participants