'use client' import { useState, useEffect, useCallback } from 'react' import { useSearchParams, useRouter } from 'next/navigation' import { Note } from '@/lib/types' import { getAllNotes, searchNotes } from '@/app/actions/notes' import { NoteInput } from '@/components/note-input' import { MasonryGrid } from '@/components/masonry-grid' import { MemoryEchoNotification } from '@/components/memory-echo-notification' import { NotebookSuggestionToast } from '@/components/notebook-suggestion-toast' import { NoteEditor } from '@/components/note-editor' import { BatchOrganizationDialog } from '@/components/batch-organization-dialog' import { AutoLabelSuggestionDialog } from '@/components/auto-label-suggestion-dialog' import { Button } from '@/components/ui/button' import { Wand2 } from 'lucide-react' import { useLabels } from '@/context/LabelContext' import { useNoteRefresh } from '@/context/NoteRefreshContext' import { useReminderCheck } from '@/hooks/use-reminder-check' import { useAutoLabelSuggestion } from '@/hooks/use-auto-label-suggestion' export default function HomePage() { console.log('[HomePage] Component rendering') const searchParams = useSearchParams() const router = useRouter() const [notes, setNotes] = useState([]) const [editingNote, setEditingNote] = useState<{ note: Note; readOnly?: boolean } | null>(null) const [isLoading, setIsLoading] = useState(true) const [notebookSuggestion, setNotebookSuggestion] = useState<{ noteId: string; content: string } | null>(null) const [batchOrganizationOpen, setBatchOrganizationOpen] = useState(false) const { refreshKey } = useNoteRefresh() const { labels } = useLabels() // Auto label suggestion (IA4) const { shouldSuggest: shouldSuggestLabels, notebookId: suggestNotebookId, dismiss: dismissLabelSuggestion } = useAutoLabelSuggestion() const [autoLabelOpen, setAutoLabelOpen] = useState(false) // Open auto label dialog when suggestion is available useEffect(() => { if (shouldSuggestLabels && suggestNotebookId) { setAutoLabelOpen(true) } }, [shouldSuggestLabels, suggestNotebookId]) // Check if viewing Notes générales (no notebook filter) const notebookFilter = searchParams.get('notebook') const isInbox = !notebookFilter // Callback for NoteInput to trigger notebook suggestion const handleNoteCreated = useCallback((note: Note) => { console.log('[NotebookSuggestion] Note created:', { id: note.id, notebookId: note.notebookId, contentLength: note.content?.length }) // Only suggest if note has no notebook and has 20+ words if (!note.notebookId) { const wordCount = (note.content || '').trim().split(/\s+/).filter(w => w.length > 0).length console.log('[NotebookSuggestion] Word count:', wordCount) if (wordCount >= 20) { console.log('[NotebookSuggestion] Triggering suggestion for note:', note.id) setNotebookSuggestion({ noteId: note.id, content: note.content || '' }) } else { console.log('[NotebookSuggestion] Not enough words, need 20+') } } else { console.log('[NotebookSuggestion] Note has notebook, skipping') } }, []) const handleOpenNote = (noteId: string) => { const note = notes.find(n => n.id === noteId) if (note) { setEditingNote({ note, readOnly: false }) } } // Enable reminder notifications useReminderCheck(notes) useEffect(() => { const loadNotes = async () => { setIsLoading(true) const search = searchParams.get('search')?.trim() || null const semanticMode = searchParams.get('semantic') === 'true' const labelFilter = searchParams.get('labels')?.split(',').filter(Boolean) || [] const colorFilter = searchParams.get('color') const notebookFilter = searchParams.get('notebook') let allNotes = search ? await searchNotes(search, semanticMode, notebookFilter || undefined) : await getAllNotes() // Filter by selected notebook if (notebookFilter) { allNotes = allNotes.filter((note: any) => note.notebookId === notebookFilter) } else { // If no notebook selected, only show notes without notebook (Notes générales) allNotes = allNotes.filter((note: any) => !note.notebookId) } // Filter by selected labels if (labelFilter.length > 0) { allNotes = allNotes.filter((note: any) => note.labels?.some((label: string) => labelFilter.includes(label)) ) } // Filter by color (filter notes that have labels with this color) // Note: We use a ref-like pattern to avoid including labels in dependencies // This prevents dialog closing when adding new labels if (colorFilter) { const labelNamesWithColor = labels .filter((label: any) => label.color === colorFilter) .map((label: any) => label.name) allNotes = allNotes.filter((note: any) => note.labels?.some((label: string) => labelNamesWithColor.includes(label)) ) } setNotes(allNotes) setIsLoading(false) } loadNotes() // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchParams, refreshKey]) // Intentionally omit 'labels' and 'semantic' to prevent reload when adding tags or from router.push return (
{/* Batch Organization Button - Only show in Inbox with 5+ notes */} {isInbox && !isLoading && notes.length >= 5 && (
)} {isLoading ? (
Loading...
) : ( setEditingNote({ note, readOnly })} /> )} {/* Memory Echo - Proactive note connections */} {/* Notebook Suggestion - IA1 */} {notebookSuggestion && ( setNotebookSuggestion(null)} /> )} {/* Batch Organization Dialog - IA3 */} { // Refresh notes to see updated notebook assignments router.refresh() }} /> {/* Auto Label Suggestion Dialog - IA4 */} { setAutoLabelOpen(open) if (!open) dismissLabelSuggestion() }} notebookId={suggestNotebookId} onLabelsCreated={() => { // Refresh to see new labels router.refresh() }} /> {/* Note Editor Modal */} {editingNote && ( setEditingNote(null)} /> )}
) }