feat: implement onboarding process and enrollment status tracking for users

This commit is contained in:
RaymondVerhoef
2026-05-26 21:33:20 +02:00
parent febc9dc7f2
commit 7066f881f9
8 changed files with 225 additions and 34 deletions

View File

@@ -161,7 +161,7 @@ const Dashboard = () => {
<div>
<h3 className="text-xl">Learning</h3>
<p className="text-fg-muted text-sm mt-1">
{curriculumActive ? `Week ${weekNumber} topic:` : 'Your topic this week:'}
{curriculumActive ? `Week ${currWeek} topic:` : 'Your topic this week:'}
</p>
</div>
{learnDone ? <Tag variant="success">Completed</Tag> : <Tag variant="accent">To Do</Tag>}

74
src/pages/Onboarding.jsx Normal file
View File

@@ -0,0 +1,74 @@
import { useState } from 'react';
import { useNavigate, Navigate } from 'react-router-dom';
import { BookOpen, ArrowRight, LogOut } from 'lucide-react';
import { useApp } from '../store/AppContext';
import Card from '../components/ui/Card';
import Button from '../components/ui/Button';
const Onboarding = () => {
const { state, enrollCurrentUser, logout } = useApp();
const navigate = useNavigate();
const [busy, setBusy] = useState(false);
const [error, setError] = useState('');
if (state.isLoading) return <div className="p-8">Loading...</div>;
if (!state.currentUser) return <Navigate to="/login" replace />;
if (state.currentUser.enrollment_status === 'active') {
return <Navigate to="/" replace />;
}
const handleStart = async () => {
setBusy(true);
setError('');
try {
await enrollCurrentUser();
navigate('/', { replace: true });
} catch (e) {
setError(e.message || 'Could not start the curriculum. Please try again.');
setBusy(false);
}
};
return (
<div className="min-h-screen bg-bg flex items-center justify-center p-4">
<div className="w-full max-w-lg">
<div className="text-center mb-8">
<img src="/images/icon.png" alt="Respellion Icon" className="h-20 mx-auto mb-4" />
<h1 className="text-3xl text-teal font-bold tracking-tight">Welcome, {state.currentUser.name}</h1>
</div>
<Card className="border border-bg-warm">
<div className="text-center py-2">
<BookOpen size={48} className="mx-auto text-teal mb-4" />
<h2 className="text-2xl font-bold mb-3">Start your learning journey</h2>
<p className="text-fg-muted mb-2">
The curriculum is a 26-week cycle of weekly learning sessions and tests.
</p>
<p className="text-fg-muted mb-6">
When you click <strong>Start</strong>, week 1 begins today. You can pick this up at any moment the cycle is yours.
</p>
{error && (
<div className="text-red-500 text-sm font-medium mb-4">{error}</div>
)}
<Button onClick={handleStart} disabled={busy} className="text-lg px-8 py-3">
{busy ? 'Starting...' : <>Start my journey <ArrowRight size={20} className="ml-2" /></>}
</Button>
<div className="mt-6">
<button
onClick={logout}
className="text-xs text-fg-muted hover:text-fg inline-flex items-center gap-1"
>
<LogOut size={12} /> Sign out
</button>
</div>
</div>
</Card>
</div>
</div>
);
};
export default Onboarding;

View File

@@ -10,6 +10,7 @@ import Button from '../components/ui/Button';
import Tag from '../components/ui/Tag';
import { useApp } from '../store/AppContext';
import { generateWeeklyQuiz, saveTestResult, getTestResult } from '../lib/testService';
import { getCurriculumWeek, getCurriculumCycle } from '../lib/curriculumService';
import { storage } from '../lib/storage';
const TIMER_SECONDS = 300; // 5 minutes
@@ -180,7 +181,7 @@ const Testen = () => {
<div className="text-center py-12">
<CheckSquare size={64} className="mx-auto text-teal/30 mb-6" />
<h1 className="text-3xl md:text-4xl font-bold text-teal mb-4">Weekly Test</h1>
<p className="text-fg-muted text-lg mb-2">Week {weekNumber}</p>
<p className="text-fg-muted text-lg mb-2">Cycle {getCurriculumCycle(weekNumber)} · Week {getCurriculumWeek(weekNumber)} of 26</p>
<p className="text-fg-muted mb-8 max-w-md mx-auto">
Test your knowledge with 5 AI-generated questions. You have 5 minutes to complete the test. Good luck!
</p>