diff --git a/memento-note/components/sidebar.tsx b/memento-note/components/sidebar.tsx index 40939ce1..9482a87f 100644 --- a/memento-note/components/sidebar.tsx +++ b/memento-note/components/sidebar.tsx @@ -32,7 +32,6 @@ import { Home, Search, GraduationCap, - FileText, Folder, FolderOpen, LayoutGrid, @@ -56,7 +55,7 @@ import { Notebook, Note } from '@/lib/types' import { toast } from 'sonner' import { motion, AnimatePresence } from 'motion/react' import { getNoteDisplayTitle } from '@/lib/note-preview' -import { listAiCreatedNotebookIds, isRecentlyCreated } from '@/lib/ai-created-highlight' +import { listAiCreatedNotebookIds } from '@/lib/ai-created-highlight' import dynamic from 'next/dynamic' const CreateNotebookDialog = dynamic(() => import('./create-notebook-dialog').then(m => ({ default: m.CreateNotebookDialog })), { ssr: false }) @@ -84,71 +83,6 @@ const SIDEBAR_WIDTH_KEY = 'memento-sidebar-width' const SIDEBAR_WIDTH_MIN_PX = 280 const SIDEBAR_WIDTH_MAX_PX = 560 -function NoteLink({ - title, - isActive, - isPinned, - autoGenerated, - isRecent, - onClick, -}: { - title: string - isActive: boolean - isPinned?: boolean - /** Note créée par l'IA (agents, wizard, …) */ - autoGenerated?: boolean - /** Créée récemment (< 48h) — point discret « nouveau » */ - isRecent?: boolean - onClick: () => void -}) { - const { language, t } = useLanguage() - const slideX = language === 'fa' || language === 'ar' ? 10 : -10 - return ( - - {autoGenerated ? ( - - ) : ( - - )} - {title} - {autoGenerated && ( - - {t('sidebar.aiBadge') || 'IA'} - - )} - {isRecent && !autoGenerated && ( - - )} - {isPinned && } - - ) -} - function SidebarBrainstorms() { const { data: sessions, isLoading } = useBrainstormSessions() const deleteBrainstorm = useDeleteBrainstorm() @@ -377,17 +311,14 @@ function SidebarReminders({ onOpenNote }: { onOpenNote: (noteId: string, noteboo function SidebarCarnetItem({ carnet, isActive, - notes, - activeNoteId, + noteCount, onCarnetClick, - onNoteClick, onAddSubNotebook, onRename, onDelete, onTogglePin, isPinned, isAiCreated, - children, isDragging, dragHandleProps, level, @@ -398,10 +329,8 @@ function SidebarCarnetItem({ }: { carnet: { id: string; name: string; initial: string; isPrivate?: boolean } isActive: boolean - notes: { id: string; title: string; isPinned?: boolean; autoGenerated?: boolean; isRecent?: boolean }[] - activeNoteId: string | null + noteCount: number onCarnetClick: () => void - onNoteClick: (noteId: string, carnetId: string) => void onAddSubNotebook: () => void onRename: () => void onDelete: () => void @@ -409,7 +338,6 @@ function SidebarCarnetItem({ isPinned: boolean /** Carnet créé via wizard IA (repère temporaire) */ isAiCreated?: boolean - children?: React.ReactNode isDragging?: boolean dragHandleProps?: React.HTMLAttributes level: number @@ -420,7 +348,8 @@ function SidebarCarnetItem({ }) { const { t, language } = useLanguage() const isRtl = language === 'fa' || language === 'ar' - const hasChildren = hasChildNotebooks || React.Children.count(children) > 0 || notes.length > 0 + const hasChildren = Boolean(hasChildNotebooks) + const showOpenFolder = hasChildren && isExpanded const [contextMenu, setContextMenu] = useState<{ x: number; y: number } | null>(null) // Close context menu on outside click @@ -434,6 +363,7 @@ function SidebarCarnetItem({ return ( { @@ -461,6 +391,8 @@ function SidebarCarnetItem({ {hasChildren ? ( { e.stopPropagation(); toggleExpand() }} className="shrink-0 w-5 h-5 flex items-center justify-center hover:bg-foreground/5 rounded-md transition-colors text-muted-foreground" > @@ -500,7 +432,7 @@ function SidebarCarnetItem({ 'w-5 h-5 flex items-center justify-center shrink-0 transition-colors', isActive ? 'text-brand-accent' : 'text-muted-foreground/80', )}> - {isExpanded ? : } + {showOpenFolder ? : } @@ -523,9 +455,9 @@ function SidebarCarnetItem({ {/* Compteur de notes (toujours visible, à droite du nom) */} - {notes.length > 0 && ( + {noteCount > 0 && ( - {notes.length} + {noteCount} )} @@ -609,42 +541,6 @@ function SidebarCarnetItem({ )} - - - {isExpanded && ( - - - - - - {children} - {isExpanded && notes.map(note => ( - onNoteClick(note.id, carnet.id)} - /> - ))} - {isExpanded && notes.length === 0 && !hasChildren && ( - - {t('sidebar.notebookEmpty')} - - )} - - - - )} - ) } @@ -693,7 +589,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) const [isRenaming, setIsRenaming] = useState(false) const [expandedIds, setExpandedIds] = useState>(new Set()) const [pinnedIds, setPinnedIds] = useState>(new Set()) - const [notebookNotes, setNotebookNotes] = useState>({}) + const [notebookNotes, setNotebookNotes] = useState>({}) const [aiNotebookIds, setAiNotebookIds] = useState>(() => new Set()) const [activeView, setActiveView] = useState('notebooks') const [sortOrder, setSortOrder] = useState('newest') @@ -838,19 +734,23 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) const filteredNotebookIds = useMemo(() => { const q = notebookSearchQuery.trim().toLowerCase() if (!q) return null - return new Set( - notebooks - .filter( - (nb) => - nb.name.toLowerCase().includes(q) || - (notebookNotes[nb.id] || []).some((n) => n.title.toLowerCase().includes(q)), - ) - .map((nb) => nb.id), - ) + const byId = new Map(notebooks.map((nb) => [nb.id, nb])) + const ids = new Set() + for (const nb of notebooks) { + const nameHit = nb.name.toLowerCase().includes(q) + const noteHit = (notebookNotes[nb.id] || []).some((n) => n.title.toLowerCase().includes(q)) + if (!nameHit && !noteHit) continue + ids.add(nb.id) + let parentId = nb.parentId ?? null + while (parentId && !ids.has(parentId)) { + ids.add(parentId) + parentId = byId.get(parentId)?.parentId ?? null + } + } + return ids }, [notebooks, notebookNotes, notebookSearchQuery]) const currentNotebookId = searchParams.get('notebook') - const currentNoteId = searchParams.get('openNote') useEffect(() => { if (!currentNotebookId) return @@ -866,6 +766,29 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) }) }, [currentNotebookId, notebooks]) + useEffect(() => { + if (!filteredNotebookIds) return + setExpandedIds((prev) => { + const next = new Set(prev) + for (const id of filteredNotebookIds) { + const kids = childNotebooks.get(id) || [] + if (kids.some((c) => filteredNotebookIds.has(c.id))) next.add(id) + } + return next + }) + }, [filteredNotebookIds, childNotebooks]) + + useEffect(() => { + if (!currentNotebookId) return + const timer = window.setTimeout(() => { + const el = notebooksPanelRef.current?.querySelector( + `[data-notebook-id="${currentNotebookId}"]`, + ) as HTMLElement | null + el?.scrollIntoView({ block: 'nearest', inline: 'nearest' }) + }, 80) + return () => window.clearTimeout(timer) + }, [currentNotebookId]) + const isDashboardRoute = isDashboardHomeRoute(pathname, searchParams) const panelView: NavigationView = pathname.startsWith('/settings') ? 'settings' : activeView @@ -981,9 +904,6 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) const mapped = notes.map((n: Note) => ({ id: n.id, title: getNoteDisplayTitle(n, t('notes.untitled')), - isPinned: n.isPinned, - autoGenerated: Boolean(n.autoGenerated), - isRecent: isRecentlyCreated(n.createdAt, 48), })) return [nb.id, mapped] as const }) @@ -1018,7 +938,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) if (list.some((n) => n.id === detail.note.id)) return prev return { ...prev, - [nbId]: [{ id: detail.note.id, title, isPinned: detail.note.isPinned }, ...list], + [nbId]: [{ id: detail.note.id, title }, ...list], } }) return @@ -1027,14 +947,14 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) const note = detail.note const title = getNoteDisplayTitle(note, t('notes.untitled')) setNotebookNotes((prev) => { - const next: Record = {} + const next: Record = {} for (const [key, list] of Object.entries(prev)) { const filtered = list.filter((n) => n.id !== note.id) if (filtered.length > 0) next[key] = filtered } if (note.notebookId) { next[note.notebookId] = [ - { id: note.id, title, isPinned: note.isPinned }, + { id: note.id, title }, ...(next[note.notebookId] || []), ] } @@ -1070,14 +990,6 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) router.push('/home?forceList=1') } - const handleNoteClick = (noteId: string, notebookId: string) => { - const params = new URLSearchParams(searchParams.toString()) - params.set('notebook', notebookId) - params.set('openNote', noteId) - params.delete('forceList') - router.push(`/home?${params.toString()}`) - } - // ── Drag state ── const [dropTarget, setDropTarget] = useState(null) const [dropAction, setDropAction] = useState<'into' | 'before' | 'after' | null>(null) @@ -1329,16 +1241,14 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) isPrivate: notebook.isPrivate, }} isActive={isActive} - notes={notes} - activeNoteId={currentNoteId} + noteCount={notes.length} onCarnetClick={() => { if (currentNotebookId === notebook.id) { - toggleExpand(notebook.id) + if (children.length > 0) toggleExpand(notebook.id) } else { handleCarnetClick(notebook.id) } }} - onNoteClick={handleNoteClick} onAddSubNotebook={() => { setCreateParentId(notebook.id) setIsCreateDialogOpen(true) @@ -1361,7 +1271,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) ) }) - }, [rootNotebooks, childNotebooks, filteredNotebookIds, currentNotebookId, currentNoteId, notebookNotes, draggedId, dropTarget, dropAction, expandedIds, pinnedIds, aiNotebookIds, toggleExpand, handleCarnetClick, handleNoteClick, handleDragStart, handleDragEnd, handleDropOnNotebook, handleStartRename]) + }, [rootNotebooks, childNotebooks, filteredNotebookIds, currentNotebookId, notebookNotes, draggedId, dropTarget, dropAction, expandedIds, pinnedIds, aiNotebookIds, toggleExpand, handleCarnetClick, handleDragStart, handleDragEnd, handleDropOnNotebook, handleStartRename]) return ( <> @@ -2137,9 +2047,6 @@ export function Sidebar({ className, user }: { className?: string; user?: any }) const mapped = notes.map((n: Note) => ({ id: n.id, title: getNoteDisplayTitle(n, t('notes.untitled')), - isPinned: n.isPinned, - autoGenerated: Boolean(n.autoGenerated), - isRecent: true, })) setNotebookNotes((prev) => ({ ...prev, [notebookId]: mapped })) } catch { /* ignore */ }
- {t('sidebar.notebookEmpty')} -