Monorepo¶
Purpose¶
Explain the directory layout, npm workspaces, and how shared packages are actually consumed.
Layout¶
akshaya-group-global/
├── apps/ # 7 npm workspaces, one per deployable frontend
│ ├── corporate-web/ # @akshaya/corporate-web
│ ├── software-web/ # @akshaya/software-web
│ ├── logistics-web/ # @akshaya/logistics-web
│ ├── commerce-web/ # @akshaya/commerce-web
│ ├── embroidery-web/ # @akshaya/embroidery-web
│ ├── admin-web/ # @akshaya/admin-web (does NOT depend on packages/ui or packages/shared)
│ └── investors-web/ # @akshaya/investors-web
├── packages/ # 2 npm workspaces, consumed by apps (not independently deployed)
│ ├── shared/ # @akshaya/shared - framework-agnostic React hooks
│ └── ui/ # @akshaya/ui - shared components + CSS design system
├── workers/
│ └── api/ # @akshaya/api-worker - the one dedicated API Worker (D1 + R2 + email)
├── infrastructure/
│ └── cloudflare/ # infrastructure/cloudflare/README.md - one-time bootstrap notes
├── scripts/ # cross-cutting Node/Bash scripts (smoke test, portal-style sync, bootstrap)
└── docs/
├── DEPLOYMENT.md # pre-existing, authoritative deploy/rollback runbook - preserved
└── engineering/ # this portal
package.json's workspaces field: ["apps/*", "packages/*", "workers/*"] - npm's own workspace
mechanism, no separate monorepo tool (no Turborepo/Nx/Lerna).
Shared packages¶
@akshaya/shared¶
Framework-agnostic React hooks, consumed by every app except admin-web:
useHashRoute (hash-based client routing), useLocalStorage, formatINR (INR currency
formatting), uid (short ID generator), useFilteredRows.
@akshaya/ui¶
Shared components and the design system CSS, also consumed by every app except admin-web:
AppShell, Page, Card, Button, Input, Select, TextArea, EmptyState, Status,
AkshayaLogoMark, plus the brand constant ({ name: 'Akshaya Group Global', tagline: 'One Group.
Multiple Possibilities.' }) and the cross-app customer-portal helpers
(customerPortalHref, isSafeReturnPath, CUSTOMER_LOGIN_SOURCES) that back corporate-web's
and software-web's own portal routes (see Platform).
admin-web is deliberately isolated from both shared packages - it depends on plain
react/react-dom only. Worth knowing before assuming a shared-package change affects every app.
Generated CSS: npm run sync:portal-styles¶
packages/ui/src/portal-shared.css is the source of truth for the customer/freelancer portal
styling; npm run sync:portal-styles (scripts/sync-portal-shared-css.mjs) copies it into
apps/corporate-web/public/customer-portal/shared.css and
apps/software-web/public/freelancer-portal/shared.css. Never hand-edit those two generated
copies - CI's check:portal-styles-drift step re-runs the sync and fails the build if the
committed copies don't match (see CI/CD → CI).
Root npm commands - "which command should I use?"¶
| I want to... | Command |
|---|---|
| Install all dependencies | npm install |
| Run Corporate locally | npm run dev:corporate |
| Run Software locally | npm run dev:software |
| Run Logistics locally | npm run dev:logistics |
| Run Commerce locally | npm run dev:commerce |
| Run Embroidery locally | npm run dev:embroidery |
| Run Admin locally | npm run dev:admin |
| Run Investors locally | npm run dev:investors |
| Run the API Worker locally | npm run dev:api |
| Build everything (what CI does) | npm run build |
| Build one app | npm run build:<app> (e.g. npm run build:corporate) |
| Regenerate the shared portal CSS | npm run sync:portal-styles |
| Check the portal CSS hasn't drifted (what CI checks) | npm run check:portal-styles-drift |
| Run all unit tests | npm test |
| Set a Worker secret (Turnstile, Admin bootstrap) | npm run configure:turnstile:sitekey / :secret, npm run configure:admin:bootstrap |
| Apply D1 migrations against production | npm run migrate:api:remote (see D1 → REMOTE/MUTATING) |
| Deploy one app by hand | npm run deploy:<app> (e.g. npm run deploy:corporate) |
| Run the production smoke test | npm run smoke:production |
Every command above is copied directly from package.json - none invented.