import { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { BookOpen, CheckSquare, Trophy, Sparkles, X, HelpCircle, Rocket, ArrowRight } from 'lucide-react'; import { useApp } from '../store/AppContext'; import Card from '../components/ui/Card'; import Button from '../components/ui/Button'; import Tag from '../components/ui/Tag'; import * as db from '../lib/db'; import { storage } from '../lib/storage'; import { getAssignedTopic } from '../lib/learningService'; import { getYearProgress, getCurriculumCycle, getCurriculumWeek, getActiveVersion } from '../lib/curriculumService'; import { getOnboardingSummary } from '../lib/onboardingService'; const Dashboard = () => { const { state } = useApp(); const { currentUser, weekNumber } = state; const [dashData, setDashData] = useState({ topic: null, learnDone: false, testResult: null, top3: [], myRank: 0, myPoints: 0, activity: [], yearProgress: null, hasCurriculum: false, theme: '', onboarding: null, }); useEffect(() => { if (!currentUser) return; const load = async () => { const [topic, learnDone, testResult, allUsers, leaderboard] = await Promise.all([ getAssignedTopic(currentUser.id, weekNumber), db.getLearnDone(currentUser.id, weekNumber), db.getQuizResult(currentUser.id, weekNumber), db.getTeamMembers(), db.getLeaderboard(), ]); const nonAdminIds = allUsers.filter(u => u.role !== 'admin').map(u => u.id); const filtered = leaderboard.filter(u => nonAdminIds.includes(u.user_id)); const top3 = filtered.slice(0, 3); const myRank = filtered.findIndex(u => u.user_id === currentUser.id) + 1; const myPoints = filtered.find(u => u.user_id === currentUser.id)?.points || 0; const activity = []; for (let w = weekNumber; w >= Math.max(1, weekNumber - 3); w--) { const [pastLearn, pastTest, pastTopic] = await Promise.all([ db.getLearnDone(currentUser.id, w), db.getQuizResult(currentUser.id, w), getAssignedTopic(currentUser.id, w), ]); if (pastTest) activity.push({ type: 'test', week: w, topic: pastTopic?.label, score: pastTest.percentage, points: pastTest.points_earned }); if (pastLearn) activity.push({ type: 'learn', week: w, topic: pastTopic?.label }); } // Load curriculum progress let yearProgress = null; let curriculumExists = false; try { const activeVersion = await getActiveVersion(); curriculumExists = !!activeVersion; if (curriculumExists) { yearProgress = await getYearProgress(currentUser.id, weekNumber); } } catch (e) { console.warn('[Dashboard] Could not load curriculum data:', e.message); } // Onboarding-track progress (independent of enrollment/curriculum). Guarded // so a missing collection or empty KB never breaks the dashboard. let onboarding = null; try { onboarding = await getOnboardingSummary(currentUser.id); } catch (e) { console.warn('[Dashboard] Could not load onboarding summary:', e.message); } setDashData({ topic, learnDone, testResult, top3, myRank, myPoints, activity, yearProgress, hasCurriculum: curriculumExists, theme: topic?.theme || '', onboarding, }); }; load(); }, [currentUser, weekNumber]); const { topic, learnDone, testResult, top3, myRank, myPoints, activity, yearProgress, hasCurriculum: curriculumActive, onboarding } = dashData; const onboardingLabel = onboarding ? (onboarding.allDone ? 'Review onboarding' : onboarding.daysCompleted > 0 ? 'Continue onboarding' : 'Start onboarding') : 'Start onboarding'; const currentCycle = getCurriculumCycle(weekNumber); const currWeek = getCurriculumWeek(weekNumber); const explainerKey = currentUser ? `dashboard:explainer-dismissed:${currentUser.id}` : null; const [explainerOpen, setExplainerOpen] = useState(false); useEffect(() => { if (!explainerKey) return; setExplainerOpen(storage.get(explainerKey, false) !== true); }, [explainerKey]); const closeExplainer = () => { setExplainerOpen(false); if (explainerKey) storage.set(explainerKey, true); }; const toggleExplainer = () => { if (explainerOpen) { closeExplainer(); } else { setExplainerOpen(true); } }; const explainerSteps = [ { icon: BookOpen, title: 'Learn', text: 'Each week the curriculum gives you a theme — a small set of related topics from the knowledge base. Work through the theme session at your own pace.' }, { icon: CheckSquare, title: 'Theme Test', text: 'When your theme session is done, take the 5-question theme test to lock in what you learned and earn points. You can also take an on-demand Topic Test anytime to drill a single topic.' }, { icon: Trophy, title: 'Climb', text: 'Points add up on the leaderboard. The curriculum runs in perpetual 26-week cycles, so there is always a next step.' }, { icon: Sparkles, title: 'Ask R42', text: 'R42, your AI study buddy, is on every screen (bottom-right). Ask it anything about your topic or the platform.' }, ]; return (

Welcome, {currentUser?.name}

{curriculumActive ? `Cycle ${currentCycle} · Week ${currWeek} of 26` : `Here is your overview for week ${weekNumber}.`}

{/* Platform explainer — toggled via header help icon, per-user dismissed state */} {explainerOpen && (

How Respellion works

New here?

A perpetual learning loop that keeps you current with the company knowledge base. Use the navigation at the top (Home, Learn, Theme Test, Topic Test, Leaderboard) to move around.

{explainerSteps.map(({ icon: Icon, title, text }) => (

{title}

{text}

))}
{/* Onboarding track CTA — only when there are themes to introduce. */} {onboarding && onboarding.themesTotal > 0 && (

New here? Take the 5-day onboarding {onboarding.allDone ? 'Onboarding complete' : `${onboarding.daysCompleted}/${onboarding.dayCount} days`}

A light tour of every theme — how Respellion works day to day and week to week.

)}
)} {/* Cycle Progress Bar (only when curriculum exists) */} {curriculumActive && yearProgress && (
{yearProgress.percentage}%

Cycle Progress

{yearProgress.completed} of {yearProgress.total} weeks completed

Cycle {currentCycle} {26 - currWeek} weeks remaining
{/* Visual week progress bar */}
{Array.from({ length: 26 }, (_, i) => { const w = i + 1; const isCurrent = w === currWeek; const isPast = w < currWeek; return (
); })}
)}

Learning

{curriculumActive ? `Week ${currWeek} topic:` : 'Your topic this week:'}

{learnDone ? Completed : To Do}

{topic ? topic.label : 'No topic assigned'}

Theme Test

This week's theme — 5 questions

{testResult ? Score: {testResult.percentage}% : To Do}
{!learnDone ? 'Complete your theme session first' : testResult ? 'Theme test completed for this week' : 'Ready to test your knowledge'}
{learnDone && !testResult ? ( ) : ( )}

Recent Activity

{activity.length === 0 ? (
No recent activity.
) : ( activity.slice(0, 5).map((act, i) => (
{act.type === 'test' ? 'T' : 'L'}

{act.type === 'test' ? 'Test completed' : 'Learning session'}: {act.topic}

Week {act.week} {act.type === 'test' && `· Score: ${act.score}%`}

{act.points > 0 &&
+{act.points} pts
}
)) )}

Mini Leaderboard

{top3.length === 0 ? (
No points yet
) : ( top3.map((u, i) => (
{i + 1}. {u.name} {u.user_id === currentUser?.id && '(You)'}
{u.points} pts
)) )} {myRank > 3 && (
{myRank}. You
{myPoints} pts
)}
); }; export default Dashboard;