The platform no longer sells SMS. Each café saves its OWN Kavenegar API
key + sender line (new Cafes columns + migration) and campaigns are sent
and billed through that account.
Backend:
- GET/PUT /sms/settings (Manager/Owner; key echoed masked, verified
against the provider before saving)
- campaign + balance use the café's credentials; SMS_NOT_CONFIGURED
error when missing; plan-tier SMS gating removed everywhere
(PlanLimitChecker, SmsMarketingService, billing status)
- platform Kavenegar config stays ONLY for login OTPs (env/DB)
- design-time DbContext factory so `dotnet ef migrations add` works
without booting the host
Dashboard:
- SMS screen: provider-settings card, not-configured callout, campaign
form disabled until configured; quota bar removed (usage stays as info)
- subscription screen + plan comparison no longer show SMS limits
Admin panel:
- Kavenegar/SMS section removed from integrations (request field now
optional; stored OTP config untouched)
- SMS limit field removed from the plan editor
- nav label "درگاه و پیامک" → "درگاه پرداخت و AI"
fa/en/ar translations. 86 tests pass; all tsc clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
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.
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>
Initial commit of the Super-Admin web panel (Next.js + TypeScript).
CI admin-web-check job was failing because the directory was never
tracked in git.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>