fix(keep-notes): sidebar chevron, labels sync, batch org errors, perf guards
- Notebooks: chevron visible when expanded (remove overflow clip), functional expand state - Labels: sync/cleanup by notebookId, reconcile after note move - Settings: refresh notebooks after cleanup; label dialog routing - ConnectionsBadge lazy-load; reminder check persistence; i18n keys Made-with: Cursor
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Button } from './ui/button'
|
||||
import {
|
||||
Dialog,
|
||||
@@ -27,21 +27,24 @@ export function BatchOrganizationDialog({
|
||||
onOpenChange,
|
||||
onNotesMoved,
|
||||
}: BatchOrganizationDialogProps) {
|
||||
const { t } = useLanguage()
|
||||
const { t, language } = useLanguage()
|
||||
const [plan, setPlan] = useState<OrganizationPlan | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [applying, setApplying] = useState(false)
|
||||
const [selectedNotes, setSelectedNotes] = useState<Set<string>>(new Set())
|
||||
|
||||
const [fetchError, setFetchError] = useState<string | null>(null)
|
||||
|
||||
const fetchOrganizationPlan = async () => {
|
||||
setLoading(true)
|
||||
setFetchError(null)
|
||||
try {
|
||||
const response = await fetch('/api/ai/batch-organize', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
language: document.documentElement.lang || 'en'
|
||||
language: language || 'en'
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -49,32 +52,38 @@ export function BatchOrganizationDialog({
|
||||
|
||||
if (data.success && data.data) {
|
||||
setPlan(data.data)
|
||||
// Select all notes by default
|
||||
const allNoteIds = new Set<string>()
|
||||
data.data.notebooks.forEach((nb: NotebookOrganization) => {
|
||||
nb.notes.forEach(note => allNoteIds.add(note.noteId))
|
||||
})
|
||||
setSelectedNotes(allNoteIds)
|
||||
} else {
|
||||
toast.error(data.error || t('ai.batchOrganization.error'))
|
||||
const msg = data.error || t('ai.batchOrganization.error')
|
||||
setFetchError(msg)
|
||||
toast.error(msg)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to create organization plan:', error)
|
||||
toast.error(t('ai.batchOrganization.error'))
|
||||
const msg = t('ai.batchOrganization.error')
|
||||
setFetchError(msg)
|
||||
toast.error(msg)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleOpenChange = (isOpen: boolean) => {
|
||||
if (!isOpen) {
|
||||
// Reset state when closing
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
fetchOrganizationPlan()
|
||||
} else {
|
||||
setPlan(null)
|
||||
setSelectedNotes(new Set())
|
||||
} else {
|
||||
// Fetch plan when opening
|
||||
fetchOrganizationPlan()
|
||||
setFetchError(null)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [open])
|
||||
|
||||
const handleOpenChange = (isOpen: boolean) => {
|
||||
onOpenChange(isOpen)
|
||||
}
|
||||
|
||||
@@ -173,6 +182,13 @@ export function BatchOrganizationDialog({
|
||||
{t('ai.batchOrganization.analyzing')}
|
||||
</p>
|
||||
</div>
|
||||
) : fetchError ? (
|
||||
<div className="flex flex-col items-center justify-center py-12 gap-4">
|
||||
<p className="text-sm text-destructive text-center">{fetchError}</p>
|
||||
<Button variant="outline" onClick={fetchOrganizationPlan}>
|
||||
{t('general.tryAgain')}
|
||||
</Button>
|
||||
</div>
|
||||
) : plan ? (
|
||||
<div className="space-y-6 py-4">
|
||||
{/* Summary */}
|
||||
|
||||
Reference in New Issue
Block a user