Frontend Infrastructure - Deep Dive Documentation¶
Generated: 2026-03-26
Scope: Sass-boilerplate-frontend-v1/src/ (store, hooks, providers, context, lib, services, actions, hocs, constants, interceptors, middleware, root layout)
Files Analyzed: 45 (41 core infra + 4 app router layouts)
Lines of Code: ~2,100
Workflow Mode: Exhaustive Deep-Dive
Overview¶
The frontend infrastructure layer provides the foundational wiring for a Next.js 15 + React 19 SaaS dashboard application. It encompasses state management (Redux Toolkit + React Query + React Context), HTTP communication (Axios interceptor), route protection (Next.js middleware), theming (next-themes + custom context), utility libraries, and server actions.
Purpose: Cross-cutting application plumbing — everything between the UI components and the backend API.
Key Responsibilities: - Authentication state lifecycle (cookie-based JWT, no refresh mechanism) - Permission-gated route access (middleware + hooks) - HTTP request/response pipeline (interceptor, token injection, error handling) - Theme management (dark/light with custom font loading) - Reusable utilities (formatting, icons, uploads, debounce, media queries) - React Query caching configuration - Server-side revalidation (ISR cache tags)
Integration Points: Consumed by 69+ component/page files. Connects to Express.js backend via 34 API hook modules. Bridges SSR (middleware, server actions, layouts) and CSR (providers, hooks, interceptors).
Consolidated Issue Summary¶
Total Issues Found: ~179 (deduplicated across 6 analysis passes)
| Severity | Count | Description |
|---|---|---|
| Critical | 16 | Security holes, broken auth flow, dead code, data corruption |
| High | 22 | Logic bugs, UX breakage, performance, dead code |
| Medium | ~63 | Anti-patterns, missing guards, inconsistencies |
| Low | ~62 | Style, naming, minor improvements |
| Systemic | 8 | Cross-cutting architectural issues |
Known Issues - Critical (16)¶
| ID | Severity | File(s) | Issue | Impact |
|---|---|---|---|---|
| C1 | CRITICAL | store/slices/authSlice.js |
Side effects in Redux reducers — getCookie()/deleteCookie() called synchronously inside reducers |
Violates Redux principles; breaks SSR, time-travel debugging, and middleware |
| C2 | CRITICAL | middleware.js |
No JWT validation — only checks cookie existence, not token validity. token=anything passes |
Any fabricated cookie bypasses middleware |
| C3 | CRITICAL | interceptors/axiosInstance.js |
403 responses trigger full logout + cookie deletion — permission-denied destroys session | Any RBAC check failure forces re-authentication |
| C4 | CRITICAL | interceptors/axiosInstance.js |
No token refresh mechanism — 401 immediately logs out, no silent refresh attempt | Users logged out the instant JWT expires |
| C5 | CRITICAL | api/auth/index.js |
refreshToken API sends no refresh token in request body — endpoint is non-functional |
Token refresh can never work even if interceptor called it |
| C6 | CRITICAL | components/providers/protected-route.jsx |
100% dead code — never imported anywhere in the codebase | False sense of security; entire protection relies on middleware.js only |
| C7 | CRITICAL | theme-provider.jsx, theme-context.jsx |
Theme state desync — next-themes and custom ThemeContext communicate via one-way CustomEvent that only fires on mount. Post-mount changes never propagate |
Components using useThemeContext().theme show stale theme |
| C8 | CRITICAL | components/providers/theme-provider.jsx |
Console.log left in production (lines 59-68) — logs all theme configuration on every mount | Leaks configuration details; performance noise |
| C9 | CRITICAL | app/layout.js, dashboard/layout.js |
Settings dispatched to Redux from two competing sources — root layout (server fetch) and dashboard layout (React Query) both dispatch to same slice | Race condition; potentially overwrites with differently-shaped data |
| C10 | CRITICAL | hooks/useDashboardData.js |
Error swallowing — try/catch in queryFn makes isError permanently false |
Consumers can never detect API failures; dashboard shows empty without explanation |
| C11 | CRITICAL | lib/uploadFile.js |
No file validation — accepts ANY file type (exe, php, etc.) with zero type/size/extension checks | Unrestricted file upload vulnerability |
| C12 | CRITICAL | lib/uploadFile.js |
No authentication — uses raw fetch() bypassing axiosInstance, uploads go without auth tokens |
Unauthenticated upload endpoint if backend doesn't independently verify |
| C13 | CRITICAL | lib/utils.js |
Full Lucide library import — import * as LucideIcons from "lucide-react" dumps ~1,000+ icons into client bundle |
Adds hundreds of KB to bundle size |
| C14 | CRITICAL | services/, lib/, hooks/ |
617 LOC dead code — menuService.js (94), menuItemService.js (145), dummyMenuData.js (282), useMenus.js (96) all have zero consumers |
Dead weight; confuses future developers |
| C15 | CRITICAL | services/, api/menu/, api/menu-builder/ |
Three competing menu implementations with identically-named hooks (useCreateMenu, useUpdateMenu, useDeleteMenu) |
Import confusion; wrong implementation silently used |
| C16 | CRITICAL | hooks/useCheckPermission.js + store/slices/authSlice.js |
Permission split-brain after page refresh — initializeAuth() sets isAuthenticated=true but user=null. Permission checks read from Redux (null), not React Query (has data) |
All permission-gated UI disappears after refresh until profile loads |
Known Issues - High (22)¶
| ID | Severity | File(s) | Issue |
|---|---|---|---|
| H1 | HIGH | store/slices/authSlice.js |
user=null while isAuthenticated=true — temporal gap between cookie check and profile fetch |
| H2 | HIGH | store/slices/authSlice.js |
Dead state.auth.token — Redux token field written but never read by any consumer |
| H3 | HIGH | store/slices/settingSlice.js |
Name collision — getAllSettings/getSettingByGroup exist as both Redux selectors AND API hook names |
| H4 | HIGH | interceptors/axiosInstance.js |
Hard window.location.href = "/login" redirect on logout — destroys all client state; not a React transition |
| H5 | HIGH | interceptors/axiosInstance.js |
Dynamic import in error handler — import('@/store') in catch block; if this fails, error is silently swallowed |
| H6 | HIGH | interceptors/axiosInstance.js |
Concurrent 401 race condition — multiple simultaneous 401 responses trigger parallel logouts |
| H7 | HIGH | hooks/useCheckPermission.js |
Fail-open design — returns true when requiredPermissions is empty or undefined |
| H8 | HIGH | hooks/useFormSubmit.js |
Brittle error detection — checks error messages with string.includes("validation") |
| H9 | HIGH | hooks/useFormSubmit.js |
Unconditional useRouter() — always called even when redirect not needed |
| H10 | HIGH | hooks/useMediaQuery.jsx |
SSR crash risk — window.matchMedia called without SSR guard |
| H11 | HIGH | hooks/useRedux.js |
100% dead code — typed Redux hooks wrapper with zero imports anywhere |
| H12 | HIGH | lib/pricing-data.js |
All pricing hardcoded in frontend — should come from backend/Stripe |
| H13 | HIGH | lib/uploadFile.js |
Redundant if/else — video and image branches are 100% identical code |
| H14 | HIGH | lib/utils.js |
handleDownloadJson is dead code — never imported anywhere |
| H15 | HIGH | hocs/createIcon.js |
Missing Next.js Image width/height or fill prop — build warning/error risk |
| H16 | HIGH | dashboard/layout.js |
isOpen localStorage parsing bug — !!"false" evaluates to true, sidebar can never persist as closed |
| H17 | HIGH | store/slices/authSlice.js |
initializeAuth doesn't fetch user profile — state.user stays null until useProfile() runs |
| H18 | HIGH | theme-provider.jsx, theme-context.jsx |
Google Font loading logic duplicated in both files |
| H19 | HIGH | services/authService.js |
Architectural anomaly — only module using services/ abstraction; all 30+ other modules call axiosInstance directly |
| H20 | HIGH | services/authService.js |
getProfile() returns response.data.data while others return response.data — inconsistent response shape |
| H21 | HIGH | api/menu-builder/menu-label.js |
Doubles base URL — constructs ${NEXT_PUBLIC_API_URL}/menu-labels but axiosInstance already has baseURL set |
| H22 | HIGH | 14 files across api/ |
keepPreviousData: true is deprecated in React Query v5 — should use placeholderData: keepPreviousData |
Known Issues - Systemic (8)¶
| ID | Issue | Scope |
|---|---|---|
| SYS1 | Zero useMemo/useCallback across all 10 custom hooks — every returned function recreated every render |
All hooks/ |
| SYS2 | 9 of 10 hooks missing 'use client' directive — rely on parent component's directive |
All hooks/ |
| SYS3 | Inconsistent error handling — some hooks swallow, some throw, some return error objects | All hooks/ |
| SYS4 | 4 coexisting state management approaches (Redux, React Query, Context, localStorage) with weak coordination | Entire frontend |
| SYS5 | Settings triple-cached — ISR (24h), React Query (1h staleTime), Redux (on component mount) — can desync | Settings flow |
| SYS6 | Zero test coverage — no test files exist for any infrastructure file | All infra |
| SYS7 | 13+ dead exports across 6 files — functions exported but never imported | lib/, services/, hooks/ |
| SYS8 | Inconsistent extension conventions — .jsx vs .js for files containing JSX |
hooks/, providers/ |
Complete File Inventory¶
1. Store — Redux State Management (3 files, ~120 LOC)¶
store/index.js¶
Purpose: Configures Redux store with Redux Toolkit. Single configureStore call combining auth and setting slices.
Exports: store (configured Redux store), RootState type (implicit)
Dependencies: @reduxjs/toolkit, ./slices/authSlice, ./slices/settingSlice
Used By: components/providers/redux-provider.jsx, interceptors/axiosInstance.js (dynamic import)
Key Detail: No custom middleware, no Redux DevTools configuration, no persistence.
Contributor Note: Adding a new slice requires import here + adding to the reducer map. No middleware pipeline to worry about.
store/slices/authSlice.js¶
Purpose: Manages authentication state — token presence, user object, authenticated flag. Includes initializeAuth (reads cookie) and logout (deletes cookie + clears state).
Exports: authSlice, initializeAuth, setUser, setToken, logout, selectAuth, selectUser (implicit via useSelector)
State Shape: { isAuthenticated: boolean, user: object|null, token: string|null }
Dependencies: @reduxjs/toolkit, cookies-next (getCookie, deleteCookie)
Used By: 9 direct importers + 10 useSelector(state => state.auth) consumers
Issues: C1, C16, H1, H2, H17 — side effects in reducers, user null gap, dead token field
Contributor Note: initializeAuth only checks cookie existence — it does NOT fetch user profile. The user object is populated later by useProfile() in dashboard layout. There is a temporal gap where isAuthenticated=true but user=null.
store/slices/settingSlice.js¶
Purpose: Stores global site settings (logo, site name, etc.) fetched from backend. Simple set/get pattern.
Exports: settingSlice, setSettings, getAllSettings, getSettingByGroup
State Shape: { settings: Array<{key, value, group}> }
Dependencies: @reduxjs/toolkit
Used By: 5 importers (root layout, dashboard layout, theme components)
Issues: H3 (name collision with API hooks), C9 (dual dispatch), settinReducer typo in store config
Contributor Note: Settings are dispatched from TWO places — root layout (server fetch + dispatch) and dashboard layout (React Query + dispatch). These can race.
2. Interceptors — HTTP Layer (1 file, ~50 LOC)¶
interceptors/axiosInstance.js¶
Purpose: Central Axios instance with baseURL from env, request interceptor (attaches JWT from cookie), response error interceptor (handles 401/403).
Exports: axiosInstance (default export)
Dependencies: axios, cookies-next, @/store (dynamic import in error handler)
Used By: 35 active consumers (all api/ hook modules)
Issues: C3, C4, H4, H5, H6 — 403 logout, no refresh, hard redirect, dynamic import in error, race condition
Contributor Note: This is the single most impactful infrastructure file — 35 modules depend on it. The error interceptor dynamically imports the store to dispatch logout, avoiding a circular dependency. The JWT cookie is non-httpOnly (documented security concern in project memory). No request queuing during token refresh (because refresh doesn't exist).
3. Custom Hooks (10 files, ~330 LOC)¶
hooks/useCheckPermission.js¶
Purpose: Checks if current user has required permission(s). Reads user.role.permissions from Redux.
Exports: useCheckPermission(requiredPermissions: string[]): boolean
Used By: 14 files (permission-wrapper, nav-permission-wrapper, page components)
Issues: H7 (fail-open), C16 (split-brain after refresh — reads from Redux where user is null)
Contributor Note: This hook returns true when requiredPermissions is empty — any component without explicit permissions is accessible to everyone. After page refresh, it reads user from Redux (null) not React Query (has data), so all permission checks fail until profile re-fetches.
hooks/useCheckSlug.js¶
Purpose: Validates slug uniqueness against backend API with debounce.
Exports: useCheckSlug(slug, type): { isAvailable, isChecking }
Used By: 5 files (blog, page, product forms)
Issues: M — no error handling for API failure, returns undefined on error
hooks/useClientPathName.jsx¶
Purpose: Returns current pathname on client side using Next.js usePathname.
Exports: useClientPathName(): string
Used By: 3 files (layout components for active link highlighting)
Issues: L — trivially thin wrapper, could be replaced by direct usePathname() call
hooks/useDashboardData.js¶
Purpose: Fetches dashboard analytics data via React Query.
Exports: useDashboardData(): { data, isLoading, isError }
Used By: 2 files (dashboard page, welcome section)
Issues: C10 — try/catch in queryFn swallows all errors; isError is always false
hooks/useDebounce.jsx¶
Purpose: Debounces a value by specified delay using setTimeout.
Exports: useDebounce(value, delay): debouncedValue
Used By: 16 files (most-consumed hook — search inputs, slug checks, filters)
Issues: L — no cleanup race condition guard, but generally correct pattern
hooks/useFormSubmit.js¶
Purpose: Generic form submission handler with toast notifications and optional redirect.
Exports: useFormSubmit(mutationFn, options): { handleSubmit, isLoading }
Used By: 4 files
Issues: H8 (brittle string matching for validation errors), H9 (unconditional useRouter)
hooks/useMediaQuery.jsx¶
Purpose: Evaluates CSS media query and returns boolean match.
Exports: useMediaQuery(query): boolean
Used By: 3 files (responsive layout decisions)
Issues: H10 — window.matchMedia without SSR guard; will throw during server render
hooks/useMenus.js¶
Purpose: CRUD operations for menus via React Query mutations.
Exports: useMenus, useMenu, useCreateMenu, useUpdateMenu, useDeleteMenu, useCreateMenuItem, useUpdateMenuItem, useDeleteMenuItem, useReorderMenuItems
Used By: 0 files — COMPLETELY DEAD CODE (confirmed via grep)
Issues: C14 — 96 LOC of dead code. Consumers use api/menu/ hooks instead.
Contributor Note: This hook uses the dead services/menuService.js and services/menuItemService.js. The live menu system uses api/menu/index.js and api/menu/menu-item.js directly.
hooks/useRedux.js¶
Purpose: Typed wrappers for useSelector and useDispatch.
Exports: useAppSelector, useAppDispatch
Used By: 0 files — COMPLETELY DEAD CODE
Issues: H11 — zero consumers. All components use useSelector/useDispatch directly.
hooks/useTableActions.js¶
Purpose: Reusable CRUD action handlers for data tables (delete, soft-delete, restore, bulk operations).
Exports: useTableActions(config): { handleDelete, handleRestore, handleBulkDelete, ... }
Used By: 9 files (all dashboard table pages)
Issues: M — no memoization on returned handlers; new function objects every render
4. Providers & Context (6 files, ~310 LOC)¶
components/providers/auth-initializer.jsx¶
Purpose: Runs initializeAuth() on mount to read JWT cookie and set Redux auth state.
Exports: AuthInitializer (wraps children)
Dependencies: Redux initializeAuth action
Used By: app/layout.js (root layout)
Issues: H17 — only reads cookie, doesn't fetch user profile. Temporal gap where authenticated but user=null.
components/providers/protected-route.jsx¶
Purpose: Client-side route guard checking isAuthenticated from Redux.
Exports: ProtectedRoute (wraps children, redirects if unauthenticated)
Used By: ZERO FILES — 100% DEAD CODE
Issues: C6 — never imported anywhere. Route protection is handled entirely by middleware.js.
Contributor Note: This file gives a false sense of security in the codebase. Delete it or wire it into the dashboard layout.
components/providers/query-provider.jsx¶
Purpose: Wraps app in QueryClientProvider with configured React Query client.
Exports: QueryProvider (wraps children)
Configuration: staleTime: 5 * 60 * 1000 (5 min), retry: 1, refetchOnWindowFocus: false
Used By: app/layout.js (root layout)
Issues: M — keepPreviousData deprecated in RQ v5 (but this file is fine; the issue is in consumers)
components/providers/redux-provider.jsx¶
Purpose: Wraps app in Redux Provider with store.
Exports: ReduxProvider (wraps children)
Dependencies: react-redux, @/store
Used By: app/layout.js (root layout)
Issues: None — minimal correct implementation
components/providers/theme-provider.jsx¶
Purpose: Integrates next-themes ThemeProvider + custom ThemeContext initialization + Google Font loading.
Exports: ThemeProvider (wraps children)
Dependencies: next-themes, @/context/theme-context, Redux settings
Used By: app/layout.js (root layout)
Issues: C7 (theme desync), C8 (console.log in production), H18 (duplicate font loading logic)
Contributor Note: This is the most complex provider. It fires a CustomEvent('theme-loaded') on mount that ThemeContext listens to, but post-mount theme changes via next-themes never fire this event. Components using useThemeContext().theme see stale data after user toggles theme.
context/theme-context.jsx¶
Purpose: Custom React Context storing theme state (mode, primary color, font family) with localStorage persistence.
Exports: ThemeContext, ThemeProvider (context provider), useThemeContext()
Used By: 8 files (theme controls, chart components for theming)
Issues: C7 (desync with next-themes), H18 (duplicate font logic)
Contributor Note: This context is a SECOND theme system alongside next-themes. The intent was to extend next-themes with custom properties (primary color, font), but the two systems don't stay synchronized.
5. Middleware (1 file, ~50 LOC)¶
middleware.js¶
Purpose: Next.js edge middleware — checks for token cookie on protected routes, redirects unauthenticated users to /login.
Exports: middleware function, config (route matcher)
Protected Routes: /dashboard/* paths
Public Routes: /, /login, /register, /forgot-password, /blogs/*, etc.
Issues: C2 — only checks request.cookies.get('token') existence. Does NOT validate JWT signature, expiry, or structure.
Contributor Note: This is the ONLY active route protection mechanism (since protected-route.jsx is dead code). It runs on the edge, so it can't do full JWT validation without a secret. Consider calling a lightweight /auth/verify endpoint or using jose for edge-compatible JWT validation.
6. Root Layout & App Router Layouts (4 files, ~200 LOC)¶
app/layout.js¶
Purpose: Root layout — wraps entire app in provider hierarchy. Fetches settings server-side and passes to Redux.
Provider Nesting Order (outermost → innermost):
1. ReduxProvider
2. QueryProvider
3. AuthInitializer
4. ThemeProvider
5. {children}
Issues: C9 — dispatches settings to Redux from server-side fetch. Dashboard layout ALSO dispatches settings from React Query. Contributor Note: Provider order matters for dependencies. Redux must be outermost (Query hooks need it). Auth must be before Theme (theme reads auth state). This order is correct.
app/(auth)/layout.js¶
Purpose: Auth pages layout (login, register, forgot-password). Minimal wrapper.
app/(main-layout)/layout.js¶
Purpose: Public pages layout (landing, blogs, contact). Includes header/footer.
app/(dashboard-layout)/dashboard/layout.js¶
Purpose: Dashboard layout — fetches user profile, navigation links, settings via React Query. Renders sidebar + header + content.
Issues: H16 (isOpen localStorage parsing bug), C9 (re-dispatches settings to Redux)
Contributor Note: This layout makes 3 parallel API calls on mount: useProfile(), useNavigationLinks(), useSettings(). All must succeed for the dashboard to render. No error boundaries exist.
7. Lib Utilities (10 files, ~600 LOC)¶
lib/utils.js¶
Purpose: General utilities — cn() (className merger), formatText(), generateAvatar(), getIconComponent(), handleDownloadJson().
Used By: 69 files (most-imported utility file — primarily for cn())
Issues: C13 (full Lucide import), H14 (handleDownloadJson dead), iconNameToPascalCase exported but only used internally
Contributor Note: cn() uses clsx + tailwind-merge. This is the standard shadcn pattern. The Lucide wildcard import is the #1 bundle size issue.
lib/helper.js¶
Purpose: Data table helpers — getSortIcon(), handleMasterCheckboxChange(), getMasterCheckboxState().
Used By: 7 files (all table pages)
Issues: M — permission logic inconsistency between handleMasterCheckboxChange and getMasterCheckboxState
lib/uploadFile.js¶
Purpose: File upload utility — sends file to backend via fetch().
Used By: 4 files (image upload components)
Issues: C11 (no validation), C12 (no auth), H13 (redundant if/else)
Contributor Note: This bypasses the entire axios infrastructure. It uses raw fetch() with no token, no error handling, no file type/size checks.
lib/editor.js¶
Purpose: BlockNote rich text editor configuration — custom slash menu items, toolbar groups, block types. Used By: 3 files (blog editor components) Issues: M — several unused imports from BlockNote packages
lib/icons.js¶
Purpose: Icon mapping — maps string icon names to Lucide React components using createIcon HOC.
Used By: 5 files (sidebar, menu builder)
Issues: L — relies on createIcon HOC which has H15 issue
lib/constant.js¶
Purpose: Global constants — API base URL, asset paths, default values. Used By: 8 files Issues: L — minimal file, no significant issues
lib/customMenuItems.js¶
Purpose: Custom BlockNote editor menu items (e.g., "Insert Image", "Insert Video").
Used By: 1 file (lib/editor.js)
Issues: M — several unused imports
lib/dummyMenuData.js¶
Purpose: Mock menu data — hardcoded nested menu tree structure. Used By: ZERO FILES — DEAD CODE (282 LOC) Issues: C14 — completely unused. Uses MongoDB-style ObjectIds despite PostgreSQL backend.
lib/menuHelpers.js¶
Purpose: Tree manipulation utilities — flattenTree(), buildTree(), findNode(), moveNode().
Used By: 4 files (menu builder components)
Issues: L — correct tree utilities, well-structured
lib/pricing-data.js¶
Purpose: Hardcoded pricing plans — 3 tiers with features, prices, billing cycles.
Used By: 2 files (pricing section, package page)
Issues: H12 — pricing should come from backend, not hardcoded in frontend. Uses "annually" while constants/package.js uses "yearly".
8. Services (3 files, ~280 LOC)¶
services/authService.js¶
Purpose: Auth API abstraction — login(), register(), logout(), getProfile(), verifyEmail().
Used By: api/auth/index.js (sole consumer)
Issues: H19 (only service file used), H20 (inconsistent response shape)
Contributor Note: This is the only active service file. It's consumed by api/auth/index.js which wraps it in React Query hooks. Every other module (30+) skips the service layer and calls axiosInstance directly.
services/menuService.js¶
Purpose: Menu CRUD operations via dummy data (not API calls).
Used By: ZERO FILES — DEAD CODE (94 LOC)
Issues: C14 — returns hardcoded data from dummyMenuData.js, never calls backend
services/menuItemService.js¶
Purpose: Menu item CRUD operations via dummy data. Used By: ZERO FILES — DEAD CODE (145 LOC) Issues: C14 — returns hardcoded data, never calls backend
9. Server Actions (2 files, ~40 LOC)¶
actions/revalidate.js¶
Purpose: Server action to revalidate Next.js ISR cache tags via revalidateTag().
Exports: revalidate(tag), revalidateBulk(tags), revalidateAll()
Used By: 10 files (all CRUD operations that need cache invalidation)
Issues: L — revalidateAll() hardcodes a list of known tags; new features require manual addition
actions/navigation-links.js¶
Purpose: Server action to revalidate the navigation-links cache tag specifically.
Exports: revalidateNavigationLinks()
Used By: 2 files
Issues: M — redundant with revalidate('navigation-links') from revalidate.js
10. HOCs (1 file, ~25 LOC)¶
hocs/createIcon.js¶
Purpose: Higher-order component that wraps an image source into a Next.js Image component with standardized props.
Exports: createIcon(src): React.Component
Used By: 1 file (lib/icons.js)
Issues: H15 — missing width/height or fill prop on Next.js Image component
11. Constants (3 files, ~250 LOC)¶
constants/landing-page-data.js¶
Purpose: Static data for landing page sections — hero text, features, testimonials, pricing plans, stats, FAQ.
Used By: 5 files (landing page section components)
Issues: M — contains gaming-template remnants ("Total Games Listed" stat, game categories). pricingPlans array is completely unused (superseded by lib/pricing-data.js).
constants/metadata.js¶
Purpose: Default Next.js metadata — title, description, OpenGraph, categories. Used By: 3 files Issues: M — contains gaming categories ("Action", "Adventure", "Strategy") in a SaaS boilerplate
constants/package.js¶
Purpose: Package/subscription constants — billing cycle options, plan type mappings.
Used By: 2 files (package pages)
Issues: M — uses "yearly" while lib/pricing-data.js uses "annually"
User Flow Analysis¶
Flow 1: App Bootstrap¶
Browser loads any page
→ Next.js SSR: middleware.js checks cookie existence
→ If /dashboard/* and no cookie → redirect /login
→ If cookie exists (any value) → allow through
→ Server render: app/layout.js
→ Fetches settings via server-side fetch (ISR cached 24h)
→ Passes settings to client
→ Client hydration:
→ ReduxProvider wraps app
→ QueryProvider configures React Query (5min staleTime)
→ AuthInitializer runs initializeAuth()
→ Reads 'token' cookie via getCookie()
→ Sets isAuthenticated=true, token=cookieValue
→ Does NOT fetch user profile (user remains null)
→ ThemeProvider initializes
→ Reads settings from Redux
→ Fires CustomEvent('theme-loaded')
→ ThemeContext picks up event, stores theme config
→ Page component renders
Gap: Between AuthInitializer completing and useProfile() running in dashboard layout, state.auth.user is null. Any permission check during this window fails.
Flow 2: Login → Dashboard¶
User submits login form
→ api/auth/useLogin mutation fires
→ authService.login(credentials) → POST /auth/login
→ On success:
→ Sets JWT cookie (non-httpOnly)
→ Dispatches setUser(user) to Redux
→ Dispatches setToken(token) to Redux
→ React Query cache: queryClient.setQueryData(['profile'], user)
→ window.location.href = '/dashboard' (FULL PAGE RELOAD)
→ Full reload triggers:
→ middleware.js: cookie exists → allow
→ app/layout.js: server-side settings fetch
→ AuthInitializer: reads cookie → isAuthenticated=true, user=null
→ dashboard/layout.js:
→ useProfile() → GET /auth/profile → populates user
→ useNavigationLinks() → fetches nav
→ useSettings() → fetches settings, RE-DISPATCHES to Redux
→ Dashboard renders with all data
Issue: Login sets user in Redux, then immediately does window.location.href which destroys that state. The dashboard re-fetches everything from scratch. The setUser dispatch before reload is wasted work.
Flow 3: Permission Check¶
Component wrapped in <PermissionWrapper requiredPermissions={['user.view']}>
→ useCheckPermission(['user.view']) called
→ Reads state.auth.user from Redux
→ If user is null (after refresh): returns false → component hidden
→ If user exists: checks user.role.permissions.includes('user.view')
→ Returns boolean
→ PermissionWrapper renders children or nothing
AFTER PAGE REFRESH:
→ AuthInitializer sets isAuthenticated=true, user=null
→ Permission checks all return false
→ Entire dashboard UI is permission-gated → shows empty
→ useProfile() fires → user populated → re-render → UI appears
→ FLASH OF EMPTY CONTENT
Critical Bug (C16): Permission checks depend on Redux user which is null after refresh. React Query has the profile data (via useProfile), but useCheckPermission reads from Redux. This creates a split-brain where the auth system says "authenticated" but the permission system says "no access."
Flow 4: Theme Switch¶
User clicks dark mode toggle (DarkModeControl component)
→ Uses next-themes setTheme('dark')
→ next-themes updates:
→ document.documentElement class
→ localStorage
→ BUT: ThemeContext is NOT notified
→ CustomEvent('theme-loaded') was only fired on mount
→ ThemeContext still has stale theme value
→ Components using useThemeContext().theme see stale data
→ Components using next-themes useTheme() see correct data
→ Chart components (using ThemeContext for chart colors) show wrong colors
Issue (C7): Two theme systems exist — next-themes (the standard) and ThemeContext (the custom extension). They don't stay synchronized after initial mount.
Flow 5: Data Fetching¶
Component needs data:
→ Uses React Query hook from api/ (e.g., useUsers())
→ Hook calls axiosInstance.get('/users')
→ axiosInstance request interceptor:
→ Reads 'token' cookie
→ Attaches as Authorization: Bearer header
→ Response:
→ 200: data returned, cached by React Query (5min stale)
→ 401: interceptor catches
→ Dynamic import('@/store')
→ Dispatches logout()
→ Deletes cookie
→ window.location.href = '/login'
→ NO TOKEN REFRESH ATTEMPT
→ 403: interceptor catches
→ SAME AS 401 — full logout (BUG: C3)
→ Other errors: thrown to React Query error boundary
Issue: There is no token refresh flow. The useRefreshToken hook exists in api/auth/index.js but has zero consumers. The authService.refreshToken endpoint doesn't even send a refresh token in the request body (C5).
Dependency Graph¶
Provider Nesting Order (verified correct)¶
<ReduxProvider> ← Redux store available everywhere
<QueryProvider> ← React Query available everywhere
<AuthInitializer> ← Reads cookie, sets Redux auth state
<ThemeProvider> ← Reads Redux settings for theme config
{children} ← App content
</ThemeProvider>
</AuthInitializer>
</QueryProvider>
</ReduxProvider>
Entry Points (not imported by other infra files)¶
middleware.js— Next.js edge middleware (entry point for server-side routing)app/layout.js— Root layout (entry point for React tree)actions/revalidate.js— Server actions (entry point for ISR invalidation)actions/navigation-links.js— Server action
Leaf Nodes (don't import other infra files)¶
hooks/useDebounce.jsx— Pure utility, no infra dependencieshooks/useMediaQuery.jsx— Pure utilityhooks/useClientPathName.jsx— Only depends on Next.jslib/menuHelpers.js— Pure tree utilitieslib/dummyMenuData.js— Static data (dead code)constants/*— All static data files
Key Dependency Chains¶
axiosInstance → (used by) → api/* hooks → (used by) → page components
↑ |
| ↓
← ← ← ← ← store/authSlice (dynamic import on error) ← ←
theme-context ← ← theme-provider → next-themes
↓ ↓
Redux settings DOM + localStorage
Circular Dependencies¶
- No actual circular dependencies detected
axiosInstance.js→store/index.jsuses dynamicimport()to break the potential cycle- Provider nesting order avoids all dependency conflicts
Dead Code Inventory¶
| File | LOC | Evidence | Action |
|---|---|---|---|
services/menuService.js |
94 | 0 imports (grep verified) | Delete |
services/menuItemService.js |
145 | 0 imports (grep verified) | Delete |
lib/dummyMenuData.js |
282 | 0 imports (grep verified) | Delete |
hooks/useMenus.js |
96 | 0 imports (grep verified) | Delete |
hooks/useRedux.js |
~20 | 0 imports (grep verified) | Delete |
components/providers/protected-route.jsx |
~40 | 0 imports (grep verified) | Delete or wire in |
lib/utils.js → handleDownloadJson |
~15 | 0 imports of this function | Remove export |
lib/utils.js → iconNameToPascalCase |
~5 | Only used internally | Remove export |
constants/landing-page-data.js → pricingPlans |
~30 | Superseded by lib/pricing-data.js |
Remove |
| Total Dead Code | ~727 LOC |
Testing Analysis¶
Test Coverage: 0%¶
No test files exist for any frontend infrastructure file. Zero statements, branches, functions, or lines covered.
Testing Gaps (Priority Order)¶
interceptors/axiosInstance.js— Most critical to test: token attachment, 401/403 handling, concurrent request behaviorhooks/useCheckPermission.js— Security-critical: test fail-open behavior, null user handling, permission matchingstore/slices/authSlice.js— Test reducers (side effect behavior),initializeAuth,logoutmiddleware.js— Test route protection: cookie present/absent, protected vs public routeshooks/useFormSubmit.js— Test error parsing, success/failure flows, redirect behaviorlib/uploadFile.js— Test file validation (once added), auth token attachment (once added)context/theme-context.jsx— Test theme switching, localStorage persistence, event handlingactions/revalidate.js— Test tag invalidation, bulk invalidation
Suggested Test Stack¶
- Unit tests: Vitest + React Testing Library (matches Next.js 15 ecosystem)
- Hook tests:
@testing-library/react-hooksorrenderHookfrom RTL - Integration tests: MSW (Mock Service Worker) for API mocking
- E2E: Playwright (already available as MCP plugin)
Architecture & Design Patterns¶
State Management Strategy (4 Systems)¶
| System | Purpose | Scope | Sync Mechanism |
|---|---|---|---|
| Redux (Toolkit) | Auth state, settings | Global, persists across routes | dispatch() from providers/layouts |
| React Query | Server data cache | Per-query, 5min stale | Automatic refetch on stale/focus |
| React Context | Theme configuration | Global | CustomEvent (fragile) |
| localStorage | Theme persistence, sidebar state | Browser-local | Manual read/write |
Conflict: Settings exist in all 4 systems simultaneously. Auth user exists in Redux AND React Query. Theme exists in Context AND localStorage AND next-themes (DOM).
Error Handling Philosophy¶
Inconsistent. Three patterns coexist:
1. Swallow silently — useDashboardData try/catch, BrevoService (backend)
2. Throw to boundary — React Query default for most api/ hooks
3. Intercept and redirect — axiosInstance catches 401/403 globally
No error boundaries exist anywhere in the frontend. API failures in layout-level fetches crash the entire page tree.
Code Organization¶
The frontend follows a feature-by-type organization:
- api/ — React Query hooks grouped by backend module
- hooks/ — Reusable custom hooks (flat directory)
- components/ — UI (by type: ui/, custom/, pages/, providers/, layout/, editor/)
- lib/ — Utility functions (flat directory)
- services/ — Mostly dead; only authService active
- store/ — Redux slices
- constants/ — Static data
- actions/ — Server actions
Related Code & Reuse Opportunities¶
Duplicate Functionality¶
| Pattern | Location 1 | Location 2 | Resolution |
|---|---|---|---|
| Menu CRUD hooks | api/menu/ |
api/menu-builder/ |
Delete menu-builder (9/11 hooks broken) |
| Theme font loading | theme-provider.jsx |
theme-context.jsx |
Consolidate to one location |
| Settings dispatch | app/layout.js |
dashboard/layout.js |
Remove one; root layout covers all |
| Pricing data | lib/pricing-data.js |
constants/landing-page-data.js |
Delete from constants |
| Permission check | hooks/useCheckPermission.js |
lib/helper.js (checkbox perms) |
Different concerns; keep both |
Best-Practice Reference Files¶
hooks/useMenus.js— Despite being dead code, uses query key factory pattern correctly with proper cache invalidation. Template for other CRUD hooks.components/providers/redux-provider.jsx— Minimal correct provider implementation. Template for new providers.actions/revalidate.js— Clean server action pattern with proper'use server'directive.
Modification Guidance¶
To Add New Functionality¶
Adding a new Redux slice:
1. Create slice in store/slices/newSlice.js
2. Import and add to reducer map in store/index.js
3. Export selectors and actions from slice file
4. Use useSelector/useDispatch directly (not useRedux.js)
Adding a new custom hook:
1. Create in hooks/useNewHook.js
2. Add 'use client' directive at top
3. Use useCallback for returned functions
4. Follow useMenus.js pattern for React Query hooks
5. Handle error states explicitly (don't swallow)
Adding a new provider:
1. Create in components/providers/new-provider.jsx
2. Add to provider nesting in app/layout.js
3. Respect dependency order (Redux → Query → Auth → Theme → new)
To Modify Existing Functionality¶
Fixing the auth flow:
1. Implement token refresh in axiosInstance.js response interceptor
2. Call useRefreshToken API (fix to actually send refresh token)
3. Queue concurrent 401s to prevent parallel logouts
4. Change 403 handling to show error, not logout
Fixing the theme system:
1. Remove ThemeContext custom event bridge
2. Have ThemeContext read directly from next-themes via useTheme()
3. Or: fire CustomEvent on every theme change, not just mount
To Remove/Deprecate¶
Safe deletions (zero consumers):
- services/menuService.js — delete
- services/menuItemService.js — delete
- lib/dummyMenuData.js — delete
- hooks/useMenus.js — delete
- hooks/useRedux.js — delete
- components/providers/protected-route.jsx — delete
Requires migration first:
- services/authService.js — inline into api/auth/index.js, then delete services/
- lib/pricing-data.js — replace with backend API, then delete
- actions/navigation-links.js — replace with revalidate('navigation-links'), then delete
Testing Checklist for Changes¶
- Verify auth state after page refresh (user should not be null)
- Verify permission-gated UI appears immediately (no flash of empty)
- Verify theme toggle updates ALL theme-dependent components
- Verify 403 response shows error message, not logout
- Verify file upload sends auth token and validates file type
- Verify no console.log in production build
- Verify bundle size doesn't include full Lucide library
- Verify settings are consistent across Redux, React Query, and ISR cache
- Verify sidebar open/close state persists across page loads
- Verify dashboard shows error state when API fails (not empty)
Contributor Checklist¶
Risks & Gotchas¶
axiosInstanceis imported by 35 modules — changes here affect the entire app- Redux
authSlicehas side effects in reducers — don't assume pure reducers middleware.jsruns on the edge — no Node.js APIs, no secrets accesslib/utils.jsis imported by 69 files — bundle impact of any addition is high- Theme system has two independent implementations that must stay in sync
- Settings are dispatched from 2 places — changes to settings shape affect both
Pre-change Verification Steps¶
grep -r "from.*<module>" src/to find all consumers before modifying any infra file- Check both
api/menu/ANDapi/menu-builder/when touching menu functionality - Verify provider nesting order if adding/removing providers
- Test with cleared localStorage and cookies to catch initialization bugs
- Test both authenticated and unauthenticated states
Suggested Tests Before PR¶
- Login → navigate dashboard → refresh page → verify UI renders correctly
- Let JWT expire → make API call → verify graceful handling (currently: forced logout)
- Toggle theme → navigate pages → verify theme persists
- Upload file → verify it's authenticated and validated
- Remove permissions from user → verify 403 shows error (not logout)
Generated by document-project workflow (deep-dive mode)
Base Documentation: docs/index.md
Scan Date: 2026-03-26
Analysis Mode: Exhaustive
Subagent Reports: 6 parallel analysis agents + 1 discovery agent