feat(progression): page /progression + section Dashboard Premium — patterns, exercices long terme, indice de préparation (Sprint 3.6c)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a752029c19
commit
a60c298605
18 changed files with 1005 additions and 7 deletions
76
src/features/progression/pages/ProgressionPage.tsx
Normal file
76
src/features/progression/pages/ProgressionPage.tsx
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* Page /progression — Sprint 3.6c.
|
||||
*
|
||||
* Gating plan via `hasAccess(plan, 'pattern_analysis')` :
|
||||
* - Free + Standard → `BlurredProgression` (aperçu flouté + CTA upgrade)
|
||||
* - Premium → `ProgressionPremium` (NotReady ou contenu complet)
|
||||
*
|
||||
* Règle D : aucun `plan === 'xxx'` — tout passe par hasAccess().
|
||||
* Règle L : tokens Direction H exclusivement.
|
||||
*/
|
||||
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Card } from '@/shared/ui/Card'
|
||||
import { Button } from '@/shared/ui/Button'
|
||||
import { hasAccess } from '@/entities/user/lib'
|
||||
import { usePlan } from '@/features/dashboard/hooks/usePlan'
|
||||
import { usePatterns } from '../hooks/usePatterns'
|
||||
import { BlurredProgression } from '../components/BlurredProgression'
|
||||
import { ProgressionPremium } from '../components/ProgressionPremium'
|
||||
|
||||
function Skeleton() {
|
||||
return (
|
||||
<div className="space-y-4" aria-busy="true" aria-label="Chargement de votre profil…">
|
||||
<div className="h-32 animate-pulse rounded-lg bg-canvas-2" />
|
||||
<div className="h-24 animate-pulse rounded-lg bg-canvas-2" />
|
||||
<div className="h-48 animate-pulse rounded-lg bg-canvas-2" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function ProgressionPage() {
|
||||
const navigate = useNavigate()
|
||||
const { data: planData, isLoading: isPlanLoading } = usePlan()
|
||||
const { data: patternsData, isLoading: isPatternsLoading, isError } = usePatterns(
|
||||
planData?.plan,
|
||||
)
|
||||
|
||||
const isPremium = planData ? hasAccess(planData.plan, 'pattern_analysis') : false
|
||||
|
||||
return (
|
||||
<main className="mx-auto max-w-3xl space-y-6 px-4 py-6">
|
||||
<header className="space-y-1">
|
||||
<h1 className="text-lg font-semibold text-ink-1">Profil de préparation</h1>
|
||||
<p className="text-sm text-ink-3">
|
||||
Repérez vos erreurs récurrentes et travaillez-les avec des exercices ciblés.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
{isPlanLoading && <Skeleton />}
|
||||
|
||||
{!isPlanLoading && planData && !isPremium && (
|
||||
<BlurredProgression onUpgrade={() => navigate('/plan')} />
|
||||
)}
|
||||
|
||||
{!isPlanLoading && planData && isPremium && (
|
||||
<>
|
||||
{isPatternsLoading && <Skeleton />}
|
||||
{isError && (
|
||||
<Card variant="default" className="border-l-4 border-l-danger p-4">
|
||||
<p className="text-sm text-danger" role="alert">
|
||||
Impossible de charger votre profil de préparation. Réessayez dans
|
||||
quelques instants.
|
||||
</p>
|
||||
<div className="mt-3">
|
||||
<Button variant="secondary" size="sm" onClick={() => navigate(0)}>
|
||||
Rafraîchir
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
{patternsData && <ProgressionPremium data={patternsData} />}
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue