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>
* fix(entitlements): show paid features as gated upsells instead of dead ends
Post-cutover, non-payers still saw fully interactive UI for paid features
(bank picker, agent-build hero, SKV VAT submission) that silently failed or
403'd on the server gate. Every surface now stays visible as a conversion
surface but is explicitly gated:
- new shared components/billing/UpgradeNote (lock icon + billing link)
- agent-build hero (dashboard + new-user checklist): routes to
/settings/billing with upgrade copy when the ai capability is missing
- bank connect: BankingSettingsPanel and the import-page PSD2 wizard swap
the bank list for an upgrade note; the import selection card swaps the
"Rekommenderat" chip for "Kräver abonnemang"
- VAT report SkatteverketPanel: gated state renders before the
connection check, so trial-connected companies see the upsell instead
of action buttons that would 403; manual-filing framing kept
- SkatteverketConnectPanel: skahmst consent note hidden while gated
(irrelevant until the consent page is reachable)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(ne-bilaga): fix time-of-day flake in SRU field-code assertion
The bare substrings '7310'/'7350' also match the #SKAPAD HHMMSS timestamp
when CI runs at 07:31/07:35, so the assertion now requires the full
'#UPPGIFT <code>' prefix.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>