Files
Momento/memento-note/components/dashboard-resume-hero.tsx
Antigravity 4fe31ebc99
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 6m55s
CI / Deploy production (on server) (push) Successful in 36s
fix(quotas): unifier le décompte IA (BYOK, rollback) et combler les fuites
Centralise la réserve via ai-quota, corrige admin unavailable (-1), brancher les routes sans quota et le host-pays brainstorm, avec usage-meter élargi, noms de clusters, MCP et ajustements dashboard/insights.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-15 20:42:25 +00:00

155 lines
6.2 KiB
TypeScript

'use client'
import { motion } from 'motion/react'
import { Clock, ChevronRight, Play, PenLine } from 'lucide-react'
import { useLanguage } from '@/lib/i18n'
import { DashboardWidgetTitleRow } from '@/components/dashboard-widget-title-row'
export interface ResumeNote {
id: string
title: string | null
excerpt: string
notebookName: string
notebookColor: string
updatedAt: string
notebookId: string | null
}
export interface DashboardResumeHeroProps {
notes: ResumeNote[]
loading?: boolean
onSelect: (id: string, notebookId: string | null) => void
onCaptureFocus?: () => void
formatRelativeTime: (date: string) => string
prefersReducedMotion?: boolean
}
export function DashboardResumeHero({
notes,
loading,
onSelect,
onCaptureFocus,
formatRelativeTime,
prefersReducedMotion,
}: DashboardResumeHeroProps) {
const { t } = useLanguage()
const hero = notes[0]
const rest = notes.slice(1, 6)
if (loading) {
return (
<div className="rounded-2xl border border-border/30 bg-white dark:bg-zinc-900 p-5 min-h-[220px] animate-pulse">
<div className="h-4 w-32 bg-stone-100 dark:bg-zinc-800 rounded mb-4" />
<div className="h-6 w-3/4 bg-stone-100 dark:bg-zinc-800 rounded mb-3" />
<div className="h-16 bg-stone-50 dark:bg-zinc-950 rounded-xl mb-3" />
<div className="space-y-2">
<div className="h-10 bg-stone-50 dark:bg-zinc-950 rounded-xl" />
<div className="h-10 bg-stone-50 dark:bg-zinc-950 rounded-xl" />
</div>
</div>
)
}
return (
<div className="rounded-2xl border border-border/30 bg-white dark:bg-zinc-900 overflow-hidden">
<div className="px-5 pt-4">
<DashboardWidgetTitleRow
widgetId="resume"
icon={<Play size={11} className="text-brand-accent" fill="currentColor" />}
title={t('homeDashboard.continue')}
className="mb-2"
/>
</div>
{!hero ? (
<div className="px-5 pb-5">
<div className="rounded-xl border border-dashed border-border/40 bg-stone-50/60 dark:bg-zinc-950/40 p-5 text-center">
<Clock size={22} className="mx-auto text-concrete/35 mb-2" strokeWidth={1.25} />
<p className="text-xs text-concrete mb-3 leading-relaxed">
{t('homeDashboard.resumeEmptyHint')}
</p>
{onCaptureFocus && (
<button
type="button"
onClick={onCaptureFocus}
className="inline-flex items-center gap-1.5 text-[9px] font-mono font-bold uppercase text-brand-accent hover:underline"
>
<PenLine size={11} />
{t('homeDashboard.resumeEmptyCta')}
</button>
)}
</div>
</div>
) : (
<>
<motion.button
type="button"
whileHover={prefersReducedMotion ? undefined : { y: -1 }}
onClick={() => onSelect(hero.id, hero.notebookId)}
className="w-full text-start px-5 pb-3 group cursor-pointer"
>
<div className="p-4 rounded-xl border border-border/25 bg-gradient-to-br from-stone-50/80 to-white dark:from-zinc-950/50 dark:to-zinc-900/80 group-hover:border-brand-accent/35 group-hover:shadow-md transition-all">
<div className="flex items-start justify-between gap-3 mb-2">
<span
className="text-[8px] font-mono font-bold uppercase px-2 py-0.5 rounded text-white shrink-0"
style={{ backgroundColor: hero.notebookColor }}
>
{hero.notebookName}
</span>
<span className="text-[9px] font-mono text-concrete/70 shrink-0">
{formatRelativeTime(hero.updatedAt)}
</span>
</div>
<h3 className="text-base sm:text-lg font-serif font-semibold text-ink dark:text-dark-ink group-hover:text-brand-accent transition-colors leading-snug mb-2 line-clamp-2">
{hero.title || t('homeDashboard.untitled')}
</h3>
{hero.excerpt ? (
<p className="text-[11px] text-concrete leading-relaxed line-clamp-3">
{hero.excerpt}
</p>
) : null}
<div className="flex items-center gap-1 mt-3 text-[9px] font-mono font-bold uppercase text-brand-accent opacity-80 group-hover:opacity-100 transition-opacity">
{t('homeDashboard.resumeOpen')}
<ChevronRight size={12} />
</div>
</div>
</motion.button>
{rest.length > 0 && (
<div className="px-5 pb-4 space-y-1.5">
<p className="text-[8px] font-mono font-bold uppercase tracking-wider text-concrete/70 mb-1">
{t('homeDashboard.resumeAlso')}
</p>
{rest.map(note => (
<button
key={note.id}
type="button"
onClick={() => onSelect(note.id, note.notebookId)}
className="w-full flex items-center gap-3 p-2.5 rounded-xl border border-border/20 bg-stone-50/40 dark:bg-zinc-950/30 hover:border-brand-accent/30 hover:bg-brand-accent/[0.03] transition-all text-start group"
>
<span
className="w-1.5 h-8 rounded-full shrink-0"
style={{ backgroundColor: note.notebookColor }}
aria-hidden
/>
<div className="min-w-0 flex-1">
<p className="text-[11px] font-semibold text-ink dark:text-dark-ink truncate group-hover:text-brand-accent transition-colors">
{note.title || t('homeDashboard.untitled')}
</p>
<p className="text-[9px] text-concrete truncate mt-0.5">
{note.notebookName}
{' · '}
{formatRelativeTime(note.updatedAt)}
</p>
</div>
<ChevronRight size={12} className="text-concrete/40 group-hover:text-brand-accent shrink-0" />
</button>
))}
</div>
)}
</>
)}
</div>
)
}