Add dismissible platform explainer to Dashboard
Introduces a per-user, dismissible "How Respellion works" card on the Dashboard so new users understand the learn/test/leaderboard loop and where to find R42 and the main navigation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { BookOpen, CheckSquare, Trophy, Sparkles, X } from 'lucide-react';
|
||||||
import { useApp } from '../store/AppContext';
|
import { useApp } from '../store/AppContext';
|
||||||
import Card from '../components/ui/Card';
|
import Card from '../components/ui/Card';
|
||||||
import Button from '../components/ui/Button';
|
import Button from '../components/ui/Button';
|
||||||
import Tag from '../components/ui/Tag';
|
import Tag from '../components/ui/Tag';
|
||||||
import * as db from '../lib/db';
|
import * as db from '../lib/db';
|
||||||
|
import { storage } from '../lib/storage';
|
||||||
import { getAssignedTopic } from '../lib/learningService';
|
import { getAssignedTopic } from '../lib/learningService';
|
||||||
import { getYearProgress, getCurriculumCycle, getCurriculumWeek, getActiveVersion } from '../lib/curriculumService';
|
import { getYearProgress, getCurriculumCycle, getCurriculumWeek, getActiveVersion } from '../lib/curriculumService';
|
||||||
|
|
||||||
@@ -88,6 +90,25 @@ const Dashboard = () => {
|
|||||||
const currentCycle = getCurriculumCycle(weekNumber);
|
const currentCycle = getCurriculumCycle(weekNumber);
|
||||||
const currWeek = getCurriculumWeek(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 dismissExplainer = () => {
|
||||||
|
setExplainerOpen(false);
|
||||||
|
if (explainerKey) storage.set(explainerKey, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const explainerSteps = [
|
||||||
|
{ icon: BookOpen, title: 'Learn', text: 'Each week you get one topic from the knowledge base. Work through the learning session at your own pace.' },
|
||||||
|
{ icon: CheckSquare, title: 'Test', text: 'Once your learning session is done, take the 5-question test to lock in what you learned and earn points.' },
|
||||||
|
{ 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 (
|
return (
|
||||||
<div className="p-6 md:p-10 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
<div className="p-6 md:p-10 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||||
<header>
|
<header>
|
||||||
@@ -99,6 +120,43 @@ const Dashboard = () => {
|
|||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
{/* Platform explainer — dismissible, per user */}
|
||||||
|
{explainerOpen && (
|
||||||
|
<Card className="relative border border-bg-warm bg-paper">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={dismissExplainer}
|
||||||
|
aria-label="Dismiss"
|
||||||
|
className="absolute top-4 right-4 text-fg-muted hover:text-fg transition-colors"
|
||||||
|
>
|
||||||
|
<X size={18} />
|
||||||
|
</button>
|
||||||
|
<div className="flex items-start gap-3 mb-5 pr-8">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-2 mb-1">
|
||||||
|
<h3 className="text-xl">How Respellion works</h3>
|
||||||
|
<Tag variant="accent" className="text-xs">New here?</Tag>
|
||||||
|
</div>
|
||||||
|
<p className="text-fg-muted text-sm">
|
||||||
|
A perpetual learning loop that keeps you current with the company knowledge base.
|
||||||
|
Use the navigation at the top (Home, Learn, Test, Leaderboard) to move around.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||||
|
{explainerSteps.map(({ icon: Icon, title, text }) => (
|
||||||
|
<div key={title} className="flex flex-col gap-2">
|
||||||
|
<div className="w-9 h-9 rounded-[var(--r-org)] bg-sage text-teal-900 flex items-center justify-center">
|
||||||
|
<Icon size={18} />
|
||||||
|
</div>
|
||||||
|
<h4 className="font-bold">{title}</h4>
|
||||||
|
<p className="text-sm text-fg-muted">{text}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Cycle Progress Bar (only when curriculum exists) */}
|
{/* Cycle Progress Bar (only when curriculum exists) */}
|
||||||
{curriculumActive && yearProgress && (
|
{curriculumActive && yearProgress && (
|
||||||
<Card className="border border-bg-warm">
|
<Card className="border border-bg-warm">
|
||||||
|
|||||||
Reference in New Issue
Block a user