Files
Momento/memento-note/components/dashboard-action-strip.tsx
Antigravity 30da592ba2
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 7m3s
CI / Deploy production (on server) (push) Successful in 23s
feat(dashboard): tableau de bord Second Brain configurable avec chargement progressif
Briefing granulaire, pistes rapides puis enrichissement async, layout persisté v5,
suggestions agents, intégration Gmail et navigation sidebar alignée sur /home.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-14 16:50:53 +00:00

127 lines
3.5 KiB
TypeScript

'use client'
import { motion } from 'motion/react'
import { Inbox, GraduationCap, Bell, Sparkles, Layers } from 'lucide-react'
import { useLanguage } from '@/lib/i18n'
export interface DashboardActionStripProps {
inboxCount: number
dueFlashcards: number
reminderCount: number
discoveryCount: number
themeCount: number
inboxPulse?: number
onInbox: () => void
onReview: () => void
onReminders: () => void
onDiscoveries: () => void
onThemes: () => void
prefersReducedMotion?: boolean
}
export function DashboardActionStrip({
inboxCount,
dueFlashcards,
reminderCount,
discoveryCount,
themeCount,
inboxPulse = 0,
onInbox,
onReview,
onReminders,
onDiscoveries,
onThemes,
prefersReducedMotion,
}: DashboardActionStripProps) {
const { t } = useLanguage()
const items = [
{
key: 'inbox',
icon: Inbox,
label: t('homeDashboard.inbox'),
value: inboxCount,
onClick: onInbox,
accent: inboxCount > 0,
pulse: inboxPulse > 0,
},
{
key: 'review',
icon: GraduationCap,
label: t('homeDashboard.review'),
value: dueFlashcards,
onClick: onReview,
accent: dueFlashcards > 0,
},
{
key: 'reminders',
icon: Bell,
label: t('homeDashboard.reminders'),
value: reminderCount,
onClick: onReminders,
accent: reminderCount > 0,
},
{
key: 'discoveries',
icon: Sparkles,
label: t('homeDashboard.aiFound'),
value: discoveryCount,
onClick: onDiscoveries,
accent: discoveryCount > 0,
},
{
key: 'themes',
icon: Layers,
label: t('homeDashboard.themes'),
value: themeCount,
onClick: onThemes,
accent: themeCount > 0,
},
]
return (
<div className="flex gap-2 overflow-x-auto custom-scrollbar pb-0.5 -mx-0.5 px-0.5">
{items.map(item => {
const Icon = item.icon
const Wrapper = item.pulse && !prefersReducedMotion ? motion.button : 'button'
const motionProps = item.pulse && !prefersReducedMotion
? {
key: `inbox-pulse-${inboxPulse}`,
animate: { scale: [1, 1.03, 1] },
transition: { duration: 0.45 },
}
: {}
return (
<Wrapper
key={item.key}
type="button"
onClick={item.onClick}
{...motionProps}
className={`shrink-0 flex items-center gap-2.5 px-3.5 py-2.5 rounded-xl border transition-all text-start min-w-[108px] ${
item.accent
? 'border-brand-accent/30 bg-brand-accent/[0.06] hover:border-brand-accent/50 hover:bg-brand-accent/10 shadow-sm'
: 'border-border/25 bg-white/60 dark:bg-zinc-900/40 hover:border-border/50'
}`}
>
<div className={`w-7 h-7 rounded-lg flex items-center justify-center shrink-0 ${
item.accent ? 'bg-brand-accent/15 text-brand-accent' : 'bg-stone-100 dark:bg-zinc-800 text-concrete'
}`}>
<Icon size={13} strokeWidth={2} />
</div>
<div className="min-w-0">
<p className={`text-lg font-serif font-bold leading-none ${
item.accent ? 'text-ink dark:text-dark-ink' : 'text-concrete/80'
}`}>
{item.value}
</p>
<p className="text-[8px] font-mono font-bold uppercase tracking-wider text-concrete truncate mt-0.5">
{item.label}
</p>
</div>
</Wrapper>
)
})}
</div>
)
}