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>
This commit is contained in:
@@ -11,10 +11,16 @@ import {
|
||||
XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer,
|
||||
FunnelChart, Funnel, LabelList,
|
||||
} from 'recharts'
|
||||
import { useLanguage } from '@/lib/i18n'
|
||||
|
||||
function MermaidLoadingFallback() {
|
||||
const { t } = useLanguage()
|
||||
return <div style={{ color: 'rgba(255,255,255,0.3)', padding: 24 }}>{t('common.loading')}</div>
|
||||
}
|
||||
|
||||
const MermaidDiagram = dynamic(
|
||||
() => import('./mermaid-diagram').then(m => ({ default: m.MermaidDiagram })),
|
||||
{ ssr: false, loading: () => <div style={{ color: 'rgba(255,255,255,0.3)', padding: 24 }}>Chargement…</div> }
|
||||
{ ssr: false, loading: () => <MermaidLoadingFallback /> }
|
||||
)
|
||||
|
||||
// ── Constants ──────────────────────────────────────────────────────────────────
|
||||
@@ -688,6 +694,7 @@ export interface SlidesRendererProps {
|
||||
}
|
||||
|
||||
export function SlidesRenderer({ spec }: SlidesRendererProps) {
|
||||
const { t } = useLanguage()
|
||||
const [current, setCurrent] = useState(0)
|
||||
const [showNotes, setShowNotes] = useState(false)
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
@@ -795,12 +802,12 @@ export function SlidesRenderer({ spec }: SlidesRendererProps) {
|
||||
|
||||
{/* Navigation buttons */}
|
||||
{current > 0 && (
|
||||
<button style={navBtn(true)} onClick={prev} aria-label="Précédent">
|
||||
<button style={navBtn(true)} onClick={prev} aria-label={t('lab.previous')}>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="15 18 9 12 15 6" /></svg>
|
||||
</button>
|
||||
)}
|
||||
{current < total - 1 && (
|
||||
<button style={navBtn(false)} onClick={next} aria-label="Suivant">
|
||||
<button style={navBtn(false)} onClick={next} aria-label={t('lab.next')}>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="9 6 15 12 9 18" /></svg>
|
||||
</button>
|
||||
)}
|
||||
@@ -830,12 +837,12 @@ export function SlidesRenderer({ spec }: SlidesRendererProps) {
|
||||
>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}>
|
||||
<span style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase', color: palette.accent }}>
|
||||
Notes de présentation
|
||||
{t('lab.notes')}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setShowNotes(false)}
|
||||
style={{ background: 'none', border: 'none', color: isDark ? '#94a3b8' : '#64748b', cursor: 'pointer', fontSize: 13, padding: 4 }}
|
||||
title="Masquer les notes"
|
||||
title={t('lab.hideNotes')}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
@@ -860,10 +867,10 @@ export function SlidesRenderer({ spec }: SlidesRendererProps) {
|
||||
display: 'flex', alignItems: 'center', gap: 6, transition: 'all 0.2s',
|
||||
backdropFilter: 'blur(4px)',
|
||||
}}
|
||||
title="Bascule des notes de présentation (Raccourci: N)"
|
||||
title={t('lab.toggleNotes')}
|
||||
>
|
||||
<span>📝</span>
|
||||
<span>Notes</span>
|
||||
<span>{t('lab.notes')}</span>
|
||||
</button>
|
||||
)}
|
||||
<div style={{ fontSize: 12, fontWeight: 600, color: isDark ? 'rgba(255,255,255,0.4)' : 'rgba(0,0,0,0.35)', background: isDark ? 'rgba(0,0,0,0.4)' : 'rgba(255,255,255,0.8)', padding: '3px 10px', borderRadius: 100, backdropFilter: 'blur(4px)', border: `1px solid ${isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)'}` }}>
|
||||
|
||||
Reference in New Issue
Block a user