Files
meezi/MEEZI_CURSOR_GUIDE.md
T
soroush.asadi 45cd028d1c chore: initial project structure and root configuration
Adds root-level config files: solution (.slnx), NuGet, global.json,
Docker Compose files for all services (API, dashboard, website, finder,
admin), environment example, and developer documentation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-27 21:33:10 +03:30

23 KiB
Raw Blame History

راهنمای کامل توسعه Meezi با Cursor

از صفر تا اولین سفارش واقعی — گام به گام


پیش‌نیازها — قبل از هر چیز نصب کن

# 1. Node.js 20+
node --version   # باید v20 یا بالاتر باشد
# دانلود: https://nodejs.org

# 2. .NET 10 SDK
dotnet --version  # باید 8.x باشد
# دانلود: https://dotnet.microsoft.com/download/dotnet/8.0

# 3. Flutter 3.x
flutter --version
# دانلود: https://docs.flutter.dev/get-started/install

# 4. Docker Desktop (برای PostgreSQL و Redis محلی)
docker --version
# دانلود: https://www.docker.com/products/docker-desktop

# 5. pnpm
npm install -g pnpm

# 6. Cursor
# دانلود: https://cursor.com

مرحله ۱ — ساختار پروژه را بساز

# یک پوشه اصلی بساز
mkdir meezi && cd meezi

# Git راه‌اندازی کن
git init
echo "node_modules/\n.next/\nbuild/\n*.user\n.env*\n!.env.example" > .gitignore

مرحله ۲ — فایل‌های کلیدی را بریز

.cursorrules — در ریشه پروژه

You are building Meezi (میزی) — a Persian-first SaaS POS and community
platform for Iranian cafés in Tehran and Karaj.

CONTEXT: Read MEEZI_PRD.md for full product details before any task.

