Files
learning-platform/src/pages/Dashboard.jsx

175 lines
7.5 KiB
JavaScript

import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
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 { getAssignedTopic } from '../lib/learningService';
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: [],
});
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 });
}
setDashData({ topic, learnDone, testResult, top3, myRank, myPoints, activity });
};
load();
}, [currentUser, weekNumber]);
const { topic, learnDone, testResult, top3, myRank, myPoints, activity } = dashData;
return (
<div className="p-6 md:p-10 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
<header>
<h1 className="text-3xl md:text-5xl mb-2">Welcome, {currentUser?.name}</h1>
<p className="text-fg-muted text-lg">Here is your overview for week {weekNumber}.</p>
</header>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Card className="flex flex-col border border-bg-warm" hoverable>
<div className="flex justify-between items-start mb-4">
<div>
<h3 className="text-xl">Learning</h3>
<p className="text-fg-muted text-sm mt-1">Your topic this week:</p>
</div>
{learnDone ? <Tag variant="success">Completed</Tag> : <Tag variant="accent">To Do</Tag>}
</div>
<h2 className="text-2xl mt-2 mb-6">{topic ? topic.label : 'No topic assigned'}</h2>
<Link to="/learn" className="mt-auto">
<Button className="w-full" variant={learnDone ? 'outline' : 'primary'}>
{learnDone ? 'Review Learning Material' : 'Start Learning Session'}
</Button>
</Link>
</Card>
<Card className="flex flex-col border border-bg-warm" hoverable>
<div className="flex justify-between items-start mb-4">
<div>
<h3 className="text-xl">Testing</h3>
<p className="text-fg-muted text-sm mt-1">Weekly test 10 questions</p>
</div>
{testResult ? <Tag variant="success">Score: {testResult.percentage}%</Tag> : <Tag variant="default">To Do</Tag>}
</div>
<div className="flex-1 flex items-center justify-center py-6 text-fg-subtle">
{!learnDone ? 'Complete your learning session first' : testResult ? 'Test completed for this week' : 'Ready to test your knowledge'}
</div>
{learnDone && !testResult ? (
<Link to="/test" className="mt-auto">
<Button className="w-full">Start Test</Button>
</Link>
) : (
<Link to="/test" className="mt-auto">
<Button variant="outline" className="w-full">{testResult ? 'View Results' : 'Start Test'}</Button>
</Link>
)}
</Card>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="lg:col-span-2">
<h3 className="text-2xl mb-4">Recent Activity</h3>
<Card className="p-0 overflow-hidden border border-bg-warm">
<div className="divide-y divide-bg-warm">
{activity.length === 0 ? (
<div className="p-8 text-center text-fg-muted">No recent activity.</div>
) : (
activity.slice(0, 5).map((act, i) => (
<div key={i} className="p-4 flex items-center gap-4 hover:bg-bg-warm transition-colors">
<div className={`w-10 h-10 rounded-[var(--r-org)] flex items-center justify-center font-bold ${
act.type === 'test' ? 'bg-accent-soft text-purple-700' : 'bg-sage text-teal-900'
}`}>
{act.type === 'test' ? 'T' : 'L'}
</div>
<div>
<p className="font-medium">{act.type === 'test' ? 'Test completed' : 'Learning session'}: {act.topic}</p>
<p className="text-sm text-fg-muted">
Week {act.week} {act.type === 'test' && `· Score: ${act.score}%`}
</p>
</div>
{act.points > 0 && <div className="ml-auto text-teal font-bold">+{act.points} pts</div>}
</div>
))
)}
</div>
</Card>
</div>
<div>
<h3 className="text-2xl mb-4">Mini Leaderboard</h3>
<Card className="border border-bg-warm">
<div className="space-y-4">
{top3.length === 0 ? (
<div className="text-center text-fg-muted py-4">No points yet</div>
) : (
top3.map((u, i) => (
<div key={u.user_id} className="flex items-center justify-between">
<div className="flex items-center gap-3">
<span className={`font-mono font-bold ${i === 0 ? 'text-yellow-500' : 'text-teal'}`}>{i + 1}.</span>
<span className="font-medium">{u.name} {u.user_id === currentUser?.id && '(You)'}</span>
</div>
<Tag variant="dark">{u.points} pts</Tag>
</div>
))
)}
{myRank > 3 && (
<div className="flex items-center justify-between pt-4 border-t border-bg-warm">
<div className="flex items-center gap-3">
<span className="font-mono font-bold text-fg-muted">{myRank}.</span>
<span className="font-medium text-teal">You</span>
</div>
<Tag variant="default">{myPoints} pts</Tag>
</div>
)}
</div>
<Link to="/leaderboard">
<Button variant="ghost" className="w-full mt-4 text-sm">View full leaderboard</Button>
</Link>
</Card>
</div>
</div>
</div>
);
};
export default Dashboard;