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>
370 lines
14 KiB
Markdown
370 lines
14 KiB
Markdown
---
|
|
title: Frontend Architecture
|
|
tags: [architecture, frontend, nextjs]
|
|
created: 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)
|
|
|
|
```mermaid
|
|
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) | `AuthGuard` → `EmailVerificationGuard` |
|
|
| `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:
|
|
|
|
```ts
|
|
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
|
|
|
|
```ts
|
|
// 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:
|
|
|
|
```tsx
|
|
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`:
|
|
- `dev` → `next dev -p 8083 --turbopack`
|
|
- `build` → `next build && cp .next/static .next/standalone/.next/ && cp -r public .next/standalone/`
|
|
- `start` → `PORT=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 |
|
|
|
|
---
|
|
|
|
## Related
|
|
|
|
- [[System Architecture]] — bird's-eye topology
|
|
- [[Backend Architecture]] — server peer
|
|
- [[Real-time Layer]] — Socket.IO plumbing
|
|
- [[Design System Overview]] · [[Theme Configuration]] · [[Components]]
|
|
- [[Tech Stack]] — versions
|
|
- [[Coding Standards]] — UI cursor-rules summary
|