'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' import { UsageMeter } from '@/components/usage-meter' import { DashboardWidgetTitleRow } from '@/components/dashboard-widget-title-row' import { pathNoteTitle } from '@/lib/dashboard/path-title' import type { DashboardWidgetId } from '@/lib/dashboard/layout' interface DashboardWidgetShellProps { widgetId: DashboardWidgetId icon: React.ReactNode title: string children: React.ReactNode compact?: boolean headerActions?: React.ReactNode } export function DashboardWidgetShell({ widgetId, icon, title, children, compact, headerActions, }: DashboardWidgetShellProps) { return (
{children}
) } function inboxDisplayTitle( note: { title: string | null; excerpt?: string }, 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 DashboardInboxWidget({ notes, loading, onOpen, onSelect, formatRelativeTime, }: { count: number notes: Array<{ id: string title: string | null excerpt?: string notebookId: string | null updatedAt?: string }> loading: boolean onOpen: () => void onSelect: (id: string, notebookId: string | null) => void formatRelativeTime?: (date: string) => string }) { const { t } = useLanguage() const reduced = !!useReducedMotion() const untitled = t('homeDashboard.untitled') return ( } title={t('homeDashboard.inbox')} compact > {loading ? (
) : notes.length === 0 ? (

{t('homeDashboard.inboxEmpty')}

) : (
{notes.map((note, idx) => ( 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" >

{inboxDisplayTitle(note, untitled)}

{t('homeDashboard.inbox')} {note.updatedAt && formatRelativeTime ? ` · ${formatRelativeTime(note.updatedAt)}` : ''}

))}
)} ) } export function DashboardRevisionWidget({ dueCount, loading, onOpen, }: { dueCount: number loading: boolean onOpen: () => void }) { const { t } = useLanguage() return ( } title={t('homeDashboard.review')} compact > {loading ? (
) : ( )} ) } export function DashboardStatsWidget({ clusterCount, bridgeCount, noteCount, loading, onOpen, }: { clusterCount: number bridgeCount: number noteCount: number loading: boolean onOpen: () => void }) { const { t } = useLanguage() return ( } title={t('homeDashboard.widgets.stats')} compact > {loading ? (
{[0, 1, 2].map(i =>
)}
) : ( )} ) } export function DashboardAgentActivityWidget({ actions, loading, onOpen, }: { actions: { id: string; agentName: string; result: string | null; createdAt: string }[] loading: boolean onOpen: () => void }) { const { t } = useLanguage() return ( } title={t('homeDashboard.widgets.agent-activity')} compact > {loading ? (
) : actions.length === 0 ? (

{t('homeDashboard.widgetAgentActivityEmpty')}

) : (
{actions.slice(0, 3).map(a => ( ))}
)} ) } export function DashboardGmailWidget({ connected, recentCaptures, loading, onOpen, }: { connected: boolean recentCaptures: number loading: boolean onOpen: () => void }) { const { t } = useLanguage() return ( } title={t('homeDashboard.gmailCaptures')} compact > {loading ? (
) : connected ? ( ) : ( )} ) } export function DashboardPinnedWidget({ notes, loading, onSelect, formatRelativeTime, }: { notes: { id: string title: string | null excerpt?: string notebookId: string | null updatedAt?: string notebook?: { name: string; color: string | null } | null }[] loading: boolean onSelect: (id: string, notebookId: string | null) => void formatRelativeTime?: (date: string) => string }) { const { t } = useLanguage() const untitled = t('homeDashboard.untitled') return ( } title={t('homeDashboard.widgets.pinned')} compact > {loading ? (
) : notes.length === 0 ? (

{t('homeDashboard.widgetPinnedEmpty')}

) : (
{notes.map(n => { const displayTitle = pathNoteTitle(n.title, n.excerpt) || untitled const notebookName = n.notebook?.name || t('homeDashboard.pinnedNoNotebook') const notebookColor = n.notebook?.color || '#C4A574' return ( ) })}
)} ) } export function DashboardActivityWidget({ data, loading, }: { data: { date: string; count: number }[] loading: boolean }) { const { t } = useLanguage() const hasActivity = data.some(d => d.count > 0) return ( } title={t('homeDashboard.widgets.activity')} > {loading ? (
) : !hasActivity ? (

{t('homeDashboard.activityEmptyHint')}

) : ( <> )} ) } export function DashboardUsageWidget() { const { t } = useLanguage() return ( } title={t('homeDashboard.widgets.usage')} compact >

{t('homeDashboard.widgetUsageHint')}

) } export function DashboardFlashcardsProgressWidget({ retentionRate, streak, totalCards, dueCount = 0, loading, onOpen, }: { retentionRate: number streak: number totalCards: number dueCount?: number loading?: boolean onOpen: () => void }) { const { t } = useLanguage() const retention = Math.max(0, Math.min(100, retentionRate)) return ( } title={t('homeDashboard.widgets.flashcards-progress')} compact > {loading ? (
) : totalCards === 0 && dueCount === 0 ? (

{t('homeDashboard.flashEmptyHint')}

) : ( )} ) }