Move template/issue editors to full pages, add public invoice SPA with PDF print, and local-aware public URLs. Co-authored-by: Cursor <cursoragent@cursor.com>
20 KiB
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 fromVITE_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
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.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 |
/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/categoriesPOST /businesses/:businessId/categories/ai-generate— AI-generated category tree ({ prompt })GET /businesses/:businessId/categories/color-presetsGET/PUT /businesses/:businessId/categories/:categoryId/variationsPOST /businesses/:businessId/categories/:categoryId/technical-form/ai-suggest
Query: ?entityType=product for product categories.
Brands
GET/POST/PATCH/DELETE /businesses/:businessId/brandsGET /businesses/:businessId/brands/:brandId
Products accept optional brandId on create/update.
Products
GET/POST/PATCH/DELETE /businesses/:businessId/productsPOST /businesses/:businessId/products/ai-create— AI-generated product draftGET /businesses/:businessId/products/:productId
Contact submissions
GET /businesses/:businessId/contact-submissions— paginated listGET /businesses/:businessId/contact-submissions/:submissionIdPOST /tenants/:host/contact-submissions— public website contact form
Business settings
GET/PATCH /businesses/:businessId/settings— includesbranding.primaryColor
Portfolios
GET/POST/PATCH/DELETE /businesses/:businessId/portfoliosGET /businesses/:businessId/portfolios/:portfolioId- Portfolio categories:
?entityType=portfolio
Store items (sellable product variants)
GET /businesses/:businessId/store-items— paginated listPOST /businesses/:businessId/store-items— batch create with price + stock
Store specials
GET/POST/PATCH/DELETE /businesses/:businessId/store-specialsGET /tenants/:host/store-specials— public API for websites
Website homepage widgets
GET/POST/PATCH/DELETE /businesses/:businessId/website/category-groups— groups withcategoryIds[]GET/POST/PATCH/DELETE /businesses/:businessId/website/brand-groups— groups withbrandIds[]GET /tenants/:host/website/category-groups— public category groupsGET/POST/PATCH/DELETE /businesses/:businessId/website/sliders— sliders withslides[](imageMediaId, title, linkUrl)GET /tenants/:host/website/sliders— public sliders
Customers
GET/POST/PATCH/DELETE /businesses/:businessId/customersGET /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/variantsGET/PUT /businesses/:businessId/products/:productId/variations
Technical forms
GET/PUT /businesses/:businessId/categories/:categoryId/technical-formPOST .../technical-form/ai-suggestGET/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 |
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, “Issued by” Meshkee footer - Local/dev link: current Vite origin (e.g.
https://meshkee.app:5174/invoices/{id}) - Production link domain:
VITE_INVOICE_PUBLIC_DOMAIN/INVOICE_PUBLIC_DOMAIN(defaultmeshkee.com) — optional full origin override via*_PUBLIC_BASE_URL - Until
meshkee.comproxies or hosts/invoices/*, production links may need that DNS/nginx wiring (viewer code ships with super-admin build)
Migrations: 036_invoices.sql, 037_invoice_name.sql, 038_invoice_templates.sql, 039_invoice_account_holder.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:
--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-fainindex.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 viaBusinessThemeProvider - RTL: Farsi inputs use
dir="rtl"and the app’s--font-fa - IRT prices: comma-separated thousands +
IRTsuffix — 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
- Push MeshkeeApp monorepo and MeshkeeApp Backend to remote
- Copy
.envfiles securely (never commit) - Run migrations on the database
- Open MeshkeeApp as Cursor workspace root
- Start a new agent chat: "Read
docs/PROJECT_CONTEXT.mdand 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/profileGET /tenants/:hostGET /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
onlineSellEnabledon 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
- Minimize scope — only change what the task requires
- Match existing patterns — services in
src/services/, CSS modules, NestJS modules per domain - Do not commit unless the user explicitly asks
- Test user:
+989122222222/password; business id 4 for sanihome.ir - Restart backend after Prisma or module changes
- Update this file when completing a major feature