diff --git a/memento-note/components/home-client.tsx b/memento-note/components/home-client.tsx
index ec096f8..d5aa37e 100644
--- a/memento-note/components/home-client.tsx
+++ b/memento-note/components/home-client.tsx
@@ -619,6 +619,11 @@ export function HomeClient({
if (detail.type === 'updated') {
patchNoteInList(detail.note.id, detail.note)
setPinnedNotes(prev => prev.map(n => n.id === detail.note.id ? { ...n, ...detail.note } : n))
+ } else if (detail.type === 'deleted') {
+ removeNoteFromList(detail.noteId)
+ setPinnedNotes(prev => prev.filter(n => n.id !== detail.noteId))
+ } else if (detail.type === 'created' && detail.note) {
+ setNotes(prev => [detail.note, ...prev])
}
}
window.addEventListener(NOTE_CHANGE_EVENT, onNoteChange)
diff --git a/memento-note/components/sidebar-panels.tsx b/memento-note/components/sidebar-panels.tsx
new file mode 100644
index 0000000..09ce8fa
--- /dev/null
+++ b/memento-note/components/sidebar-panels.tsx
@@ -0,0 +1,348 @@
+'use client'
+
+import { useRouter } from 'next/navigation'
+import { cn } from '@/lib/utils'
+import { useLanguage } from '@/lib/i18n'
+import React, { useEffect, useState } from 'react'
+import { motion, AnimatePresence } from 'motion/react'
+import { getAllNotes, getNotesWithReminders, toggleReminderDone } from '@/app/actions/notes'
+import { useBrainstormSessions, useDeleteBrainstorm } from '@/hooks/use-brainstorm'
+import {
+ ChevronRight, Lock, Plus, Pencil, Trash2, Sparkles, Clock,
+ Pin, PinOff, GripVertical, FileText, Folder, FolderOpen,
+} from 'lucide-react'
+
+export function NoteLink({
+ title, isActive, isPinned, onClick,
+}: {
+ title: string; isActive: boolean; isPinned?: boolean; onClick: () => void
+}) {
+ const { language } = useLanguage()
+ const slideX = language === 'fa' || language === 'ar' ? 10 : -10
+ return (
+
+
+ {title}
+ {isPinned && }
+
+ )
+}
+
+export function SidebarBrainstorms() {
+ const { data: sessions, isLoading } = useBrainstormSessions()
+ const deleteBrainstorm = useDeleteBrainstorm()
+ const router = useRouter()
+ const { t } = useLanguage()
+
+ if (isLoading) {
+ return (
+
+ {[1, 2, 3].map(i =>
)}
+
+ )
+ }
+
+ if (!sessions || sessions.length === 0) {
+ return (
+
+
+
{t('brainstorm.noSessions')}
+
+
+ )
+ }
+
+ return (
+
+ {sessions.slice(0, 10).map(s => (
+
+
+ {(s as any)._owned !== false && (
+
+ )}
+
+ ))}
+
+ )
+}
+
+export function SidebarReminders({ onOpenNote }: { onOpenNote: (noteId: string, notebookId: string | null) => void }) {
+ const { t } = useLanguage()
+ const [reminders, setReminders] = useState<
+ { id: string; title: string | null; reminder: Date | string | null; isReminderDone: boolean; notebookId: string | null }[]
+ >([])
+ const [loading, setLoading] = useState(true)
+ const [togglingId, setTogglingId] = useState(null)
+
+ const reload = () => { getNotesWithReminders().then((rows) => setReminders(rows as typeof reminders)) }
+
+ useEffect(() => {
+ let cancelled = false
+ setLoading(true)
+ getNotesWithReminders().then((rows) => { if (!cancelled) setReminders(rows as typeof reminders) }).finally(() => { if (!cancelled) setLoading(false) })
+ return () => { cancelled = true }
+ }, [])
+
+ const handleComplete = async (e: React.MouseEvent, noteId: string) => {
+ e.stopPropagation(); setTogglingId(noteId)
+ await toggleReminderDone(noteId, true)
+ setReminders(prev => prev.map(r => r.id === noteId ? { ...r, isReminderDone: true } : r))
+ setTogglingId(null)
+ }
+
+ const handleSnooze = async (e: React.MouseEvent, noteId: string) => {
+ e.stopPropagation()
+ const snoozeDate = new Date(Date.now() + 60 * 60 * 1000)
+ setTogglingId(noteId)
+ try {
+ await fetch(`/api/notes/${noteId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ reminder: snoozeDate.toISOString(), isReminderDone: false }) })
+ setReminders(prev => prev.map(r => r.id === noteId ? { ...r, reminder: snoozeDate, isReminderDone: false } : r))
+ } catch (e) { console.error('[silent-catch]', e); reload() }
+ setTogglingId(null)
+ }
+
+ if (loading) {
+ return (
+
+ {[1, 2, 3].map((i) =>
)}
+
+ )
+ }
+
+ const now = new Date()
+ const active = reminders.filter((r) => !r.isReminderDone && r.reminder)
+ const overdue = active.filter((r) => new Date(r.reminder!) < now)
+ const upcoming = active.filter((r) => new Date(r.reminder!) >= now)
+
+ if (active.length === 0) {
+ return (
+
+
+
{t('reminders.emptyDescription')}
+
+ )
+ }
+
+ const renderItem = (note: (typeof active)[0], overdueItem?: boolean) => (
+
+
+
+
+
+
+
+ )
+
+ return (
+
+ {overdue.length > 0 && (
+
+
{t('reminders.overdue')}
+
{overdue.map((n) => renderItem(n, true))}
+
+ )}
+ {upcoming.length > 0 && (
+
+
{t('reminders.upcoming')}
+
{upcoming.map((n) => renderItem(n))}
+
+ )}
+
+ )
+}
+
+export function SidebarCarnetItem({
+ carnet, isActive, notes, activeNoteId, onCarnetClick, onNoteClick, onAddSubNotebook, onRename, onDelete, onTogglePin, isPinned,
+ children, isDragging, dragHandleProps, level, isExpanded, toggleExpand, hasChildNotebooks, hasActiveDescendant,
+}: {
+ carnet: { id: string; name: string; initial: string; isPrivate?: boolean }
+ isActive: boolean
+ notes: { id: string; title: string; isPinned?: boolean }[]
+ activeNoteId: string | null
+ onCarnetClick: () => void
+ onNoteClick: (noteId: string, carnetId: string) => void
+ onAddSubNotebook: () => void
+ onRename: () => void
+ onDelete: () => void
+ onTogglePin: () => void
+ isPinned: boolean
+ children?: React.ReactNode
+ isDragging?: boolean
+ dragHandleProps?: React.HTMLAttributes
+ level: number
+ isExpanded: boolean
+ toggleExpand: () => void
+ hasChildNotebooks?: boolean
+ hasActiveDescendant?: boolean
+}) {
+ const { t, language } = useLanguage()
+ const isRtl = language === 'fa' || language === 'ar'
+ const hasChildren = hasChildNotebooks || React.Children.count(children) > 0 || notes.length > 0
+ const [contextMenu, setContextMenu] = useState<{ x: number; y: number } | null>(null)
+
+ useEffect(() => {
+ if (!contextMenu) return
+ const handler = () => setContextMenu(null)
+ window.addEventListener('click', handler)
+ return () => window.removeEventListener('click', handler)
+ }, [contextMenu])
+
+ return (
+
+
{ e.preventDefault(); e.stopPropagation(); setContextMenu({ x: e.clientX, y: e.clientY }) }}
+ >
+ {level > 0 &&
}
+ {level > 0 &&
}
+
+
e.stopPropagation()}>
+
+
+ {hasChildren ? (
+
+ ) :
}
+
{ e.stopPropagation(); onRename() }}
+ className={cn(
+ 'flex-1 flex items-center gap-2.5 px-2 py-1.5 rounded-lg transition-all duration-300 group/item cursor-pointer relative overflow-hidden',
+ isActive ? 'bg-white dark:bg-white/10 shadow-sm border border-border/40' : 'hover:bg-white/40 dark:hover:bg-white/5'
+ )}>
+ {isActive && }
+
+ {isExpanded ? : }
+
+
+
+ {carnet.name}
+
+ {carnet.isPrivate && }
+ {!isActive && hasActiveDescendant && }
+
+ {notes.length > 0 && (
+
+ {notes.length}
+
+ )}
+
+ {isPinned &&
}
+
+
+
+
+
+
+
+
+ {contextMenu && (
+ e.stopPropagation()}
+ >
+
+
+
+
+
+
+
+ )}
+
+
+ {isExpanded && (
+
+
+
+
+ {children}
+ {isExpanded && notes.map(note => (
+
onNoteClick(note.id, carnet.id)} />
+ ))}
+ {isExpanded && notes.length === 0 && !hasChildren && (
+ {t('sidebar.notebookEmpty')}
+ )}
+
+
+
+ )}
+
+
+ )
+}