Also add optional English name fields on the add-customer modal to match the API. Co-authored-by: Cursor <cursoragent@cursor.com>
26 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: August 11, 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 (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, 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:
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 (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 |
/finance |
Finance hub (Invoices + Transactions tiles) | Yes |
/invoices |
Business-issued invoices list (?userId= filter) |
Yes |
/invoices/new |
Issue invoice to a user | Yes |
/invoices/:invoiceId/edit |
Edit invoice (locked when approved) | Yes |
/invoices/templates |
Invoice templates + item templates | Yes |
/invoices/templates/new |
Create invoice template | Yes |
/invoices/templates/:templateId |
Edit invoice template | 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/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—branding,dashboard,store,modulesmodules.enabled: optional modules — business:products,store,portfolio,blog,warehouse,videos,finance; 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);nonehides 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/:hostincludesenabledModules+homeCharts
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
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 |
041_invoice_status_approved.sql |
Invoice status approved |
058_invoice_user_id.sql |
invoices.user_id billed user |
After schema changes: npx prisma generate and restart the backend.
Invoices (super-admin + business)
Billed party is always a User (user_id). business_id is tenant/context. Platform invoices: owner_scope=platform (super-admin). Business-issued: owner_scope=business + issuer_business_id.
| Layer | Purpose |
|---|---|
| Invoice item templates | Reusable line items (title, duration, worktime, desc, price, discounted) |
| Invoice templates | Full blueprints: name, top text, items, key points, bank accounts |
| Invoices | Issued to a user — from template or blank |
| Endpoint | Purpose |
|---|---|
GET/POST /invoice-item-templates |
Platform line-item presets (super-admin) |
PATCH/DELETE /invoice-item-templates/:id |
Update/remove platform preset |
GET/POST /invoice-templates |
Platform full invoice templates |
GET/PATCH/DELETE /invoice-templates/:id |
Platform template CRUD |
GET/POST /businesses/:businessId/invoice-item-templates |
Business item templates |
PATCH/DELETE .../invoice-item-templates/:id |
Business item template CRUD |
GET/POST /businesses/:businessId/invoice-templates |
Business invoice templates |
GET/PATCH/DELETE .../invoice-templates/:id |
Business invoice template CRUD |
GET/POST /businesses/:businessId/invoices |
List / issue (?userId= filter; body userId required for business) |
GET/PUT/PATCH/DELETE .../invoices/:invoiceId |
Detail, content, status, delete |
GET /public/invoices/:publicId |
Public show (platform or business; issued/approved/paid) |
POST /public/invoices/:publicId/approve |
Public approve (issued → approved) |
Auth: platform template routes → super-admin. Business routes → BusinessPermissionGuard (invoices.* / invoice_templates.*); super-admin on invoice paths still sees platform invoices for that business.
Invoice fields: userId + nested user, optional name, topText, notes, invoiceTemplateId, status, publicUrl, nested items, keyPoints, accounts.
Public invoice viewer:
- Super-admin SPA
/invoices/:publicId(PublicInvoicePage) — print-to-PDF; opaque 12-digitpublicId - Payload includes
locale(fa|enfrom businesssettings.branding.defaultLocale) — page is RTL + Farsi chrome whenfa - Platform invoices:
https://{INVOICE_PUBLIC_DOMAIN}/invoices/{publicId}(defaultmeshkee.com) - Business invoices:
https://{primaryBusinessDomain}/invoices/{publicId}(e.g.sanihome.ir)
Migrations: 036_invoices.sql … 041_invoice_status_approved.sql, 058_invoice_user_id.sql
Super Admin UI: /settings templates; /businesses/:id/invoices list/issue/edit; public /invoices/:publicId
Business UI: sidebar Finance group → /finance (hub), /invoices, /transactions; home Finance tile; Users row Receipt icon → /invoices?userId=; i18n en/fa via useT
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 - 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_productsgates customer My Products (/my-products,/my-products/new) which callbusinesses/:id/my-user-products - 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 + 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) - 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
onlineSellEnabledon public website checkout - Postman collection updates for variants endpoints
- Add
sanihome.ir(and other tenants) in production Super Admin so tenant APIs resolve - Customer dashboard “my invoices” view
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