'use client' import { useState } from 'react' import { Button } from './ui/button' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from './ui/dialog' import { Checkbox } from './ui/checkbox' import { Wand2, Loader2, ChevronRight, CheckCircle2 } from 'lucide-react' import { toast } from 'sonner' import { useLanguage } from '@/lib/i18n' import type { OrganizationPlan, NotebookOrganization } from '@/lib/ai/services' interface BatchOrganizationDialogProps { open: boolean onOpenChange: (open: boolean) => void onNotesMoved: () => void } export function BatchOrganizationDialog({ open, onOpenChange, onNotesMoved, }: BatchOrganizationDialogProps) { const { t } = useLanguage() const [plan, setPlan] = useState(null) const [loading, setLoading] = useState(false) const [applying, setApplying] = useState(false) const [selectedNotes, setSelectedNotes] = useState>(new Set()) const fetchOrganizationPlan = async () => { setLoading(true) try { const response = await fetch('/api/ai/batch-organize', { method: 'POST', credentials: 'include', }) const data = await response.json() if (data.success && data.data) { setPlan(data.data) // Select all notes by default const allNoteIds = new Set() data.data.notebooks.forEach((nb: NotebookOrganization) => { nb.notes.forEach(note => allNoteIds.add(note.noteId)) }) setSelectedNotes(allNoteIds) } else { toast.error(data.error || 'Failed to create organization plan') } } catch (error) { console.error('Failed to create organization plan:', error) toast.error('Failed to create organization plan') } finally { setLoading(false) } } const handleOpenChange = (isOpen: boolean) => { if (!isOpen) { // Reset state when closing setPlan(null) setSelectedNotes(new Set()) } else { // Fetch plan when opening fetchOrganizationPlan() } onOpenChange(isOpen) } const toggleNoteSelection = (noteId: string) => { const newSelected = new Set(selectedNotes) if (newSelected.has(noteId)) { newSelected.delete(noteId) } else { newSelected.add(noteId) } setSelectedNotes(newSelected) } const toggleNotebookSelection = (notebook: NotebookOrganization) => { const newSelected = new Set(selectedNotes) const allNoteIds = notebook.notes.map(n => n.noteId) // Check if all notes in this notebook are already selected const allSelected = allNoteIds.every(id => newSelected.has(id)) if (allSelected) { // Deselect all allNoteIds.forEach(id => newSelected.delete(id)) } else { // Select all allNoteIds.forEach(id => newSelected.add(id)) } setSelectedNotes(newSelected) } const handleApply = async () => { if (!plan || selectedNotes.size === 0) { toast.error('No notes selected') return } setApplying(true) try { const response = await fetch('/api/ai/batch-organize', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ plan, selectedNoteIds: Array.from(selectedNotes), }), }) const data = await response.json() if (data.success) { toast.success( t('ai.batchOrganization.success', { count: data.data.movedCount }) || `${data.data.movedCount} notes moved successfully` ) onNotesMoved() onOpenChange(false) } else { toast.error(data.error || 'Failed to apply organization plan') } } catch (error) { console.error('Failed to apply organization plan:', error) toast.error('Failed to apply organization plan') } finally { setApplying(false) } } const getSelectedCountForNotebook = (notebook: NotebookOrganization) => { return notebook.notes.filter(n => selectedNotes.has(n.noteId)).length } const getAllSelectedCount = () => { if (!plan) return 0 return plan.notebooks.reduce( (acc, nb) => acc + getSelectedCountForNotebook(nb), 0 ) } return ( {t('ai.batchOrganization.title')} {t('ai.batchOrganization.description')} {loading ? (

{t('ai.batchOrganization.analyzing')}

) : plan ? (
{/* Summary */}

{t('ai.batchOrganization.notesToOrganize', { count: plan.totalNotes, })}

{t('ai.batchOrganization.selected', { count: getAllSelectedCount(), })}

{/* No notebooks available */} {plan.notebooks.length === 0 ? (

{plan.unorganizedNotes === plan.totalNotes ? t('ai.batchOrganization.noNotebooks') : t('ai.batchOrganization.noSuggestions')}

) : ( <> {/* Organization plan by notebook */} {plan.notebooks.map((notebook) => { const selectedCount = getSelectedCountForNotebook(notebook) const allSelected = selectedCount === notebook.notes.length && selectedCount > 0 return (
{/* Notebook header */}
toggleNotebookSelection(notebook)} aria-label={`Select all notes in ${notebook.notebookName}`} />
{notebook.notebookIcon} {notebook.notebookName} ({selectedCount}/{notebook.notes.length})
{/* Notes in this notebook */}
{notebook.notes.map((note) => (
toggleNoteSelection(note.noteId)} > toggleNoteSelection(note.noteId)} aria-label={`Select note: ${note.title || 'Untitled'}`} />

{note.title || t('notes.untitled') || 'Untitled'}

{note.content}

{Math.round(note.confidence * 100)}% confidence {note.reason && ( {note.reason} )}
))}
) })} {/* Unorganized notes warning */} {plan.unorganizedNotes > 0 && (

{t('ai.batchOrganization.unorganized', { count: plan.unorganizedNotes, })}

)} )}
) : null}
) }