Files
meezi/docs/MEEZI_FEATURE_ROADMAP_PLAN.md
soroush.asadi 03376b3ea1 feat(docker): multi-stage Dockerfiles with npmmirror registry
Rewrites dashboard and finder Dockerfiles to use a clean multi-stage
build (deps → builder → runner) that installs npm packages inside
Alpine Linux, avoiding the SWC musl binary issue when building from
Windows host. Uses registry.npmmirror.com for reliable installs from
restricted networks (Iran).

- docker/api/Dockerfile: .NET 10 multi-stage build
- docker/web/Dockerfile: Node 20-alpine multi-stage, npmmirror
- docker/finder/Dockerfile: Node 20-alpine multi-stage, npmmirror
- docker/website/Dockerfile: marketing website build
- scripts/: PowerShell helper scripts for local dev

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

16 KiB
Raw Permalink Blame History

Meezi — Feature Roadmap Plan

Purpose: Implementation plan for growth, operations, integrations, platform, and quality items.
Audience: Product + engineering (solo/small team with Cursor).
Conventions: .cursorrules, ApiResponse<T>, multi-tenant CafeId, messages/{fa,ar,en}.json, plan tiers Free / Pro / Business / Enterprise.
Last updated: 2026-05-22

Implementation status (started)

Item Status
Q-1 docs/SECURITY.md Done
Q-2 Playwright (web/dashboard/e2e/) Done (API smoke + discover page)
Q-3 k6 tests/load/public-abuse.js Done
G-1 API discover filters Done
G-1 UI /[locale]/discover Done (MVP)
Order DisplayNumber (digits-only) Done
Table board SignalR (via KDS hub) Done
O-4 Terminal Redis enforcement Done (API + settings UI)
P-1 Admin split (web/admin + compose) Partial (redirect from dashboard)
G-1 discover detail + Neshan embed Done
G-6 Review owner reply (dashboard + public) Done
G-4 Loyalty earn on pay Done (1 pt / 10k ت)
O-1 Public queue ticket + plan gate + SMS Done
O-3 Shift close UI Done (/shifts)
I-2 Snappfood outbound on status/pay Done

How to use this doc

  • Work in phases (below); each phase is 24 weeks of focused PRs.
  • Split work into small PRs: api | dashboard | mobile | infra | docs.
  • Mark items Built (thin) vs Greenfield — dont rebuild what already exists.
  • Plan gates are called out per feature; wire via IPlatformCatalogService + PlanLimitMiddleware / IPlanLimitChecker.

Current baseline (relevant to this roadmap)

Area Already in repo
Discover profile CafeDiscoverProfile, merchant/admin editor, GET /api/public/discover, taxonomy
Reviews CafeReview, public create; OwnerReply on entity — UI/public display thin
Queue QueueController, queue-screen.tsx, feature flag queue in seeder
Loyalty Customer.LoyaltyPoints field — no earn/redeem rules or UI
Delivery Inbound webhooks (Snappfood/Tap30/Digikala), DeliveryStatusSyncServiceoutbound Snappfood partial
Terminals PlanLimits.MaxTerminals, JWT/header patterns — enforcement incomplete
Security Turnstile + Redis abuse limits — docs/SECURITY.md missing
Admin Meezi.Admin.API exists; admin UI still in web/dashboard

Phase overview

flowchart LR
  P0[Phase 0\nQuality + Ops]
  P1[Phase 1\nDiscover public]
  P2[Phase 2\nGrowth CRM]
  P3[Phase 3\nOperations]
  P4[Phase 4\nIntegrations]
  P5[Phase 5\nPlatform Enterprise]
  P0 --> P1
  P1 --> P2
  P2 --> P3
  P3 --> P4
  P1 --> P4
  P4 --> P5
