Files
nick-doc/10 - Services/frontend.md
Siavash Sameni e52ffce48a docs: sync vault with codebase state (2026-06-12)
- Update backend, frontend, scanner, deployment, amanat-assist service docs
- Update System Overview, Scanner Architecture, Telegram Mini App flow
- Update 10 - Services/README.md
- Add Tenant data model, Tenant API reference, Tenant Storefront Flow
- Add Multi-Shop Branch Project Scan (2026-06-10)
- Add tenant.md service doc
- Append activity log entry
- Reflects archived/search/stats route fix and new E2E test suite

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 11:42:18 +04:00

419 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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
<Script src="https://telegram.org/js/telegram-web-app.js" strategy="beforeInteractive" />
```
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-<version>` 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 300800ms API response times on `dev.amn.gg` are WAN RTT-bound (~235ms), not DB-bound. Server-side requests from loopback `:8083` show 312ms. 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 <paths>`, 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*