Files
nick-doc/PRD - Mini App & Marketplace Sprint (v2.11).md
2026-06-08 14:01:52 +03:30

8.4 KiB

PRD — Mini App & Marketplace Sprint (v2.11)

Status: Shipped
Version: frontend v2.11.0 → v2.11.2 / backend v2.11.0 → v2.11.1
Date: 2026-06-08
Scope: Telegram Mini App UX, purchase-request archive, payment history, seller shop settings, category filters


1. Summary

This sprint shipped 10 planned tasks plus several hotfixes discovered in production. The main themes were:

  • Archive system — buyers can archive completed/cancelled requests to keep the active list clean
  • Payment history — web dashboard table + mini-app overlay showing full escrow transaction history
  • Seller category selection — sellers pick categories in shop settings; buyers use them to filter the marketplace
  • Mini App home dashboard — stats strip, recent requests, archive shortcut, AI assistant overlay
  • Correctness fixes — payment token validation, Telegram link env-var, notification deep-link

2. Features Shipped

2.1 Purchase Request Archive

Problem: Completed and cancelled requests pile up in the buyer's request list, making it hard to find active ones.

Solution: Three-layer archive system:

  • Requests with status completed or cancelled are auto-archived on status transition (backend service layer)
  • Buyer can manually archive any request via a row action in the web dashboard list
  • Bulk-archive available via checkbox selection
  • Archived requests hidden by default; accessible via "Archived" toggle in the web app and the archive overlay in the mini app

Backend changes:

  • purchase_requests table: added is_archived, archived_at, archived_by columns (migration 0029_purchase_request_archive.sql)
  • Self-healing boot guard (ensurePurchaseRequestArchiveColumns) added to connectDatabase() to survive cold deployments where the migration hasn't been applied yet
  • New routes: GET /marketplace/purchase-requests/archived, POST /marketplace/purchase-requests/:id/archive, POST /marketplace/purchase-requests/bulk-archive
  • getMyPurchaseRequests filters out archived by default; accepts ?includeArchived=true

Frontend changes:

  • buyer-request-list-view.tsx: archive row action, showArchived toggle, bulk selection
  • TelegramArchivedRequestsView: full-screen overlay in mini app
  • Archive menu row on mini app home screen (home → archived requests)

Production incident: Migration 0029 wasn't applied before the first deploy of v2.11.0. All GET /marketplace/purchase-requests calls returned HTTP 500 because Drizzle's schema referenced columns that didn't exist in production. Fixed by adding the self-healing boot guard and documenting the manual SQL path.


2.2 Payment History

Problem: No way to see a consolidated list of all escrow payments (as buyer or seller) in either the web app or the mini app.

Backend: New endpoint GET /payment/history?role=buyer|seller|all — returns payments filtered by the caller's role.

Web app: PaymentHistoryView — data-grid table with columns: date, request ID, counterparty, amount, status, payment method. Accessible from the "تاریخچه" nav item under Payments in the dashboard sidebar.

Mini app: TelegramPaymentHistoryView — scrollable list with icon/status/direction/amount per row. Opened via the "پرداخت‌ها و امانت" quick-action row on the home screen (previously this row only navigated to the account tab).


2.3 Seller Category Selection

Problem: Sellers had no way to tag their shop with categories. Buyers filtering the marketplace by category got back all sellers regardless.

Backend: Added category_ids jsonb column to shop_settings (additive migration via ensurePostgresShopSettingsSchema). normalizeSellerListing now derives categoryIds from the seller's active templates if not set manually.

Web app:

  • ShopSettingsGeneral: Autocomplete multi-select using real useGetCategories() data
  • seller-filters-drawer.tsx / seller-filters-result.tsx: category filter now uses {value, label}[] objects; label shown in active-filter chips

2.4 Mini App Home Dashboard

Problem: Home screen was a plain banner with quick-action links — no at-a-glance status.

