- 04 - Flows/Telegram Mini App.md: major expansion — TelegramSellerShopView, TelegramCartView, TelegramAccountView, useTelegramCart/useTelegramShops hooks, full nav model, SDK surface table, shop→cart→checkout handoff flow - 01 - Architecture/Frontend Architecture.md: add Telegram Mini App section, TON Connect dependency, update to v2.8.59 - 09 - Audits/Activity Log.md: new entry for frontend@9bafbbb (v2.8.57–2.8.59) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
18 KiB
title, tags, created, updated
| title | tags | created | updated | |||
|---|---|---|---|---|---|---|
| Frontend Architecture |
|
2026-05-23 | 2026-06-03 |
Frontend Architecture
Module-level architecture of the Next.js 16 (App Router) + TypeScript + MUI v9 frontend. The current integration worktree observed locally is on integrate-main-into-development.
[!info] Repo:
git@git.manko.yoga:222/nick/frontend.git· Active integration branch observed locally:integrate-main-into-development· Version: 2.8.59 (package.json) · Dev port3000, Docker port8083.
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)
│ ├── telegram/ # Telegram Mini App shell (see §19)
│ │ ├── layout.tsx # TMA root — TonConnectUIProvider + minimal providers
│ │ ├── shop/ # Seller list + product browsing
│ │ ├── cart/ # In-shell cart + checkout handoff
│ │ └── account/ # Account tab (dashboard parity)
│ ├── 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|telegram|...)
├── components/ # Reusable UI primitives (hook-form, table, upload, editor, ...)
├── layouts/ # Page-template wrappers (auth-centered, auth-split, dashboard, main, telegram)
├── 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'innext.config.tsfor 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.tsxand Suspense boundaries. - MUI cache is wired up via
@mui/material-nextjsin 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).
The Telegram Mini App shell (app/telegram/) uses its own slimmer layout that replaces the dashboard shell with TonConnectUIProvider and skips the settings drawer (see §19).
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 |
telegram/* |
layouts/telegram (bottom-tab shell) |
Telegram initData token guard + role check |
Guards live in frontend/src/auth/ (HOC + hook). They consult the JWT-derived user context and redirect unauthenticated to /auth/jwt/sign-in?returnTo=.... The Telegram guard additionally validates window.Telegram.WebApp.initData before issuing a session.
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 hasindex.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
QueryClientcreated once at the root layout withdefaultOptions: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:
- Opens the connection after authentication (token from storage).
- Auto-joins user-specific rooms (
user-{id},seller-{id}orbuyer-{id}based on role). - Exposes a
useSocket()hook returningsocket,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 |
The Telegram Mini App shell reuses the same SocketProvider — live socket updates are available in the TMA shop, cart, and account tabs.
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.
TON wallet support is handled separately via @ton/core + @tonconnect/ui-react in the Telegram Mini App layer (see §19).
10. Internationalization
i18next+react-i18nextinitialized insrc/locales/locales-config.tswith 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-rtlis wired into the MUI cache whendir === 'rtl'. - Date formatting via
dayjswith locale auto-loaded. - Number formatting via
Intl.NumberFormat+ helper atlocales/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.
Note
The Telegram Mini App shell does not render the settings drawer; theme and direction are inherited from the parent app's stored settings at launch.
14. Editor (TipTap)
components/editor/ wraps @tiptap/react with these extensions enabled:
StarterKit(paragraph, headings, bold/italic, lists, blockquote)Link(URL parsing)Image(upload viacomponents/upload)Underline,TextAlignCodeBlock+CodeBlockLowlight(syntax highlighting vialowlight)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 --turbopackbuild→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/app/telegram/layout.tsx |
TMA shell — TonConnectUIProvider + slim 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 |
19. Telegram Mini App (TMA) layer
Overview
The app ships a dedicated Telegram Mini App shell at app/telegram/. It is served from the same Next.js process and Docker image as the main web app; no separate deployment is required. The Telegram bot registers the Mini App URL pointing at /telegram.
Provider tree (TMA layout)
The TMA layout replaces the full dashboard shell with a minimal provider stack:
flowchart TB
A[TelegramLayout]
A --> B[AppRouterCacheProvider]
B --> C[ThemeProvider]
C --> D[QueryClientProvider]
D --> E[SocketProvider]
E --> F[TonConnectUIProvider<br/>manifestUrl: /tonconnect-manifest.json]
F --> G[SnackbarProvider]
G --> H[Children — telegram routes]
TonConnectUIProvider is the only addition relative to the web tree. Settings drawer, i18n provider, and auth guards are replaced by a Telegram initData token guard.
Routes and features
| Route | Description |
|---|---|
telegram/shop |
Seller list with product browsing; infinite scroll |
telegram/shop/[seller] |
Single seller's catalogue |
telegram/cart |
In-shell shopping cart; checkout hands off to full web checkout URL |
telegram/account |
Account tab with dashboard parity: profile, wallet, order history |
Authentication flow
- Telegram injects
window.Telegram.WebApp.initDataon launch. - The TMA guard sends
initDatato/api/auth/telegramfor HMAC verification. - On success the backend issues a short-lived JWT that the axios instance attaches as
Bearer. - Role-based access (seller vs buyer views) is honoured via the same guard mechanism used in the dashboard.
Real-time
SocketProvider is reused unchanged. The TMA shop, cart, and account tabs receive live socket updates (new messages, payment status, cart changes) on the same room infrastructure as the web dashboard.
TON Connect (Telegram Wallet)
Dependencies added: @ton/core, @tonconnect/ui-react.
TonConnectUIProvider wraps the TMA routes and exposes a useTonConnectUI() hook. The manifest at public/tonconnect-manifest.json declares the app identity to the TON Connect protocol.
Current status: the wallet connection UI is in place (connect / disconnect / show address). Actual TON payment processing is not yet wired to the backend — the provider is pre-positioned for a future TON payment rail on the escrow platform. When that rail is built, the checkout handoff in telegram/cart will be extended to emit a TON transaction instead of redirecting to the web checkout.
Constraints and differences from web
- No settings drawer (theme follows web localStorage, defaults to light/ltr).
- No TipTap editor or file-upload dropzone in TMA routes.
@mui/x-date-pickersand DataGrid are not loaded in the TMA bundle.- COOP/COEP headers required for WalletConnect popups are relaxed for TMA routes because Telegram's WebView does not support
SharedArrayBuffer.
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