Frontend Architecture — Next.js 15 Dashboard¶
Part:
Sass-boilerplate-frontend-v1/| Type: Web Application Generated: 2026-02-12 | Last Modified: 2026-03-26 | Scan Level: Exhaustive | Workflow: document-project v1.2.0
Executive Summary¶
Next.js 15 dashboard application using App Router with React 19. Features a modular component architecture with Radix UI primitives (shadcn/ui pattern), Redux Toolkit for auth state, TanStack React Query for server state, and TailwindCSS 4 for styling. Provides both public-facing pages (blog, landing, contact) and a protected admin dashboard. Includes security hardening (DOMPurify XSS prevention across all 9 dangerouslySetInnerHTML sites, iframe sandboxing), dynamic theme/font customization, a cache revalidation dashboard, drag-and-drop menu builder v2, and an AI voice integration module.
Language¶
This frontend is written in JavaScript (.js / .jsx files), not TypeScript. While the backend uses TypeScript and all frontend tooling (Next.js, Redux Toolkit, React Query) has first-class TypeScript support, the frontend currently does not use type checking. A TypeScript migration is possible but not yet implemented.
File counts (verified 2026-03-26): 413 total .js/.jsx files — 74 pages, 4 layouts, 31 API hook files, 42 UI components, 33 custom components, 15 editor components, 128 page components, 9 layout components, 5 providers, 7 theme-control components, 10 custom hooks, 3 services, 10 lib/utils, 3 constants, 2 Redux slices, 2 server action files (4 functions).
Technology Stack¶
| Category | Technology | Version | Purpose |
|---|---|---|---|
| Framework | Next.js | 15.3.6 | Full-stack React framework |
| UI Library | React | 19.0 | Component library |
| Styling | TailwindCSS | 4.x | Utility-first CSS |
| State (Global) | Redux Toolkit | 2.8 | Auth state management |
| State (Server) | TanStack React Query | 5.85 | Data fetching/caching |
| UI Primitives | Radix UI | Various | Accessible components |
| Forms | React Hook Form | 7.62 | Form state management |
| Rich Text | BlockNote | 0.35 | Block-based editor |
| Charts | ApexCharts | 5.3 | Interactive charts (dashboard analytics) |
| Charts | Recharts | 2.15 | Declarative charts (additional visualizations) |
| Animations | Framer Motion | 12.23 | Animation library |
| Drag & Drop | @dnd-kit | 6.3/10.0 | Sortable interfaces (menu builder v2) |
| Icons | Lucide React | 0.510 | Icon system |
| Dates | date-fns | 4.1 | Date utilities |
| HTTP | Axios | 1.11 | HTTP client |
| Cookies | cookies-next | 6.1 | Cookie management |
| Auth | jwt-decode | 4.0 | JWT token parsing |
| Notifications | react-hot-toast | 2.6 | Toast notifications |
| Theme | next-themes | 0.4 | Dark/light mode |
| Code Highlight | PrismJS | 1.30 | Syntax highlighting |
| Transliteration | transliteration | 2.3 | Slug generation |
| Date Picker | react-day-picker | 9.8 | Calendar component |
| Sanitization | DOMPurify | 3.3 | XSS prevention (HTML sanitization) |
| Command Menu | cmdk | 1.1 | Command palette UI |
Note: Two charting libraries coexist. ApexCharts is used for the main dashboard analytics; Recharts is used for supplementary visualizations. Consider consolidating to one library to reduce bundle size (~150KB combined).
Directory Structure¶
Sass-boilerplate-frontend-v1/
├── src/
│ ├── app/ # Next.js App Router (74 pages)
│ │ ├── layout.js # Root layout — provider hierarchy
│ │ ├── globals.css # Global styles + TailwindCSS
│ │ ├── (auth)/ # Auth route group (public)
│ │ │ ├── layout.js # Auth layout with FooterCredit
│ │ │ ├── login/page.js
│ │ │ ├── register/page.js
│ │ │ └── verify-email/[token]/page.js
│ │ ├── (dashboard-layout)/ # Dashboard route group (protected)
│ │ │ └── dashboard/
│ │ │ ├── layout.js # Dashboard layout (sidebar + topnav)
│ │ │ ├── page.js # Dashboard home
│ │ │ ├── analytics/ # Analytics page
│ │ │ ├── users/ # User management
│ │ │ ├── blogs/ # Blog management (5 pages incl. tags)
│ │ │ ├── packages/ # Package management (4 pages)
│ │ │ ├── products/ # Product management (4 pages)
│ │ │ ├── pages/ # CMS pages (3 pages)
│ │ │ ├── roles-permissions/ # RBAC management (3 pages)
│ │ │ ├── settings/ # Settings (7 pages: account, site, profile, menu-builder, menu-builder-old, menu-label, revalidate)
│ │ │ ├── contacts/ # Contact submissions
│ │ │ ├── email-templates/ # Email template visual builder (3 pages: list, create, edit)
│ │ │ ├── invited-users/ # User invitations
│ │ │ ├── pricing-plan/ # Pricing display
│ │ │ ├── ai-integration/ # AI voice interview with speech recognition
│ │ │ ├── ai-integration-old/ # Legacy AI integration (dead code candidate)
│ │ │ ├── auth/ # Auth page templates (5 variants)
│ │ │ ├── form-elements/ # Form showcase (6 pages)
│ │ │ ├── table/ # Table showcase (4 pages: basic, advanced, v2 variants)
│ │ │ ├── test/ # Test pages (nested 3 levels)
│ │ │ └── [UI demos]/ # accordion, alerts, avatar, breadcrumb, carousel, pagination
│ │ ├── (main-layout)/ # Public route group
│ │ │ ├── layout.js
│ │ │ ├── page.js # Landing/home page
│ │ │ ├── blogs/ # Public blog (3 pages incl. tags)
│ │ │ ├── contact/ # Contact form
│ │ │ ├── author/[slug]/ # Author profile
│ │ │ ├── category/[slug]/ # Category listing
│ │ │ └── [slug]/ # Dynamic CMS pages
│ │ ├── forgot-password/page.js
│ │ └── reset-password/[token]/page.js
│ ├── components/ # Reusable components
│ │ ├── ui/ # 42 base UI components (Radix + shadcn pattern)
│ │ ├── custom/ # 33 custom business components
│ │ ├── pages/ # 128 page-specific compositions
│ │ ├── layout/ # 9 layout components (sidebar, topnav)
│ │ ├── common/ # Shared business components
│ │ ├── forms/ # Form components
│ │ ├── charts/ # Chart components
│ │ ├── tables/ # Data table components
│ │ ├── editor/ # 15 rich text editors (13 BlockNote + 2 custom BasicEditor)
│ │ ├── providers/ # 5 context providers
│ │ └── theme-control/ # 7 theme customization UI components
│ ├── api/ # React Query hooks (20 domains, 139 hooks across 31 files)
│ │ ├── auth/ # Login, register, verify, reset (6 hooks)
│ │ ├── blogs/ # Blog CRUD + categories + tags (12 hooks)
│ │ ├── blog-categories/ # Legacy hook (1, double-prefix bug)
│ │ ├── blog-tags/ # Legacy hook (1, double-prefix bug)
│ │ ├── contacts/ # Contact submissions + bulk ops (8 hooks)
│ │ ├── dashboard/ # Analytics data (1 hook)
│ │ ├── email-templates/ # Template management (10 hooks)
│ │ ├── google-fonts/ # Google Fonts API (1 hook)
│ │ ├── invited-users/ # Invitation + resend + check (12 hooks)
│ │ ├── menu/ # Menu CRUD (6 hooks)
│ │ ├── menu-builder/ # Menu builder + reorder + tree (13 hooks + 4 utils)
│ │ ├── menu-item/ # Menu item operations (3 hooks)
│ │ ├── menu-labels/ # Legacy hook (1, double-prefix bug)
│ │ ├── packages/ # Package management (10 hooks)
│ │ ├── package-categories/ # Legacy hook (1, double-prefix bug)
│ │ ├── pages/ # CMS page CRUD (10 hooks)
│ │ ├── permissions/ # Permission CRUD (10 hooks)
│ │ ├── products/ # EMPTY — no hooks exported (BUG)
│ │ ├── product-categories/ # Legacy hook (1, double-prefix bug — not used)
│ │ ├── public/ # Server-side fetching (7 server fns + 1 client hook)
│ │ ├── revalidate/ # Cache revalidation (3 hooks — NEW)
│ │ ├── roles/ # Role management (9 hooks)
│ │ ├── settings/ # Settings by group (5 hooks)
│ │ ├── account-settings/ # Account settings (4 hooks)
│ │ └── users/ # User management (11 hooks)
│ ├── store/ # Redux store
│ │ ├── index.js # Store configuration
│ │ └── slices/
│ │ ├── authSlice.js # Auth state (user, token, isAuthenticated)
│ │ └── settingSlice.js # Site settings by group
│ ├── hooks/ # 10 custom hooks
│ │ ├── useCheckPermission.js # RBAC permission check
│ │ ├── useCheckSlug.js # Slug uniqueness validation
│ │ ├── useClientPathName.jsx # Client-side pathname
│ │ ├── useDashboardData.js # Dashboard analytics
│ │ ├── useDebounce.jsx # Input debouncing
│ │ ├── useFormSubmit.js # Form submission handler
│ │ ├── useMediaQuery.jsx # Responsive breakpoints
│ │ ├── useMenus.js # Navigation menu fetching
│ │ ├── useRedux.js # Typed Redux hooks
│ │ └── useTableActions.js # Table CRUD operations
│ ├── services/ # 3 service files
│ │ ├── authService.js # Auth API methods
│ │ ├── menuService.js # Menu data (permanently hardcoded to dummy data)
│ │ └── menuItemService.js # Menu item data (permanently hardcoded to dummy data)
│ ├── interceptors/
│ │ └── axiosInstance.js # Axios config + auth interceptors
│ ├── context/ # React context definitions
│ ├── actions/ # Server actions (2 files, 4 functions)
│ ├── hocs/ # Higher-order components
│ ├── constants/ # 3 static constants files
│ ├── lib/ # 10 utility files (cn(), formatters)
│ ├── assets/ # Static assets
│ └── middleware.js # Next.js route middleware
├── package.json
├── CLAUDE.md # AI assistant context
└── README.md
Route Map (74 Pages)¶
Authentication (5 pages)¶
| Route | Page | Auth |
|---|---|---|
/login |
Login form | Public |
/register |
Registration | Public |
/verify-email/[token] |
Email verification | Public |
/forgot-password |
Password reset request | Public |
/reset-password/[token] |
Password reset form | Public |
Dashboard — Core (3 pages)¶
| Route | Page |
|---|---|
/dashboard |
Dashboard home with analytics widgets |
/dashboard/analytics |
Detailed analytics page |
/dashboard/[...notfound] |
Dashboard 404 catch-all |
Dashboard — Content Management (10 pages)¶
| Route | Page |
|---|---|
/dashboard/blogs |
Blog listing with bulk operations |
/dashboard/blogs/create |
Create blog with BlockNote editor |
/dashboard/blogs/[slug] |
Edit blog post |
/dashboard/blogs/categories |
Blog category management |
/dashboard/blogs/tags |
Blog tag management (NEW) |
/dashboard/pages |
CMS page listing |
/dashboard/pages/create |
Create CMS page |
/dashboard/pages/[slug] |
Edit CMS page |
/dashboard/email-templates |
Email template visual builder listing |
/dashboard/email-templates/[id] |
Edit email template |
Dashboard — E-Commerce (9 pages)¶
| Route | Page |
|---|---|
/dashboard/packages |
Package listing |
/dashboard/packages/create |
Create package |
/dashboard/packages/[id] |
Edit package |
/dashboard/packages/categories |
Package categories |
/dashboard/products |
Product listing |
/dashboard/products/create |
Create product |
/dashboard/products/[id] |
Edit product |
/dashboard/products/categories |
Product categories |
/dashboard/pricing-plan |
Public pricing display |
Dashboard — Administration (14 pages)¶
| Route | Page |
|---|---|
/dashboard/users |
User management |
/dashboard/invited-users |
User invitation management |
/dashboard/contacts |
Contact form submissions |
/dashboard/roles-permissions/roles |
Role management |
/dashboard/roles-permissions/permissions |
Permission assignment |
/dashboard/settings/site-settings |
Site configuration |
/dashboard/settings/account-settings |
Account settings |
/dashboard/settings/profile |
User profile |
/dashboard/settings/menu-builder |
Drag-drop menu builder (v2, @dnd-kit) |
/dashboard/settings/menu-builder-old |
Legacy menu builder (dead code candidate) |
/dashboard/settings/menu-label |
Menu label management |
/dashboard/settings/revalidate |
Cache revalidation dashboard (NEW) |
/dashboard/ai-integration |
AI voice interview with speech recognition |
/dashboard/ai-integration-old |
Legacy AI integration (dead code candidate) |
Public Pages (8 pages)¶
| Route | Page |
|---|---|
/ |
Landing/home page |
/blogs |
Public blog listing |
/blogs/[slug] |
Blog post detail with SEO |
/blogs/tags |
Blog posts by tag (NEW) |
/contact |
Contact form |
/author/[slug] |
Author profile |
/category/[slug] |
Category listing |
/[slug] |
Dynamic CMS pages |
UI Showcase Pages (~25 pages)¶
Auth templates (5), form elements (6), table variants (4 including v2 variants), accordion, alerts, avatar, breadcrumb, carousel, pagination, components, test pages (nested 3 levels).
Permission-Protected Pages (4 pages, 5 checks)¶
| Route | Permission Required |
|---|---|
/dashboard/blogs/create |
blog.create |
/dashboard/blogs/[slug] |
blog.update |
/dashboard/email-templates/[id] |
email-template.create |
/dashboard/settings/revalidate |
cache.view + cache.update |
Warning: Only 4 out of ~50 dashboard pages enforce client-side permission checks. All other pages rely solely on middleware auth (logged-in check) and backend permission validation.
Provider Hierarchy¶
// app/layout.js
<ReduxProvider store={store}>
<QueryProvider>
<AuthInitializer>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<Toaster position="top-right" />
{children}
</ThemeProvider>
</AuthInitializer>
</QueryProvider>
</ReduxProvider>
| Provider | Purpose |
|---|---|
| ReduxProvider | Global store (auth state) |
| QueryProvider | TanStack Query client with defaults |
| AuthInitializer | Syncs cookie token → Redux + wires dispatch to axios |
| ThemeProvider | Dark/light mode via CSS class |
Authentication Architecture¶
1. Middleware (middleware.js)¶
- Protects
/dashboard/*routes — redirects to/loginif notokencookie - Redirects authenticated users from
/login,/register→/dashboard - Matcher:
["/login", "/register", "/dashboard/:path*"]
2. Redux Store (2 Slices)¶
Auth Slice (authSlice.js):
initialState: { user: null, token: null, isAuthenticated: false }
reducers: setAuth, logout, initializeAuth, updateUser
Setting Slice (settingSlice.js):
Known bug: Store config registers this as
settinReducer(typo — missing 'g').
3. Axios Interceptor¶
- Request: Adds
Authorization: Bearer {token}from cookie - Response: On 401/403 → deletes cookie, dispatches logout, redirects to
/login - Connected to Redux via
setStoreDispatch()from AuthInitializer
Known bug: 403 (Forbidden) triggers full logout + redirect. Only 401 (Unauthorized) should trigger logout; 403 should show an "access denied" message instead, since the user is authenticated but lacks permission.
4. Auth Initializer¶
- Runs on mount: syncs Redux from cookie + wires Redux dispatch to axios
State Management¶
| Layer | Tool | Scope |
|---|---|---|
| Auth State | Redux Toolkit (authSlice) | Global user/token/isAuthenticated |
| Settings State | Redux Toolkit (settingSlice) | Site settings by group |
| Server State | TanStack React Query | API data with caching/invalidation |
| Server Cache | Next.js fetch cache | Public pages with tag-based revalidation |
| UI State | React useState | Component-local |
| Form State | React Hook Form | Form values/validation |
| Theme State | next-themes | Dark/light mode |
Known issue — Triple-cache desync: Three independent caching layers (React Query, Redux settingSlice, Next.js fetch cache) can hold stale versions of the same data. No synchronization mechanism exists between them.
UI Component Library (42 Components)¶
Base UI components in components/ui/ following shadcn/ui pattern with Radix primitives:
Layout: card, sheet, collapsible, accordion, tabs Input: input, textarea, label, checkbox, radio-group, select, searchable-dropdown, SearchableDropdown (PascalCase duplicate), multi-select, slider, switch, date-inputs, calendar Display: badge, avatar, progress, skeleton, dashboard-skeletons, chart, code-box, heading, section-heading, author-card, sandboxed-html-preview, sanitized-html-preview Feedback: alert, alert-dialog, dialog, tooltip, popover, spinner, loading Navigation: button, breadcrumb, dropdown-menu, command Theme: theme-toggle
Note:
SearchableDropdown.jsxandsearchable-dropdown.jsxboth exist — naming inconsistency that could cause import confusion.
Custom Components (33 Components)¶
Business-level components in components/custom/ including modals, permission wrappers, data tables, image upload, pagination, status badges, and form fields. Notable components:
- PermissionWrapper — renders children only if user has required permission (render-phase redirect side effect)
- Modal system — custom modal used by 32+ consumers (zero accessibility attributes)
- DataTable — generic table with sorting, pagination, bulk actions
- ImageUpload — drag-and-drop with preview
Note: Two Button components coexist (
ui/buttonvscustom/button) used interchangeably across flows. Three competing modal systems: custom/modal (32 consumers, zero a11y) vs ui/dialog (5 consumers, full a11y) vs ui/alert-dialog (never imported anywhere).
API Integration Layer¶
Hook Architecture¶
// api/{domain}/index.js
export const useItems = (params) => useQuery({
queryKey: ["{domain}", params],
queryFn: () => api.get("/{endpoint}", { params }).then(r => r.data),
});
export const useCreateItem = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (data) => api.post("/{endpoint}", data),
onSuccess: () => qc.invalidateQueries({ queryKey: ["{domain}"] }),
});
};
API Hook Domains (139 hooks across 31 files in 25 domains)¶
| Domain | File(s) | Hooks | Notes |
|---|---|---|---|
| auth | 1 | 6 | Login, register, verify, reset |
| blogs | 1 | 12 | Blog CRUD + categories + tags |
| blog-categories | 1 | 1 | Legacy useEffect hook, double-prefix URL bug |
| blog-tags | 1 | 1 | Legacy useEffect hook, double-prefix URL bug |
| contacts | 1 | 8 | Contact submissions + bulk ops |
| dashboard | 1 | 1 | Analytics data |
| email-templates | 1 | 10 | Template management |
| google-fonts | 1 | 1 | Font list with localStorage cache |
| invited-users | 1 | 12 | Invitation + resend + check |
| menu | 1 | 6 | Menu CRUD |
| menu-builder | 1 | 13+4 utils | Menu builder + reorder + tree |
| menu-item | 1 | 3 | Menu item operations |
| menu-labels | 1 | 1 | Legacy useEffect hook, double-prefix URL bug |
| packages | 1 | 10 | Package management |
| package-categories | 1 | 1 | Legacy useEffect hook, double-prefix URL bug |
| pages | 1 | 10 | CMS page CRUD |
| permissions | 1 | 10 | Permission CRUD |
| products | 1 | 0 | EMPTY file — no hooks exported (BUG) |
| product-categories | 1 | 1 | Legacy, not actively used |
| public | 1 | 7+1 | 7 server fns + 1 client hook (see Server-Side Data Fetching) |
| revalidate | 1 | 3 | NEW: useRevalidateTag, useBulkRevalidateTags, useRevalidateAllCache |
| roles | 1 | 9 | Role management |
| settings | 1 | 5 | Settings by group |
| account-settings | 1 | 4 | Account-level settings |
| users | 1 | 11 | User management |
Legacy hooks: 4 files (
blog-categories,blog-tags,menu-labels,package-categories) containuseEffect-based data fetching that duplicates existing React Query hooks. All 4 have double-prefix URL bugs (e.g.,/api/api/blog-categories).
Server-Side Data Fetching¶
Public pages use Next.js fetch() with cache tags for ISR-style revalidation. The project does not use unstable_cache or generateStaticParams.
Server Functions (src/api/public/)¶
7 server functions + 1 client hook:
| Function | Cache Tag | Purpose |
|---|---|---|
getBlogs() |
blogs |
Public blog listing |
getBlogBySlug(slug) |
blog-details |
Single blog post |
getDynamicPage(slug) |
dynamic-page |
CMS page content |
getNavigationLinks() |
navigation-links |
Menu/nav data |
getPackages() |
packages-data |
Pricing packages |
getSettingsByGroup(group) |
settings-group |
Site settings |
getAllSettings() |
all-settings |
All settings (landing page) |
usePublicSettings() |
— | Client-side React Query hook |
Cache Configuration¶
- Revalidation time:
NEXT_PUBLIC_CACHE_TIMEenv var (default: 1 hour) - 7 cache tags in use:
blogs,blog-details,dynamic-page,navigation-links,packages-data,settings-group,all-settings - Revalidation triggered via dashboard at
/dashboard/settings/revalidate
Cache Revalidation System (NEW)¶
Added in the 2026-03-12 to 2026-03-26 sprint. Provides a dashboard UI for on-demand cache invalidation.
| Component | Purpose |
|---|---|
/dashboard/settings/revalidate page |
Admin UI for cache management |
useRevalidateTag hook |
Invalidate a single cache tag |
useBulkRevalidateTags hook |
Invalidate multiple tags at once |
useRevalidateAllCache hook |
Purge all 7 cache tags |
Server actions (src/actions/) |
revalidateTag() / revalidatePath() calls |
- Protected by
cache.view+cache.updatepermissions - Targets all 7 cache tags listed above
- Uses Next.js server actions for tag-based revalidation
Menu Builder v2 (NEW)¶
Replaced the legacy menu builder with a WordPress-style drag-and-drop hierarchy system.
| Feature | Implementation |
|---|---|
| Drag & Drop | @dnd-kit/core + @dnd-kit/sortable |
| Hierarchy | Nested tree structure with parent/child relationships |
| Components | 8 dedicated components in pages/menu-builder/ |
| API | 13 React Query hooks + 4 utility functions |
| Legacy | Old builder preserved at /dashboard/settings/menu-builder-old |
Pricing Section Refactor (NEW)¶
The pricing section was refactored to support monthly/yearly toggle:
PricingContent— handles monthly/yearly switching logicPricingSection— wrapper/container, separated from content logic
Custom Hooks¶
| Hook | Purpose |
|---|---|
useCheckPermission(perm) |
Check current user's RBAC permission |
useCheckSlug(slug, type) |
Validate slug uniqueness via API |
useDashboardData() |
Aggregate dashboard analytics data |
useFormSubmit(config) |
Generic form submission with toast + invalidation |
useMenus(slug) |
Fetch navigation menu by slug |
useRedux() |
Typed useSelector + useDispatch |
useTableActions(config) |
Table CRUD: edit, delete, bulk, restore |
useDebounce(value, delay) |
Debounce input values |
useMediaQuery(query) |
Responsive breakpoint detection |
useClientPathName() |
Client-side pathname access |
Dynamic Metadata¶
// app/layout.js
export async function generateMetadata() {
const settings = await fetch(`${API_URL}/settings/admin/prefix`);
// Parses "admin.meta_title" → nested object
return { title: settings.admin.meta_title || "Sass Boilerplate" };
}
/settings/admin/prefix API
- Supports Open Graph and Twitter cards
- Falls back to "Sass Boilerplate"
Theme Customization¶
Dynamic theme and font system:
| Component | File | Purpose |
|---|---|---|
FontSizeControl |
theme-control/font-size-control.jsx |
Dynamic font size adjustment |
FontFamilyControl |
theme-control/font-family-control.jsx |
Google Fonts family picker |
ThemeProvider |
providers/theme-provider.jsx |
Enhanced with dynamic font loading |
ThemeContextProvider |
context/theme-context.jsx |
Font size/family state management |
- Font families loaded from Google Fonts API (
useGoogleFontshook with localStorage caching) - Font settings stored in site settings and applied via CSS custom properties
- Poppins font import removed from root layout (fonts now dynamic)
Security Hardening¶
| Fix | Component | Description |
|---|---|---|
| XSS | All 9 dangerouslySetInnerHTML sites |
DOMPurify sanitization applied to every instance |
| XSS | sanitized-html-preview.jsx |
Reusable DOMPurify HTML rendering component |
| XSS | sandboxed-html-preview.jsx |
Secure iframe for email template HTML preview |
| Iframe | IframeEmbed.jsx |
URL allowlist (YouTube/Vimeo/Dailymotion) + sandbox attribute |
Note:
SanitizedHTMLPreviewandSandboxedHtmlPrevieware the two approved patterns for rendering user-supplied HTML. All new features should use one of these instead of rawdangerouslySetInnerHTML.
Environment Variables¶
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_API_URL |
Backend API base URL (client-side) |
API_URL |
Backend API base URL (server-side/SSR) |
NEXT_PUBLIC_CACHE_TIME |
ISR revalidation time in hours (default: 1) |
Development Commands¶
npm run dev # Start with Turbopack (port 3000)
npm run build # Production build
npm start # Start production server
npm run lint # ESLint (Next.js core-web-vitals)
Styling¶
- TailwindCSS 4 with
@tailwindcss/postcss - Dynamic fonts via Google Fonts API (Poppins removed; fonts now configurable via settings)
- Dark mode via
classattribute +next-themes - shadcn/ui component pattern with CSS variables for theming
tw-animate-cssfor animation utilities@/import alias forsrc/directory
Known issue:
goldcolor tokens are completely undefined in Tailwind config (16 uses across 7 files fail silently).light-50is also missing a@thememapping (15 uses across 10 files).
Known Issues¶
| # | Severity | Issue | Location |
|---|---|---|---|
| 1 | Medium | settinReducer typo (missing 'g') in store config |
store/index.js |
| 2 | High | 403 Forbidden triggers full logout (should only be 401) | interceptors/axiosInstance.js |
| 3 | High | useSetupTwoFactor uses useQuery for PUT (regenerates TOTP on every mount/refetch) |
api/account-settings/ |
| 4 | High | useBackupCodes uses useQuery for POST (overwrites backup codes on refetch) |
api/account-settings/ |
| 5 | Medium | Triple-cache desync: React Query + Redux + Next.js fetch cache, no sync mechanism | Multiple |
| 6 | Low | products/index.js is empty — dead file, no product API hooks exported |
api/products/ |
| 7 | Medium | 4 legacy hooks with double-prefix URL bugs (/api/api/...) |
api/blog-categories/, blog-tags/, menu-labels/, package-categories/ |
| 8 | Medium | Zero ErrorBoundary components anywhere in the app |
— |
| 9 | Medium | Zero loading.jsx files (no route-level loading states) |
app/ |
| 10 | Medium | Zero error.jsx files (no route-level error boundaries) |
app/ |
| 11 | Low | menuService and menuItemService permanently hardcoded to dummy data |
services/ |
| 12 | Low | gold and light-50 Tailwind tokens undefined (silent CSS failures) |
Multiple |
| 13 | Low | Only 4 of ~50 dashboard pages enforce client-side permission checks | app/(dashboard-layout)/ |
Dead Code Candidates (~30 files, ~4000+ LOC)¶
Files that appear unused or superseded. Removal candidates pending verification:
| File/Directory | LOC (est.) | Reason |
|---|---|---|
ui/alert-dialog.jsx |
~60 | Never imported anywhere |
ui/searchable-dropdown.jsx |
~80 | Lowercase duplicate of SearchableDropdown.jsx |
ui/command.jsx |
~100 | No known consumer |
ui/dashboard-skeletons.jsx |
~120 | Possibly superseded by component-level skeletons |
layout/no-scroll-restoration.jsx |
~30 | No known consumer |
pages/menu-builder-old/ (12 files) |
~800 | Entirely replaced by menu-builder/ (v2) |
pages/dashboard/dashboard-page.jsx |
~150 | Superseded by v2/v3 dashboard |
pages/dashboard/charts.jsx |
~100 | Superseded by charts-v2 |
pages/dashboard/yearly-sales.jsx |
~80 | Superseded by v2 |
ai-integration-old/ (7 files) |
~500 | Replaced by ai-integration/ |
Total removable: ~30 files, ~4000+ LOC. Removing these would reduce bundle size and maintenance burden.
Generated by BMAD Document Project workflow v1.2.0 — Exhaustive scan, 2026-02-12 | Last modified: 2026-03-26