feat(ui-polish): sidebar icons + topbar + dashboard redesign
- 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>
This commit is contained in:
parent
b68f160bce
commit
4005673ae8
16 changed files with 1188 additions and 171 deletions
111
src/features/dashboard/components/DashboardFreeView.tsx
Normal file
111
src/features/dashboard/components/DashboardFreeView.tsx
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* 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>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue