Skip to content

Web & Admin Frontends ​

Both apps/web and apps/admin are React 18 + Vite + Tailwind apps that share a design system and the same conventions. The web app is the farmer/trader trading app; admin is the operations panel.

apps/web — the trading app ​

text
apps/web/src/
├── main.tsx           Sentry init, QueryClient, Router, i18n bootstrap
├── App.tsx            100+ routes with legacy redirects
├── i18n.ts            react-i18next init (en + hi)
├── locales/           en.json, hi.json (~500 keys, ~10 namespaces)
├── lib/               api client wiring, queryClient, feature flags, gps provider
├── routes/            Route definitions / guards
├── pages/             60+ page components (Auth, Buy, Sell, Products, Deals, Farmer, Trader, Reports…)
├── components/
│   ├── ui/            Reusable primitives (Button, Input, Select, Card, Badge, BottomSheet, Toast…)
│   ├── layout/        AppLayout, Sidebar, BottomNav, PageHeader, PageTitle, BackButton, BannerSearch
│   ├── leads/         Lead boards, cards, inline offer/bid actions, contact share
│   ├── products/      Product form, taxonomy picker, metrics form, photos
│   ├── farms/         Farm maps (Leaflet), photos, crop pills
│   ├── inquiries/     Thread list, composer, thread view
│   ├── dashboard/     Quota strip, inquiry ticker, sparklines
│   ├── tiles/         Tile + TileMenu for hub pages
│   ├── search/        Search rail / results
│   └── forms/         Shared form building blocks
├── hooks/             Custom hooks (useQuota, useDeals, useReports, useTaxonomy, messaging stream…)
├── stores/            Zustand auth store
└── types/             Local types (prefer @cropto/types)

Routing polarity (critical) ​

Routes are organised by intent, not resource type:

  • /buy = "I want to buy" → shows other users' sell leads → I make offers.
  • /sell = "I want to sell" → shows other users' buy leads → I make bids.

/buy and /sell are 4-tile hub landing pages; the discover boards live at /buy/browse and /sell/browse. Canonical permalinks /leads/sell/:id and /leads/buy/:id are the stable URL form for SMS deep-links (they auto-route to the owner or counterparty view).

Page-chrome rules (enforced across the app) ​

  • <PageTitle> on every list/board page (title + subtitle + count + CTA).
  • <PageHeader> with a back-button on every detail/create/edit page.
  • True root pages (Dashboard, /farms, /products, /notifications, /settings) have no back button; tile-menu spoke pages get a back-to-hub link via useHubBackLink().
  • Pages render content directly under the root — AppLayout already provides page padding, max-width and centering; re-adding them double-pads.

State & forms ​

  • Server state → React Query via packages/hooks (query keys centralised in packages/hooks/src/query-keys.ts).
  • API calls → the typed packages/api-client (Axios). Never raw fetch.
  • Forms → react-hook-form + zodResolver, inline errors, scrollToFirstError.
  • Number/price display → the shared utils/format.ts helpers (formatPrice, formatPricePerKg, formatWeight, formatNumber) — Indian grouping, no raw floats.

apps/admin — the operations panel ​

text
apps/admin/src/
├── App.tsx            Routes + ProtectedRoute (role ∈ {admin, sub-admin})
├── api/adminApi.ts    Typed fetchers — all admin calls go through here (never axios directly)
├── stores/authStore   Keeps BOTH access + refresh tokens; single-flight refresh interceptor
├── components/        UI primitives ported from web + admin-specific Badge, StatCard, DataTable
├── pages/             Auth, Dashboard, Users, Farmers, Deals, Grievances, Lookups, Taxonomy, System
└── locales/           en.json (i18next kept so hi.json can drop in)

Admin pages (all <PageTitle>/<PageHeader> compliant, inline confirmations, no popups):

RoutePurpose
/dashboardV2 platform stats
/users, /users/:idKYC patch, membership card, sub-admin grant, search-history (audit-gated)
/farmersFarmer directory with filters
/dealsDeals dashboard — status + fulfilment filters, overdue/backfilled badges
/grievances, /grievances/:idGrievance queue + SLA-timed adjudication (also open to sub-admins)
/lookupsTabbed CRUD for affiliations / varieties / crops
/taxonomy, /taxonomy/suggestionsTwo-pane tree manager (rename, GST bracket, metric fields) + suggestion approval
/systemRetention purge (typed-"PURGE" confirm), order backfill, Bull Board link

The admin auth is hardened: it keeps both tokens, runs a single-flight refresh on TOKEN_EXPIRED, classifies terminal-vs-transient refresh failures (a 5xx never logs the admin out), and gates ProtectedRoute on role ∈ {admin, sub-admin} — a signed-in farmer gets a full-page NotAuthorized state, not a 403-ing shell.

Shared design system ​

Both apps use the same Tailwind theme (primary green #1B6B3A), the same component conventions (44px min tap targets, mobile-first, empty states, skeleton loaders), and the NativeWind-safe class subset so components are Phase-2 portable.

Internal technical documentation — Cropto