45cd028d1c
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>
763 lines
23 KiB
Markdown
763 lines
23 KiB
Markdown
# راهنمای کامل توسعه 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<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` — در ریشه پروژه
|
||
|
||
```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<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
|
||
|
||
```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<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
|
||
|
||
```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<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"
|
||
```
|