STACK:
- Backend:  ASP.NET Core 10 (C#) in src/Meezi.API
- Web:      Next.js 14 TypeScript in web/dashboard
- Mobile:   Flutter 3 Dart in mobile/meezi_app
- DB:       PostgreSQL + Redis
- ORM:      Entity Framework Core 10

PRODUCT:
- Brand: Meezi (میزی) | میزت منتظرته
- Markets: Tehran + Karaj (V1)
- Languages: Farsi (default) + Arabic + English
- Competitor: Sepidz (legacy license, no SaaS)
- Pricing: Free / Pro 1.49M / Business 3.49M / Enterprise

C# RULES:
- Async/await everywhere — never .Result or .Wait()
- EVERY EF query filters by CafeId (multi-tenant)
- Return ApiResponse<T> always: { bool Success, T? Data, ApiError? Error }
- Use record types for DTOs
- FluentValidation for all inputs
- Hangfire for background jobs
- SignalR for real-time KDS
- Never Console.WriteLine — use ILogger<T>

NEXT.JS RULES:
- next-intl for i18n — ALL strings in messages/fa.json, ar.json, en.json
- RTL for fa/ar — LTR for en
- ms-* me-* ps-* pe-* for spacing (never ml mr pl pr)
- TanStack Query v5 for server state
- Zustand for cart and UI state
- date-fns-jalali for ALL dates — never show Gregorian
- Numbers: .toLocaleString('fa-IR')
- Currency: n.toLocaleString('fa-IR') + ' ت'
- shadcn/ui components always

FLUTTER RULES:
- Riverpod 2 for all state
- GoRouter for navigation
- Drift SQLite for offline
- shamsi_date for all dates
- 3 locales: fa (RTL), ar (RTL), en (LTR)
- Feature-first: lib/features/{feature}/

SECURITY:
- Validate CafeId ownership on every protected endpoint
- Rate limit OTP: 5/hour per phone via Redis
- Never log phone, national ID, or payment tokens
- Soft delete only — never hard delete

API FORMAT:
Success: { "success": true, "data": {...} }
Error:   { "success": false, "error": { "code": "...", "message": "..." } }
List:    { "success": true, "data": [...], "meta": { "total": 0, "page": 1 } }

docker-compose.yml — در ریشه پروژه

version: '3.8'
services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: meezi
      POSTGRES_USER: meezi
      POSTGRES_PASSWORD: meezi_local_pass
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:

مرحله ۳ — Backend راه‌اندازی کن

# پوشه backend
mkdir src && cd src

# پروژه‌های .NET بساز
dotnet new webapi -n Meezi.API --use-controllers
dotnet new classlib -n Meezi.Core
dotnet new classlib -n Meezi.Infrastructure
dotnet new classlib -n Meezi.Shared

# Solution بساز
cd ..
dotnet new sln -n Meezi
dotnet sln add src/Meezi.API
dotnet sln add src/Meezi.Core
dotnet sln add src/Meezi.Infrastructure
dotnet sln add src/Meezi.Shared

# Reference ها
cd src/Meezi.API
dotnet add reference ../Meezi.Core
dotnet add reference ../Meezi.Infrastructure
dotnet add reference ../Meezi.Shared

cd ../Meezi.Infrastructure
dotnet add reference ../Meezi.Core
dotnet add reference ../Meezi.Shared

حالا Cursor را باز کن و این prompt را بده:

I'm building Meezi.API — ASP.NET Core 10 Web API for a SaaS POS system.
Read .cursorrules for full context.

Add these NuGet packages to Meezi.API:
- Npgsql.EntityFrameworkCore.PostgreSQL
- Microsoft.EntityFrameworkCore.Design
- FluentValidation + FluentValidation.DependencyInjectionExtensions
- AutoMapper.Extensions.Microsoft.DependencyInjection
- Hangfire.AspNetCore + Hangfire.PostgreSql
- Serilog.AspNetCore + Serilog.Sinks.Console
- StackExchange.Redis
- Microsoft.AspNetCore.Authentication.JwtBearer
- Microsoft.AspNetCore.SignalR
- QuestPDF (for PDF generation)
- EPPlus (for Excel export)
- CsvHelper

Add these to Meezi.Infrastructure:
- Npgsql.EntityFrameworkCore.PostgreSQL
- Microsoft.EntityFrameworkCore.Tools

Then create this folder structure inside Meezi.API:
Controllers/ Services/ Jobs/ Middleware/ Hubs/ Extensions/

And this in Meezi.Core:
Entities/ Enums/ Interfaces/ Constants/

And this in Meezi.Infrastructure:
Data/ Data/Migrations/ Repositories/ ExternalServices/

Create the base ApiResponse<T> record in Meezi.Shared/ApiResponse.cs
Create Program.cs with full middleware pipeline setup.

مرحله ۴ — Database Schema

در Cursor بده:

Create all EF Core entity classes in Meezi.Core/Entities/ based on this schema:

Cafe: Id(string/cuid), Name, NameAr, NameEn, Slug(unique), Phone, 
      Address, City, LogoUrl, PlanTier(enum), PlanExpiresAt, IsVerified,
      PreferredLanguage, CreatedAt

Branch: Id, CafeId(FK), Name, Address, City

Table: Id, CafeId, BranchId(nullable), Number, Capacity(default 4),
       Floor, QrCode(unique), IsActive

Employee: Id, CafeId, Name, Phone, NationalId, Role(enum EmployeeRole),
          BaseSalary, PinCode, CreatedAt

MenuCategory: Id, CafeId, Name, NameAr, NameEn, SortOrder, TaxId, 
              DiscountPercent, IsActive

MenuItem: Id, CafeId, CategoryId(FK), Name, NameAr, NameEn, 
          Description, Price, ImageUrl, IsAvailable

Order: Id, CafeId, BranchId, TableId, CustomerId, EmployeeId,
       OrderType(enum), Status(enum), CouponId, DiscountAmount,
       Subtotal, TaxTotal, Total, SnappfoodOrderId, CreatedAt

OrderItem: Id, OrderId(FK), MenuItemId(FK), Quantity, UnitPrice, Notes

Payment: Id, OrderId(FK), Method(enum PaymentMethod), Amount, 
         Status(enum), Reference

Customer: Id, CafeId, Name, Phone, NationalId, BirthDateJalali,
          Group(enum CustomerGroup), LoyaltyPoints, ReferredBy, CreatedAt

Coupon: Id, CafeId, Code, Type(enum CouponType), Value, MinOrderAmount,
        MaxDiscount, UsageLimit, UsedCount, TargetGroup, 
        StartsAt, ExpiresAt, IsActive

Tax: Id, CafeId, Name, Rate, IsDefault, IsRequired, IsCompound

EmployeeSalary: Id, EmployeeId(FK), MonthYear(string YYYY-MM),
                BaseSalary, OvertimePay, Deductions, NetSalary, IsPaid

Attendance: Id, EmployeeId(FK), Date, ClockIn, ClockOut, Notes

Shift: Id, EmployeeId(FK), DayOfWeek(int), ShiftType(enum: Morning/Evening/Off)

LeaveRequest: Id, EmployeeId(FK), StartDate, EndDate, Reason,
              Status(enum: Pending/Approved/Rejected), ReviewedBy

Enums to create in Meezi.Core/Enums/:
PlanTier: Free, Pro, Business, Enterprise
OrderType: DineIn, Takeaway, Delivery  
OrderStatus: Pending, Confirmed, Preparing, Ready, Delivered, Cancelled
PaymentMethod: Cash, Card, Credit
EmployeeRole: Owner, Manager, Cashier, Waiter, Chef, Delivery
CustomerGroup: Regular, VIP, New, Employee
CouponType: Percentage, FixedAmount, FreeItem
ShiftType: Morning, Evening, DayOff

Then create AppDbContext in Meezi.Infrastructure/Data/AppDbContext.cs
with all DbSets and proper relationships.
Create the initial EF migration called "InitialCreate".

مرحله ۵ — Auth System

Build the complete auth system for Meezi:

1. In Meezi.Infrastructure/ExternalServices/KavenegarSmsService.cs:
   - HTTP client calling Kavenegar REST API
   - Method: SendOtpAsync(string phone, string otp)
   - Read API key from IConfiguration

2. In Meezi.API/Services/AuthService.cs:
   - SendOtpAsync(string phone): 
     * Generate 6-digit OTP
     * Store in Redis with key "otp:{phone}" TTL 5 minutes
     * Store attempt counter "otp:attempts:{phone}" TTL 1 hour
     * Block if attempts > 5 (return error RATE_LIMITED)
     * Call KavenegarSmsService
   - VerifyOtpAsync(string phone, string code, string cafeId):
     * Check Redis for code
     * If valid: generate JWT with claims { userId, cafeId, role, planTier, lang }
     * Delete OTP from Redis
     * Return token + refresh token

3. In Meezi.API/Controllers/AuthController.cs:
   POST /api/auth/send-otp  → body: { phone }
   POST /api/auth/verify-otp → body: { phone, code }
   POST /api/auth/refresh   → body: { refreshToken }

4. In Meezi.API/Middleware/TenantMiddleware.cs:
   - Extract cafeId from JWT claims
   - Inject ITenantContext into request scope
   - Return 401 if cafeId missing on protected routes

5. In Meezi.API/Middleware/PlanLimitMiddleware.cs:
   - Read planTier from JWT
   - Check against PlanLimits constants
   - Return PLAN_LIMIT_REACHED error if exceeded

JWT claims: { sub: userId, cafeId, role, planTier, lang, iat, exp }
Token expiry: 7 days access, 30 days refresh

مرحله ۶ — Core POS APIs

Build these API endpoints in Meezi.API/Controllers/:

MenuController.cs:
GET    /api/cafes/{cafeId}/menu/categories
POST   /api/cafes/{cafeId}/menu/categories
PATCH  /api/cafes/{cafeId}/menu/categories/{id}
DELETE /api/cafes/{cafeId}/menu/categories/{id}
GET    /api/cafes/{cafeId}/menu/items
POST   /api/cafes/{cafeId}/menu/items
PATCH  /api/cafes/{cafeId}/menu/items/{id}
PATCH  /api/cafes/{cafeId}/menu/items/{id}/availability

TablesController.cs:
GET    /api/cafes/{cafeId}/tables
POST   /api/cafes/{cafeId}/tables        → auto-generate QR code URL
GET    /api/q/{qrCode}                   → public, returns cafeSlug + tableId

OrdersController.cs:
GET    /api/cafes/{cafeId}/orders        → with status filter
POST   /api/cafes/{cafeId}/orders        → create + check plan limits
PATCH  /api/cafes/{cafeId}/orders/{id}/status
POST   /api/cafes/{cafeId}/orders/{id}/payments  → record payment(s)
GET    /api/cafes/{cafeId}/orders/live   → SSE or SignalR for KDS

Every controller must:
- Use [Authorize] attribute
- Validate CafeId matches JWT claim
- Use FluentValidation for request body
- Return ApiResponse<T> format
- Use async/await throughout

مرحله ۷ — Next.js Dashboard

# از ریشه پروژه
mkdir web && cd web
npx create-next-app@latest dashboard --typescript --tailwind --app --src-dir
cd dashboard

# پکیج‌های اضافی
pnpm add @tanstack/react-query zustand next-intl date-fns-jalali
pnpm add @hookform/resolvers react-hook-form zod
pnpm add recharts lucide-react
pnpm add -D @types/node

در Cursor:

Set up the Next.js 14 dashboard for Meezi with these steps:

1. Configure next-intl for 3 locales: fa (default, RTL), ar (RTL), en (LTR)
   - Create messages/fa.json, messages/ar.json, messages/en.json
   - Wrap app in [locale] dynamic route
   - Set dir="rtl" for fa/ar, dir="ltr" for en
   - Load Vazirmatn font for fa/ar, Inter for en

2. Install and configure shadcn/ui:
   npx shadcn-ui@latest init
   Add components: button, input, card, dialog, table, select,
                   badge, skeleton, toast, sheet, dropdown-menu

3. Create the app layout at app/[locale]/(dashboard)/layout.tsx:
   - Sidebar with icons for: POS, CRM, Coupons, Inventory, HR, 
     Reports, Reservations, SMS, Taxes, Settings
   - Top bar with: cafe name, plan badge, language switcher, user menu
   - RTL-aware: sidebar on RIGHT for fa/ar, LEFT for en

4. Create Zustand store at lib/stores/cart.store.ts:
   State: { items: CartItem[], couponCode, appliedCoupon, tableId }
   Actions: addItem, removeItem, updateQty, applyCoupon, clearCart

5. Create API client at lib/api/client.ts:
   - Axios instance with baseURL from env
   - JWT interceptor (attach token from localStorage)
   - 401 interceptor (redirect to login)
   - Response unwrapper (extract .data from ApiResponse<T>)

6. Create the main POS page at app/[locale]/(dashboard)/pos/page.tsx:
   - Left panel (60%): category tabs + item grid (3 cols)
   - Right panel (40%): order items + coupon + tax + split payment
   - All text from fa.json translation keys
   - Farsi numbers everywhere
   - Toman currency format

مرحله ۸ — Flutter App

# از ریشه پروژه
mkdir mobile && cd mobile
flutter create meezi_app --org ir.meezi --platforms android,ios,windows
cd meezi_app

pubspec.yaml — جایگزین کن:

name: meezi_app
description: Meezi - میزی - سیستم کافه و رستوران

environment:
  sdk: '>=3.3.0 <4.0.0'
  flutter: ">=3.19.0"

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter

  # State
  flutter_riverpod: ^2.5.1
  riverpod_annotation: ^2.3.5

  # Navigation
  go_router: ^14.2.0

  # HTTP
  dio: ^5.4.3
  retrofit: ^4.3.0

  # Local DB (offline)
  drift: ^2.18.0
  sqlite3_flutter_libs: ^0.5.24
  path_provider: ^2.1.3
  path: ^1.9.0

  # i18n
  intl: ^0.19.0

  # Shamsi date
  shamsi_date: ^1.1.1

  # QR
  qr_flutter: ^4.1.0
  mobile_scanner: ^5.2.3

  # Printer
  esc_pos_utils_plus: ^2.0.1
  bluetooth_print: ^4.4.0

  # Notifications
  firebase_messaging: ^15.1.0
  flutter_local_notifications: ^17.2.3

  # Storage
  flutter_secure_storage: ^9.2.2
  shared_preferences: ^2.3.2

  # UI
  cached_network_image: ^3.3.1
  image_picker: ^1.1.2
  shimmer: ^3.0.0
  flutter_svg: ^2.0.10+1

  # Utils
  freezed_annotation: ^2.4.1
  json_annotation: ^4.9.0
  equatable: ^2.0.5
  uuid: ^4.4.2

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^2.4.11
  freezed: ^2.5.2
  json_serializable: ^6.8.0
  retrofit_generator: ^8.1.0
  riverpod_generator: ^2.4.0
  drift_dev: ^2.18.0

در Cursor:

Set up the Flutter app structure for Meezi following feature-first architecture.

Create this folder structure in lib/:
  app/
    router.dart       → GoRouter with all routes
    providers.dart    → Riverpod ProviderScope setup
  features/
    auth/             → OTP login screen
    pos/              → Owner POS tablet view
    menu/             → Customer menu browse
    cart/             → Cart + checkout
    track/            → Order tracking
    reserve/          → Table reservation
    discover/         → کافه‌یاب discovery
    hr/               → Employee clock-in + leave request
    profile/          → Customer profile + history
  core/
    api/
      api_client.dart        → Dio + Retrofit setup
      interceptors.dart      → JWT + error interceptors
    db/
      app_database.dart      → Drift database
      daos/                  → Data access objects
    sync/
      sync_engine.dart       → Offline queue + sync logic
    i18n/
      app_localizations.dart
    utils/
      jalali_utils.dart      → shamsi_date helpers
      currency_utils.dart    → Toman formatting
  shared/
    widgets/
    theme/
      app_theme.dart         → RTL-aware theme

Routes to create in router.dart:
/                   → redirect to /discover or /pos based on user role
/login              → OTP auth screen
/discover           → Café discovery (public)
/cafe/:slug         → Café public profile
/cafe/:slug/menu    → Menu (reads tableId from QR)
/cafe/:slug/cart    → Checkout
/order/:id/track    → Delivery tracking
/reserve/:slug      → Table reservation
/pos                → Owner POS (requires OWNER/MANAGER/CASHIER role)
/profile            → Customer profile

Configure localization for fa (RTL), ar (RTL), en (LTR).
Create MaterialApp.router with locale switching support.

مرحله ۹ — Docker و اجرای محلی

# از ریشه پروژه
docker-compose up -d
# PostgreSQL روی port 5432 بالا می‌آید
# Redis روی port 6379

# Migration اجرا کن
cd src/Meezi.API
dotnet ef database update

# Backend اجرا کن
dotnet run
# روی https://localhost:7001 بالا می‌آید

# Dashboard اجرا کن (terminal جدید)
cd web/dashboard
pnpm dev
# روی http://localhost:3000 بالا می‌آید

# Flutter اجرا کن (terminal جدید)
cd mobile/meezi_app
flutter run -d chrome    # برای تست وب
flutter run              # برای اندروید/شبیه‌ساز

مرحله ۱۰ — Prompt های آماده برای هر ماژول

CRM + جستجو با کد ملی:

Build the CRM module:
- GET /api/cafes/{cafeId}/customers?q={search} 
  Search by name, phone, or nationalId (10-digit)
- POST /api/cafes/{cafeId}/customers
  Fields: name, phone, nationalId, birthDateJalali (YYYY/MM/DD), group
- Check FREE plan limit: max 50 customers → return PLAN_LIMIT_REACHED
- Next.js page: /crm with search input, customer cards, add modal
- All in Farsi with RTL layout

HR + حضور و غیاب Flutter:

Build the HR attendance feature in Flutter:
- Screen: features/hr/attendance_screen.dart
- Show employee's today shift (Morning 8-16 / Evening 16-00 / DayOff)
- Clock In button: POST /api/employees/{id}/attendance/clock-in
- Clock Out button: POST /api/employees/{id}/attendance/clock-out
- Leave request form: reason + date range in Shamsi calendar
- Works OFFLINE: queue attendance locally, sync when online
- Shamsi date display throughout
- Farsi UI with RTL layout

KDS آشپزخانه realtime:

Build the Kitchen Display System:
- SignalR Hub in Meezi.API/Hubs/KdsHub.cs
  - Group orders by cafeId
  - Broadcast new orders to kitchen screen
  - Broadcast status changes
- Next.js page: /kds
  - Connect to SignalR hub
  - Show live order cards (Pending → Preparing → Ready)
  - Color coded: yellow=Pending, blue=Preparing, green=Ready
  - Each card: table number, items list, time elapsed
  - Click to advance status
  - Auto-refresh every 30s as fallback
  - All Farsi, RTL layout

گزارش مالی + Excel خروجی:

Build the reports API:
GET /api/cafes/{cafeId}/reports/daily?date=1403-10-16
  Returns: totalOrders, newCustomers, returningCustomers,
           revenue, taxTotal, discountTotal, topItems(5)

GET /api/cafes/{cafeId}/reports/monthly?month=1403-10
  Returns: daily breakdown, totalRevenue, totalCosts breakdown
  (salary from EmployeeSalary, plus manual cost entries)
  netProfit = revenue - costs

GET /api/cafes/{cafeId}/reports/export?month=1403-10&format=excel
  Returns: Excel file using EPPlus
  Sheets: Sales, Items, Customers, Employees

Next.js page /reports:
  - Stats cards: today customers (new vs returning)
  - Bar chart (Recharts): 7-day revenue vs cost
  - Top items list
  - Monthly P&L table
  - Export button
  All Farsi, RTL, Toman currency, Shamsi dates

پرداخت ZarinPal:

Build ZarinPal subscription payment in C#:

1. PaymentService.cs:
   - InitiateSubscriptionAsync(cafeId, planTier, months):
     * Call ZarinPal /pg/v4/payment/request.json
     * Store pending payment record
     * Return payment URL
   - VerifyPaymentAsync(authority, status):
     * Call ZarinPal /pg/v4/payment/verify.json
     * On success: update cafe PlanTier + PlanExpiresAt
     * Send confirmation SMS via Kavenegar

2. BillingController.cs:
   POST /api/billing/subscribe → body: { planTier, months }
     → returns { paymentUrl }
   GET  /api/billing/verify?Authority=...&Status=OK
     → ZarinPal callback, redirect to dashboard
   GET  /api/billing/status
     → current plan, expiry, usage stats

3. Hangfire job: SubscriptionRenewalReminderJob
   - Run daily
   - Find cafes expiring in 3 days
   - Send SMS reminder via Kavenegar

Snappfood Integration:

Build Snappfood webhook receiver:

POST /api/webhooks/snappfood (no auth, verify with HMAC secret)
  - Parse Snappfood order payload
  - Map to Meezi Order:
    * OrderType = Delivery
    * Status = Confirmed (already paid via Snappfood)
    * SnappfoodOrderId = external ID
    * Items mapped from Snappfood items to MenuItems by name match
  - Save order to DB
  - Broadcast to KDS via SignalR
  - Return 200 OK within 5 seconds

Also:
PATCH /api/cafes/{cafeId}/orders/{id}/status
  - When status → Delivered: notify Snappfood API if SnappfoodOrderId exists

مرحله ۱۱ — اشتباهات رایج که باید از آن‌ها بپرهیزی

❌ نکن:
- Console.WriteLine در C# → از ILogger<T> استفاده کن
- ml-4 یا mr-4 در Next.js → از ms-4 و me-4 استفاده کن
- new Date() برای نمایش تاریخ → از date-fns-jalali استفاده کن
- متن فارسی hardcode در کامپوننت → همه در fa.json باشد
- query بدون .Where(x => x.CafeId == cafeId) → داده tenant دیگر برمی‌گرداند
- setState در Flutter برای business logic → از Riverpod استفاده کن
- .Result یا .Wait() در C# → deadlock می‌دهد

✅ بکن:
- هر mutation در Flutter اول به Drift ذخیره کن، بعد sync کن
- هر endpoint با [Authorize] + validate CafeId شروع کن
- هر string قابل نمایش را در fa.json/ar.json/en.json بریز
- تمام اعداد نمایشی را با .toLocaleString('fa-IR') فرمت کن
- هر job background را در Hangfire بریز، نه await inline

مرحله ۱۲ — ترتیب توسعه پیشنهادی (Solo با Cursor)

هفته ۱-۲:   Backend foundation (auth + schema + tenant middleware)
هفته ۳-۴:   POS core (menu + orders + tables + QR + payments)
هفته ۵-۶:   Dashboard Next.js (POS screen + KDS + basic reports)
هفته ۷-۸:   CRM + Coupons + SMS marketing (Kavenegar)
هفته ۹-۱۰:  HR module (shifts + attendance + payroll)
هفته ۱۱-۱۲: Flutter customer app (QR + menu + cart + reservation)
هفته ۱۳-۱۴: Discovery platform (کافه‌یاب) + Reviews
هفته ۱۵-۱۶: ZarinPal billing + Taraz + Snappfood + production deploy

اولین beta: 5 کافه در شمال تهران — هفته ۸
اولین پول: هفته ۱۶

چک‌لیست شروع روز اول

[ ] Node 20+ نصب است
[ ] .NET 10 SDK نصب است  
[ ] Flutter 3.x نصب است
[ ] Docker Desktop در حال اجرا است
[ ] Cursor نصب است و باز است
[ ] پوشه meezi/ ساخته شده
[ ] .cursorrules در ریشه قرار دارد
[ ] MEEZI_PRD.md در ریشه قرار دارد
[ ] docker-compose up -d اجرا شده
[ ] در Cursor: Cmd+L → "Read .cursorrules and MEEZI_PRD.md, 
    then start Sprint 1: create the .NET solution structure"