Skip to content

Shared UI and Editor Patterns

The dashboard uses shadcn/ui — a set of accessible, composable components built on Radix UI primitives. Configuration lives in components.json.

All shadcn/ui components are in src/components/ui/ and include: Button, Card, Dialog, Drawer, DropdownMenu, Form, Input, Select, Sheet, Table, Tabs, Tooltip, and ~30 more.

ComponentLocationPurpose
DashboardLayoutsrc/components/layout/Wrapper with sidebar + header
Headersrc/components/layout/Top navigation bar
Sidebarsrc/components/layout/Left nav with role-based links

All forms use React Hook Form with Zod schemas for validation. The pattern:

const schema = z.object({
title: z.string().min(1, "Title is required"),
});
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
});

The rich text editor is CKEditor 5 Classic, wrapped in a custom component CKEditorWithS3 at src/components/ui/ckeditor.tsx.

It includes:

  • Custom toolbar configuration
  • Dark/light mode visual adaptation (synced with ThemeContext)
  • Built-in upload adapter wired to the signed S3 URL flow — images dropped or pasted into the editor are uploaded to S3 automatically

Toast notifications use Sonner via a <Toaster> component mounted at the app root. Call toast.success(), toast.error(), etc. from any component.

PageSkeleton is the standard loading fallback used inside Suspense boundaries for lazy-loaded routes.