'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 (
)
}
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 (
)
}
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')}
)}
)}
)
}