feat: enhance validation and formatting in editor and sidebar components

This commit is contained in:
Anish Sarkar 2025-12-16 21:27:31 +05:30
parent 7bd638668a
commit 4cef8cc93f
6 changed files with 106 additions and 110 deletions

View file

@ -187,8 +187,12 @@ export function AppSidebarProvider({
// Sort notes by updated_at (most recent first), fallback to created_at if updated_at is null
const sortedNotes = [...notesData.items].sort((a, b) => {
const dateA = a.updated_at ? new Date(a.updated_at).getTime() : new Date(a.created_at).getTime();
const dateB = b.updated_at ? new Date(b.updated_at).getTime() : new Date(b.created_at).getTime();
const dateA = a.updated_at
? new Date(a.updated_at).getTime()
: new Date(a.created_at).getTime();
const dateB = b.updated_at
? new Date(b.updated_at).getTime()
: new Date(b.created_at).getTime();
return dateB - dateA; // Descending order (most recent first)
});

View file

@ -1,13 +1,6 @@
"use client";
import {
FileText,
type LucideIcon,
MoreHorizontal,
Plus,
RefreshCw,
Trash2,
} from "lucide-react";
import { FileText, type LucideIcon, MoreHorizontal, Plus, RefreshCw, Trash2 } from "lucide-react";
import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import { useCallback, useMemo, useState } from "react";
@ -86,7 +79,7 @@ export function AllNotesSidebar({
const sidebarElement = document.querySelector(
'[data-slot="sidebar"][data-sidebar="sidebar"]'
) as HTMLElement;
if (sidebarElement) {
const rect = sidebarElement.getBoundingClientRect();
// Set the left position to be the right edge of the sidebar
@ -116,7 +109,7 @@ export function AllNotesSidebar({
// Update on window resize and scroll
window.addEventListener("resize", updatePosition);
window.addEventListener("scroll", updatePosition, true);
// Use MutationObserver to watch for sidebar state changes
const observer = new MutationObserver(updatePosition);
const sidebarWrapper = document.querySelector('[data-slot="sidebar-wrapper"]');
@ -234,7 +227,6 @@ export function AllNotesSidebar({
}}
disabled={isDeletingNote}
className={cn("group/item relative", isDeletingNote && "opacity-50")}
size="sm"
>
<note.icon className="h-4 w-4 shrink-0" />
<span className={cn("truncate", isDeletingNote && "opacity-50")}>{note.name}</span>
@ -270,9 +262,7 @@ export function AllNotesSidebar({
className={isDeleteAction ? "text-destructive" : ""}
>
<ActionIcon className="mr-2 h-4 w-4" />
<span>
{isDeletingNote && isDeleteAction ? "Deleting..." : action.name}
</span>
<span>{isDeletingNote && isDeleteAction ? "Deleting..." : action.name}</span>
</DropdownMenuItem>
);
})}
@ -290,7 +280,7 @@ export function AllNotesSidebar({
ref={sidebarRef}
aria-label="All notes sidebar"
className={cn(
"fixed top-16 bottom-0 z-[100] w-80 bg-sidebar text-sidebar-foreground shadow-xl",
"fixed top-0 bottom-0 z-[100] w-80 bg-sidebar text-sidebar-foreground shadow-xl",
"transition-all duration-300 ease-out",
!open && "pointer-events-none"
)}
@ -319,84 +309,83 @@ export function AllNotesSidebar({
}
}}
>
<div className="flex h-full flex-col">
{/* Header */}
<div className="flex h-16 shrink-0 items-center justify-between px-4">
<h2 className="text-sm font-semibold">{t("all_notes") || "All Notes"}</h2>
</div>
{/* Content */}
<ScrollArea className="flex-1">
<div className="p-2">
<SidebarGroup>
<SidebarGroupContent>
{isLoadingNotes ? (
<SidebarMenuItem>
<SidebarMenuButton disabled size="sm">
<span className="text-xs text-muted-foreground">
{t("loading") || "Loading..."}
</span>
</SidebarMenuButton>
</SidebarMenuItem>
) : notesError ? (
<SidebarMenuItem>
<SidebarMenuButton disabled size="sm">
<span className="text-xs text-destructive">
{t("error_loading_notes") || "Error loading notes"}
</span>
</SidebarMenuButton>
</SidebarMenuItem>
) : allNotes.length > 0 ? (
<SidebarMenu className="list-none">
{allNotes.map((note) => (
<NoteItemComponent key={note.id || note.name} note={note} />
))}
</SidebarMenu>
) : (
<SidebarMenuItem className="list-none">
{onAddNote ? (
<SidebarMenuButton
onClick={() => {
onAddNote();
onOpenChange(false);
}}
className="text-muted-foreground hover:text-sidebar-foreground text-xs"
size="sm"
>
<Plus className="h-4 w-4" />
<span>{t("create_new_note") || "Create a new note"}</span>
</SidebarMenuButton>
) : (
<SidebarMenuButton disabled className="text-muted-foreground text-xs" size="sm">
<FileText className="h-4 w-4" />
<span>{t("no_notes") || "No notes yet"}</span>
</SidebarMenuButton>
)}
</SidebarMenuItem>
)}
</SidebarGroupContent>
</SidebarGroup>
</div>
</ScrollArea>
{/* Footer with Add Note button */}
{onAddNote && (
<div className="p-2">
<Button
onClick={() => {
onAddNote();
onOpenChange(false);
}}
className="w-full"
size="sm"
>
<Plus className="mr-2 h-4 w-4" />
{t("create_new_note") || "Create a new note"}
</Button>
</div>
)}
<div className="flex h-full flex-col">
{/* Header */}
<div className="flex h-16 shrink-0 items-center justify-between px-4 border-b border-sidebar">
<h2 className="text-sm font-semibold">{t("all_notes") || "All Notes"}</h2>
</div>
</section>
{/* Content */}
<ScrollArea className="flex-1">
<div className="p-2">
<SidebarGroup>
<SidebarGroupContent>
{isLoadingNotes ? (
<SidebarMenuItem>
<SidebarMenuButton disabled>
<span className="text-xs text-muted-foreground">
{t("loading") || "Loading..."}
</span>
</SidebarMenuButton>
</SidebarMenuItem>
) : notesError ? (
<SidebarMenuItem>
<SidebarMenuButton disabled>
<span className="text-xs text-destructive">
{t("error_loading_notes") || "Error loading notes"}
</span>
</SidebarMenuButton>
</SidebarMenuItem>
) : allNotes.length > 0 ? (
<SidebarMenu className="list-none">
{allNotes.map((note) => (
<NoteItemComponent key={note.id || note.name} note={note} />
))}
</SidebarMenu>
) : (
<SidebarMenuItem className="list-none">
{onAddNote ? (
<SidebarMenuButton
onClick={() => {
onAddNote();
onOpenChange(false);
}}
className="text-muted-foreground hover:text-sidebar-foreground text-xs"
>
<Plus className="h-4 w-4" />
<span>{t("create_new_note") || "Create a new note"}</span>
</SidebarMenuButton>
) : (
<SidebarMenuButton disabled className="text-muted-foreground text-xs">
<FileText className="h-4 w-4" />
<span>{t("no_notes") || "No notes yet"}</span>
</SidebarMenuButton>
)}
</SidebarMenuItem>
)}
</SidebarGroupContent>
</SidebarGroup>
</div>
</ScrollArea>
{/* Footer with Add Note button */}
{onAddNote && (
<div className="p-2">
<Button
onClick={() => {
onAddNote();
onOpenChange(false);
}}
className="w-full"
size="sm"
>
<Plus className="mr-2 h-4 w-4" />
{t("create_new_note") || "Create a new note"}
</Button>
</div>
)}
</div>
</section>
);
// Render sidebar via portal to avoid stacking context issues
@ -406,4 +395,3 @@ export function AllNotesSidebar({
return createPortal(sidebarContent, document.body);
}

View file

@ -15,11 +15,7 @@ import {
import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl";
import { useCallback, useState, useRef } from "react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import {
DropdownMenu,
DropdownMenuContent,
@ -100,7 +96,6 @@ export function NavNotes({ notes, onAddNote, defaultOpen = true, searchSpaceId }
onClick={() => router.push(note.url)}
disabled={isDeletingNote}
className={`group/item relative ${isDeletingNote ? "opacity-50" : ""}`}
size="sm"
>
<note.icon className="h-4 w-4 shrink-0" />
<span className={`truncate ${isDeletingNote ? "opacity-50" : ""}`}>{note.name}</span>
@ -164,9 +159,7 @@ export function NavNotes({ notes, onAddNote, defaultOpen = true, searchSpaceId }
isOpen ? "rotate-90" : ""
}`}
/>
<span className="text-xs font-medium text-sidebar-foreground/70">
{t("notes") || "Notes"}
</span>
<span>{t("notes") || "Notes"}</span>
</SidebarGroupLabel>
</CollapsibleTrigger>
<div className="absolute top-1.5 right-1 flex items-center gap-0.5 opacity-0 group-hover/header:opacity-100 transition-opacity">
@ -223,13 +216,12 @@ export function NavNotes({ notes, onAddNote, defaultOpen = true, searchSpaceId }
<SidebarMenuButton
onClick={onAddNote}
className="text-muted-foreground hover:text-sidebar-foreground text-xs"
size="sm"
>
<Plus className="h-4 w-4" />
<span>{t("create_new_note") || "Create a new note"}</span>
</SidebarMenuButton>
) : (
<SidebarMenuButton disabled className="text-muted-foreground text-xs" size="sm">
<SidebarMenuButton disabled className="text-muted-foreground text-xs">
<FileText className="h-4 w-4" />
<span>{t("no_notes") || "No notes yet"}</span>
</SidebarMenuButton>
@ -252,4 +244,3 @@ export function NavNotes({ notes, onAddNote, defaultOpen = true, searchSpaceId }
</SidebarGroup>
);
}