# راهنمای کامل توسعه Meezi با Cursor > از صفر تا اولین سفارش واقعی — گام به گام --- ## پیش‌نیازها — قبل از هر چیز نصب کن ```bash # 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 ``` --- ## مرحله ۱ — ساختار پروژه را بساز ```bash # یک پوشه اصلی بساز 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 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 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` — در ریشه پروژه ```yaml 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 راه‌اندازی کن ```bash # پوشه 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 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 format - Use async/await throughout ``` --- ## مرحله ۷ — Next.js Dashboard ```bash # از ریشه پروژه 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) 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 ```bash # از ریشه پروژه mkdir mobile && cd mobile flutter create meezi_app --org ir.meezi --platforms android,ios,windows cd meezi_app ``` ### `pubspec.yaml` — جایگزین کن: ```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 و اجرای محلی ```bash # از ریشه پروژه 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 استفاده کن - 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" ```