Files
Momento/memento-note/components/onboarding/onboarding-step-features.tsx
Antigravity 6b4ed8514f
Some checks failed
CI / Lint, Unit Tests & Build (push) Successful in 5m37s
CI / Deploy production (on server) (push) Has been cancelled
Epic 6: Stories 6-2 (Markdown roundtrip) + 6-3 (Brainstorm PPTX + Canvas)
Story 6-2 — Markdown roundtrip export/import:
- lib/editor/markdown-export.ts: tiptapHTMLToMarkdown, markdownToHTML, looksLikeMarkdown
- lib/editor/markdown-paste-extension.ts: TipTap extension paste Markdown → blocs
- note-editor-toolbar.tsx: export .md + import .md (file picker)
- rich-text-editor.tsx: intégration MarkdownPasteExtension
- 40 tests unitaires markdown-export.test.ts

Story 6-3 — Brainstorm PPTX + Canvas:
- lib/brainstorm/export-pptx.ts: génération PPTX 5 slides (pptxgenjs)
- app/api/brainstorm/[sessionId]/export-pptx/route.ts: route POST protégée
- brainstorm-page.tsx: bouton PPTX, auto-select session, fix emoji, fix router.replace
- wave-canvas.tsx: fitTrigger recentrage, légende bas-droite

Onboarding activation wizard (Story 6-1):
- components/onboarding/: wizard multi-étapes, hints éditeur
- app/api/onboarding/: route PATCH onboarding
- prisma/migrations: champs onboarding user

Locales: 15 langues mises à jour (brainstorm, markdown, onboarding keys)
Sprint: 6-1 done, 6-2 review, 6-3 review

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-29 11:24:56 +00:00

117 lines
4.5 KiB
TypeScript

'use client'
import { useState } from 'react'
import { motion, AnimatePresence } from 'motion/react'
import { Pencil, CreditCard, Lightbulb, Check, ArrowRight, Sparkles } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { useLanguage } from '@/lib/i18n'
interface Props {
onDone: () => void
onTry: (href: string) => void
}
const ACTIONS = [
{ icon: Pencil, color: 'text-violet-500', bg: 'bg-violet-500/10', key: 'write', href: '/home' },
{ icon: CreditCard, color: 'text-blue-500', bg: 'bg-blue-500/10', key: 'flashcards', href: '/revision' },
{ icon: Lightbulb, color: 'text-amber-500', bg: 'bg-amber-500/10', key: 'brainstorm', href: '/brainstorm' },
]
export function OnboardingStepFeatures({ onDone, onTry }: Props) {
const { t } = useLanguage()
const [checked, setChecked] = useState<Set<string>>(new Set())
function handleTry(key: string, href: string) {
setChecked(prev => new Set([...prev, key]))
onTry(href)
}
const allChecked = checked.size === ACTIONS.length
return (
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -16 }}
className="flex flex-col items-center gap-5 w-full"
>
<motion.div
initial={{ scale: 0.7, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ delay: 0.1, type: 'spring', stiffness: 200 }}
className="flex h-20 w-20 items-center justify-center rounded-2xl bg-amber-500/10 text-amber-500"
>
<Sparkles className="h-10 w-10" />
</motion.div>
<div className="space-y-1 text-center">
<h2 className="text-2xl font-bold tracking-tight text-foreground">
{t('onboarding.step_features_title')}
</h2>
<p className="text-sm text-muted-foreground max-w-xs mx-auto">
{t('onboarding.step_features_subtitle')}
</p>
</div>
<div className="flex flex-col gap-2.5 w-full">
{ACTIONS.map(({ icon: Icon, color, bg, key, href }, i) => {
const done = checked.has(key)
return (
<motion.div
key={key}
initial={{ opacity: 0, x: -12 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.08 + i * 0.08 }}
className={`flex items-center gap-3 rounded-xl border p-3 transition-all ${
done ? 'border-emerald-500/30 bg-emerald-500/5' : 'border-border bg-muted/10 hover:border-border/80'
}`}
>
{/* Checkmark */}
<span className={`shrink-0 flex h-5 w-5 items-center justify-center rounded-full border-2 transition-colors ${
done ? 'border-emerald-500 bg-emerald-500' : 'border-border'
}`}>
<AnimatePresence>
{done && (
<motion.span initial={{ scale: 0 }} animate={{ scale: 1 }} exit={{ scale: 0 }}>
<Check className="h-3 w-3 text-white" />
</motion.span>
)}
</AnimatePresence>
</span>
{/* Icon */}
<span className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${bg} ${color}`}>
<Icon className="h-4 w-4" />
</span>
{/* Text */}
<div className="flex-1 min-w-0">
<p className={`text-sm font-medium leading-tight ${done ? 'text-muted-foreground' : 'text-foreground'}`}>
{t(`onboarding.action_${key}_title`)}
</p>
<p className="text-xs text-muted-foreground leading-tight mt-0.5">
{t(`onboarding.action_${key}_desc`)}
</p>
</div>
{/* Essayer — minimise wizard + navigue */}
<button
onClick={() => handleTry(key, href)}
className={`shrink-0 flex items-center gap-1 text-xs font-medium px-2.5 py-1.5 rounded-lg transition-opacity ${color} ${bg} ${done ? 'opacity-40' : 'hover:opacity-80'}`}
>
{done ? t('onboarding.action_done') : t('onboarding.action_try')}
{!done && <ArrowRight className="h-3 w-3" />}
</button>
</motion.div>
)
})}
</div>
<Button onClick={onDone} size="lg" className="w-full gap-2 mt-1">
{allChecked ? t('onboarding.step_features_cta_all') : t('onboarding.step_features_cta')}
<ArrowRight className="h-4 w-4" />
</Button>
</motion.div>
)
}