Les boutons suivent la couleur d’apparence, les libellés trop petits ou trop techniques sont clarifiés, et le catalogue des fournisseurs se met à jour tout seul. Co-authored-by: Cursor <cursoragent@cursor.com>
120 lines
4.2 KiB
TypeScript
120 lines
4.2 KiB
TypeScript
'use client'
|
|
|
|
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
|
|
}
|
|
|
|
function resumeDisplayTitle(note: ResumeNote, fallback: string): string {
|
|
const titled = note.title?.trim()
|
|
if (titled) return titled
|
|
const excerpt = note.excerpt?.trim()
|
|
if (!excerpt) return fallback
|
|
return excerpt.length > 80 ? `${excerpt.slice(0, 80).trim()}…` : excerpt
|
|
}
|
|
|
|
export function DashboardResumeHero({
|
|
notes,
|
|
loading,
|
|
onSelect,
|
|
onCaptureFocus,
|
|
formatRelativeTime,
|
|
}: DashboardResumeHeroProps) {
|
|
const { t } = useLanguage()
|
|
const untitled = t('homeDashboard.untitled')
|
|
const visible = notes.slice(0, 6)
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="rounded-2xl border border-border/30 bg-white dark:bg-zinc-900 p-5 min-h-[180px] animate-pulse">
|
|
<div className="h-4 w-32 bg-stone-100 dark:bg-zinc-800 rounded mb-4" />
|
|
<div className="space-y-2">
|
|
<div className="h-12 bg-stone-50 dark:bg-zinc-950 rounded-xl" />
|
|
<div className="h-12 bg-stone-50 dark:bg-zinc-950 rounded-xl" />
|
|
<div className="h-12 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>
|
|
|
|
{visible.length === 0 ? (
|
|
<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>
|
|
) : (
|
|
<div className="px-5 pb-4 space-y-1.5">
|
|
{visible.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">
|
|
{resumeDisplayTitle(note, 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>
|
|
)
|
|
}
|