Files
nick-doc/01 - Architecture/Frontend Architecture.md
Siavash Sameni dceaf82934 audit: 2026-05-30 full-codebase audit — report, issues, docs, runbooks
Full-codebase-audit 2026-05-30 outputs:
- Audit report: 09 - Audits/Full Codebase Audit - 2026-05-30.md
- 81 issue files ISSUE-055..135 (decisions + 1 skipped no-brainer).
- Scanner docs from scratch (was zero): architecture, data model, API ref, payment
  flow, operations runbook + repo README.
- Doc-sync updates across API reference, data models, flows, design system.
- Secret Rotation Runbook (08 - Operations) for the exposed credentials.
- Reusable workflow guide (07 - Development) + .claude/workflows/full-codebase-audit.js.

Issues remain status:open intentionally — the code fixes are uncommitted-then-committed
working-tree changes per repo and aren't "resolved" until merged/deployed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 18:48:04 +04:00

14 KiB

title, tags, created
title tags created
Frontend Architecture
architecture
frontend
nextjs
2026-05-23

Frontend Architecture

Module-level architecture of the Next.js 16 (App Router) + TypeScript + MUI v7 frontend at /Users/mojtabaheidari/code/frontend (development branch).

[!info] Repo: git@git.manko.yoga:222/nick/frontend.git · Branch: development · Version: 1.9.6 (package.json:4) · Dev port 3000, Docker port 8083.


1. Folder tree

frontend/src/
├── app/                       # Next.js 16 App Router (server + client islands)
│   ├── layout.tsx             # Root layout — providers, fonts, Sentry
│   ├── (public)/              # Unauthenticated routes
│   │   ├── post/[slug]/       # Blog reader
│   │   └── shop/[seller]/[id] # Public seller / item view
│   ├── auth/jwt/              # sign-in, sign-up, verify, reset, update
│   ├── dashboard/             # AuthGuard + EmailVerificationGuard
│   │   ├── overview/          # KPI / home tiles
│   │   ├── chat/              # Real-time chat
│   │   ├── account/           # Profile, address, notifications, wallet, passkey
│   │   ├── request/           # Buyer purchase requests
│   │   ├── request-template/  # Seller request templates
│   │   ├── payment/           # Payment history / detail
│   │   ├── points/            # Loyalty hub (transactions, referrals, levels)
│   │   ├── disputes/          # Dispute hub
│   │   ├── user/              # Admin user management
│   │   ├── post/              # Admin blog editor
│   │   ├── shop-settings/     # Seller shop config
│   │   └── shops/             # Browse / checkout (dashboard scope)
│   ├── error/                 # Global error page
│   └── not-found.tsx          # 404
├── sections/                  # Page-specific composition modules (one folder per feature)
│   └── (chat|payment|request|request-template|dispute|user|points|...)
├── components/                # Reusable UI primitives (hook-form, table, upload, editor, ...)
├── layouts/                   # Page-template wrappers (auth-centered, auth-split, dashboard, main)
├── theme/                     # MUI theme creation, palette, typography, overrides
├── settings/                  # Settings drawer (mode, layout, direction, color, font)
├── contexts/                  # React Context providers (socket-context)
├── hooks/                     # 50+ custom hooks (use-boolean, use-socket, use-web3-wagmi, …)
├── lib/                       # Cross-cutting libs (axios.ts with interceptors)
├── locales/                   # i18next config + langs/{en,fa,ar,fr,cn,vi}/*.json
└── types/                     # Cross-app TypeScript types

2. Rendering strategy

  • App Router with output: 'standalone' in next.config.ts for production single-binary serving.
  • Server components by default, client components opted into with "use client". Heavy interactive pages (chat, editor, dashboard) are mostly client-side; SEO routes (shop/, post/[slug]) leverage server rendering.
  • Turbopack in dev (next dev --turbopack).
  • Streaming via loading.tsx and Suspense boundaries.
  • MUI cache is wired up via @mui/material-nextjs in the root layout to avoid FOUC across server/client boundary.

3. Provider tree (root layout)

flowchart TB
    A[RootLayout]
    A --> B[AppRouterCacheProvider<br/>MUI emotion cache]
    B --> C[ThemeProvider<br/>theme + RTL stylis]
    C --> D[LocalizationProvider<br/>dayjs adapter]
    D --> E[I18nProvider<br/>i18next]
    E --> F[QueryClientProvider<br/>React Query]
    F --> G[SocketProvider<br/>Socket.IO context]
    G --> H[SnackbarProvider<br/>notistack]
    H --> I[Children — routes]

