feat(dashboard): tableau de bord Second Brain configurable avec chargement progressif
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 7m3s
CI / Deploy production (on server) (push) Successful in 23s

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:
Antigravity
2026-07-14 16:50:53 +00:00
parent d38a99586b
commit 30da592ba2
62 changed files with 7741 additions and 335 deletions

View File

@@ -47,7 +47,7 @@ export function MobileActionSheet({
const end = $pos.end(1)
editor.chain().focus().setTextSelection({ from: start, to: end }).run()
toast.success(t('richTextEditor.blockSelected') || 'Bloc sélectionné en entier')
toast.success(t('richTextEditor.blockSelected'))
onClose()
}
@@ -62,7 +62,7 @@ export function MobileActionSheet({
const nodeText = editor.state.doc.slice(start, end)
editor.chain().focus().insertContentAt(end, nodeText.content.toJSON()).run()
toast.success(t('richTextEditor.blockDuplicated') || 'Bloc dupliqué')
toast.success(t('richTextEditor.blockDuplicated'))
onClose()
}
@@ -75,7 +75,7 @@ export function MobileActionSheet({
const start = $pos.before(1)
const end = $pos.after(1)
editor.chain().focus().deleteRange({ from: start, to: end }).run()
toast.success(t('richTextEditor.blockDeleted') || 'Bloc supprimé')
toast.success(t('richTextEditor.blockDeleted'))
onClose()
}
@@ -85,7 +85,7 @@ export function MobileActionSheet({
onClose()
const tab = action === 'improve' ? 'actions' : 'chat'
window.dispatchEvent(new CustomEvent('memento-open-ai', { detail: { tab, scroll: action } }))
toast.info(t('richTextEditor.aiActionStarted') || 'IA Note sollicitée...')
toast.info(t('richTextEditor.aiActionStarted'))
}
return createPortal(
@@ -101,26 +101,26 @@ export function MobileActionSheet({
<div className="mobile-action-sheet-body">
{/* Section 1 : Actions de bloc */}
<div className="mobile-action-sheet-section">
<h4 className="section-title">{t('mobile.blockActions') || 'Actions sur le bloc'}</h4>
<h4 className="section-title">{t('mobile.blockActions')}</h4>
<div className="grid grid-cols-3 gap-2">
<button type="button" className="action-tile-btn" onClick={handleSelectAllBlock}>
<FileText size={20} className="text-blue-500" />
<span>{t('mobile.selectAll') || 'Sélectionner tout'}</span>
<span>{t('mobile.selectAll')}</span>
</button>
<button type="button" className="action-tile-btn" onClick={handleDuplicateBlock}>
<Copy size={20} className="text-emerald-500" />
<span>{t('mobile.duplicate') || 'Dupliquer'}</span>
<span>{t('mobile.duplicate')}</span>
</button>
<button type="button" className="action-tile-btn text-rose-500" onClick={handleDeleteBlock}>
<Trash2 size={20} className="text-rose-500" />
<span>{t('mobile.delete') || 'Supprimer'}</span>
<span>{t('mobile.delete')}</span>
</button>
</div>
</div>
{/* Section 2 : IA Note */}
<div className="mobile-action-sheet-section">
<h4 className="section-title">{t('mobile.aiNote') || 'IA Note'}</h4>
<h4 className="section-title">{t('mobile.aiNote')}</h4>
<div className="grid grid-cols-4 gap-2">
<button type="button" className="action-tile-btn" onClick={() => handleAiAction('improve')}>
<Wand2 size={18} className="text-purple-500 animate-pulse" />
@@ -143,42 +143,42 @@ export function MobileActionSheet({
{/* Section 3 : Format de bloc */}
<div className="mobile-action-sheet-section">
<h4 className="section-title">{t('mobile.convertFormat') || 'Convertir le format'}</h4>
<h4 className="section-title">{t('mobile.convertFormat')}</h4>
<div className="flex gap-2 overflow-x-auto pb-2 scrollbar-none">
<button
type="button"
className="format-pill-btn"
onClick={() => { editor.chain().focus().setParagraph().run(); onClose() }}
>
{t('mobile.paragraph') || 'Paragraphe'}
{t('mobile.paragraph')}
</button>
<button
type="button"
className="format-pill-btn"
onClick={() => { editor.chain().focus().toggleHeading({ level: 1 }).run(); onClose() }}
>
<Heading1 size={14} className="inline mr-1" /> {t('notes.heading1') || 'Titre 1'}
<Heading1 size={14} className="inline mr-1" /> {t('notes.heading1')}
</button>
<button
type="button"
className="format-pill-btn"
onClick={() => { editor.chain().focus().toggleHeading({ level: 2 }).run(); onClose() }}
>
<Heading2 size={14} className="inline mr-1" /> {t('notes.heading2') || 'Titre 2'}
<Heading2 size={14} className="inline mr-1" /> {t('notes.heading2')}
</button>
<button
type="button"
className="format-pill-btn"
onClick={() => { editor.chain().focus().toggleHeading({ level: 3 }).run(); onClose() }}
>
<Heading3 size={14} className="inline mr-1" /> {t('notes.heading3') || 'Titre 3'}
<Heading3 size={14} className="inline mr-1" /> {t('notes.heading3')}
</button>
<button
type="button"
className="format-pill-btn"
onClick={() => { editor.chain().focus().toggleBlockquote().run(); onClose() }}
>
<Quote size={14} className="inline mr-1" /> {t('mobile.quote') || 'Citation'}
<Quote size={14} className="inline mr-1" /> {t('mobile.quote')}
</button>
</div>
</div>