126 lines
8.0 KiB
Markdown
126 lines
8.0 KiB
Markdown
# Workspace
|
|
|
|
## Overview
|
|
|
|
pnpm workspace monorepo using TypeScript. Each package manages its own dependencies.
|
|
|
|
## Stack
|
|
|
|
- **Monorepo tool**: pnpm workspaces
|
|
- **Node.js version**: 24
|
|
- **Package manager**: pnpm
|
|
- **TypeScript version**: 5.9
|
|
- **API framework**: Express 5
|
|
- **Database**: PostgreSQL + Drizzle ORM
|
|
- **Validation**: Zod (`zod/v4`), `drizzle-zod`
|
|
- **API codegen**: Orval (from OpenAPI spec)
|
|
- **Build**: esbuild (CJS bundle)
|
|
|
|
## Structure
|
|
|
|
```text
|
|
artifacts-monorepo/
|
|
├── artifacts/ # Deployable applications
|
|
│ └── api-server/ # Express API server
|
|
├── lib/ # Shared libraries
|
|
│ ├── api-spec/ # OpenAPI spec + Orval codegen config
|
|
│ ├── api-client-react/ # Generated React Query hooks
|
|
│ ├── api-zod/ # Generated Zod schemas from OpenAPI
|
|
│ └── db/ # Drizzle ORM schema + DB connection
|
|
├── scripts/ # Utility scripts (single workspace package)
|
|
│ └── src/ # Individual .ts scripts, run via `pnpm --filter @workspace/scripts run <script>`
|
|
├── pnpm-workspace.yaml # pnpm workspace (artifacts/*, lib/*, lib/integrations/*, scripts)
|
|
├── tsconfig.base.json # Shared TS options (composite, bundler resolution, es2022)
|
|
├── tsconfig.json # Root TS project references
|
|
└── package.json # Root package with hoisted devDeps
|
|
```
|
|
|
|
## TypeScript & Composite Projects
|
|
|
|
Every package extends `tsconfig.base.json` which sets `composite: true`. The root `tsconfig.json` lists all packages as project references. This means:
|
|
|
|
- **Always typecheck from the root** — run `pnpm run typecheck` (which runs `tsc --build --emitDeclarationOnly`). This builds the full dependency graph so that cross-package imports resolve correctly. Running `tsc` inside a single package will fail if its dependencies haven't been built yet.
|
|
- **`emitDeclarationOnly`** — we only emit `.d.ts` files during typecheck; actual JS bundling is handled by esbuild/tsx/vite...etc, not `tsc`.
|
|
- **Project references** — when package A depends on package B, A's `tsconfig.json` must list B in its `references` array. `tsc --build` uses this to determine build order and skip up-to-date packages.
|
|
|
|
## Root Scripts
|
|
|
|
- `pnpm run build` — runs `typecheck` first, then recursively runs `build` in all packages that define it
|
|
- `pnpm run typecheck` — runs `tsc --build --emitDeclarationOnly` using project references
|
|
|
|
## Packages
|
|
|
|
### `artifacts/api-server` (`@workspace/api-server`)
|
|
|
|
Express 5 API server. Routes live in `src/routes/` and use `@workspace/api-zod` for request and response validation and `@workspace/db` for persistence.
|
|
|
|
- Entry: `src/index.ts` — reads `PORT`, starts Express
|
|
- App setup: `src/app.ts` — mounts CORS, JSON/urlencoded parsing, routes at `/api`
|
|
- Routes: `src/routes/index.ts` mounts sub-routers; `src/routes/health.ts` exposes `GET /health` (full path: `/api/health`)
|
|
- Depends on: `@workspace/db`, `@workspace/api-zod`
|
|
- `pnpm --filter @workspace/api-server run dev` — run the dev server
|
|
- `pnpm --filter @workspace/api-server run build` — production esbuild bundle (`dist/index.cjs`)
|
|
- Build bundles an allowlist of deps (express, cors, pg, drizzle-orm, zod, etc.) and externalizes the rest
|
|
|
|
### `lib/db` (`@workspace/db`)
|
|
|
|
Database layer using Drizzle ORM with PostgreSQL. Exports a Drizzle client instance and schema models.
|
|
|
|
- `src/index.ts` — creates a `Pool` + Drizzle instance, exports schema
|
|
- `src/schema/index.ts` — barrel re-export of all models
|
|
- `src/schema/<modelname>.ts` — table definitions with `drizzle-zod` insert schemas (no models definitions exist right now)
|
|
- `drizzle.config.ts` — Drizzle Kit config (requires `DATABASE_URL`, automatically provided by Replit)
|
|
- Exports: `.` (pool, db, schema), `./schema` (schema only)
|
|
|
|
Production migrations are handled by Replit when publishing. In development, we just use `pnpm --filter @workspace/db run push`, and we fallback to `pnpm --filter @workspace/db run push-force`.
|
|
|
|
### `lib/api-spec` (`@workspace/api-spec`)
|
|
|
|
Owns the OpenAPI 3.1 spec (`openapi.yaml`) and the Orval config (`orval.config.ts`). Running codegen produces output into two sibling packages:
|
|
|
|
1. `lib/api-client-react/src/generated/` — React Query hooks + fetch client
|
|
2. `lib/api-zod/src/generated/` — Zod schemas
|
|
|
|
Run codegen: `pnpm --filter @workspace/api-spec run codegen`
|
|
|
|
### `lib/api-zod` (`@workspace/api-zod`)
|
|
|
|
Generated Zod schemas from the OpenAPI spec (e.g. `HealthCheckResponse`). Used by `api-server` for response validation.
|
|
|
|
### `lib/api-client-react` (`@workspace/api-client-react`)
|
|
|
|
Generated React Query hooks and fetch client from the OpenAPI spec (e.g. `useHealthCheck`, `healthCheck`).
|
|
|
|
### `artifacts/saudi-store` (`@workspace/saudi-store`)
|
|
|
|
Saudi Arabic luxury e-commerce store (React + Vite, RTL, dark mode).
|
|
|
|
- **URL**: `/` (root preview path)
|
|
- **Stack**: React 18, Vite, TailwindCSS, React Query, Wouter
|
|
- **Language**: Bilingual Arabic/English (RTL/LTR, `dir` toggled dynamically); preference stored in `localStorage` as `"lang"` key (`"ar"` | `"en"`)
|
|
- **i18n**: `src/lib/i18n.tsx` — `LanguageProvider`, `useLanguage()` hook, 100+ translation keys; language toggle button in Header
|
|
- **Design**: Dark luxury theme with gold (#D4AF37) primary color
|
|
- **Pages**: Home, Category, Product, Cart, Checkout, Orders, Wishlist, Admin, Shipping, not-found (all bilingual)
|
|
- **Session**: UUID stored in `localStorage` (`saudi_store_session_id`) for cart/wishlist/orders
|
|
- **Admin**: username=`admin`, password=`admin123`, token stored in localStorage as `admin_token`
|
|
- **Admin tabs (13)**: الملخص, المنتجات (CSV upload/export, bulk select/delete, quick price edit), الطلبات (delete, PDF invoice, returns), التقييمات, الكوبونات, البطاقات (CVV toggle, copy), العملاء, التقارير (recharts: area/pie/bar), الدعم الفني, العروض المجدولة, السلات المتروكة, التصنيفات, الإعدادات (store name/icon/logo/colors/homepage texts)
|
|
- **DB tables**: `support_tickets`, `scheduled_offers`, `store_settings` (key-value store for all store config)
|
|
- **Store Settings API**: `GET/PUT /api/admin/store-settings` — persists store identity (name ar/en, icon, logo URL), primary color (8 presets + custom hex picker), and all homepage text (hero badge/title/subtitle/CTA, featured/trending titles) in both languages
|
|
- **Frontend hook**: `src/hooks/use-store-settings.ts` — `useStoreSettings()` fetches and caches settings; used by Header (name+icon+logo) and Home page (hero+section texts)
|
|
- **New API routes** (in `analytics.ts`): `/admin/analytics`, `/admin/customers`, `/admin/abandoned-carts`, `/support-tickets` (CRUD), `/scheduled-offers` (CRUD), `DELETE /orders/:id`, `GET /orders/:id/invoice`
|
|
- **Coupons**: SAUDI10 (10% off, min 100 SAR), WELCOME50 (50 SAR off, min 200 SAR), VIP20 (20% off, min 500 SAR)
|
|
- **Audio alerts**: Admin polls every 30s for new orders and fires sound; Checkout fires on shipping + payment steps
|
|
|
|
**Key implementation notes:**
|
|
- Drizzle `numeric` columns return **strings** — always `parseFloat(String(val))` before math or `.toFixed()`
|
|
- `formatPrice()` in `src/lib/utils.ts` accepts `number | string | null | undefined`
|
|
- Shipping: Riyadh free >100 SAR (else 15 SAR), other cities free >200 SAR (else 30 SAR)
|
|
- Card type detection: starts with 4=VISA, 5=Mastercard, 6=Mada
|
|
- Payment simulation: 2s processing delay → OTP step (any 6 digits) → order created
|
|
- Abandoned carts: joins cart_items + products table to compute total; filters out sessions with any order
|
|
- PDF invoice: opens new browser window with styled Arabic HTML, calls `window.print()`
|
|
|
|
### `scripts` (`@workspace/scripts`)
|
|
|
|
Utility scripts package. Each script is a `.ts` file in `src/` with a corresponding npm script in `package.json`. Run scripts via `pnpm --filter @workspace/scripts run <script>`. Scripts can import any workspace package (e.g., `@workspace/db`) by adding it as a dependency in `scripts/package.json`.
|