mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
Initial commit: Meshkee dashboards monorepo.
Includes business, customer, and super-admin apps with shared packages and production deploy scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,453 @@
|
||||
# MeshkeeApp — Project Context
|
||||
|
||||
> **For AI agents:** Read this file at the start of a new chat before making changes.
|
||||
> Update this document when a major feature is completed or architecture changes.
|
||||
|
||||
Last updated: July 2026
|
||||
|
||||
---
|
||||
|
||||
## Repository layout
|
||||
|
||||
**Primary workspace:** open **`MeshkeeApp`** (this monorepo) as the Cursor workspace root.
|
||||
|
||||
```
|
||||
MeshkeeApp/
|
||||
apps/
|
||||
super-admin/ # @meshkee/super-admin — port 5174 — meshkee.app
|
||||
business/ # @meshkee/business-dashboard — port 5173 — business.{domain}
|
||||
customer/ # @meshkee/customer-dashboard — port 5175 — customer.{domain}
|
||||
packages/
|
||||
dashboard-core/ # @meshkee/dashboard-core — API client, auth types, utils, tokens
|
||||
dashboard-ui/ # @meshkee/dashboard-ui — shared React components
|
||||
docs/ # This file + agent context
|
||||
```
|
||||
|
||||
**Backend (separate repo):** sibling folder `MeshkeeApp Backend` — NestJS + Prisma + PostgreSQL. Not inside this monorepo.
|
||||
|
||||
**Stale workspace:** `MeshkeeApp Gen2` is empty/outdated — do not use it.
|
||||
|
||||
---
|
||||
|
||||
## Apps overview
|
||||
|
||||
| App | Package | Port | Local URL | Domain guard |
|
||||
|-----|---------|------|-----------|--------------|
|
||||
| Super Admin | `@meshkee/super-admin` | 5174 | `https://meshkee.app:5174` | `AdminDomainGuard` |
|
||||
| Business | `@meshkee/business-dashboard` | 5173 | `http://business.sanihome.ir:5173` | `BusinessDomainGuard` |
|
||||
| Customer | `@meshkee/customer-dashboard` | 5175 | `http://customer.sanihome.ir:5175` | `CustomerDomainGuard` |
|
||||
|
||||
### Shared packages migration
|
||||
|
||||
| App | `@meshkee/dashboard-core` | `@meshkee/dashboard-ui` |
|
||||
|-----|---------------------------|-------------------------|
|
||||
| Customer | ✅ wired | ✅ wired |
|
||||
| Super Admin | ⏳ pending | ⏳ pending |
|
||||
| Business | ⏳ pending | ⏳ pending |
|
||||
|
||||
When migrating an app to shared packages, prefer importing from `@meshkee/dashboard-core` / `@meshkee/dashboard-ui` instead of duplicating code.
|
||||
|
||||
---
|
||||
|
||||
## Tech stack
|
||||
|
||||
### All dashboard apps
|
||||
- React 19, TypeScript, Vite, React Router
|
||||
- CSS Modules + design tokens (`src/index.css`; customer also imports `@meshkee/dashboard-core/styles/tokens.css`)
|
||||
- Auth via JWT (`src/context/AuthContext.tsx`, `src/services/authService.ts`)
|
||||
- API client: `src/lib/api.ts` — base URL from `VITE_API_BASE_URL`
|
||||
|
||||
### Backend
|
||||
- NestJS, Prisma, PostgreSQL, Redis
|
||||
- S3-compatible storage (Parmin) for media
|
||||
- API prefix: `/api/v1`
|
||||
- BigInt IDs serialized as strings in JSON
|
||||
|
||||
---
|
||||
|
||||
## Local development
|
||||
|
||||
### 1. Monorepo setup
|
||||
|
||||
```bash
|
||||
cd MeshkeeApp
|
||||
npm install
|
||||
```
|
||||
|
||||
### 2. Backend (sibling folder)
|
||||
|
||||
```bash
|
||||
cd "../MeshkeeApp Backend"
|
||||
cp .env.example .env # fill DATABASE_URL, JWT secrets, S3 keys, GROQ_API_KEY (or OPENAI_API_KEY)
|
||||
npm install
|
||||
# Run SQL migrations in database/migrations/ (in order)
|
||||
npx prisma generate
|
||||
npm run start:dev # default http://localhost:3000
|
||||
```
|
||||
|
||||
### 3. Dashboard dev servers
|
||||
|
||||
From the monorepo root:
|
||||
|
||||
```bash
|
||||
npm run dev:super-admin # https://meshkee.app:5174
|
||||
npm run dev:business # http://business.sanihome.ir:5173
|
||||
npm run dev:customer # http://customer.sanihome.ir:5175
|
||||
```
|
||||
|
||||
Or from an app directory:
|
||||
|
||||
```bash
|
||||
cd apps/business && npm run dev
|
||||
```
|
||||
|
||||
Copy each app's `.env.example` → `.env` before first run.
|
||||
|
||||
**Super Admin HTTPS:** generate local certs with mkcert (see `apps/super-admin/.env.example`).
|
||||
|
||||
### 4. Hosts file
|
||||
|
||||
Add to `/etc/hosts` (one line per tenant):
|
||||
|
||||
```
|
||||
127.0.0.1 meshkee.app
|
||||
127.0.0.1 business.sanihome.ir
|
||||
127.0.0.1 customer.sanihome.ir
|
||||
127.0.0.1 business.safeteb.com
|
||||
127.0.0.1 customer.safeteb.com
|
||||
127.0.0.1 safeteb.com
|
||||
```
|
||||
|
||||
**Multi-tenant:** leave `VITE_BUSINESS_DOMAIN` unset in business/customer `.env`. Each dashboard build resolves the tenant from `window.location.hostname` (`business.safeteb.com` → `safeteb.com`). One dev server and one production deploy serve all businesses; the backend maps domain → business in the DB.
|
||||
|
||||
### 5. Test accounts
|
||||
|
||||
| App | Phone | Password | Notes |
|
||||
|-----|-------|----------|-------|
|
||||
| Business | `+989122222222` | `password` | sanihome.ir → **business_id `4`** |
|
||||
| Customer | (same user) | `password` | customer.sanihome.ir tenant |
|
||||
| Super Admin | (platform admin) | — | see backend seed / team records |
|
||||
|
||||
---
|
||||
|
||||
## App routes
|
||||
|
||||
### Super Admin (`apps/super-admin`)
|
||||
|
||||
| Path | Page |
|
||||
|------|------|
|
||||
| `/login` | Login |
|
||||
| `/` | Home |
|
||||
| `/businesses` | Businesses list |
|
||||
| `/users` | Users |
|
||||
| `/websites` | Websites / domains |
|
||||
| `/profile` | Profile |
|
||||
|
||||
### Customer (`apps/customer`)
|
||||
|
||||
| Path | Page |
|
||||
|------|------|
|
||||
| `/login` | Login |
|
||||
| `/checkout` | Shopping cart checkout (standalone layout — login → cart → delivery → payment) |
|
||||
| `/checkout/login` | Checkout sign-in step |
|
||||
| `/checkout/cart` | Cart review |
|
||||
| `/checkout/delivery` | Address or pickup |
|
||||
| `/checkout/payment` | Discount code + payment |
|
||||
| `/checkout/success` | Order confirmation |
|
||||
| `/` | Home |
|
||||
| `/profile` | Profile |
|
||||
| `/addresses` | Addresses |
|
||||
| `/orders` | Orders |
|
||||
| `/favorites` | Favorites |
|
||||
|
||||
### Business (`apps/business`)
|
||||
|
||||
| Path | Page | Backend connected? |
|
||||
|------|------|-------------------|
|
||||
| `/login` | Login | Yes |
|
||||
| `/` | Home | Partial |
|
||||
| `/products` | Products hub | — |
|
||||
| `/products/categories` | Category tree + variations | Yes |
|
||||
| `/products/brands` | Product brands (linear list) | Yes |
|
||||
| `/products/list` | My Products grid | Yes |
|
||||
| `/products/new` | Add product | Yes |
|
||||
| `/products/edit/:id` | Edit product | Yes |
|
||||
| `/products/detail/:id` | Product details + gallery | Yes |
|
||||
| `/products/settings` | Product moderation settings | Yes |
|
||||
| `/store` | Store hub | Partial |
|
||||
| `/store/items` | Store items (product variants) | Yes |
|
||||
| `/store/settings` | Online sell + order process steps | Yes |
|
||||
| `/customers` | Business customers list | Yes |
|
||||
| `/store/orders` | Orders list + filters | Yes |
|
||||
| `/blog` | Blog hub | Yes |
|
||||
| `/blog/list` | My Blogs grid | Yes |
|
||||
| `/blog/new`, `/blog/edit/:id` | Add/edit blog | Yes |
|
||||
| `/blog/categories` | Blog category tree | Yes |
|
||||
| `/blog/settings` | Comment moderation | Yes |
|
||||
| `/portfolios` | Portfolios hub | Yes |
|
||||
| `/portfolios/list` | My Portfolios grid | Yes |
|
||||
| `/portfolios/detail/:id` | Portfolio detail + gallery | Yes |
|
||||
| `/portfolios/new`, `/portfolios/edit/:id` | Add/edit portfolio | Yes |
|
||||
| `/portfolios/categories` | Portfolio category tree | Yes |
|
||||
| `/portfolios/settings` | Comment moderation | Yes |
|
||||
| `/website` | Website hub | Placeholder |
|
||||
| `/website/sliders` | Homepage sliders | Yes |
|
||||
| `/website/special-categories` | Featured category groups | Yes |
|
||||
| `/website/special-brands` | Featured brand groups | Yes |
|
||||
| `/website/special-items` | Special item carousels | Yes |
|
||||
| `/website/contact` | Contact us submissions list | Yes |
|
||||
| `/website/subscriptions` | Subscriptions | Placeholder |
|
||||
| `/website/faq` | FAQ | Placeholder |
|
||||
| `/website/badges` | Badges | Placeholder |
|
||||
| `/website/e-payment` | E-payment | Placeholder |
|
||||
|
||||
---
|
||||
|
||||
## API surface (business-scoped)
|
||||
|
||||
All routes require JWT + business permission. `businessId` comes from tenant context after login.
|
||||
|
||||
### Categories
|
||||
- `GET/POST/PATCH/DELETE /businesses/:businessId/categories`
|
||||
- `POST /businesses/:businessId/categories/ai-generate` — AI-generated category tree (`{ prompt }`)
|
||||
- `GET /businesses/:businessId/categories/color-presets`
|
||||
- `GET/PUT /businesses/:businessId/categories/:categoryId/variations`
|
||||
- `POST /businesses/:businessId/categories/:categoryId/technical-form/ai-suggest`
|
||||
|
||||
Query: `?entityType=product` for product categories.
|
||||
|
||||
### Brands
|
||||
- `GET/POST/PATCH/DELETE /businesses/:businessId/brands`
|
||||
- `GET /businesses/:businessId/brands/:brandId`
|
||||
|
||||
Products accept optional `brandId` on create/update.
|
||||
|
||||
### Products
|
||||
- `GET/POST/PATCH/DELETE /businesses/:businessId/products`
|
||||
- `POST /businesses/:businessId/products/ai-create` — AI-generated product draft
|
||||
- `GET /businesses/:businessId/products/:productId`
|
||||
|
||||
### Contact submissions
|
||||
- `GET /businesses/:businessId/contact-submissions` — paginated list
|
||||
- `GET /businesses/:businessId/contact-submissions/:submissionId`
|
||||
- `POST /tenants/:host/contact-submissions` — public website contact form
|
||||
|
||||
### Business settings
|
||||
- `GET/PATCH /businesses/:businessId/settings` — includes `branding.primaryColor`
|
||||
|
||||
### Portfolios
|
||||
- `GET/POST/PATCH/DELETE /businesses/:businessId/portfolios`
|
||||
- `GET /businesses/:businessId/portfolios/:portfolioId`
|
||||
- Portfolio categories: `?entityType=portfolio`
|
||||
|
||||
### Store items (sellable product variants)
|
||||
- `GET /businesses/:businessId/store-items` — paginated list
|
||||
- `POST /businesses/:businessId/store-items` — batch create with price + stock
|
||||
|
||||
### Store specials
|
||||
- `GET/POST/PATCH/DELETE /businesses/:businessId/store-specials`
|
||||
- `GET /tenants/:host/store-specials` — public API for websites
|
||||
|
||||
### Website homepage widgets
|
||||
- `GET/POST/PATCH/DELETE /businesses/:businessId/website/category-groups` — groups with `categoryIds[]`
|
||||
- `GET/POST/PATCH/DELETE /businesses/:businessId/website/brand-groups` — groups with `brandIds[]`
|
||||
- `GET /tenants/:host/website/category-groups` — public category groups
|
||||
- `GET/POST/PATCH/DELETE /businesses/:businessId/website/sliders` — sliders with `slides[]` (imageMediaId, title, linkUrl)
|
||||
- `GET /tenants/:host/website/sliders` — public sliders
|
||||
|
||||
### Customers
|
||||
- `GET/POST/PATCH/DELETE /businesses/:businessId/customers`
|
||||
- `GET /businesses/:businessId/customers/search` — autocomplete for order cart
|
||||
|
||||
### Orders
|
||||
- `GET/POST/PATCH/DELETE /businesses/:businessId/orders`
|
||||
|
||||
### Product variants & variation values
|
||||
- `GET/POST/DELETE /businesses/:businessId/products/:productId/variants`
|
||||
- `GET/PUT /businesses/:businessId/products/:productId/variations`
|
||||
|
||||
### Technical forms
|
||||
- `GET/PUT /businesses/:businessId/categories/:categoryId/technical-form`
|
||||
- `POST .../technical-form/ai-suggest`
|
||||
- `GET/PUT /businesses/:businessId/products/:productId/technical-info`
|
||||
|
||||
### Media
|
||||
- `POST /businesses/:businessId/media` — upload to S3
|
||||
|
||||
---
|
||||
|
||||
## Feature status (business app)
|
||||
|
||||
### Connected to backend
|
||||
- Auth, tenant, business context
|
||||
- Product categories, variations, products CRUD, media upload
|
||||
- Product variation values, store items, draft cart, admin orders
|
||||
- Category technical data forms, product technical data
|
||||
- Product/store settings, blog, portfolios, contact submissions
|
||||
|
||||
### Still local / dummy data
|
||||
- Product comments modal (`apps/business/src/data/productComments.ts`)
|
||||
- Legacy seeds in `apps/business/src/data/`
|
||||
|
||||
---
|
||||
|
||||
## Product variations — business rule
|
||||
|
||||
**Product Variations modal:** user selects which category variation **values** apply to the product. Saved via `PUT .../products/:id/variations`.
|
||||
|
||||
**Store items:** sellable variants with one value per variation type + price + stock. Saved via `POST .../store-items` (batch).
|
||||
|
||||
---
|
||||
|
||||
## Database migrations (Backend)
|
||||
|
||||
Run in order from `MeshkeeApp Backend/database/migrations/`:
|
||||
|
||||
| Migration | Purpose |
|
||||
|-----------|---------|
|
||||
| `009_categories_name_fa.sql` | `name_fa` on categories — **required** or categories 500 |
|
||||
| `010_category_variations.sql` | variation tables |
|
||||
| `016_product_variation_values.sql` | product variation values |
|
||||
|
||||
After schema changes: `npx prisma generate` and restart the backend.
|
||||
|
||||
---
|
||||
|
||||
## Key files by area
|
||||
|
||||
### Shared packages
|
||||
| Area | Path |
|
||||
|------|------|
|
||||
| API client factory | `packages/dashboard-core/src/api/createApiClient.ts` |
|
||||
| IRT price utils | `packages/dashboard-core/src/utils/irtPrice.ts` |
|
||||
| Design tokens | `packages/dashboard-core/src/styles/tokens.css` |
|
||||
| Toast, Breadcrumbs, etc. | `packages/dashboard-ui/src/` |
|
||||
|
||||
### Business app
|
||||
| Area | Path |
|
||||
|------|------|
|
||||
| Categories | `apps/business/src/pages/CategoriesPage.tsx` |
|
||||
| Products | `apps/business/src/pages/MyProductsPage.tsx`, `AddNewProductPage.tsx` |
|
||||
| Store / orders | `apps/business/src/pages/StoreItemsPage.tsx`, `OrdersPage.tsx` |
|
||||
| Services | `apps/business/src/services/` |
|
||||
| ID helper | `apps/business/src/utils/id.ts` — `createId()` fallback |
|
||||
|
||||
### Backend (sibling repo)
|
||||
| Area | Path |
|
||||
|------|------|
|
||||
| Products | `src/products/products.service.ts` |
|
||||
| Categories | `src/categories/categories.service.ts` |
|
||||
| Media / S3 | `src/media/media.service.ts` |
|
||||
| Prisma schema | `prisma/schema.prisma` |
|
||||
|
||||
---
|
||||
|
||||
## UI conventions
|
||||
|
||||
Match **Super Admin** compact field sizing across all dashboards — see `.cursor/rules/ui-compact-fields.mdc`:
|
||||
|
||||
```css
|
||||
--field-height: 38px;
|
||||
--field-padding-y: 9px;
|
||||
--field-font-size: 13px;
|
||||
```
|
||||
|
||||
Global input sizing lives in `packages/dashboard-core/src/styles/tokens.css` (customer) and each app’s `index.css` (business / super-admin). Change password → shared `PasswordResetModal` from `@meshkee/dashboard-ui`.
|
||||
|
||||
- **Farsi text:** Customer app → Yekan Bakh (`src/fonts/yekanbakh.css`, `--font-fa` in `index.css`); business/super-admin → IranYekan (`iranyekan.css`, `.faText`). Input + placeholder must share the same stack — see `.cursor/rules/ui-farsi-fonts.mdc`
|
||||
- **Per-business theme:** Super Admin sets `branding.primaryColor`; business app applies via `BusinessThemeProvider`
|
||||
- **RTL:** Farsi inputs use `dir="rtl"` and the app’s `--font-fa`
|
||||
- **IRT prices:** comma-separated thousands + `IRT` suffix — see `.cursor/rules/ui-irt-price.mdc`
|
||||
- **Toasts:** see `.cursor/rules/ui-toasts.mdc`
|
||||
- **Icon controls:** see `.cursor/rules/ui-control-buttons.mdc`, `ui-tooltips.mdc`
|
||||
|
||||
---
|
||||
|
||||
## Known issues & fixes
|
||||
|
||||
| Problem | Cause | Fix |
|
||||
|---------|-------|-----|
|
||||
| Categories 500 | Missing `name_fa` column | Run migration `009` |
|
||||
| `crypto.randomUUID is not a function` | Non-secure origin | Use `createId()` from `apps/business/src/utils/id.ts` |
|
||||
| Media upload 400 | Bad MIME / empty data URL | `ensureJpegUploadFile()`; backend Sharp validation |
|
||||
| Super Admin HTTPS warning | Missing mkcert certs | Run mkcert per `apps/super-admin/.env.example` |
|
||||
| Workspace approval popups | Wrong workspace root | Open `MeshkeeApp` monorepo root in Cursor |
|
||||
|
||||
---
|
||||
|
||||
## Build commands
|
||||
|
||||
```bash
|
||||
npm run build:packages # shared packages only
|
||||
npm run build:customer
|
||||
npm run build:business
|
||||
npm run build:super-admin
|
||||
npm run build # everything
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Git & handoff checklist
|
||||
|
||||
1. Push **MeshkeeApp** monorepo and **MeshkeeApp Backend** to remote
|
||||
2. Copy `.env` files securely (never commit)
|
||||
3. Run migrations on the database
|
||||
4. Open **MeshkeeApp** as Cursor workspace root
|
||||
5. Start a new agent chat: **"Read `docs/PROJECT_CONTEXT.md` and continue…"**
|
||||
|
||||
---
|
||||
|
||||
## Branding: logo → favicon
|
||||
|
||||
When a business logo is uploaded/changed in **Business Profile**, the backend generates a **48×48 PNG favicon** (sharp), stores it as media, and links `businesses.favicon_media_id`.
|
||||
|
||||
| Surface | How favicon is applied |
|
||||
|---------|------------------------|
|
||||
| Business dashboard | `TenantBrandingProvider` → `applyDocumentFavicon` |
|
||||
| Customer dashboard | same |
|
||||
| Public website (sanihome) | `generateMetadata().icons` from `faviconUrl` |
|
||||
|
||||
APIs expose `faviconUrl` (falls back to `logoUrl` if favicon missing):
|
||||
|
||||
- `GET /businesses/:id/profile`
|
||||
- `GET /tenants/:host`
|
||||
- `GET /tenants/:host/website/business-info`
|
||||
|
||||
Migration: `database/migrations/033_business_favicon.sql`
|
||||
|
||||
---
|
||||
|
||||
## Production deploy (dashboards)
|
||||
|
||||
| | |
|
||||
|--|--|
|
||||
| VPS | `45.149.76.52` |
|
||||
| Super Admin | `https://manage.meshkee.com` |
|
||||
| Business | `https://business.<apex>` (e.g. `business.sanihome.ir`) |
|
||||
| Customer | `https://customer.<apex>` |
|
||||
| API | `https://api.meshkee.com/api/v1` (host `185.164.72.119`) |
|
||||
| Docs | `docs/DEPLOY.md` |
|
||||
|
||||
SSL: Certbot cert `meshkee-dashboards` + cron `ssl-sync.sh` every 2h (option A: `GET /api/v1/internal/ssl/hosts`).
|
||||
|
||||
---
|
||||
|
||||
## Suggested next work
|
||||
|
||||
- Migrate business and super-admin to `@meshkee/dashboard-core` / `@meshkee/dashboard-ui`
|
||||
- Connect product comments to backend
|
||||
- Wire order status transitions to store `orderProcessSteps`
|
||||
- Enforce `onlineSellEnabled` on public website checkout
|
||||
- Postman collection updates for variants endpoints
|
||||
- Add `sanihome.ir` (and other tenants) in production Super Admin so tenant APIs resolve
|
||||
|
||||
---
|
||||
|
||||
## Agent instructions
|
||||
|
||||
1. **Minimize scope** — only change what the task requires
|
||||
2. **Match existing patterns** — services in `src/services/`, CSS modules, NestJS modules per domain
|
||||
3. **Do not commit** unless the user explicitly asks
|
||||
4. **Test user:** `+989122222222` / `password`; business id **4** for sanihome.ir
|
||||
5. **Restart backend** after Prisma or module changes
|
||||
6. Update **this file** when completing a major feature
|
||||
Reference in New Issue
Block a user