Order matters: theme must wrap query (because mutations show snackbars styled by theme); socket wraps snackbar (so socket-driven notifications can fire snackbars).


4. Route layout & guards

Route group Layout Guard chain
(public) layouts/main none
auth/jwt/* layouts/auth-centered or auth-split redirect if already authed
dashboard/* layouts/dashboard (sidebar + topbar + breadcrumbs) AuthGuardEmailVerificationGuard
dashboard/user/* dashboard + role: admin
dashboard/post/* (editor) dashboard + role: admin
dashboard/shop-settings/* dashboard + role: seller

Guards live in frontend/src/auth/ (HOC + hook). They consult the JWT-derived user context and redirect unauthenticated to /auth/jwt/sign-in?returnTo=....


5. Sections vs components vs hooks

The codebase enforces a three-layer split:

Layer Lives in Purpose Example
Section src/sections/<feature>/ Page-specific composition; orchestrates components sections/chat/view/ChatView.tsx
Component src/components/<name>/ Reusable across sections components/hook-form/RHFTextField.tsx
Hook src/hooks/<name>.ts Encapsulated stateful behavior hooks/use-chat-socket.ts

Tip

Per the cursor rules (backend/.cursor/rules/ui-development-standards.mdc), every component folder has index.ts (barrel export), <name>.tsx (component), classes.ts (styled-component class names), types.ts (interface). Following this layout keeps each component refactorable.


6. State management

The frontend deliberately uses three state mechanisms, each for one concern:

Concern Tool Where
Server data React Query every useXxxQuery / useXxxMutation hook
Cross-page shared state React Context contexts/socket-context.tsx, settings context, auth context
Per-component / local UI useState, useReducer, use-boolean, use-set-state inside components

No Redux, no MobX, no Recoil. The cursor rules also mention Zustand as the preferred client store if one is added, but the dev branch currently relies on React Query + Context.

React Query setup

  • QueryClient created once at the root layout with defaultOptions:
    • queries: { staleTime: 30_000, retry: 1, refetchOnWindowFocus: false }
    • mutations: { retry: 0 }
  • Query keys are tuples — convention: ['<resource>', <filter|id|...>] e.g. ['requests', { status: 'open' }].
  • Mutations invalidate related keys in onSuccess.

7. API client (src/lib/axios.ts)

A single axios instance underpins every HTTP call:

const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
  withCredentials: true,
});

// Request interceptor — attach token
api.interceptors.request.use((cfg) => {
  const token = getStoredToken();
  if (token) cfg.headers.Authorization = `Bearer ${token}`;
  cfg.headers['X-Request-Id'] = randomUUID();
  return cfg;
});

// Response interceptor — handle 401, normalize errors
api.interceptors.response.use(
  (res) => res.data,
  async (err) => {
    if (err.response?.status === 401 && !retried) {
      // attempt refresh-token flow
      await refresh();
      return api.request(err.config);
    }
    throw normalizeError(err);
  }
);

Endpoint constants live alongside the hook that uses them — no central api/endpoints.ts.


8. Real-time integration

The SocketProvider in contexts/socket-context.tsx:

  1. Opens the connection after authentication (token from storage).
  2. Auto-joins user-specific rooms (user-{id}, seller-{id} or buyer-{id} based on role).
  3. Exposes a useSocket() hook returning socket, connected, helper emitters.

Higher-level hooks build on this:

Hook Subscribes to
use-chat-socket chat:new-message, typing indicators, read receipts
use-conversations conversation list updates
use-notifications notification:received
use-purchase-requests request:offer-received, status changes
use-marketplace-socket broad market events
use-unified-real-time multi-event aggregator

See Real-time Layer for the full event catalog.


9. Web3 integration

// wagmi config (approx — confirm in src/web3/ or src/lib/wagmi.ts)
const config = createConfig({
  chains: [bsc, polygon, mainnet, sepolia],
  transports: {
    [bsc.id]: http(`https://...alchemy.com/${KEY}`),
    [polygon.id]: http(`https://polygon-mainnet.g.alchemy.com/v2/${KEY}`),
    ...
  },
  connectors: [
    injected(),               // MetaMask
    walletConnect({ projectId: NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID }),
  ],
});

Wallet UI: connect / disconnect / show address / show balance via use-web3-wagmi, use-web3-context. The current checkout target is the Request Network in-house flow; the DePay widget package remains legacy/frontier context and should not be treated as the primary path.


10. Internationalization

  • i18next + react-i18next initialized in src/locales/locales-config.ts with 6 langs (en, fa, ar, fr, cn, vi).
  • Translation files in langs/<locale>/*.json (e.g., common.json).
  • Direction (ltr|rtl) lives in settings; stylis-plugin-rtl is wired into the MUI cache when dir === 'rtl'.
  • Date formatting via dayjs with locale auto-loaded.
  • Number formatting via Intl.NumberFormat + helper at locales/utils/number-format-locale.ts.
  • DataGrid has Persian-specific locale at custom-fa-data-grid-locale.ts.

See Internationalization & RTL for full detail.


11. Forms & validation

react-hook-form + zod schema via @hookform/resolvers/zod. Custom field wrappers in components/hook-form/:

Wrapper Wraps
RHFTextField MUI TextField
RHFSelect MUI Select
RHFAutocomplete MUI Autocomplete
RHFCheckbox MUI Checkbox
RHFRadioGroup MUI RadioGroup
RHFSwitch MUI Switch
RHFUpload custom Dropzone (components/upload)
RHFEditor TipTap editor wrapper
RHFDatePicker @mui/x-date-pickers
RHFPhoneInput react-phone-number-input
RHFCountrySelect components/country-select

A typical form section:

const schema = z.object({ email: z.string().email(), password: z.string().min(8) });
const methods = useForm({ resolver: zodResolver(schema) });
return (
  <FormProvider {...methods}>
    <RHFTextField name="email" label="Email" />
    <RHFTextField name="password" label="Password" type="password" />
    <LoadingButton type="submit" loading={methods.formState.isSubmitting}>Sign in</LoadingButton>
  </FormProvider>
);

12. Theming

src/theme/index.ts creates the theme; src/theme/options/ contains palette / typography / overrides. Light & dark variants share tokens; the active mode is read from the Settings drawer.

  • Primary: Public Sans Variable (per cursor rules).
  • Secondary: Barlow.
  • Breakpoints: xs=600, sm=600, md=900, lg=1200, xl=1536.
  • Shape radius: 8 (default), customizable in settings.

See Theme Configuration and Design System Overview.


13. Settings drawer

src/settings/ provides a side-drawer that lets a user toggle:

  • Mode (light / dark / system)
  • Contrast (default / high)
  • Layout (vertical / mini / horizontal nav)
  • Direction (ltr / rtl)
  • Color preset (one of N curated palettes)
  • Font family override

State persists in localStorage under settings-key.


14. Editor (TipTap)

components/editor/ wraps @tiptap/react with these extensions enabled:

  • StarterKit (paragraph, headings, bold/italic, lists, blockquote)
  • Link (URL parsing)
  • Image (upload via components/upload)
  • Underline, TextAlign
  • CodeBlock + CodeBlockLowlight (syntax highlighting via lowlight)
  • Placeholder

Used in:

  • Blog post editor (dashboard/post/new, dashboard/post/[id]/edit)
  • Long-form description fields in Purchase Request & Request Template forms
  • Dispute evidence narrative

Content is stored as HTML in MongoDB. react-markdown + remark-gfm are available where Markdown rendering is preferred (e.g., chat messages).


15. File uploads

components/upload/ provides a dropzone with:

  • Multi-file selection
  • Drag-and-drop
  • Per-file progress
  • Preview (components/file-thumbnail/ shows by MIME)
  • Removal
  • Total-size enforcement (5 MB default, matches MAX_FILE_SIZE)

Backed by /api/file/* (multipart upload).


16. Sentry

@sentry/nextjs is initialised at app boot. Errors include user context (role, userId) and breadcrumbs (route changes, API failures). Source maps uploaded at build time.


17. Build & deploy

package.json:

  • devnext dev -p 8083 --turbopack
  • buildnext build && cp .next/static .next/standalone/.next/ && cp -r public .next/standalone/
  • startPORT=8083 node .next/standalone/server.js

Dockerfile is a 2-stage multi-stage build that produces .next/standalone/ and copies into a small node:22-alpine runner image with non-root user nextjs. Healthcheck via curl http://localhost:8083.

CI: .gitea/workflows/deploy.yml runs scripts/deploy.sh on push to main / master.

See Docker Setup, CI-CD Pipeline, and Deployment.


18. Notable files for orientation

File Why it matters
src/app/layout.tsx Provider tree
src/lib/axios.ts Every HTTP call goes through this
src/contexts/socket-context.tsx Realtime plumbing
src/theme/index.ts Theme creation entry
src/locales/locales-config.ts i18next setup
src/settings/context/ Settings context (persistence)
next.config.ts Standalone build, COOP/COEP headers for Web3 popups