Added:

  • TelegramHomeStats — 3-up stat strip: total requests (tappable → requests tab), active escrow payments (tappable → payment history overlay), unread notifications (tappable → notifications overlay)
  • Recent activity section: the 3 most recent requests rendered as tappable rows
  • Archive shortcut row: direct entry to the archived-requests overlay
  • TelegramWelcomeBanner: personalized greeting using Telegram display name with fallback to "کاربر" / "User"

2.5 AI Assistant In-App Overlay

Problem: "دستیار هوشمند" CTA previously opened assist.amn.gg in a full WebView navigation, which destroyed the mini app session — the back button led nowhere.

Solution: Assistant now loads inside an <iframe> overlay with a manual back button at the top. The mini app stays mounted underneath. Auth token and user profile JSON passed as URL params so the assistant can authenticate without a cross-origin fetch.


Problem: Clicking a notification that had an actionUrl (e.g. /dashboard/buyer/marketplace/request/{id}) called router.push() which navigated the Next.js router to a web-app URL. Inside the Telegram WebView this produced a 404.

Fix: TelegramNotificationsView no longer uses router.push. It extracts the request ID from the actionUrl via regex (/marketplace\/requests?\/([a-zA-Z0-9_-]+)/) and calls onOpenRequest(id) — which sets openRequestId in the mini-app shell, closing the notifications overlay and opening the request detail overlay in-app.


Problem: NEXT_PUBLIC_TELEGRAM_MINI_APP_URL was hardcoded in two places (telegram-app-button.tsx, tonconnect-provider.tsx), causing the twaReturnUrl to point to the wrong environment in staging.

Fix: Centralised to src/utils/telegram-link.tsgetTelegramMiniAppUrl() reading the env var with a safe fallback.


2.8 Payment Token Validation

Problem: paymentService.ts had currency: 'USDT' hardcoded. If a request was priced in USDC the payment record stored the wrong token, causing downstream balance-check mismatches.

Fix: resolvePaymentCurrency(token) validates against DEFAULT_ALLOWED_TOKENS = ['USDC', 'USDT'] and throws if the token is unsupported. AmnScanner webhook handler logs a warning when the scanned token doesn't match the payment record.


2.9 Mini App Requests Pagination

Problem: Buyers with many requests saw an endless scroll — the entire list rendered at once inside the Telegram WebView, which has no native scroll chrome.

Fix: TelegramRequestsView renders 8 items at a time (PAGE_SIZE = 8). A "نمایش بیشتر (N)" button appends the next page. Changing search/filter/sort resets to page 1.


3. Migration Notes

0029_purchase_request_archive.sql

Idempotent — safe to run multiple times:

ALTER TABLE purchase_requests ADD COLUMN IF NOT EXISTS is_archived   BOOLEAN   NOT NULL DEFAULT false;
ALTER TABLE purchase_requests ADD COLUMN IF NOT EXISTS archived_at   TIMESTAMP;
ALTER TABLE purchase_requests ADD COLUMN IF NOT EXISTS archived_by   TEXT;
CREATE INDEX IF NOT EXISTS idx_purchase_requests_is_archived ON purchase_requests (is_archived);

Apply via:

docker exec -i amanat-postgres psql -U amanat -d amanat < src/db/migrations/0029_purchase_request_archive.sql

Or redeploy backend — the boot guard runs ADD COLUMN IF NOT EXISTS on startup automatically.


4. Deployment Checklist

  • Backend: redeploy via Arcane (does NOT auto-deploy on push)
  • Frontend: auto-deploys on push to main
  • DB migration 0029 applied (or rely on boot guard)
  • Verify NEXT_PUBLIC_TELEGRAM_MINI_APP_URL is set in production env

5. Known Limitations / Follow-up

  • Payment history view shows direction: 'in'/'out' from the backend's IPayment record; if direction is not set, defaults to buyer (outgoing). May need backend to populate direction reliably for all historical records.
  • Mini app payment history does not yet support pagination — fetches up to 100 records at once. Sufficient for current user volumes.
  • Task 10 (UI like wallet + tutorial videos) was descoped from this sprint. The wallet-style home stats strip is the v1 of that work.