---
title: Frontend Service — amn-frontend
tags: [service, frontend, nextjs, react, web3, telegram]
created: 2026-06-08
updated: 2026-06-12
---
# Frontend Service — amn-frontend
## 1. Overview
`amn-frontend` is the primary user-facing application for the Amanat (AMN) escrow marketplace. It serves buyers, sellers, and admins through a unified Next.js 16 App Router application with a Persian-first (RTL) UI.
| Field | Value |
|---|---|
| Package name | `amn-frontend` |
| Version | **2.11.89** |
| Status | Active — `main` deployed to `dev.amn.gg`; `feature/white-label-shops` deployed to `multi.amn.gg` |
| Framework | Next.js 16 (App Router + Turbopack), React 19, TypeScript strict |
| Dev port | `8083` (both local and Docker) |
| Package manager | `yarn@1.22.22` |
| Node requirement | `>=20` |
| Repo | `git@git.tbs.amn.gg:escrow/frontend.git` |
The app covers the full escrow lifecycle: request creation, multi-seller offer collection, negotiation, on-chain payment (BSC/ETH/Base/TON), delivery confirmation, dispute handling, loyalty points, tenant admin for white-label shops, public seller-shop browsing, and a Telegram Mini App shell for mobile-native access.
> [!note] Multi-shop branch
> `feature/white-label-shops` adds `TenantProvider`, `/dashboard/admin/tenants`, custom-domain controls, bot activation links, and the `WEBAPP_ENABLED` middleware gate that keeps `multi.amn.gg` Mini App-first while leaving dashboard/auth routes reachable.
---
## 2. Tech Stack
| Layer | Library / Version | Notes |
|---|---|---|
| Framework | `next@^16.1.1` | App Router, Turbopack dev server |
| UI runtime | `react@^19.1.0`, `react-dom@^19.1.0` | Server + Client Components |
| Component library | `@mui/material@^9.0.1` | MUI v9 with Emotion; `@mui/lab`, `@mui/x-data-grid`, `@mui/x-date-pickers`, `@mui/x-tree-view` |
| Styling engine | `@emotion/react`, `@emotion/styled`, `stylis-plugin-rtl` | RTL support via stylis |
| State / data fetching | `@tanstack/react-query@^5.83.0`, `swr@^2.3.3` | TanStack Query is primary; SWR used in some legacy paths |
| Real-time | `socket.io-client@^4.8.1` | Bidirectional events; custom `SocketContext` |
| Forms | `react-hook-form@^7.77.0`, `@hookform/resolvers@^5.0.1`, `zod@^4.0.10` | Schema validation via Zod v4 |
| i18n | `i18next@^26.3.0`, `react-i18next@^17.0.8` | 6 locales (en, fa, ar, fr, cn, vi); RTL for fa/ar |
| Web3 — EVM | `wagmi@^2.19.5`, `viem@^2.31.7`, `ethers@^6.15.0` | WalletConnect + MetaMask + Trezor |
| Web3 — TON | `@tonconnect/ui-react@^2.4.4`, `@ton/core@^0.63.1` | TON wallet payments |
| Hardware wallet | `@trezor/connect-web@^9.7.3` | Trezor signing flow |
| Chain indexing | `alchemy-sdk@^3.6.1` | Alchemy for multi-chain queries |
| Rich text editor | `@tiptap/react@^3.23.6` + extensions | Used in post/blog editor |
| Charts | `apexcharts@^5.10.1`, `react-apexcharts@^2.1.0` | Dashboard KPI charts |
| Animation | `framer-motion@^12.13.0` | Page transitions and UI motion |
| Carousel | `embla-carousel-react@8.6.0` | Product / shop carousels |
| Maps | `mapbox-gl@^3.12.0`, `react-map-gl@^8.0.4` | Address / location pickers |
| HTTP client | `axios@^1.11.0` | Centralised instance with auth interceptors in `src/lib/axios.ts` |
| Notifications | `notistack@^3.0.2`, `sonner@^2.0.3` | Snackbar + toast |
| Error monitoring | `@sentry/nextjs@^10.22.0` | SDK wraps Next.js build + runtime |
| CAPTCHA | `@marsidev/react-turnstile@^1.5.2` | Cloudflare Turnstile |
| Dates | `dayjs@^1.11.13`, `date-fns-jalali@^4.1.0-0` | Jalali (Persian) calendar support |
| QR code | `qrcode@^1.5.4` | Wallet payment QR generation |
| Fonts | DM Sans, Inter, Nunito Sans, Public Sans, Barlow | Variable fonts via `@fontsource-variable` |
---
## 3. App Router Page Structure
All routes live under `frontend/src/app/`. The dev server and Docker container both bind port `8083`.
### Top-level route segments
| Route | Type | Purpose |
|---|---|---|
| `/` | Public | Landing / marketing home |
| `/api/health` | API Route | Container health-check endpoint |
| `/api/llm` | API Route | LLM proxy for amanat-assist features |
| `/auth/jwt/*` | Public | Sign-in, sign-up, OTP verify, password reset, update |
| `/checkout/request-network/*` | Public | Request Network payment checkout shell |
| `/dashboard/*` | Protected | Main authenticated app (see below) |
| `/design-preview` | Internal | Theme/component sandbox |
| `/error` | Public | Global error display |
| `/payment/callback`, `/payment/cancel` | Public | Payment gateway redirect landing |
| `/post/[slug]` | Public | Blog post reader |
| `/shop/[seller]/[id]` | Public | Public seller shop / item view |
| `/store/items`, `/store/checkout` | Public | Storefront browsing and checkout |
| `/telegram` | Mini App | Telegram Mini App shell (see §7) |
### Dashboard sub-routes (AuthGuard + EmailVerificationGuard)
| Route | Purpose |
|---|---|
| `/dashboard` → `/dashboard/overview` | KPI home tiles, recent activity |
| `/dashboard/chat` | Real-time escrow chat |
| `/dashboard/account/*` | Profile, address, notifications, wallet, passkey |
| `/dashboard/request/*` | Buyer purchase requests |
| `/dashboard/request-template/*` | Seller request templates |
| `/dashboard/payment/*` | Payment history and detail |
| `/dashboard/points/*` | Loyalty hub — transactions, referrals, levels |
| `/dashboard/disputes/*` | Dispute creation and management |
| `/dashboard/seller/*` | Seller-side offer management |
| `/dashboard/shop-settings/*` | Seller shop configuration (incl. Telegram config) |
| `/dashboard/shops/*` | Browse / checkout from within dashboard |
| `/dashboard/user/*` | Admin user management |
| `/dashboard/post/*` | Admin blog editor (Tiptap) |
| `/dashboard/admin/tenants/*` | Tenant admin (white-label shops; `feature/white-label-shops` only) |
| `/dashboard/assist/*` | AI assistant (amanat-assist) |
---
## 4. Key Sections / Features
### Marketplace and escrow flow
The primary buyer journey:
1. Buyer submits a **purchase request** (`/dashboard/request/new`) — product description, budget, chain preference.
2. Sellers see the request and submit **offers** via request templates (`/dashboard/request-template`).
3. Buyer selects an offer; both sides enter the **escrow chat** (`/dashboard/chat`).
4. Buyer initiates payment — on-chain via Wagmi/Trezor or off-chain via Request Network.
5. After delivery, buyer releases escrow funds; on dispute, both parties access `/dashboard/disputes`.
### Dashboard
Multi-role dashboard accessible post-login. Guards:
- `AuthGuard` — redirects unauthenticated users to `/auth/jwt/sign-in`.
- `EmailVerificationGuard` — blocks unverified accounts on key routes.
Sidebar nav adapts to role: buyer, seller, admin, or multi-tenant operator.
### Admin
`/dashboard/user` and `/dashboard/post` are admin-only sections gated by role check in the layout. Tenant admin (`/dashboard/admin/tenants`) is only visible on the `feature/white-label-shops` build.
### Telegram Mini App
Full Telegram Mini App (TMA) at `/telegram`. See §7 for integration details.
### White-label shops
`feature/white-label-shops` branch adds multi-tenancy: `TenantProvider` (Context), `/dashboard/admin/tenants` CRUD, custom domain config per tenant, and a `WEBAPP_ENABLED` middleware flag to route Mini App-first for `multi.amn.gg`.
### Blog / content
Public blog at `/post/[slug]` rendered server-side. Admin writes posts via Tiptap rich-text editor at `/dashboard/post`.
### AI assistant (amanat-assist)
`/api/llm` proxies requests to the amanat-assist backend service. The dashboard `/assist` section provides the in-app chat interface.
---
## 5. State Management
| Layer | Mechanism | Usage |
|---|---|---|
| Server cache / async state | `@tanstack/react-query` | All API data fetching, mutation, background refetch |
| Legacy async state | `swr` | Some older sections not yet migrated to TQ |
| Real-time events | `SocketContext` (`src/contexts/`) | Socket.io connection; exposes socket via `useSocket` hook |
| Global UI state | React Context (multiple providers) | Auth, Settings (theme/direction/language), Web3 |
| Form state | `react-hook-form` + Zod | All forms; validation on client |
| Zustand | Not in use | No Zustand dependency in package.json |
The root layout stacks providers in order: `ThemeProvider` → `SettingsProvider` → `AuthProvider` → `QueryClientProvider` → `SocketContextProvider` → `Web3Provider`.
The Telegram layout uses a minimal provider stack: `TonConnectUIProvider` + `QueryClientProvider` only — no dashboard providers.
---
## 6. Internationalization
| Detail | Value |
|---|---|
| Library | `i18next@^26.3.0` + `react-i18next@^17.0.8` |
| Language detection | `i18next-browser-languagedetector` + `accept-language` (server hint) |
| Locales shipped | English (`en`), Persian/Farsi (`fa`), Arabic (`ar`), French (`fr`), Chinese (`cn`), Vietnamese (`vi`) |
| RTL locales | `fa`, `ar` — `direction: rtl` applied at theme level; `stylis-plugin-rtl` transforms MUI Emotion styles |
| Jalali calendar | `date-fns-jalali@^4.1.0-0` — date pickers switch to Jalali for `fa` locale |
| Translation files | `src/locales/langs/{en,fa,ar,fr,cn,vi}/*.json` (lazy-loaded via `i18next-resources-to-backend`) |
| Telegram locales | `src/sections/telegram/locales/{en,fa}.ts` — standalone namespace for TMA strings |
| Default locale | Determined by browser; Persian is the primary product locale |
RTL layout direction is set in `SettingsProvider` and passed to the MUI theme. The `stylis-plugin-rtl` plugin auto-mirrors margin/padding/float/border-radius CSS properties.
---
## 7. Telegram Mini App Integration
### Loading mechanism
`/app/telegram/layout.tsx` injects the Telegram SDK via `next/script` with `strategy="beforeInteractive"`:
```tsx
```
No `@telegram-apps/sdk` npm package is used. The native CDN script approach is chosen for compatibility with Telegram's own versioned releases.
### WebApp wrapper
`src/utils/telegram-webapp.ts` provides a typed wrapper around `window.Telegram.WebApp`, exposing:
- `initData` / `initDataUnsafe` — raw launch parameters
- `colorScheme`, `themeParams` — Telegram UI theme
- `MainButton`, `BackButton` — native Telegram controls
- `close()`, `expand()`, `ready()` helper calls
### Auth flow
1. On TMA load, the app extracts `initData` from `window.Telegram.WebApp`.
2. The frontend calls `POST /api/auth/telegram` with the signed `initData` string.
3. Backend verifies the HMAC signature against `TELEGRAM_BOT_TOKEN` and issues a JWT.
4. The JWT is stored in memory / cookie, and subsequent API calls use the standard auth header.
Replay protection on this path is intentionally absent — Telegram may reuse `initData` across reloads. The backend relies on HMAC verification + `auth_date` freshness only.
### TMA route structure
| Route | Purpose |
|---|---|
| `/telegram` | Entry point — reads initData, authenticates, redirects |
| `/telegram/shop` | Seller list and product browsing |
| `/telegram/cart` | In-app cart and checkout handoff |
| `/telegram/account` | Account tab (mirrors dashboard/account) |
### Components
`src/sections/telegram/components/` contains TMA-native components:
- `telegram-header.tsx` — top navigation bar styled to Telegram theme
- `telegram-chat-row.tsx`, `telegram-chat-bubble.tsx`, `telegram-chat-composer.tsx` — inline chat UI
- `telegram-request-stepper.tsx` — step-through purchase request wizard
- `telegram-cart-fab.tsx` — floating cart icon
- `telegram-onboarding-sheet.tsx` — first-run onboarding bottom sheet
- `telegram-filter-drawer.tsx`, `telegram-list-row.tsx`, `telegram-list-controls.tsx` — marketplace list views
- `telegram-theme-toggle.tsx`, `telegram-language-toggle.tsx` — in-app settings
- `telegram-unlinked-state.tsx` — shown when no shop is linked to the bot
### Shop-settings Telegram config
`/dashboard/shop-settings` includes a Telegram configuration UI where sellers can:
- Link their Telegram bot to their shop
- Set the Mini App URL
- Preview the bot launch button (`TelegramAppButton` component)
---
## 8. Web3 Integration
### EVM (Ethereum / BSC / Base / Polygon / Arbitrum)
| Component | Detail |
|---|---|
| Wagmi | `wagmi@^2.19.5` — React hooks for wallet connection, transaction signing, contract reads |
| Viem | `viem@^2.31.7` — low-level EVM client used by wagmi internally |
| Ethers | `ethers@^6.15.0` — used in `src/web3/web3Service.ts` for legacy contract interaction |
| WalletConnect | Project ID via `NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID` |
| Alchemy | Per-chain API keys (`NEXT_PUBLIC_ALCHEMY_API_KEY_{MAINNET,ARBITRUM,BASE,POLYGON,SEPOLIA}`) for RPC and indexing |
| Escrow wallet | `NEXT_PUBLIC_ESCROW_WALLET_ADDRESS` — the platform escrow contract / EOA |
Chains supported: Ethereum Mainnet, BSC (BNB Chain), Base, Polygon, Arbitrum, Sepolia (testnet).
Provider hierarchy in `src/web3/context/`:
- `wagmi-provider.tsx` — wraps app in `WagmiProvider` + `QueryClientProvider`
- `web3-provider.tsx` — DePay + custom payment orchestration layer
- `web3-context.tsx` + `use-web3-context.ts` — React Context for payment state
### Trezor
`@trezor/connect-web@^9.7.3` handles hardware wallet signing. `src/web3/components/web3-signing-card.tsx` surfaces the Trezor confirmation UI within the payment flow.
### TON
`@tonconnect/ui-react@^2.4.4` and `@ton/core@^0.63.1` enable TON wallet connections and payments. The Telegram layout wraps the Mini App in `TonConnectUIProvider` so TON payments work natively within Telegram.
### Payment UI components
| Component | Location |
|---|---|
| `web3-connect-card.tsx` | Wallet selection / connection modal |
| `web3-payment.tsx` | Payment execution with status tracking |
| `web3-signing-card.tsx` | Trezor/hardware wallet signing prompt |
### Request Network
`/app/checkout/request-network/` is the payment shell for Request Network-based payments. The checkout flow is server-side rendered and uses a dedicated layout outside the dashboard guard.
---
## 9. CI/CD
Two Woodpecker pipelines run on the CI host at `89.58.32.32` (`linux/arm64`).
### `production.yml` — `main` branch → dev.amn.gg
Trigger: push to `main` or `master`.
| Step | Action |
|---|---|
| `get-version` | Reads `package.json` version → writes `dev-` to `.tags` |
| `build-and-deploy` | `docker build -t git.tbs.amn.gg/escrow/frontend:dev .` then `docker compose up -d --no-deps --pull never frontend` against `/opt/escrow-dev/docker-compose.yml` |
| `notify` | `node scripts/ci/tg-notify.cjs` → Telegram notification (success/failure) |
Image tag: `git.tbs.amn.gg/escrow/frontend:dev`. No registry push — image is built locally on the CI host. `pull_policy: never` in the compose override prevents watchtower from pulling a stale remote image.
### `multi.yml` — `feature/white-label-shops` → multi.amn.gg
Trigger: push to `feature/white-label-shops`.
| Step | Action |
|---|---|
| `build-local` | `docker build` with hardcoded `NEXT_PUBLIC_*` build-args for `multi.amn.gg` → tags as `escrow-multi-frontend:local` |
| `deploy` | `docker compose up -d --force-recreate --no-deps frontend` against `/opt/arcane/data/projects/escrow-multi` |
| `notify` | `node scripts/ci/tg-notify.cjs` → Telegram notification |
> [!important] Version bump required
> Every push that triggers a build must increment the patch version in `package.json`. Container images are tagged by version — an unchanged version overwrites the previous image and loses history. See RTK.md version policy.
### Telegram CI notifications
`scripts/ci/tg-notify.cjs` is the CI notification script. It reads `TG_TOKEN` and `TG_USERS` from Woodpecker secrets. Messages must not use `parse_mode` (HTML/Markdown) to avoid Telegram API 400 errors from unescaped characters in commit messages.
---
## 10. Local Development Quick-Start
```bash
# Prerequisites: Node >=20, yarn 1.22.22
cd frontend/
# Install dependencies
yarn install
# Copy and populate env vars
cp .env.local.example .env.local
# Edit .env.local with backend URL, API keys, etc.
# Start dev server (Turbopack, port 8083)
yarn dev
# Alternative: webpack (for debugging Turbopack-specific issues)
yarn dev:webpack
# Type-check
npx tsc --noEmit
# Lint
yarn lint
# Unit tests
yarn test
# E2E tests (requires running backend)
yarn playwright:install # once
yarn test:e2e
# Production build (outputs standalone server)
yarn build
yarn start
```
The standalone server output is at `.next/standalone/server.js`. The `build` script copies static assets and public folder into the standalone bundle automatically.
---
## 11. Environment Variables
All `NEXT_PUBLIC_*` variables are baked into the client bundle at build time. Server-side and secret variables are runtime-only.
### Client-side (build-time baked)
| Variable | Purpose |
|---|---|
| `NEXT_PUBLIC_API_URL` | Backend API base URL (e.g. `https://dev.amn.gg/api`) |
| `NEXT_PUBLIC_BACKEND_URL` | Backend root URL (used for non-API paths) |
| `NEXT_PUBLIC_APP_URL` | Canonical frontend URL |
| `NEXT_PUBLIC_SOCKET_URL` | Socket.io server URL |
| `NEXT_PUBLIC_APP_NAME` | Display name of the application |
| `NEXT_PUBLIC_APP_VERSION` | App version string (mirrors `package.json` version) |
| `NEXT_PUBLIC_ASSETS_DIR` | Public assets base path |
| `NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID` | WalletConnect Cloud project ID |
| `NEXT_PUBLIC_ALCHEMY_API_KEY_MAINNET` | Alchemy RPC key — Ethereum Mainnet |
| `NEXT_PUBLIC_ALCHEMY_API_KEY_BASE` | Alchemy RPC key — Base |
| `NEXT_PUBLIC_ALCHEMY_API_KEY_ARBITRUM` | Alchemy RPC key — Arbitrum |
| `NEXT_PUBLIC_ALCHEMY_API_KEY_POLYGON` | Alchemy RPC key — Polygon |
| `NEXT_PUBLIC_ALCHEMY_API_KEY_SEPOLIA` | Alchemy RPC key — Sepolia testnet |
| `NEXT_PUBLIC_ESCROW_WALLET_ADDRESS` | Platform escrow wallet / contract address |
| `NEXT_PUBLIC_TELEGRAM_BOT_ID` | Telegram Bot ID for Mini App auth |
| `NEXT_PUBLIC_TELEGRAM_MINI_APP_URL` | Deep link URL for TMA launches |
| `NEXT_PUBLIC_TURNSTILE_SITE_KEY` | Cloudflare Turnstile site key |
| `NEXT_PUBLIC_GOOGLE_CLIENT_ID` | Google OAuth client ID |
| `NEXT_PUBLIC_MAPBOX_API_KEY` | Mapbox GL access token |
| `NEXT_PUBLIC_ENABLE_TEST_PAYMENT` | `"true"` to show test payment UI |
| `NEXT_PUBLIC_PASSKEY_RP_ID` | WebAuthn relying party ID |
| `NEXT_PUBLIC_PASSKEY_ORIGIN` | WebAuthn origin |
### Multi-stack build-arg overrides (Woodpecker `multi.yml`)
The multi pipeline passes `NEXT_PUBLIC_APP_URL`, `NEXT_PUBLIC_API_URL`, `NEXT_PUBLIC_BACKEND_URL`, `NEXT_PUBLIC_SERVER_URL`, `NEXT_PUBLIC_SOCKET_URL`, `NEXT_PUBLIC_PASSKEY_RP_ID`, and `NEXT_PUBLIC_PASSKEY_ORIGIN` as `--build-arg` to target `multi.amn.gg`.
> [!warning] Two stacks, two bot tokens
> `NEXT_PUBLIC_TELEGRAM_BOT_ID` must differ between the `escrow-dev` and `escrow-multi` stacks. Sharing a bot token causes Telegram webhook delivery to break for one of the stacks.
---
## 12. Known Issues / Open Items
| ID | Area | Description |
|---|---|---|
| FE-01 | Performance | Measured 300–800ms API response times on `dev.amn.gg` are WAN RTT-bound (~235ms), not DB-bound. Server-side requests from loopback `:8083` show 3–12ms. Fix: CDN/edge delivery or server-side rendering that avoids client roundtrips. |
| FE-02 | Cart isolation | Global payment socket broadcasts previously wiped every user's cart. Provider gate added in v2.8.4; backend room-scoping remains an open follow-up. |
| FE-03 | Real-time | Socket.io rooms are not yet fully scoped by provider on the backend — global broadcast events can still leak cross-user in some edge cases. |
| FE-04 | TMA auth | Telegram `initData` replay protection is intentionally absent. If the threat model changes, a server-side session deduplication layer would be needed at `/api/auth/telegram`. |
| FE-05 | State migration | Some data-fetching paths still use `swr` rather than `@tanstack/react-query`. Incremental migration to TQ is ongoing. |
| FE-06 | Multi-chain scanner | The AMN scanner payment rail watches specific chains. Cross-chain support (multi-seller + multi-chain) is not fully wired. |
| FE-07 | Trezor | `@trezor/connect-web` requires a popup; CSP and popup-blocker edge cases exist in some Telegram In-App Browser contexts. |
| FE-08 | Pre-push hook | The backend repo has a pre-push TSC hook that blocks on full-tree errors; a parallel agent's mid-refactor tree can block clean frontend commits. Always use explicit `git add `, never `git add -A`. |
| FE-09 | CI version policy | Every CI-triggering push must bump the patch version. An unchanged version silently overwrites the previous image tag. |
| FE-10 | TON payments | TON wallet integration is present but not fully tested end-to-end on mainnet. Verify TON mainnet contract addresses before enabling for buyers. |
---
*Last updated: 2026-06-12 — reflects v2.11.89*