PRODUCT.md anti-references call out the SaaS-template / emoji-in-chrome
register: the reference products (Linear, Stripe, Things 3) don't put
platform emoji in product chrome. Three small drift spots:
- Balance saved-cards link used a 💳 emoji as the leading icon.
Replaced with a new CreditCardIcon added to the barrel (Heroicons-
style stroke, matches the chevron / chat / wallet siblings).
- Subscription paused-info banner used a ⏸️ emoji. Replaced with a
new PauseIcon added to the barrel. Same 24×24 stroke, currentColor,
preserves the urgent-400 tint via inline style.
- Balance payment-method min/max range used a literal en-dash ('–')
in source. PRODUCT.md's anti-em-dash rule covers the en-dash by
the same logic. Switched to a translated 'to' connector
(t('common.rangeTo', 'to')) so other locales can translate freely.
DESIGN.md 'Tunable-but-Scarce Rule' caps accent at <=10% of any
screen and reserves the accent + status hues for action / status,
never decoration. Four sites were spending those tokens on pure
chrome:
- Subscription.tsx purchased-traffic bar: zone.mainHex linear-gradient
on a non-status progress bar; the per-purchase bar inherited the
user's GLOBAL traffic-zone color (a fresh purchase could read
'critical' just because the user's overall usage was). Replaced
with solid accent-500 fill — same affordance, honest semantics.
- SubscriptionCardActive tariff badge: zone-colored linear-gradient
background + zone-colored label. The tariff name has no traffic-zone
semantics, so tinting it by the global traffic zone was a
Status-Hue Lockout violation. Switched to glass innerBg/innerBorder.
- Balance hero card: bg-gradient-to-br from-accent-500/10 + glow prop
on Card. Removed; flat surface, the giant numeric is the affordance.
Eliminates the SaaS hero-metric template tell.
- Login background: two stacked fixed inset gradients (linear +
accent radial halo) read as the airdrop / crypto aesthetic
PRODUCT.md explicitly anti-references. Replaced with the plain
body bg-dark-950.
- Balance page: handle select_subscription response — show subscription
picker when promo code adds days and user has multiple tariffs
- API: activatePromocode now accepts optional subscriptionId
- AdminPromocodeCreate: show trial tariff info when creating
trial_subscription promo code, warn if no trial tariff configured
- Move PAID_STATUSES/FAILED_STATUSES from Balance.tsx and TopUpResult.tsx
to src/utils/paymentStatus.ts
- Eliminates code duplication between the two pages
Add overpaid (PAL24) to PAID_STATUSES, system_fail and refund_paid
(Heleket) to FAILED_STATUSES. Harmonize Balance.tsx redirect detection
with the same full status sets and case-insensitive matching.
Replace toast-based payment return with animated result page showing
pending/success/failed/timeout states. Extract shared Spinner,
AnimatedCheckmark and AnimatedCrossmark components. Add sessionStorage
persistence with validation and TTL for cross-redirect payment data.
Unify ProtectedRoute with withLayout prop, add aria-live accessibility.
- WebSocketNotifications: invalidate purchase-options on all 10 balance-affecting events
- Dashboard: invalidate purchase-options after trial activation
- Balance: invalidate purchase-options after payment return and promocode activation
- PromoOffersSection: invalidate purchase-options and balance after promo claim
Replace all bare useAuthStore(), useBlockingStore(), and
useSuccessNotification() calls with individual field selectors.
This prevents components from re-rendering when unrelated store
fields change.
- 21 useAuthStore usages across 20 files: individual selectors
for 1-2 fields, useShallow for 3+ fields
- 5 useBlockingStore usages: individual selectors
- 2 useSuccessNotification usages: individual selectors
- Add /balance/top-up route for payment method selection
- Add /balance/top-up/:methodId route for amount entry and payment
- Remove TopUpModal component (619 lines)
- Simplify InsufficientBalancePrompt to use navigate instead of modals
- Support ?amount and ?returnTo query params for cross-page data flow
- Fix nav isActive to highlight Balance on sub-routes
- Remove unused MainButton platform abstraction
- Increase toast accent opacity for better visibility
Major changes:
- Redesign cabinet with Linear-style components and top navigation
- Replace detail modals with dedicated pages (users, broadcasts, email templates)
- Add email channel support for broadcasts
- Remove pull-to-refresh, improve drag-and-drop on touch devices
- Fix Telegram Mini App: fullscreen, back button, scroll restoration
- Unify admin pages color scheme to design system
- Mobile-first improvements: horizontal tabs for settings, better touch targets