Phase Theme Outcome Duration
0 Quality & ops docs Safe public surface, CI confidence 12 weeks
1 Public discover Consumer-facing کافه‌یاب for Tehran/Karaj 23 weeks
2 Growth & community Loyalty, reviews 2.0, badges 34 weeks
3 Operations Queue polish, printers, shifts, terminals 34 weeks
4 Integrations Maps, delivery parity, hardware onboarding 34 weeks
5 Platform & Enterprise Admin split, API keys, audit, export 46 weeks

Phase 0 — Quality & operations (do first)

Q-1 — docs/SECURITY.md (ops)

Effort S (1 day)
Deliverable Turnstile setup, Redis limits, rate-limit table, Arvan WAF/CDN rules (OTP, /api/public/*, /api/q/*), X-Forwarded-For, incident checklist
Acceptance On-call can enable CAPTCHA and edge rules without reading source

Q-2 — Playwright E2E (dashboard)

Effort M (35 days)
Scope web/dashboard/e2e/: auth OTP mock or test phone, POS happy path (table → item → pay), QR public order smoke (optional second project)
CI Job on PR; secrets for test DB/API
Acceptance 23 stable tests green in GitHub Actions

Q-3 — Load tests (public QR + OTP)

Effort M (23 days)
Tool k6 or NBomber script in tests/load/
Scenarios GET /api/q/{code}, GET /api/public/.../menu, POST guest order, POST /api/auth/send-otp
Acceptance Document p95 targets; verify 429 / RATE_LIMITED under abuse

Q-4 — Package / Docker hardening

Effort S

Phase 0 exit: SECURITY doc published, 2+ E2E tests, load script runnable locally.


Phase 1 — Growth: public discover homepage

G-1 — Public discover web app (Tehran / Karaj)

Effort L (1.52 weeks)
Plan Free to browse; Pro+ cafés appear when discover_profile filled & IsVerified
Route web/dashboard/src/app/[locale]/(public)/discover/ or separate web/discover (lighter SEO) — recommend public routes inside dashboard first to reuse API client + i18n
API Extend GET /api/public/discover with filters: city (تهران/کرج), themes[], vibes[], occasions[], spaceFeatures[], noise, priceTier, minRating, sort (rating, distance later)
Backend Filter in SQL/EF on deserialized DiscoverProfileJson (JSONB query on PostgreSQL) or materialized columns if perf needed
UI Filter chips (taxonomy from GET /api/public/discover-profile/taxonomy), café cards (cover, rating, badges, price tier), detail page → menu link / map link
i18n discoverPublic.* in fa/ar/en
Acceptance User can filter “date + outdoor + کرج” and open café detail; RTL correct

G-2 — Neshan maps (discover + detail) — can start in Phase 1 or 4

See I-1 below; for discover MVP, static map embed on detail is enough.

Phase 1 exit: Public discover listing + detail live at /fa/discover (or dedicated host).


Phase 2 — Growth & community (depth)

G-3 — Customer accounts (lightweight)

Effort L (2 weeks)
Plan Pro+ for “registered guests”; optional SMS OTP customer auth
Model CustomerAccount (phone PK per platform or per cafe), link to Customer on first order; JWT role=customer scoped to optional cafeId or global
API POST /api/auth/customer/send-otp, verify-otp, GET /api/customers/me/orders, GET /api/customers/me/reservations
Apps meezi_app: login, order history; discover web: “my orders”
Acceptance Returning guest sees past orders after OTP; no merge with staff JWT

G-4 — Loyalty points (earn / redeem)

Effort ML (1.5 weeks)
Built Customer.LoyaltyPoints
Rules LoyaltyRule per cafe: earn % of paid order, min redeem, expiry; Business+ feature flag loyalty
API Earn on order Paid; redeem as discount line on POS; PATCH adjust (manager)
UI CRM customer row, POS pay panel preview, SMS on milestone (optional)
Acceptance Closed order increases points; redeem reduces total with audit row

G-5 — Café badges (Enterprise)

Effort M (1 week)
Plan Enterprise only; admin assigns badges
Model CafeBadge (key, labelFa, icon, assignedAt) or JSON on Cafe
API Admin CRUD; public discover DTO includes badges[]
UI Admin café detail; discover cards show badge chips
Acceptance Only Enterprise cafés display admin-assigned badges

G-6 — Review photos + owner responses (polish)

Effort M (1 week)
Built OwnerReply, OwnerRepliedAt on CafeReview
Photos CafeReviewPhoto (url, sort); upload via public or authenticated; max 3 photos, 5MB, MIME validate
API POST /api/public/cafes/{slug}/reviews multipart; PATCH /api/cafes/{cafeId}/reviews/{id}/reply (owner)
UI Dashboard reviews screen: reply editor; public discover detail: reviews + photos
Moderation IsHidden flag; admin can hide (abuse)
Acceptance Owner reply visible on public page; photos optional

Phase 2 exit: Loyalty + review 2.0 + badges; customer OTP optional for pilot.


Phase 3 — Operations

O-1 — Queue / waitlist (polish, not greenfield)

Effort SM (35 days)
Built IQueueService, queue-screen.tsx
Gaps Plan gate queue (Business+); public “take number” QR/tablet; SMS when called (Kavenegar); TV display mode (fullscreen Next page); branch-scoped boards
API POST /api/public/{cafeId}/queue/tickets (anonymous, rate limited)
Acceptance Walk-in gets number; staff calls next; plan limit blocks Free tier

O-2 — Kitchen printer routing per station

Effort L (2 weeks)
Model KitchenStation (name, printerAddress/bluetoothId), MenuCategory.StationId, order route split on submit
API CRUD stations; KDS ticket includes stationId
Dashboard Settings → stations; map categories
Mobile POS meezi_pos Phase 2: bluetooth_print per station ticket
Acceptance Drink items print to bar printer; food to kitchen

O-3 — Cash drawer / shift close reports

Effort ML (1.5 weeks)
Model CashShift (openedAt, closedAt, openingFloat, countedCash, expectedCash, variance, userId)
API Open/close shift; Z-report snapshot (orders, payments, voids, discounts)
UI POS: “بستن شیفت”; PDF/printable summary; manager-only
HR tie-in Optional link to Employee clock-out
Acceptance Cannot close shift with open tables; report matches day orders

O-4 — Multi-terminal enforcement

Effort M (1 week)
Built PlanLimits.MaxTerminals, X-Meezi-Terminal-Id mentioned for POS
Implementation Redis set terminals:{cafeId} with TTL; register on staff login/refresh; reject 4th terminal on Free with PLAN_LIMIT_REACHED
Dashboard Settings → active terminals list + revoke
Acceptance Free cafe blocked on 2nd concurrent terminal session

Phase 3 exit: Queue production-ready; shift close; terminals enforced; printer routing MVP.


Phase 4 — Integrations

I-1 — Neshan maps (discover + delivery radius)

Effort M (1 week)
Config Neshan:ApiKey in platform settings / cafe settings
Discover Geocode café address; embed map on detail; optional “near me” sort (browser geolocation + distance)
Delivery radius Cafe.DeliveryRadiusKm + circle check for guest delivery orders (future)
Acceptance Map loads on café page; cities filtered Tehran/Karaj

I-2 — Snappfood outbound status updates

Effort M (1 week)
Built DeliveryStatusSyncService, ISnappfoodClient
Work On order status → Ready/OutForDelivery/Delivered call Snappfood API; idempotent; Hangfire retry; log failures to WebhookLog
Dashboard Delivery settings: vendor id, test webhook
Acceptance Status change in KDS triggers outbound call when SnappfoodOrderId set

I-3 — Digikala / Tap30 delivery parity

Effort L (2 weeks)
Built Normalizers + webhook ingress pattern
Work Symmetric outbound sync; commission rules; admin integration toggles; menu mapping table DeliveryMenuMapping
Acceptance Same lifecycle as Snappfood for each enabled platform

I-4 — Hardware bundle onboarding (tablet + printer)

Effort M (1 week product + 1 week ops)
Not code-only SKU in admin; café HardwareBundlePurchasedAt
App flow Wizard: download POS APK, pair printer (BLE), register terminal id, test print
Docs PDF checklist Farsi; support ticket auto-tag hardware
Acceptance New Pro signup can complete wizard end-to-end

Phase 4 exit: Maps on discover; delivery platforms symmetric; hardware wizard documented.


Phase 5 — Platform & Enterprise

P-1 — Separate admin web + Compose services

Effort L (23 weeks)
Work New web/admin Next app; move src/app/[locale]/admin/**; env NEXT_PUBLIC_ADMIN_API_URL; docker-compose.admin.yml (admin-api + admin-web); CORS split
API Merchant Meezi.API strips /api/admin/* when migration complete
Acceptance Admin users never hit merchant dashboard origin; two compose profiles documented

P-2 — API keys (Enterprise)

Effort M (1 week)
Model CafeApiKey (hash, prefix, scopes, expiresAt, lastUsedAt)
Auth Authorization: Bearer mk_... middleware path; scopes: orders:read, menu:write, etc.
UI Dashboard settings (Enterprise): create/revoke keys
Acceptance External script can GET orders with key; keys tenant-scoped

P-3 — Audit log for owners

Effort M (1 week)
Model AuditEvent (cafeId, userId, action, entityType, entityId, diffJson, ip, at)
Instrument Order void, refund, settings change, plan change, employee role change
UI Settings → audit feed; filter by date/user
Acceptance Owner sees who voided a line item

P-4 — Data export & GDPR-style tooling

Effort ML (1.5 weeks)
Export Hangfire job: ZIP JSON (customers, orders, reviews) per café; signed download link 24h
Delete Soft-delete existing; POST /api/cafes/{id}/privacy/erase-customer anonymize PII (phone hash, name redacted)
Retention Document policy in privacy page
Acceptance Owner can export month of CRM; erase one customer on request

Phase 5 exit: Admin split deployed; Enterprise API keys + audit; export/erase available.


Cross-cutting requirements (every phase)

Rule Action
Multi-tenant All EF queries filter CafeId
Plans Feature flags in PlatformPlanDefinitions + IsFeatureEnabledForCafeAsync
Public abuse Rate limit + optional Turnstile on new POST routes
i18n No hardcoded UI strings
Tests At least one integration test per new controller; E2E for critical UX

Suggested PR order (first 10 PRs)

  1. docs/SECURITY.md (Q-1)
  2. Playwright smoke POS (Q-2)
  3. Public discover filters API + page (G-1)
  4. Discover map embed Neshan (I-1 minimal)
  5. Review reply UI + public display (G-6 partial)
  6. Loyalty earn on pay (G-4)
  7. Terminal enforcement (O-4)
  8. Queue public ticket + plan gate (O-1)
  9. Snappfood outbound hardening (I-2)
  10. Shift close MVP (O-3)

Effort summary

Bucket Items Rough total
Quality Q-1Q-4 ~2 weeks
Growth & community G-1G-6 ~68 weeks
Operations O-1O-4 ~56 weeks
Integrations I-1I-4 ~56 weeks
Platform P-1P-4 ~68 weeks

Calendar (1 dev): ~2024 weeks sequential. Parallel (2 dev): Phase 1 + 0 in parallel; Phase 3 + 4 overlap after Phase 2 API stable.


Out of scope (unless product changes)

  • Full Sepidz parity
  • Native iOS/Android store apps separate from Flutter
  • Real-time ML ranking for discover (start with filter + sort)
  • Full Taraz production (see CURRENT_STATE_FOR_PLANNING.md)

Planning prompts for Cursor

Copy into a session with this file attached:

  1. “Implement G-1 PR-1 only: extend GET /api/public/discover filters + tests.”
  2. “Implement O-4 terminal registration with Redis and PlanLimits.”
  3. “Scaffold web/admin and move admin routes from dashboard.”

End of roadmap — update phase exit criteria as items ship.