6d71770f2e0b913ce2888d9b9eb7e9d90c2fc60c
203 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9a27858125 |
ci: trust Nexus mirror CA in backend dotnet restore (fixes skipped deploys)
The mirror's Let's Encrypt cert renewed under the new ISRG Root YR root, which isn't in the dotnet SDK image's trust store. `dotnet restore` validates TLS and fails (NU1301 / unable to get local issuer certificate), so both backend CI jobs fail and the deploy is skipped. The npm jobs are unaffected because they already pass --strict-ssl=false. Pin the mirror's intermediate (CN=YR2, CA:TRUE, valid to Sept 2028) and add it as a trust anchor before restore in: - CI api-build + admin-api-build jobs (.gitea/workflows/ci-cd.yml) - docker/api/Dockerfile + docker/admin-api/Dockerfile (deploy image builds) Also set NUGET_CERT_REVOCATION_MODE=offline in the CI restore steps to avoid CRL/OCSP fetches to lencr.org (filtered from Iran). Permanent fix is server-side (re-chain to ISRG Root X1 or update trust stores); this unblocks CI/deploys without depending on that. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
5078af2dd7 |
feat(pos): clickable POS v2 redesign prototype at /pos2 (static, no backend)
CI/CD / CI · API (dotnet build + test) (push) Failing after 3m18s
CI/CD / CI · Admin API (dotnet build) (push) Failing after 3m17s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m9s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 1m2s
CI/CD / Deploy · all services (push) Has been skipped
Responsive RTL big-touch reimagining of the POS order screen for judging the redesign on real devices before decomposing the 1568-line pos-screen.tsx + wiring real logic. Self-contained: mock menu + local cart, no API/store/SignalR. - 3 zones: category chips + item grid · order ticket · sticky action bar. - lg+ side-panel ticket; smaller screens get a "view order" bar + slide-over (covers landscape tablet, portrait tablet, phone). - Big-touch (56px primary / 44px qty), brand green #0F6E56, Toman totals. Send/Pay are mock toasts. tsc clean. |
||
|
|
4123654077 |
build(meezi_app): Android Maven mirrors for Iran (Aliyun)
CI/CD / CI · API (dotnet build + test) (push) Successful in 12m40s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 10m30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m7s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 23s
Google's Android maven2 artifacts (AGP, androidx, Kotlin) 404 from Iran like
pub.dev does. Route Gradle resolution through the reachable Aliyun mirrors:
- android/settings.gradle.kts (pluginManagement) + build.gradle.kts (allprojects)
now list maven.aliyun.com/repository/{gradle-plugin,google,central} before the
originals (kept as fallback).
- BUILD_IRAN.md documents the full setup incl. the machine-local GRADLE_USER_HOME
init.gradle needed for Flutter's included flutter_tools/gradle build.
Verified: dependency resolution now succeeds via the mirrors (AGP + kotlin-compiler
download from Aliyun). The APK build itself is currently blocked only by low disk
space on this machine, not configuration.
|
||
|
|
55e0c9499d |
content(website): reflect latest features across all pages
CI/CD / CI · API (dotnet build + test) (push) Successful in 44s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 34s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 1m49s
Full pass over the marketing site so every page reflects the current product: - Features page: +"Works Offline" and +"Get Discovered on Koja" cards (NEW badges); rewrote "Real-time Notifications" to describe the dashboard sound+toast alert. - Solutions: added offline / Koja / real-time-alert bullets across cafés, restaurants, chains, and cloud kitchens. - Tour: POS step now mentions offline + auto-sync; kitchen step describes the sound+toast new-order alert on any screen. - Docs: +Offline Mode and +Koja Discovery module guides (fa/en). - appPromo: waiter-app "new order" alert notes sound. - privacy/terms: "Last updated" June 2025 → June 2026. Website build clean. |
||
|
|
c8ea364ca2 |
build(meezi_app): Flutter Koja now builds for web (mirror + platform gen + fixes)
CI/CD / CI · API (dotnet build + test) (push) Successful in 43s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 23s
Connectivity fixed: pub.dev/googleapis are 403 under sanctions, so PUB_HOSTED_URL + FLUTTER_STORAGE_BASE_URL now point at the reachable Flutter mirror (pub.flutter-io.cn / storage.flutter-io.cn) — set persistently on the build machine. - `flutter create --platforms=android,web --org ir.meezi` generated android/ + web/ (#33). `flutter build web` succeeds; `flutter analyze` errors all cleared. - pubspec: intl ^0.19.0 → ^0.20.2 (SDK pins 0.20.2 via flutter_localizations). - Fixed 3 pre-existing compile errors (app had never been built): * attendance: JalaliFormatter.yyyyMMdd() removed → build the date from year/month/day. * qr_scan: dropped a call to a non-existent CartNotifier.setTable (table context is already set via tableContextProvider just above). * widget_test: default counter test referenced MyApp → minimal MeeziApp smoke test. - discover_screen: drop redundant foundation import; value → initialValue (non-deprecated). Verified: flutter build web ✓. (Android packaging still needs a Gradle/Maven mirror.) |
||
|
|
af1794925d |
feat(meezi_app): café profile parity — cover, open badge, gallery, hours (code-only)
CI/CD / CI · API (dotnet build + test) (push) Successful in 41s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 33s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 22s
Enhances the café detail screen toward web-Koja parity. Parsing verified against
the real backend DTOs (CafePublicDto / WorkingHoursPublicDto), still unbuilt (pub blocked).
- Cover image hero (coverImageUrl), open/closed badge (isOpenNow).
- Photo gallery (galleryUrls) horizontal strip.
- Working hours rendered from the day-keyed WorkingHoursPublicDto ({sat..fri} of
{isOpen,open,close}), Sat→Fri with Persian day labels.
|
||
|
|
2652736d31 |
feat(meezi_app): discovery screen parity — rich filters + taxonomy (code-only)
Brings the Flutter discover screen toward web-Koja parity. Unverified (pub blocked). - DiscoverFilters is now a copyWith class so the many optional filters set safely. - Adds an "open now" chip, rating chips, sort, and a taxonomy-driven filter sheet (themes/vibes/occasions/space-features as multi-select chips + price tier), feeding the rich discover() query. Active-filter badge + pull-to-refresh. - Café cards show open/closed status. |
||
|
|
1d79dde5e1 |
feat(meezi_app): Meezi green theme + rich discovery API (Koja parity, code-only)
CI/CD / CI · API (dotnet build + test) (push) Successful in 42s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 37s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 23s
Head-start on the Koja-Flutter build while pub access is unavailable (pub.dev 403 under sanctions). NOT yet built/verified — needs `flutter create` + `pub get` once package access is restored. - core/theme/app_theme.dart: centralized MeeziTheme (brand green #0F6E56, Material 3, filled/outlined buttons, inputs), wired into main.dart (was a brown seed, no theme). - public_api.dart: discover() gains the full filter set (themes/vibes/occasions/ spaceFeatures/noise/priceTier/size/openNow) + discoverNearby/nlpParse/discoverTaxonomy, matching the web Koja's backend surface. Follows the existing dio pattern. |
||
|
|
45dab8b253 |
test: update ReportPlanGate test for maxDays signature
CI/CD / CI · API (dotnet build + test) (push) Successful in 41s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 32s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 1m32s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
e46d833371 |
feat(plans): report-history + AI-3D limits read from the catalog (S3 finish)
CI/CD / CI · API (dotnet build + test) (push) Successful in 43s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m4s
CI/CD / CI · Admin Web (tsc) (push) Has been cancelled
CI/CD / CI · Website (tsc) (push) Has been cancelled
CI/CD / CI · Koja (tsc) (push) Has been cancelled
CI/CD / Deploy · all services (push) Has been cancelled
The last two limits that still read hardcoded PlanLimits now come from the admin-editable catalog, so editing them in the admin panel takes effect: - ReportPlanGate is now limit-driven (takes int maxDays, not a tier); ReportsController resolves MaxReportHistoryDays from catalog.GetLimitsAsync. LimitMessage is generic (reflects the actual days). EnsureReportDateAllowed is now async. - MenuAi3dGenerationService.ResolveLimitAsync reads MaxMenuAi3dPerMonth from the catalog. Every plan limit + feature gate is now DB-driven and admin-editable. 86 tests pass. |
||
|
|
dcdb0d5747 |
feat(realtime): global guest-order alert on the dashboard
CI/CD / CI · API (dotnet build + test) (push) Successful in 47s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 32s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m4s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 3m9s
Guest orders from the QR/digital menu already notified via SignalR, but only screens that were open (KDS/POS/tables) reacted — and silently (a data refresh, no alert). So staff on any other screen never knew a menu order arrived. - Add a global useOrderAlerts() mounted in the dashboard shell: connects to /hubs/kds, joins the café group, and on a new GUEST order plays a chime + shows a toast (localized fa/en/ar) + nudges order/KDS/POS lists to refresh — on every screen. - Filter to guest QR-menu orders only (not staff POS orders): LiveOrderDto now carries Source, set in MapLiveOrder (+ the delivery/snappfood mappers). 86 API tests pass; dashboard tsc + build clean. |
||
|
|
9b2f15151d |
feat(website): reflect new features + 5-tier pricing
CI/CD / CI · API (dotnet build + test) (push) Successful in 50s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 45s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m4s
CI/CD / CI · Admin Web (tsc) (push) Successful in 36s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 3m18s
- Pricing: add the Starter tier (now Free·Starter·Pro·Business·Enterprise), fix currency ₺ (Turkish Lira) → Toman, and rewrite every plan's bullets to the agreed matrix (Free: 6 tables/30 orders/Koja/offline + watermark; Starter: watermark-removal/custom-styling/review-reply; Pro: CRM/reports/taxes/payroll/ delivery/3 branches; Business: 3D + AI-3D + unlimited; Enterprise: API/white-label/ SLA/24-7). 5-column responsive grid. - Features: add two headliner cards that were missing — "Works offline" and "Get discovered on Koja". fa/en. Website tsc + build clean. |
||
|
|
7d06f149d3 |
feat(plans): menu watermark on Free (removed by paid feature)
Guest QR menu shows a "ساختهشده با میزی" watermark under the menu unless the café's
plan has the `watermark_removed` feature (Starter+).
- PublicMenuDto gains ShowWatermark; PublicService computes it from
IsFeatureEnabledForCafeAsync("watermark_removed") for both slug and branch menus.
- Guest menu renders the watermark footer when showWatermark.
- NoOpPlatformCatalogService test double (all features on) for the PublicService
ctor; QrMenuTests updated.
86 tests pass; dashboard tsc clean.
|
||
|
|
2487f9e30f |
feat(plans): Stage 3b — DB-driven gates for reviews/styling/limits
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m0s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 42s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 2m51s
Make more plan rules read the admin-editable catalog instead of hardcoded values: - Review reply gated by the `review_reply` feature (Starter+) — 403 if not in plan. - Custom menu styling gated by `custom_menu_styling` (Starter+): only blocks an actual theme change, so a normal settings save re-sending the current theme is fine. - Menu categories/items limits now read catalog.GetLimitsAsync (Free categories editable; message no longer hardcodes a number). - Terminals limit reads the catalog (enforcement in TerminalRegistryService + the displayed max in TerminalsController). Remaining (small): menu watermark (Free shows it, `watermark_removed` removes it — needs the public-menu render), report-history (static ReportPlanGate) and AI-3D routing — these already enforce the correct matrix values, just not yet editable. 86 tests pass; build clean. |
||
|
|
8f738f6469 |
feat(plans): Stage 4 — full admin plan/feature editor
The admin → Plans screen now edits EVERYTHING per plan (the backend already accepted it; only the UI was partial): - All limits (orders/day, tables, terminals, branches, menu categories, menu items, customers, report history, SMS, AI-3D) with an "unlimited (∞)" toggle. - Display names (fa/en), monthly price, sort order, billable-online, active on/off. - Per-plan feature checkboxes grouped by module, plus an "all features (*)" toggle (Enterprise). Sourced from the live feature catalog (/api/admin/features). - Plans listed in sort order (Free·Starter·Pro·Business·Enterprise). - i18n fa/en/ar. Admin tsc + build clean. |
||
|
|
7f52b2823f |
feat(plans): Stage 3a — enforce tables cap from the DB catalog
PlanLimitChecker already enforces orders/customers/branches/SMS from the
admin-editable catalog (GetLimitsAsync). Add the tables cap the same way
(POST /api/cafes/{cafeId}/tables → MaxTables), so Free's 6-table limit is both
enforced and admin-editable. Terminals/categories/report-history already enforce
the correct matrix values via PlanLimits defaults; routing them through the
catalog for editability + the watermark/styling/review-reply feature gates are
the remaining S3 items.
86 tests pass.
|
||
|
|
c5d5a4006a |
feat(plans): Stage 2 — seed 5-tier matrix + feature catalog (editable defaults)
- CanonicalPlans(): single source for Free·Starter·Pro·Business·Enterprise with the locked feature sets (Free is broad: KDS/queue/Koja/offline/reviews/reservations/ coupons/employees; Starter +watermark-removal/custom-styling/review-reply; Pro +CRM/ reports/taxes/HR/delivery/expenses/branches; Business +3D/AI-3D; Enterprise *). - Feature catalog: + offline, employees, watermark_removed, custom_menu_styling, review_reply, api, white_label. - New Starter plan (690k Toman default, billable, sort 1). - One-time, version-guarded matrix upgrade (catalog.planMatrixVersion=2): brings the existing (never-yet-admin-edited) prod plans to the canonical limits/features/order/ price and inserts Starter. Runs once; won't clobber later admin edits. - Replaced the additive feature-merge (which would wrongly re-add menu_3d to Pro). Defaults only — admins will be able to change everything in S4. 86 tests pass. |
||
|
|
4cb640814a |
feat(plans): Stage 1 — Starter tier + admin-editable limit model
Foundation for the configurable plan system (Free·Starter·Pro·Business·Enterprise). - PlanTier: append Starter=4 (no renumber → no data migration; existing tier ints keep meaning). Ordering/display via PlanDefinition.SortOrder; gating uses explicit tier sets, never `tier >= X`. - PlanLimits: locked 5-tier matrix — Free orders/day 50→30, tables (new) Free 6/ Starter 15/Pro 40/∞, categories Free 3→10, menu items now unlimited, terminals/ branches/report-history ladders incl. Starter; CRM/analytics = explicit Pro+; AI-3D = Business+. SMS quotas kept (Free/Starter 0, Pro 50, Business 200) until the pay-as-you-go credit system ships (don't break paid SMS). - PlanLimitsData (LimitsJson shape): + MaxTables/MaxMenuCategories/MaxMenuItems/ MaxMenuAi3dPerMonth; ForTier now derives from PlanLimits (single source of truth). No migration. 86 tests pass. Next: S2 seed 5 plans + feature catalog (editable), S3 wire enforcement to DB, S4 admin editor. |
||
|
|
4c98c2cce1 |
feat(auth): extend token lifetimes for long offline gaps
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m24s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 1m1s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 2m16s
A user can be offline for months (offline-first dashboard) and must stay logged in / be able to sync on reconnect. Access 7d→30d, refresh 30d→365d, so a ~3-month offline gap still has a valid refresh token on reconnect (queued writes sync, no forced logout). Client only logs out on a server 401, never while offline. |
||
|
|
db0c3a4a02 |
feat(hr): add employees from the dashboard
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m10s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 1m40s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 1m32s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 9m24s
Previously the only Employee records were the Owner (created at café signup) and
one Manager per branch — there was no way to add a waiter/cashier/chef. Adds it.
Backend:
- POST /api/cafes/{cafeId}/employees (HrController). Owner/Manager only; creating a
Manager requires Owner; Owner cannot be created here. Validates name/phone/role,
enforces one-employee-per-phone, validates branch belongs to the café, and can
optionally set username/password login in the same step (same hashing + uniqueness
as the credentials endpoint). Returns EmployeeSummaryDto.
Dashboard:
- New "Team" tab on the HR screen (now the default): employee roster (name, role,
phone, base salary) + an "Add employee" button (owner/manager) opening an inline
form — name, phone, role, optional branch, optional base salary, optional login.
- Role labels + all form strings in fa/en/ar.
86 API tests pass; dashboard tsc + build clean.
|
||
|
|
f1756b491e |
feat(admin): rich text editor for blog content (TipTap)
CI/CD / CI · API (dotnet build + test) (push) Successful in 3m33s
CI/CD / CI · Admin API (dotnet build) (push) Failing after 3m18s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Failing after 3m43s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Has been skipped
Blog post bodies were plain <textarea>s labelled "Markdown". Replace with a TipTap rich editor (bold/italic/strike, H1–H3, lists, blockquote, code, links, undo/redo), RTL-aware, producing HTML. - New RichTextEditor component (TipTap v2: react + starter-kit + pm + link + placeholder), immediatelyRender:false for Next SSR, self-contained content styling, external-value sync. - Wired into the FA/EN content fields of the blog editor; labels no longer say "Markdown" (fa/en/ar). - Website blog page now renders HTML when the body is HTML and falls back to MDXRemote for older Markdown posts (backward-compatible). Content is authored only by trusted SystemAdmins, so HTML is stored/rendered directly. Admin build + website typecheck clean. |
||
|
|
97a9481627 |
feat(media): content-hash dedup for uploads + media-library endpoint
Uploads previously wrote every file to disk with a fresh GUID name, so the
same image uploaded twice produced two identical files. Now:
- New MediaAsset table records each stored upload (SHA-256 hash, size, type,
url, kind, scope) + migration. Indexed on (CafeId, ContentHash).
- MediaStorageService computes the content hash on upload; if an identical file
already exists for that café it returns the existing URL instead of writing a
duplicate (covers images, videos, 3D models). Dedup lookup/record run via a
scoped DbContext (the service is a singleton) and never block an upload on
failure.
- GET /api/cafes/{cafeId}/media lists the café's library (newest first, optional
?kind=) so the UI can let users pick an existing file instead of re-uploading.
86 API tests pass.
|
||
|
|
eb165db182 |
feat(offline): make every dashboard write durable offline (P2–P5)
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m0s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 38s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 36s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 3m11s
Builds on the outbox engine to take the whole dashboard offline in one place
instead of wiring 114 mutation sites individually.
Frontend (single chokepoint = the API client):
- offline-write: any write auto-queues to the outbox on offline/network failure
and returns an optimistic value; the online path is unchanged apart from an
Idempotency-Key header (so even online retries de-dup). entityType is derived
from the URL; POSTs get a remappable local id.
- client.doWrite unifies POST/PUT/PATCH/DELETE through this path. WriteOptions
gains `offline: "queue" | "reject" | "manual"`.
- Guardrails: auth / billing / payments / SMS / exports are online-only and throw
OFFLINE_UNAVAILABLE offline rather than queueing (no queued double-charges or
surprise SMS blasts). use-api-error resolves the friendly localized message
(fa/en/ar).
- submit-order opts out ("manual") to keep its richer local-Order mock; shared
helpers de-duplicated into offline-write.
- Request persistent storage on mount so unsynced writes survive eviction.
Backend:
- IdempotencyCleanupJob: daily purge of idempotency records older than 7 days
(the table now gets a row per keyed write). Registered in Hangfire. No migration.
86 API tests pass; dashboard tsc + build clean.
|
||
|
|
3b468b48d9 |
feat(dashboard/offline): generic idempotent outbox + ID remapping
CI/CD / CI · API (dotnet build + test) (push) Successful in 48s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 53s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 3m12s
Completes offline Phase 1 (frontend). Generalises the POS-orders-only queue into a reusable write engine and fixes the two correctness bugs in the old path. - offline-db: generic `outbox` store (DB v3, order_queue/kv preserved) with enqueue/list/update/remove + a persisted client→server id map. - outbox.ts: drains in causal order — remaps local_* ids to server ids (blocking an op until its creator syncs), sends each op with its idempotency key, and classifies failures (offline → stop; 5xx / in-progress → retry; 4xx → poison after 5 attempts). remap/blocked logic validated against representative cases. - client: apiPost/Put/Patch/Delete take an optional idempotencyKey → `Idempotency-Key` header; ApiClientError now carries HTTP status. - submit-order: generates ONE idempotency key per submit, used for both the online attempt and the queued replay → server de-dups (no more double-create); offline create carries createsClientId so a later add-items remaps onto the real order instead of spawning a second order. - use-offline-sync: drains the outbox, one-time migrates legacy order_queue items, invalidates queries after a successful sync. tsc + production build clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
f4583f5169 |
feat(api/offline): idempotency-key middleware for safe write retries
Backend half of offline Phase 1. Lets the offline outbox replay a write after a
lost response without executing it twice (e.g. an order whose POST reached the
server but whose reply never came back).
- IdempotencyRecord entity + table (unique index on (Scope, Key)); migration
AddIdempotencyRecords. Standalone POCO — no tenant/soft-delete filters.
- IdempotencyMiddleware (after TenantMiddleware, before plan-limit/controllers):
opt-in via `Idempotency-Key` header on POST/PUT/PATCH/DELETE.
* Completed key → replays stored status+body with `Idempotent-Replay: true`.
* In-progress key → 409 IDEMPOTENCY_IN_PROGRESS; the unique index serializes
racing first requests; stale (>60s) reservations are recovered after a crash.
* Only <500 responses are cached; 5xx is released so the client can retry.
Bookkeeping runs in isolated DI scopes so it never contaminates the controller's
unit of work. Keys are scoped per café — no cross-tenant collisions.
- 5 middleware tests (replay/execute-once, distinct key, pass-through, tenant
isolation, 5xx-not-cached). Full suite 86 passing.
Next in Phase 1: generalize the POS order queue into a generic client outbox that
sends these keys and remaps client→server ids.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
132f0921e0 |
feat(dashboard/offline): persist React Query cache for offline reads
First slice of offline-first (Phase 1). Makes every dashboard area *viewable* offline with last-synced data, instead of empty lists on an offline reload (previously only next-pwa's 10-min API cache survived). - offline-db: add a generic `kv` IndexedDB store (DB v2, preserves order_queue) with kvGet/kvSet/kvDelete; all degrade silently on quota/unavailable. - query-persister: debounced snapshot of the React Query cache via dehydrate/hydrate (no new dependency). Restore is guarded by a schema buster, 24h max-age, and a café scope so one tenant never hydrates another's data. - providers: gcTime 24h so hydrated data isn't GC'd; restore on mount + persist on cache changes, re-scoped when the signed-in café changes. No write-path changes; the existing POS order queue is untouched. Next in Phase 1: generalize that queue into an idempotent outbox with client→server ID remapping. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
bb0be19dac |
feat(billing): queue subscriptions bought while active + cancel queued
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m1s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 49s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m3s
CI/CD / CI · Admin Web (tsc) (push) Successful in 34s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 48s
CI/CD / Deploy · all services (push) Successful in 3m9s
Before, buying a plan immediately switched the tier and stacked the duration.
Now a purchase made while the café still has paid coverage is QUEUED to start
when the current coverage ends, and the owner can cancel a queued one.
Model:
- SubscriptionPayment gains EffectiveFrom/EffectiveTo; status gains Scheduled
(paid, queued) and Cancelled. EF migration AddSubscriptionScheduling (nullable).
BillingService:
- On payment completion, compute coverage end (latest of active expiry + furthest
queued period). If it is in the future → Scheduled (queued, café tier/expiry
untouched); else activate immediately as before. Periods chain correctly.
- GetStatusAsync lazily promotes any due queued period to active, and returns the
queue (QueuedPlans).
- CancelQueuedAsync cancels a Scheduled period (owner-only) and re-packs the queue
so later periods slide earlier. Active prepaid plan is never cut short; no
automatic refund (manual, per product decision).
- Confirmation SMS distinguishes "activated until X" vs "queued, starts X".
API: BillingStatusDto.QueuedPlans + DELETE /api/billing/queued/{paymentId}.
Dashboard:
- Subscription screen shows a "Queued subscriptions" card (tier, window, cancel
with confirm).
- Checkout shows "you already have an active subscription — this will start on
{date}" when the café is still covered.
- i18n fa/en/ar.
81 API tests pass; dashboard typechecks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
15def7ff1c |
feat: delete actions for warehouse/reservations/coupons/customers + Koja listing toggle
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m10s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 52s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 55s
CI/CD / Deploy · all services (push) Successful in 3m29s
Delete (every manageable entity that only had "add" now has delete):
- Ingredients (warehouse): new DELETE /inventory/ingredients/{id} (soft-delete via
the global DeletedAt filter — no FK trouble with recipes/movements) + NoOp stub +
trash button in the materials cards.
- Reservations: new DELETE /reservations/{id} (soft-delete) + per-card delete button.
- Coupons & Customers: backend DELETE already existed; wired delete buttons in the UI.
- Shared ConfirmDialog component used by all delete flows (RTL-aware).
- Audit result: tables/branches/taxes/kitchen-stations/expenses/menu/terminals already
had delete; HR has no "add" so no delete needed; shifts intentionally excluded
(financial open/close records, not add-style entities).
Koja visibility:
- New Cafe.ShowOnKoja flag, default TRUE (DB default true so existing cafés stay
listed). Discover query now filters IsVerified && !Deleted && ShowOnKoja.
- public-profile GET/PUT expose showOnKoja; dashboard public-profile panel has an
on-by-default toggle that persists immediately. Platform IsVerified gate unchanged.
- EF migration AddCafeShowOnKoja (defaultValue: true).
Also: added the missing errors.generic i18n key (fa/en/ar) so useApiError's fallback
resolves instead of rendering the literal "errors.generic". 81 API tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
60e2ac1355 |
fix(auth): non-rotating, sliding refresh tokens to stop the OTP storm
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m53s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 1m37s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 1m8s
CI/CD / Deploy · all services (push) Successful in 1m40s
Login already issues a 7-day access token + 30-day refresh token, and the dashboard persists the session and silently refreshes on 401 — so a session should last well over a week. The real cause of "re-login every time / massive OTP" was single-use refresh-token rotation: RefreshAsync revoked the presented token and minted a new one, so when a café runs POS + KDS + queue display at once (or two tabs), the first refresh won the race and every other concurrent refresh hit the now-revoked token -> INVALID_TOKEN -> forced logout -> OTP. Make refresh idempotent and race-safe: - IssueTokensAsync takes an optional existingRefreshToken; on refresh we reuse the presented token and re-store it (sliding the 30-day TTL) instead of minting a new one. Login still mints a fresh token. - RefreshAsync no longer revokes the presented token. Net effect: concurrent refreshes all succeed; an active session slides forward and effectively never forces re-auth. Access stays 7 days, refresh 30 days. All 81 API tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
a37d93f6cd |
fix(ui): force dir=ltr on remaining RTL pill toggles
CI/CD / CI · API (dotnet build + test) (push) Successful in 45s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 1m1s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m4s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 5m43s
The branch-menu-overrides availability switch (dashboard) and the BlogToggle (admin website editor) still moved their knob with translate-x while inheriting RTL, so the knob escaped the track on the right. Pin both to dir="ltr" like the other switches. All four role="switch" toggles in the codebase now share the fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
7122df57b2 |
feat(menu): delete category/item + fix RTL availability toggle
CI/CD / CI · API (dotnet build + test) (push) Successful in 56s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 58s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m4s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 3m18s
- Add DELETE /api/cafes/{cafeId}/menu/items/{id} (DeleteItemAsync soft-delete,
mirroring the existing category delete) — item delete had no backend route.
- Dashboard menu admin: destructive "delete" action in the item and category
edit modals, behind a shared confirm dialog (AlertDialog). Deleting the
selected category falls back to "all items".
- Fix the availability ToggleSwitch in RTL: force dir="ltr" so the knob's
translate-x stays inside the track instead of escaping on the right
(same fix as the admin-panel toggles).
- i18n: deleteItem/deleteCategory confirm + success strings (fa/en/ar).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
72f95aa0db |
fix(demo-seed): stop truncating ingredient/table ids to 36 chars
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m9s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 47s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Successful in 36s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 1m24s
BuildDemoIngredients/BuildDemoTables built ids as
"{cafeId}_ing_{guid}"[..36]. For a real cafe (32-char hex id) the
first 36 chars are just "{cafeId}_ing" — the unique guid is cut off,
so all 15 ingredients (and all 10 tables) get the SAME id, causing a
primary-key collision on SaveChanges -> 500. cafe_demo_001 has a short
id so the guid survived, which is why the bug only hit real cafes.
The Id columns are text (no length limit), so the truncation served no
purpose. Removed [..36] from both so the full unique id is kept.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
bab3453e41 |
fix(auth): read role claim under mapped name so Owner/Manager gates work
CI/CD / CI · API (dotnet build + test) (push) Successful in 43s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 32s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m4s
CI/CD / CI · Admin Web (tsc) (push) Successful in 36s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 1m27s
ROOT CAUSE of demo-seed/billing/etc. returning 403 for real owners: .NET's JWT
handler remaps the short "role" claim to ClaimTypes.Role on inbound, so
TenantMiddleware's FindFirst("role") returned null and tenant.Role (EmployeeRole?)
stayed null. EnsureManager/EnsureOwner then rejected even a valid Owner token with
MANAGER_REQUIRED / OWNER_REQUIRED, while reads (no role gate) worked and
[Authorize(Roles=...)] worked (it reads the remapped claim). Now reads the role
under both MeeziClaimTypes.Role ("role") and ClaimTypes.Role. Same fix applied to
the AuthController whoami role. Fixes demo seed, subscription billing, and every
other tenant.Role-gated action.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
24da1e0522 |
feat(orders): per-item kitchen/bar notes (POS + QR app + KDS)
CI/CD / CI · API (dotnet build + test) (push) Successful in 57s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 57s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 2m43s
Lets the POS agent and the QR/app customer attach a free-text note to each order line (e.g. "no tomato", "extra hot") that reaches the kitchen/bar. - Backend already supported it (OrderItem.Notes persists; CreateOrderItemRequest and OrderItemDto carry Notes; LiveOrderDto items include it) — this wires the UI. - cart.store: add setNotes(menuItemId, notes); notes already travel in getPendingLines and round-trip via hydrateFromOrder. - POS pos-screen: a note input under each cart line. - QR guest menu: a note input under each cart line (QrCartLine.note). - KDS: render the note prominently under each item so kitchen/bar sees it. - i18n: pos.itemNotePlaceholder + qrMenu.itemNote (fa/ar/en). Note: notes are captured on items being added; editing a note on an already-submitted line is out of scope (no pending delta to re-send). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
2203ecbdaf |
fix(koja): remove over-broad short-URL redirect that 500'd the home page
CI/CD / CI · API (dotnet build + test) (push) Failing after 3m18s
CI/CD / CI · Admin API (dotnet build) (push) Failing after 2m22s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 36s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Has been skipped
The redirect source "/:slug([a-z0-9][a-z0-9-]*[a-z0-9])" matched single-segment paths including the locale itself, so /fa redirected to /fa/cafe/fa (slug "fa") and /en to /fa/cafe/en — non-existent cafés that returned Internal Server Error. Visiting koja.meezi.ir (-> /fa) hit this. Removed the redirect so the home page renders; short café URLs can be re-added via middleware with reserved-word guards. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
1aaab6c593 |
fix(admin): integrations save uses rendered list (fixes dropped Zarinpal merchantId)
CI/CD / CI · API (dotnet build + test) (push) Successful in 58s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 33s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 1m42s
The integrations form rendered from (gateways state, falling back to fetched data) but SAVED from the state and edited via updateGateway on . If gateways hadn't hydrated, edits (e.g. Zarinpal merchantId) were written to an empty array and the save sent nothing. Now updateGateway seeds from fetched data on first edit, and the save maps over — render, edit, and save share one source. NOTE: prod admin had also been stale because recent deploys aborted on the main-API crash before the admin containers restarted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
09bba5f8cd |
fix(seed): count soft-deleted rows + make platform seeding non-fatal
CI/CD / CI · API (dotnet build + test) (push) Successful in 45s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 33s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m4s
CI/CD / CI · Admin Web (tsc) (push) Successful in 34s
CI/CD / CI · Website (tsc) (push) Successful in 43s
CI/CD / CI · Koja (tsc) (push) Successful in 48s
CI/CD / Deploy · all services (push) Successful in 1m20s
Root cause of the crash-loop: a soft-deleted Free plan still occupies its Tier in the unique index, but the existing-row check queried THROUGH the soft-delete global filter and missed it, so the seeder re-inserted Free and violated IX_PlatformPlanDefinitions_Tier on boot. Fixes: (1) IgnoreQueryFilters() on the plan/feature existing-checks so soft-deleted tiers/keys are counted; (2) wrap plan/feature/location seeding in try/catch so any seeding failure logs and startup continues — non-essential seeding must never crash-loop the API. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
3b8dcf3af6 |
fix(seed): dedupe plans by Tier and features by Key (hotfix crash-loop)
CI/CD / CI · API (dotnet build + test) (push) Successful in 2m39s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m2s
CI/CD / CI · Admin Web (tsc) (push) Successful in 34s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 1m27s
The previous change deduped on Id, but the unique constraints are on PlatformPlanDefinitions.Tier and PlatformFeatures.Key. Prod's existing Free plan has a different Id, so seeding re-inserted a Free-tier row and crashed on IX_PlatformPlanDefinitions_Tier (23505), crash-looping the API. Now skips any tier/key that already exists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
087563bce7 |
feat(settings): use-my-current-location button; surface ticket-load error
CI/CD / CI · Admin API (dotnet build) (push) Successful in 52s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 36s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / CI · API (dotnet build + test) (push) Successful in 41s
CI/CD / Deploy · all services (push) Failing after 2m34s
Location card gets a 'موقعیت فعلی من' button that fills lat/lng from the browser's geolocation. Support ticket list now shows the resolved (localized) error instead of a generic message, so a failure is diagnosable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
e839db7331 |
fix(koja): default to fa (no browser locale guess); guard null discoverProfile
Koja auto-detected locale from the browser Accept-Language (en for many Persian users); set localeDetection:false so locale-less URLs default to fa. Also guarded cafe.discoverProfile across the cafe page, cafe card, and JSON-LD — a café without a discover profile crashed the page (500). The cafe page now resolves the café first and notFound()s an unknown slug before fetching menu/reviews. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
a83edf7667 |
fix: seed all plans/features in prod (upsert); fix admin toggle RTL knob
CI/CD / CI · API (dotnet build + test) (push) Successful in 50s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 39s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Failing after 5m44s
Plan + feature seeding was dev-gated and all-or-nothing, so production only had the Free plan (admin Plans page showed one). Now runs in every environment and upserts missing rows (adds Pro/Business/Enterprise on top of the existing Free). Also force LTR on the admin toggle switch so the knob doesn't render off-track under the RTL page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
75d5bbc84a |
fix(i18n): localize API error messages by code (no more raw English)
Error toasts surfaced the raw English backend message. Added an errors namespace (fa/ar/en) keyed by error code + a useApiError() resolver that maps ApiClientError.code to the localized message (fallback to a localized generic). Wired into menu, tables, demo banner, and subscription checkout; hardened getErrorMessage so it never returns the raw backend message. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
7519f474f3 |
fix(demo): allow Manager to seed demo data + surface seed errors
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m1s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 46s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 3m15s
The dashboard demo-data banner is shown to Owner and Manager, but the /demo/seed endpoint required strictly Owner, so a Manager clicking it got a silent 403 (the banner had no error handler) — appearing as 'nothing happens, no tables or items'. The endpoint now allows Owner or Manager, and the banner shows the error on failure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
35494d8b32 |
fix(i18n): keep locale on website->dashboard links; dashboard defaults to fa
Marketing-site login/register/dashboard links were locale-less (app.meezi.ir/login), so the dashboard auto-detected locale from the browser Accept-Language (en-US) and redirected Persian users to /en. Links now carry the current locale, and the dashboard sets localeDetection:false so any locale-less entry defaults to fa (Iran-first) instead of guessing from the browser. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
4c7783884c |
feat(map): backfill café coordinates from city on startup (prod-safe)
CI/CD / CI · API (dotnet build + test) (push) Successful in 56s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 32s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 36s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 48s
CI/CD / Deploy · all services (push) Successful in 1m40s
Real cafés without a map pin now get approximate coordinates at their city centre (with a deterministic per-café offset) on every boot, in all environments, so the public Iran map lights up with merchant dots. Only fills rows where Latitude/Longitude is null and the city is recognised (20 major Iranian cities); never overwrites an owner-set pin. Owners can drop an exact pin from Settings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
8ce0b3e3e8 |
feat(discover): seed showcase café coordinates so the map shows blinking lights
CI/CD / CI · API (dotnet build + test) (push) Successful in 40s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 32s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m4s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 48s
CI/CD / Deploy · all services (push) Successful in 4m11s
Showcase cafés (dev/staging only) now get Latitude/Longitude scattered around their real city (Tehran/Karaj) with a deterministic per-id offset, so the homepage Iran map renders a realistic cluster of blinking merchant lights. Backfills existing rows where coords are null. Production cafés get coordinates when owners set their location in dashboard Settings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
b5a6b1b68d |
fix(website): accurate Iran border on homepage map + slow on/off marker blink
CI/CD / CI · API (dotnet build + test) (pull_request) Successful in 46s
CI/CD / CI · Admin API (dotnet build) (pull_request) Successful in 31s
CI/CD / CI · Dashboard (tsc) (pull_request) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (pull_request) Successful in 35s
CI/CD / CI · Website (tsc) (pull_request) Successful in 45s
CI/CD / CI · Koja (tsc) (pull_request) Successful in 51s
CI/CD / Deploy · all services (pull_request) Has been skipped
Replaced the rough 40-point hand-drawn polygon with the real national border (74 vertices, Natural Earth via world.geo.json) and fitted the projection bounding box to Iran's true extent, so the silhouette is recognisable and café markers stay aligned. Reworked the marker animation from a radar-style expanding ring into a slow 3.6s ease-in-out lamp fade (opacity 1->0.2->1) with a halo that glows on and off in sync. Verified via the SVG timeline: opacity 1.0 at 0s, 0.2 at 1.8s, 1.0 at 3.6s. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
f813cc4854 |
test: repair test suite broken by feature drift (red -> 81 passing)
CI/CD / CI · API (dotnet build + test) (pull_request) Successful in 43s
CI/CD / CI · Admin API (dotnet build) (pull_request) Successful in 44s
CI/CD / CI · Dashboard (tsc) (pull_request) Successful in 1m7s
CI/CD / CI · Admin Web (tsc) (pull_request) Successful in 36s
CI/CD / CI · Website (tsc) (pull_request) Successful in 45s
CI/CD / CI · Koja (tsc) (pull_request) Successful in 51s
CI/CD / Deploy · all services (pull_request) Has been skipped
The test project no longer compiled: recent feature commits changed interfaces and DTOs without updating the test doubles/call sites, so the whole suite (and therefore CI) was failing to build. - NoOpInventoryService: add IInventoryService.GetPurchasesSummaryAsync and the new string? userId param on AdjustAsync. - NoOpLoyaltyService: add ILoyaltyService.RedeemOnOrderAsync. - NoOpOrderNotificationService: add NotifyCallWaiterAsync. - New NoOpAbuseProtectionService and NoOpMediaStorageService test doubles. - QrMenuTests: ReviewService/PublicService gained IAbuseProtectionService + IHttpContextAccessor (and ReviewService an IMediaStorageService); wire the new no-op doubles + a real HttpContextAccessor. - PrintingTests: OrderDto gained a DisplayNumber int between CreatedAt and Items; pass it. - DiscoverFilterTests: add missing `using Xunit;` and the new openNow arg on DiscoverFilterParams.FromQuery. Result: dotnet test -> Passed: 81, Failed: 0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
024a455ab3 |
fix: menu item/category create, demo banner reach, token refresh, blog publish
Dashboard & API bug fixes for owner-reported breakage: - MenuController validators (PosValidators): NameEn was required but the dashboard sends null when blank, so every manual menu-item create failed and category create failed 100% (the form never sends nameEn). Now optional. - DemoDataBanner: only showed when a cafe was exactly empty, so showcase-seeded cafes (2-3 cats / 3-5 items) could never trigger the one-click seed. Widened gate to sparse menus (<5 cats && <10 items) and added a clear "nothing to add" message when already populated. - client.ts: added one-time JWT refresh-and-retry on 401 (shared in-flight promise) before bouncing to /login. Expired access tokens silently broke ticket list, add-table, and other reads. - Surface API errors as toasts on menu + table mutations (were swallowed silently, so failures looked like "nothing happens"). - Admin blog editor: saving an edit dropped IsPublished (defaulted false, silently unpublishing the post on every save); now persisted with a toggle. Also hoisted the inner Field component to module scope - it was remounting every input on each keystroke and dropping focus. - Admin integrations: replaced raw radio gateway selector with a styled RadioDot matching the iOS toggles. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
f687178238 |
fix(migration): add [Migration] attribute so EF discovers AddCafeLocation
CI/CD / CI · API (dotnet build + test) (push) Successful in 57s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 46s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 1m27s
Manual migration was missing the [Migration("...")] and [DbContext] attributes
that EF Core requires to discover and apply migrations via MigrateAsync().
Without them the Latitude/Longitude columns were never added to Cafes, causing
every query involving the Cafe entity to throw 42703 column-not-found errors.
Columns must be applied manually on the server before the next deploy:
ALTER TABLE "Cafes" ADD COLUMN IF NOT EXISTS "Latitude" double precision, ...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|