'use client'
import Link from 'next/link'
import { usePathname, useSearchParams, useRouter } from 'next/navigation'
import { cn } from '@/lib/utils'
import {
Settings,
Plus,
ChevronRight,
Lock,
BookOpen,
Bot,
Inbox,
FlaskConical,
ArrowUpDown,
Archive,
MessageSquare,
Sparkles,
Trash2,
} from 'lucide-react'
import { useLanguage } from '@/lib/i18n'
import { useNoteRefreshOptional } from '@/context/NoteRefreshContext'
import { useEffect, useState } from 'react'
import { getAllNotes } from '@/app/actions/notes'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { useNotebooks } from '@/context/notebooks-context'
import { Notebook, Note } from '@/lib/types'
import { motion, AnimatePresence } from 'motion/react'
import { getNoteDisplayTitle } from '@/lib/note-preview'
import { CreateNotebookDialog } from './create-notebook-dialog'
import { NotificationPanel } from './notification-panel'
type NavigationView = 'notebooks' | 'agents'
type SortOrder = 'newest' | 'oldest' | 'alpha'
function NoteLink({
title,
isActive,
onClick,
}: {
title: string
isActive: boolean
onClick: () => void
}) {
return (
{title}
)
}
function SidebarCarnetItem({
carnet,
isActive,
notes,
activeNoteId,
onCarnetClick,
onNoteClick,
}: {
carnet: { id: string; name: string; initial: string; isPrivate?: boolean }
isActive: boolean
notes: { id: string; title: string }[]
activeNoteId: string | null
onCarnetClick: () => void
onNoteClick: (noteId: string) => void
}) {
return (
{carnet.initial}
{carnet.name}
{carnet.isPrivate && }
{isActive && (
{notes.map(note => (
onNoteClick(note.id)}
/>
))}
{notes.length === 0 && (
No notes yet
)}
)}
)
}
export function Sidebar({ className, user }: { className?: string; user?: any }) {
const pathname = usePathname()
const searchParams = useSearchParams()
const router = useRouter()
const { t } = useLanguage()
const { refreshKey } = useNoteRefreshOptional()
const { notebooks } = useNotebooks()
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false)
const [notebookNotes, setNotebookNotes] = useState>({})
const [activeView, setActiveView] = useState('notebooks')
const [sortOrder, setSortOrder] = useState('newest')
const [showSortMenu, setShowSortMenu] = useState(false)
const currentNotebookId = searchParams.get('notebook')
const currentNoteId = searchParams.get('openNote')
// Determine if inbox is active (no notebook filter, on home page)
const isInboxActive =
pathname === '/' &&
!searchParams.get('notebook') &&
!searchParams.get('label') &&
!searchParams.get('archived') &&
!searchParams.get('trashed')
// Sync activeView with current route
useEffect(() => {
if (pathname.startsWith('/agents') || pathname.startsWith('/lab')) {
setActiveView('agents')
}
}, [pathname])
const displayName = user?.name || user?.email || ''
const initial = displayName ? displayName.charAt(0).toUpperCase() : '?'
useEffect(() => {
if (!currentNotebookId) return
if (notebookNotes[currentNotebookId]) return
getAllNotes(false, currentNotebookId).then(notes => {
const mapped = notes.map((n: Note) => ({
id: n.id,
title: getNoteDisplayTitle(n, t('notes.untitled') || 'Untitled'),
}))
setNotebookNotes(prev => ({ ...prev, [currentNotebookId!]: mapped }))
})
}, [currentNotebookId, refreshKey])
// BUG FIX: clicking a carnet always forces list (editorial) view
const handleCarnetClick = (notebookId: string) => {
const params = new URLSearchParams()
params.set('notebook', notebookId)
// forceList resets to editorial view in home-client
params.set('forceList', '1')
router.push(`/?${params.toString()}`)
}
const handleInboxClick = () => {
router.push('/?forceList=1')
}
const handleNoteClick = (noteId: string) => {
const params = new URLSearchParams(searchParams.toString())
params.set('openNote', noteId)
params.delete('forceList')
router.push(`/?${params.toString()}`)
}
// Sort notebooks
const sortedNotebooks = [...notebooks].sort((a: Notebook, b: Notebook) => {
if (sortOrder === 'alpha') return a.name.localeCompare(b.name)
if (sortOrder === 'newest') return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
if (sortOrder === 'oldest') return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
return 0
})
const sortLabels: Record = {
newest: t('sidebar.sortNewest') || 'Newest first',
oldest: t('sidebar.sortOldest') || 'Oldest first',
alpha: t('sidebar.sortAlpha') || 'A → Z',
}
return (
<>
>
)
}