feat: dashboard Second Brain, essai 7 jours et vérification e-mail
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 7m14s
CI / Deploy production (on server) (push) Successful in 1m25s

Rendre le dashboard actionnable (inbox, peek, carte mentale), aligner la facturation sur l’essai 7 jours, et bloquer le login e-mail tant que l’adresse n’est pas confirmée.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Antigravity
2026-08-30 07:19:36 +00:00
parent 69c99e4f4f
commit 80ccc1f6de
95 changed files with 4158 additions and 618 deletions

View File

@@ -1,5 +1,6 @@
'use client'
import { motion, useReducedMotion } from 'motion/react'
import { Inbox, GraduationCap, Mail, Pin, Bot, BarChart3 } from 'lucide-react'
import { useLanguage } from '@/lib/i18n'
import { RevisionHeatmap } from '@/components/flashcards/revision-heatmap'
@@ -42,14 +43,19 @@ export function DashboardWidgetShell({
export function DashboardInboxWidget({
count,
notes,
loading,
onOpen,
onSelect,
}: {
count: number
notes: Array<{ id: string; title: string | null; notebookId: string | null }>
loading: boolean
onOpen: () => void
onSelect: (id: string, notebookId: string | null) => void
}) {
const { t } = useLanguage()
const reduced = !!useReducedMotion()
return (
<DashboardWidgetShell
widgetId="inbox"
@@ -58,19 +64,42 @@ export function DashboardInboxWidget({
compact
>
{loading ? (
<div className="h-10 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
<div className="space-y-2">
<div className="h-10 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
<div className="h-8 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
</div>
) : notes.length === 0 ? (
<p className="text-[11px] text-concrete italic py-1">{t('homeDashboard.inboxEmpty')}</p>
) : (
<button
type="button"
onClick={onOpen}
className="w-full flex items-center justify-between gap-3 p-3 rounded-xl border border-border/20 hover:border-brand-accent/30 hover:bg-brand-accent/[0.03] transition-all text-start"
>
<div>
<p className="text-2xl font-serif font-bold text-ink dark:text-dark-ink leading-none">{count}</p>
<p className="text-[10px] text-concrete mt-1">{t('homeDashboard.toOrganize')}</p>
</div>
<span className="text-[9px] font-mono uppercase font-bold text-brand-accent">{t('homeDashboard.widgetOpen')} </span>
</button>
<div className="space-y-1.5">
{notes.map((note, idx) => (
<motion.button
key={note.id}
type="button"
onClick={() => onSelect(note.id, note.notebookId)}
initial={reduced ? false : { opacity: 0, x: 8 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: reduced ? 0 : 0.22, delay: reduced ? 0 : Math.min(idx * 0.05, 0.15), ease: [0.16, 1, 0.3, 1] }}
className="w-full text-start p-2.5 rounded-xl border border-border/20 hover:border-brand-accent/30 hover:bg-brand-accent/[0.03] transition-all"
>
<p className="text-[11px] text-ink dark:text-dark-ink truncate">
{note.title || t('homeDashboard.untitled')}
</p>
</motion.button>
))}
<button
type="button"
onClick={onOpen}
className="w-full flex items-center justify-between gap-2 px-1 pt-1 text-start"
>
<span className="text-[9px] font-mono uppercase font-bold text-concrete">
{t('homeDashboard.inboxSeeAll', { count })}
</span>
<span className="text-[9px] font-mono uppercase font-bold text-brand-accent">
{t('homeDashboard.widgetOpen')}
</span>
</button>
</div>
)}
</DashboardWidgetShell>
)