Files
dashboards/docs/PROJECT_CONTEXT.md
T

20 KiB
Raw Blame History

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 26, 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

cd MeshkeeApp
npm install

2. Backend (sibling folder)

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:

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:

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.comsafeteb.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)
/businesses/:businessId/invoices Business invoices list
/businesses/:businessId/invoices/new Issue invoice (full page)
/users Users
/websites Websites / domains
/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
/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
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 (issuedapproved), “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.sql040_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.tscreateId() 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:

--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 apps 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 apps --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

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 TenantBrandingProviderapplyDocumentFavicon
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

  • 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 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