- Sidebar: lucide-react icons, lock on gated items, upgrade badge on "Mon plan", user footer with avatar initials + plan label, "EX|PRIA" logo header
- Topbar: sticky with backdrop-blur, breadcrumb via centralized route-titles.ts, search placeholder, keyboard shortcuts + notifications icons
- Dashboard: split into Free/Standard/Premium views (ARCHITECTURE.md §3 aligned)
- NclcHero: NCLC display + gauge 5→10 + SVG score ring
- StatCards: simulations remaining + NCLC estimé + dernier score with delta
- RecentSimulations: 3 latest with NCLC badge + chevron nav
- NextStepCard: static recommendation per plan
- PaywallBanner: full-width redesign + fixed dead Boréal tokens
- Removed orphan MobileHeader.tsx (0 consumers)
Typecheck: OK · Tests: 122/122 ✅
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
111 lines
3.9 KiB
TypeScript
111 lines
3.9 KiB
TypeScript
/**
|
|
* DashboardFreeView — vue Dashboard pour le plan Découverte.
|
|
*
|
|
* Spécificités Free :
|
|
* - Pas d'appel `useSimulationsList` (gate 'dashboard' à false côté backend).
|
|
* - Hero NCLC en état placeholder (pas d'historique lisible).
|
|
* - Stat cards avec "NCLC estimé —" et "Dernier score —".
|
|
* - Recommandation statique vers la première simulation EE T2.
|
|
* - Bannière upsell Standard en bas.
|
|
*
|
|
* Règle D : aucun `plan === 'free'` — c'est le parent (DashboardPage) qui
|
|
* route vers cette vue via hasAccess.
|
|
* Règle H : aucune logique métier — les données viennent des props.
|
|
* Règle L : tokens du design system exclusivement.
|
|
*/
|
|
|
|
import { useNavigate } from 'react-router-dom'
|
|
import { Plus } from 'lucide-react'
|
|
import { Button } from '@/shared/ui/Button'
|
|
import { Badge } from '@/shared/ui/Badge'
|
|
import { NclcHero } from './NclcHero'
|
|
import { StatCards } from './StatCards'
|
|
import { NextStepCard } from './NextStepCard'
|
|
import { PaywallBanner } from './PaywallBanner'
|
|
|
|
interface DashboardFreeViewProps {
|
|
displayName: string
|
|
simulationsUsed: number
|
|
simulationsRemaining: number
|
|
canStartSimulation: boolean
|
|
}
|
|
|
|
const FREE_CONSEIL =
|
|
"Commencez par une simulation d'Expression Écrite pour découvrir votre niveau. " +
|
|
'Le rapport détaillé et le suivi NCLC se débloquent avec le plan Standard.'
|
|
|
|
export function DashboardFreeView({
|
|
displayName,
|
|
simulationsUsed,
|
|
simulationsRemaining,
|
|
canStartSimulation,
|
|
}: DashboardFreeViewProps) {
|
|
const navigate = useNavigate()
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Header */}
|
|
<header className="flex flex-wrap items-center justify-between gap-4">
|
|
<div className="flex flex-wrap items-center gap-3">
|
|
<h1 className="text-2xl font-bold text-ink-primary">Bonjour, {displayName}</h1>
|
|
<Badge variant="plan" planValue="free">
|
|
Plan Découverte
|
|
</Badge>
|
|
</div>
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<Button variant="secondary" size="sm" onClick={() => navigate('/plan')}>
|
|
Passer en Premium →
|
|
</Button>
|
|
<Button
|
|
variant="primary"
|
|
size="sm"
|
|
icon={<Plus className="size-4" />}
|
|
disabled={!canStartSimulation}
|
|
onClick={() => navigate('/simulation/ee')}
|
|
>
|
|
Nouvelle simulation
|
|
</Button>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Hero NCLC — placeholder en Free */}
|
|
<NclcHero currentNclc={null} conseil={FREE_CONSEIL} lastScore={null} />
|
|
|
|
{/* Stat cards — NCLC et dernier score vides */}
|
|
<StatCards
|
|
plan="free"
|
|
simulationsUsed={simulationsUsed}
|
|
simulationsRemaining={simulationsRemaining}
|
|
recentSimulations={[]}
|
|
/>
|
|
|
|
{/* Prochaine étape + (pas de simulations récentes en Free) */}
|
|
<div className="grid gap-4 lg:grid-cols-[1fr_360px]">
|
|
<section
|
|
aria-label="Premiers pas"
|
|
className="rounded-[var(--radius-md)] border border-border bg-surface p-6"
|
|
>
|
|
<p className="text-[11px] font-semibold uppercase tracking-wider text-ink-tertiary">
|
|
Pour bien démarrer
|
|
</p>
|
|
<h2 className="mt-2 text-lg font-semibold text-ink-primary">Votre première simulation</h2>
|
|
<p className="mt-2 text-sm text-ink-secondary">
|
|
Choisissez une tâche d'Expression Écrite pour obtenir un premier score et une estimation
|
|
NCLC. Vos 5 simulations gratuites vous attendent.
|
|
</p>
|
|
</section>
|
|
|
|
<NextStepCard
|
|
title="Démarrez par l'Écrit T2"
|
|
conseil="Article d'opinion — le format le plus représentatif du TCF Canada."
|
|
tags={['20 min', '120-150 mots']}
|
|
ctaLabel="Commencer"
|
|
ctaTo="/simulation/ee"
|
|
/>
|
|
</div>
|
|
|
|
{/* Bannière upsell */}
|
|
<PaywallBanner />
|
|
</div>
|
|
)
|
|
}
|