feat(brainstorm): Option D — toast + peek panel + navigation volontaire
Some checks failed
CI / Lint, Unit Tests & Build (push) Failing after 4m37s
CI / Deploy production (on server) (push) Has been skipped

3 points corrigés (plus d'éjection automatique du brainstorm):

1. Convertir idée → note:
   - Avant: setTimeout 2s → router.push (éjection forcée)
   - Après: toast.success persistant 10s + peek.open (drawer latéral)

2. Exporter session → note synthèse:
   - Avant: setTimeout 2s → router.push (éjection forcée)
   - Après: toast.success + peek.open

3. Voir note référencée:
   - Avant: router.push (éjection immédiate)
   - Après: peek.open (drawer latéral)

Comportement:
- Toast sonner avec bouton 'Ouvrir' (navigation volontaire)
- NotePeekPanel mode overlay (slide droite, lecture seule, KaTeX)
- Canvas brainstorm reste interactif derrière
- Bouton Maximize2 dans le peek → router.push (choix explicite)
- Anciens convertToast/exportToast supprimés (45 lignes JSX retirées)
This commit is contained in:
Antigravity
2026-07-05 19:09:26 +00:00
parent 6e13b6d207
commit d1336b1fea

View File

@@ -1,4 +1,5 @@
'use client' 'use client'
import { toast } from 'sonner'
import React, { useState, useMemo, useEffect, useRef, useCallback } from 'react' import React, { useState, useMemo, useEffect, useRef, useCallback } from 'react'
import { useRouter, useSearchParams } from 'next/navigation' import { useRouter, useSearchParams } from 'next/navigation'
@@ -26,9 +27,11 @@ import {
LayoutGrid, LayoutGrid,
X, X,
Edit2, Edit2,
Maximize2,
} from 'lucide-react' } from 'lucide-react'
import dynamic from 'next/dynamic' import dynamic from 'next/dynamic'
import { useLanguage } from '@/lib/i18n' import { useLanguage } from '@/lib/i18n'
import { useNotePeek, NotePeekPanel } from '@/components/note-peek'
import { import {
useBrainstormSession, useBrainstormSession,
useBrainstormSessions, useBrainstormSessions,
@@ -76,6 +79,7 @@ const WAVE_COLORS: Record<number, { border: string; bg: string; text: string }>
export function BrainstormPage() { export function BrainstormPage() {
const router = useRouter() const router = useRouter()
const peek = useNotePeek()
const searchParams = useSearchParams() const searchParams = useSearchParams()
const { t, language } = useLanguage() const { t, language } = useLanguage()
const { data: authSession } = useSession() const { data: authSession } = useSession()
@@ -132,8 +136,7 @@ export function BrainstormPage() {
const renameSession = useRenameBrainstormSession(activeSessionId || '') const renameSession = useRenameBrainstormSession(activeSessionId || '')
const [impactToast, setImpactToast] = useState<{ notesEnriched: number; notesMarkedDry: number } | null>(null) const [impactToast, setImpactToast] = useState<{ notesEnriched: number; notesMarkedDry: number } | null>(null)
const [exportError, setExportError] = useState<string | null>(null) const [exportError, setExportError] = useState<string | null>(null)
const [exportToast, setExportToast] = useState<{ noteTitle: string; notebookName: string } | null>(null)
const [convertToast, setConvertToast] = useState<{ noteTitle: string; noteId: string } | null>(null)
const [remoteMove, setRemoteMove] = useState<{ ideaId: string; x: number; y: number; _seq: number } | null>(null) const [remoteMove, setRemoteMove] = useState<{ ideaId: string; x: number; y: number; _seq: number } | null>(null)
const [playbackIdeas, setPlaybackIdeas] = useState<any[] | null>(null) const [playbackIdeas, setPlaybackIdeas] = useState<any[] | null>(null)
const [viewMode, setViewMode] = useState<'canvas' | 'list'>('canvas') const [viewMode, setViewMode] = useState<'canvas' | 'list'>('canvas')
@@ -242,11 +245,15 @@ export function BrainstormPage() {
try { try {
const result = await convertIdea.mutateAsync(idea.id) const result = await convertIdea.mutateAsync(idea.id)
if (result?.id) { if (result?.id) {
setConvertToast({ noteTitle: result.title || idea.title, noteId: result.id }) toast.success(t('brainstorm.noteCreated') || 'Note created', {
setTimeout(() => { description: result.title || idea.title,
setConvertToast(null) action: {
router.push(`/home?openNote=${result.id}`) label: t('notePeek.openFully') || 'Open',
}, 2000) onClick: () => router.push(`/home?openNote=${result.id}`),
},
duration: 10000,
})
peek.open(result.id)
} }
} catch {} } catch {}
} }
@@ -287,11 +294,15 @@ export function BrainstormPage() {
const result = await exportBrainstorm.mutateAsync() const result = await exportBrainstorm.mutateAsync()
if (result?.id) { if (result?.id) {
const notebookName = result._notebookName || t('brainstorm.exportDefaultNotebookName') const notebookName = result._notebookName || t('brainstorm.exportDefaultNotebookName')
setExportToast({ noteTitle: result.title || t('brainstorm.exportDefaultNoteTitle'), notebookName }) toast.success(t('brainstorm.noteCreated') || 'Note created', {
setTimeout(() => { description: `${result.title || t('brainstorm.exportDefaultNoteTitle')}${notebookName}`,
setExportToast(null) action: {
router.push(`/home?openNote=${result.id}`) label: t('notePeek.openFully') || 'Open',
}, 2000) onClick: () => router.push(`/home?openNote=${result.id}`),
},
duration: 10000,
})
peek.open(result.id)
return return
} }
const impact = await finalizeBrainstorm.mutateAsync() const impact = await finalizeBrainstorm.mutateAsync()
@@ -846,7 +857,7 @@ export function BrainstormPage() {
</div> </div>
{ref.noteId && ( {ref.noteId && (
<button <button
onClick={() => router.push(`/home?openNote=${ref.noteId}`)} onClick={() => ref.noteId && peek.open(ref.noteId)}
className="shrink-0 px-2 py-1 text-[9px] font-bold uppercase tracking-wider rounded-lg bg-foreground/5 hover:bg-foreground/10 text-muted-foreground hover:text-foreground transition-all" className="shrink-0 px-2 py-1 text-[9px] font-bold uppercase tracking-wider rounded-lg bg-foreground/5 hover:bg-foreground/10 text-muted-foreground hover:text-foreground transition-all"
> >
{t('brainstorm.viewNote') || 'View'} {t('brainstorm.viewNote') || 'View'}
@@ -1124,49 +1135,15 @@ export function BrainstormPage() {
)} )}
</AnimatePresence> </AnimatePresence>
<AnimatePresence> {/* Peek panel — drawer latéral pour aperçu note sans quitter le brainstorm */}
{exportToast && ( <NotePeekPanel
<motion.div note={peek.note}
initial={{ y: 100, opacity: 0 }} blockId={peek.blockId}
animate={{ y: 0, opacity: 1 }} loading={peek.loading}
exit={{ y: 100, opacity: 0 }} mode="overlay"
className="fixed bottom-8 left-1/2 -translate-x-1/2 z-50 px-6 py-4 bg-emerald-600 text-white rounded-2xl shadow-2xl flex items-center gap-3 text-sm font-medium" onClose={peek.close}
> onOpenFully={(n) => { router.push(`/home?openNote=${n.id}`); peek.close() }}
<Wind size={18} /> />
<div className="flex flex-col">
<span className="font-bold">{exportToast.noteTitle}</span>
<span className="text-[11px] text-emerald-100">
{t('brainstorm.exportNotebookPrefix')} {exportToast.notebookName}
</span>
</div>
<div className="ml-3 flex items-center gap-1.5 text-emerald-200 text-[10px]">
<div className="w-1.5 h-1.5 rounded-full bg-emerald-300 animate-pulse" />
{t('brainstorm.exportOpening')}
</div>
</motion.div>
)}
</AnimatePresence>
<AnimatePresence>
{convertToast && (
<motion.div
initial={{ y: 100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 100, opacity: 0 }}
className="fixed bottom-8 left-1/2 -translate-x-1/2 z-50 px-6 py-4 bg-emerald-600 text-white rounded-2xl shadow-2xl flex items-center gap-3 text-sm font-medium"
>
<FileText size={18} />
<div className="flex flex-col">
<span className="font-bold">{convertToast.noteTitle}</span>
<span className="text-[11px] text-emerald-100">{t('brainstorm.noteCreated') || 'Note Created'}</span>
</div>
<div className="ml-3 flex items-center gap-1.5 text-emerald-200 text-[10px]">
<div className="w-1.5 h-1.5 rounded-full bg-emerald-300 animate-pulse" />
{t('brainstorm.exportOpening')}
</div>
</motion.div>
)}
</AnimatePresence>
</div> </div>
) )
} }