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:
Sepehr Ramezani
2026-04-13 22:07:09 +02:00
parent fa7e166f3e
commit 39671c6472
16 changed files with 469 additions and 303 deletions

View File

@@ -15,6 +15,7 @@ import { EditNotebookDialog } from './edit-notebook-dialog'
import { NotebookSummaryDialog } from './notebook-summary-dialog'
import { useLanguage } from '@/lib/i18n'
import { useLabels } from '@/context/LabelContext'
import { LabelManagementDialog } from '@/components/label-management-dialog'
import { Notebook } from '@/lib/types'
// Map icon names to lucide-react components
@@ -53,6 +54,7 @@ export function NotebooksList() {
const [deletingNotebook, setDeletingNotebook] = useState<Notebook | null>(null)
const [summaryNotebook, setSummaryNotebook] = useState<Notebook | null>(null)
const [expandedNotebook, setExpandedNotebook] = useState<string | null>(null)
const [labelsDialogOpen, setLabelsDialogOpen] = useState(false)
const currentNotebookId = searchParams.get('notebook')
@@ -97,7 +99,7 @@ export function NotebooksList() {
}
const handleToggleExpand = (notebookId: string) => {
setExpandedNotebook(expandedNotebook === notebookId ? null : notebookId)
setExpandedNotebook((prev) => (prev === notebookId ? null : notebookId))
}
const handleLabelFilter = (labelName: string, notebookId: string) => {
@@ -126,6 +128,7 @@ export function NotebooksList() {
return (
<>
<LabelManagementDialog open={labelsDialogOpen} onOpenChange={setLabelsDialogOpen} />
<div className="flex flex-col pt-1">
{/* Header with Add Button */}
<div className="flex items-center justify-between px-6 py-2 mt-2 group cursor-pointer text-gray-500 hover:text-gray-800 dark:hover:text-gray-300">
@@ -157,7 +160,7 @@ export function NotebooksList() {
onDragOver={(e) => handleDragOver(e, notebook.id)}
onDragLeave={handleDragLeave}
className={cn(
"flex flex-col mr-2 rounded-r-full overflow-hidden transition-all relative",
"flex flex-col mr-2 rounded-r-full transition-all relative",
!notebook.color && "bg-primary/10 dark:bg-primary/20",
isDragOver && "ring-2 ring-primary ring-dashed"
)}
@@ -186,39 +189,56 @@ export function NotebooksList() {
onSummary={() => setSummaryNotebook(notebook)}
/>
<button
onClick={() => handleToggleExpand(notebook.id)}
className={cn("transition-colors p-1", !notebook.color && "text-primary hover:text-primary/80 dark:text-primary-foreground dark:hover:text-primary-foreground/80")}
type="button"
onClick={(e) => {
e.stopPropagation()
handleToggleExpand(notebook.id)
}}
className={cn(
"shrink-0 rounded-full p-1 transition-colors",
!notebook.color &&
"text-primary hover:text-primary/80 dark:text-primary-foreground dark:hover:text-primary-foreground/80"
)}
style={notebook.color ? { color: notebook.color } : undefined}
aria-expanded={isExpanded}
>
<ChevronDown className={cn("w-4 h-4 transition-transform", isExpanded && "rotate-180")} />
<ChevronDown className={cn("h-4 w-4 transition-transform", isExpanded && "rotate-180")} />
</button>
</div>
</div>
{/* Contextual Labels Tree */}
{isExpanded && labels.length > 0 && (
{isExpanded && (
<div className="flex flex-col pb-2">
{labels.map((label: any) => (
<button
key={label.id}
onClick={() => handleLabelFilter(label.name, notebook.id)}
className={cn(
"pointer-events-auto flex items-center gap-4 pl-12 pr-4 py-2 hover:bg-black/5 dark:hover:bg-white/10 transition-colors rounded-r-full mr-2",
searchParams.get('labels')?.includes(label.name) && "font-bold text-gray-900 dark:text-white"
)}
>
<Tag className="w-4 h-4 text-gray-500" />
<span className="text-xs font-medium text-gray-600 dark:text-gray-300">
{label.name}
</span>
</button>
))}
{labels.length === 0 ? (
<p className="pointer-events-none pl-12 pr-4 py-2 text-xs text-muted-foreground">
{t('sidebar.noLabelsInNotebook')}
</p>
) : (
labels.map((label: any) => (
<button
key={label.id}
type="button"
onClick={() => handleLabelFilter(label.name, notebook.id)}
className={cn(
'pointer-events-auto flex items-center gap-4 pl-12 pr-4 py-2 rounded-r-full mr-2 transition-colors',
'hover:bg-accent/60 text-muted-foreground hover:text-foreground',
searchParams.get('labels')?.includes(label.name) &&
'font-semibold text-foreground'
)}
>
<Tag className="h-4 w-4 shrink-0" />
<span className="text-xs font-medium truncate">{label.name}</span>
</button>
))
)}
<button
onClick={() => router.push('/settings/labels')}
className="pointer-events-auto flex items-center gap-2 pl-12 pr-4 py-2 mt-1 text-gray-500 hover:text-gray-800 hover:bg-black/5 dark:hover:bg-white/10 rounded-r-full mr-2 transition-colors group/label"
type="button"
onClick={() => setLabelsDialogOpen(true)}
className="pointer-events-auto flex items-center gap-2 pl-12 pr-4 py-2 mt-1 rounded-r-full mr-2 transition-colors text-muted-foreground hover:text-foreground hover:bg-accent/60 group/label"
>
<Plus className="w-3 h-3 group-hover/label:scale-110 transition-transform" />
<span className="text-xs font-medium opacity-80">{t('sidebar.editLabels') || 'Edit Labels'}</span>
<Plus className="h-3 w-3 shrink-0 group-hover/label:scale-110 transition-transform" />
<span className="text-xs font-medium">{t('sidebar.editLabels')}</span>
</button>
</div>
)}