mirror of
https://git.meshkee.com/Meshkee/dashboards.git
synced 2026-08-11 22:30:58 +04:30
Business and super-admin can set Customer vs Manager roles (Admin only for super-admin), and the customers page drops clutter while filtering by all/customers/managers. Co-authored-by: Cursor <cursoragent@cursor.com>
591 lines
26 KiB
Markdown
591 lines
26 KiB
Markdown
# 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: August 9, 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 (Parspack) 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, OPENAI_API_KEY (or GROQ_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 (migrate-from-old + delete-data for portfolios/blogs(+news)/customers + categories; single-select). **Add domain** accepts optional Git repo URL → provisions storefront on websites VM and sets `deploy_slug` |
|
||
| `/businesses/:businessId/invoices` | Business invoices list |
|
||
| `/businesses/:businessId/invoices/new` | Issue invoice (full page) |
|
||
| `/users` | Users (business filter: Customer vs Manager → Admin/Editor/Viewer; Admin assignable by super-admin only; owners locked) |
|
||
| `/websites` | Websites / domains (Deploy when `deploy_slug` set) |
|
||
| `/settings` | Platform settings (invoice templates + item templates) |
|
||
| `/settings/invoice-templates/new` | Create invoice template |
|
||
| `/settings/invoice-templates/:templateId` | Edit invoice template |
|
||
| `/invoices/:invoiceId` | Public invoice viewer (no auth; print to PDF) |
|
||
| `/profile` | Profile |
|
||
|
||
### Customer (`apps/customer`)
|
||
|
||
| Path | Page |
|
||
|------|------|
|
||
| `/login` | Login (password, register, forgot-password SMS, one-time OTP 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 |
|
||
| `/my-products` | Customer stock listings (gated by `customer_products`) — list API |
|
||
| `/my-products/:id` | User product details |
|
||
| `/my-products/new` | Add user product (3 steps: basics, images, technical) — create API |
|
||
| `/my-products/:id/edit` | Edit user product — update API |
|
||
|
||
### Business (`apps/business`)
|
||
|
||
| Path | Page | Backend connected? |
|
||
|------|------|-------------------|
|
||
| `/login` | Login (password, forgot-password SMS, one-time OTP 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 |
|
||
| `/customer-products` | Customer user-product listings (admin API) | Yes |
|
||
| `/customer-products/new` | Admin create user product (under admin name) | Yes |
|
||
| `/customer-products/:id` | Customer user-product details | Yes |
|
||
| `/customer-products/:id/edit` | Admin edit user product | 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 |
|
||
|
||
---
|
||
|
||
## Auth (login pages)
|
||
|
||
Business and customer login pages share the same SMS-backed flows (super-admin stays password-only).
|
||
|
||
| Flow | API | Notes |
|
||
|------|-----|--------|
|
||
| Password login | `POST /auth/login` | Rejects unverified cell when SMS is enabled |
|
||
| Register | `POST /auth/register` | Customer by website domain; with SMS on, customer goes to OTP step |
|
||
| Send OTP | `POST /auth/send-otp` | Redis 5-min code via Gama |
|
||
| One-time login | `POST /auth/login-otp` | Code only → tokens + marks cell verified |
|
||
| Forgot password | `POST /auth/reset-password` | Code + new password |
|
||
| Verify only | `POST /auth/verify-otp` | Marks verified; no tokens |
|
||
|
||
Auth helpers: `apps/*/src/services/authService.ts` (`login`, `loginWithOtp`, `resetPassword`, `sendOtp`, `register`). `AuthContext` exposes `login` + `loginWithOtp`.
|
||
|
||
---
|
||
|
||
## 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` — `branding`, `dashboard`, `store`, `modules`
|
||
- `modules.enabled`: optional modules — business: `products`, `store`, `portfolio`, `blog`, `warehouse`, `videos`; customer: `customer_products`. Missing key → all business modules enabled (legacy); customer modules stay opt-in. Super-admin only for modules PATCH.
|
||
- `modules.charts`: two home chart slots (`none`, `orders_30d`, `customers_joined_1y`, `blog_views_30d`, `products_added_1y`); `none` hides that slot; defaults orders + customers.
|
||
- Always-on (not in modules list): business — customers, website, profile, settings, home; customer — home, profile, addresses, orders, favorites
|
||
- Tenant public: `GET /tenants/:host` includes `enabledModules` + `homeCharts`
|
||
|
||
### 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
|
||
|
||
### User products (customer listings)
|
||
- Customer (own stock): `GET/POST /businesses/:businessId/my-user-products`, `GET/PATCH/DELETE .../:productId`, `POST .../:productId/promote`, categories + technical-form + media
|
||
- Business admin (all listings): `GET/POST /businesses/:businessId/user-products`, `GET/PATCH/DELETE .../:productId`, `PATCH .../:productId/status` (`draft` | `published` | `rejected` | `archived`), `POST .../:productId/promote`, categories + technical-form. Admin create is attributed to the admin user.
|
||
|
||
---
|
||
|
||
## 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 |
|
||
| `036_invoices.sql` | Platform/business invoices + invoice item templates |
|
||
| `037_invoice_name.sql` | Optional invoice name |
|
||
| `038_invoice_templates.sql` | Full invoice templates + key points / accounts |
|
||
| `039_invoice_account_holder.sql` | Account holder name on bank accounts |
|
||
| `040_invoice_public_id.sql` | Opaque 12-digit `public_id` for public links |
|
||
|
||
After schema changes: `npx prisma generate` and restart the backend.
|
||
|
||
### Invoices (super-admin)
|
||
|
||
Two template layers + issued invoices:
|
||
|
||
| Layer | Purpose |
|
||
|-------|---------|
|
||
| **Invoice item templates** | Reusable line items (title, duration, worktime, desc, price, discounted) |
|
||
| **Invoice templates** | Full blueprints: name, top text, items (from item templates or custom), duplicatable key points, duplicatable bank accounts (bank name, account holder, card, IBAN) |
|
||
| **Invoices** | Issued to a business — start from an invoice template (editable) or blank |
|
||
|
||
| Endpoint | Purpose |
|
||
|----------|---------|
|
||
| `GET/POST /invoice-item-templates` | Platform line-item presets |
|
||
| `PATCH/DELETE /invoice-item-templates/:id` | Update/remove line-item preset |
|
||
| `GET/POST /invoice-templates` | Platform full invoice templates |
|
||
| `GET/PATCH/DELETE /invoice-templates/:id` | Get / update / delete invoice template |
|
||
| `GET/POST /businesses/:businessId/invoices` | List / issue invoices for a business |
|
||
| `GET/PATCH/DELETE /businesses/:businessId/invoices/:invoiceId` | Detail, status update, delete |
|
||
| `GET /public/invoices/:id` | Public show payload (issued/paid, no auth) |
|
||
|
||
**Invoice fields:** optional `name`, `topText`, `notes`, `invoiceTemplateId`, `status`, `publicUrl`, nested `items`, `keyPoints`, `accounts` (bank name, account holder, card, IBAN).
|
||
|
||
**Public invoice viewer (platform):**
|
||
- Route: super-admin SPA `/invoices/:id` (`PublicInvoicePage`) — glass layout, print-to-PDF, Approve (`issued` → `approved`), “Issued by” Meshkee footer
|
||
- Status `approved`: set from public show page; content becomes immutable for admins
|
||
- Edit: list pencil → `/businesses/:businessId/invoices/:invoiceId/edit` (hidden when approved)
|
||
- Links use opaque **12-digit `publicId`** (not sequential PK) — `GET /public/invoices/:publicId`
|
||
- Local/dev link: current Vite origin (e.g. `https://meshkee.app:5174/invoices/{publicId}`)
|
||
- Production link domain: `VITE_INVOICE_PUBLIC_DOMAIN` / `INVOICE_PUBLIC_DOMAIN` (default `meshkee.com`) — optional full origin override via `*_PUBLIC_BASE_URL`
|
||
- Until `meshkee.com` proxies or hosts `/invoices/*`, production links may need that DNS/nginx wiring (viewer code ships with super-admin build)
|
||
|
||
**Migrations:** `036_invoices.sql` … `040_invoice_public_id.sql`
|
||
|
||
**Super Admin UI:**
|
||
- `/settings` — Invoice templates list + item templates (top text preview = one-line ellipsis)
|
||
- `/settings/invoice-templates/new` · `/settings/invoice-templates/:id` — full-page template editor (not modal)
|
||
- `/businesses/:businessId/invoices` — list + view modal
|
||
- `/businesses/:businessId/invoices/new` — full-page issue form
|
||
- `/invoices/:id` — public viewer (no auth)
|
||
- Schema supports future `owner_scope=business` (business-owned templates)
|
||
|
||
---
|
||
|
||
## 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` |
|
||
| Invoices | `src/invoices/` |
|
||
| Media / S3 | `src/media/media.service.ts` |
|
||
| Prisma schema | `prisma/schema.prisma` |
|
||
|
||
### Super Admin
|
||
| Area | Path |
|
||
|------|------|
|
||
| Settings / invoice templates | `apps/super-admin/src/pages/SettingsPage.tsx` |
|
||
| Template / issue editors | `InvoiceTemplateEditorPage.tsx`, `IssueInvoicePage.tsx` |
|
||
| Shared draft fields | `apps/super-admin/src/components/InvoiceDraftFields.tsx` |
|
||
| Public invoice viewer | `apps/super-admin/src/pages/PublicInvoicePage.tsx` |
|
||
| Business invoices | `apps/super-admin/src/pages/BusinessInvoicesPage.tsx` |
|
||
| Invoice API client | `apps/super-admin/src/services/invoiceService.ts` |
|
||
| Invoice URL helpers | `apps/super-admin/src/lib/config.ts` |
|
||
|
||
---
|
||
|
||
## 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`
|
||
- **Per-business modules:** Super Admin **Modules** column on Businesses list; stored in `settings.modules.enabled` (business + customer sections); business Home + Sidebar hide disabled business modules; `customer_products` gates customer My Products (`/my-products`, `/my-products/new`) which call `businesses/:id/my-user-products`
|
||
- **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 + Super Admin **Sync dashboard SSL** (`POST /domains/ssl-sync` → dashboards agent). Storefront SSL: **Issue website SSL** (`POST /domains/website-ssl`) → websites agent `/ssl`. Per-row lock: **Ensure SSL** (`POST /domains/:id/issue-ssl`) probes apex + `business.*` + `customer.*`, issues only what’s missing (websites agent for apex when `deploy_slug` set; blocking dashboard ssl-sync when business/customer need certs).
|
||
|
||
---
|
||
|
||
## Locale (FA / EN)
|
||
|
||
Dashboards support **Farsi + English** via shared `LocaleProvider` (`@meshkee/dashboard-ui`) and `LanguageSelect` in headers.
|
||
|
||
| Piece | Where |
|
||
|-------|--------|
|
||
| Locale state | `packages/dashboard-ui` → `LocaleContext` |
|
||
| Default language per business | `settings.branding.defaultLocale`: `'fa' \| 'en'` (default **`fa`**) |
|
||
| Applied on open | Business + customer `TenantBrandingProvider` once from tenant branding |
|
||
| Super-admin edit | Businesses list **Lang** column + edit modal |
|
||
| Business UI copy | `apps/business/src/i18n/messages.ts` + `useT()` |
|
||
| Customer UI copy | `apps/customer/src/i18n/` (parallel pattern) |
|
||
| Document titles | Locale-aware via `routeTitles` + `useDashboardDocumentTitle` |
|
||
| Fonts | `--font-ui: var(--font-en), var(--font-fa)` — see `.cursor/rules/ui-farsi-fonts.mdc` |
|
||
|
||
RTL: `dir="rtl"` when locale is `fa`.
|
||
|
||
---
|
||
|
||
## Business home dashboard
|
||
|
||
Home is **not** a 1:1 mirror of the sidebar. Tiles are curated (no Settings tile).
|
||
|
||
| Feature | Detail |
|
||
|---------|--------|
|
||
| Section cards | Count pill + arrow (settings/website have arrow only); hover fills primary |
|
||
| Counts | Existing list `total` with `pageSize=1` — light, not a new stats join |
|
||
| Charts (6/12 each) | Orders (+ add-to-basket) and Customers (+ active logins), last 30 days |
|
||
| Chart API | `GET .../orders/activity?days=30`, `GET .../customers/activity?days=30` |
|
||
| Chart colors | Primary series = theme primary; accent = purple for red theme, cyan for blue themes (`--chart-accent` from `businessPrimaryColors`) |
|
||
| Page aura | Slow-moving radial blobs on `body::before` (tokens + business `index.css`) |
|
||
|
||
Products overview page uses the same i18n + theme-aware `ProductActivityChart` (added vs updated, 12 months).
|
||
|
||
---
|
||
|
||
## Suggested next work
|
||
|
||
- Point `meshkee.com/invoices/*` at the public invoice viewer (proxy or dedicated host)
|
||
- Business-dashboard invoice templates + issue flow (`owner_scope=business`)
|
||
- Migrate business and super-admin fully onto `@meshkee/dashboard-core` / `@meshkee/dashboard-ui` (LocaleProvider already shared)
|
||
- Finish FA/EN coverage on remaining business form pages (many labels still English)
|
||
- 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
|