feat(billing): make the expired-trial state visible with a clear upgrade path (#1725)
getCompanyEntitlements now derives an entitlementState (trial / trial_expired / lapsed_subscription / paid / none) plus trialExpiredAt from the grants it already fetches, reading company_subscriptions.status inside the existing Promise.all so churned payers get 'abonnemang' copy instead of 'provperiod'. The state threads through CompanyContext and the dashboard layout. Two new surfaces, both hidden in sandbox: - SubscriptionTouchpoint replaces the sidebar trial pill: countdown while the trial runs, a persistent muted upgrade link to /settings/billing once it lapses (visible even collapsed, icon-only with aria-label), and the first mobile bottom-sheet touchpoint. - TrialExpiredDialog: one-time on-entry notice with 'Se abonnemang' and a ghost dismiss; acknowledgement persists per user+company in user_preferences.ui_state.trial_expired_ack (read server-side, no flash), set on dismiss and click-through alike. Narrows the 2026-07-11 'no trial-expired nag' decision at the founder's direction after a user could not find the upgrade path at all; see DECISIONS.md. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Jakob Wennberg
Claude Fable 5
parent
834cc4d0e8
commit
c402421908
@@ -1086,3 +1086,4 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
|
||||
[2026-08-19] Inline rättelse bank guard anchors to the linked bank amount, per account, not to the pre-state and not to the 19xx group net: a non-zero change on a 19xx/cash-ledger account is allowed iff the post-state net on that account equals the signed sum of the linked transactions resolved to it (once per transaction, split links by allocated_amount; NULL cash_account_id resolves to the primary cash account, then 1930). Per account rather than group so a wrong-bank-account booking (1930 vs 1940) stays a storno job: a group check would let the net drift between accounts and break per-account bank reconciliation. When no anchor resolves the old strict refusal stands. Reskontra sides (15xx/24xx) stay strictly net-preserving because their anchor is the payment row, not a bank amount.
|
||||
[2026-08-19] Import mapping step gets a bulk "Bekräfta alla föreslagna" for the VAT-treatment review gate, batching the per-row confirm semantics unchanged (defaults kept, rows marked reviewed): a Fortnox chart routinely puts 70+ class 3/4 accounts behind the gate and the one-click-per-row flow across 50-row pages was an observed live migration dead end (Boltonshield 2026-08-18, stuck at "50 kvar"). Rejected: auto-skipping review for accounts unused by the imported vouchers, because the chart rows are still created with the suggested treatment and a silently wrong default on a soon-used account is exactly what the review gate exists to catch.
|
||||
[2026-08-19] Dialog overflow hardening: Dialog/Sheet titles and descriptions get break-words at the primitive; AccountCombobox's non-flat dropdown is portaled to document.body with viewport-clamped geometry (same rationale as info-tooltip's TooltipContent portal, since DialogContent's overflow-y-auto otherwise grows a horizontal scrollbar around the 34rem panel); the four rattelse-family dialog explainers are unified behind one RattelseExplainer HelpPopover (convention 7, MatchVoucherDialog precedent) instead of four near-duplicate inline paragraphs; a dialog-overflow-risk ratchet in no-new-antipatterns.mjs keeps bare-1fr tracks, dialog whitespace-nowrap and unportaled wide overlays from coming back.
|
||||
[2026-08-19] Trial-expired visibility: the 2026-07-11 "no trial-expired nag" call is narrowed at the founder's direction (user-reported discoverability failure 2026-08-18): one on-entry dialog with persisted acknowledgement (user_preferences.ui_state.trial_expired_ack, per user AND company, never localStorage) plus a non-dismissable quiet chrome link (SubscriptionTouchpoint replaces the vanishing countdown pill, stays visible when the rail is collapsed, and adds the first mobile touchpoint). Free tier stays a legitimate resting state: no recurring nag, muted chrome tone, no ochre. getCompanyEntitlements also reads company_subscriptions.status inside its existing Promise.all (zero extra round-trips) so churned payers get "abonnemang" copy instead of "provperiod". Hem AttnLine variant skipped: the page already carries the otherAccountHint AttnLine and design.md allows max one per page.
|
||||
|
||||
Reference in New Issue
Block a user