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>
This commit is contained in:
@@ -0,0 +1,762 @@
|
||||
# راهنمای کامل توسعه 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"
|
||||
```
|
||||
Reference in New Issue
Block a user