diff --git a/DECISIONS.md b/DECISIONS.md index a6e878d8..c2c510ee 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -1086,3 +1086,4 @@ One line per decision: `[YYYY-MM-DD] : `. 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. diff --git a/app/(dashboard)/layout.tsx b/app/(dashboard)/layout.tsx index 87fe97ae..f7e3ca60 100644 --- a/app/(dashboard)/layout.tsx +++ b/app/(dashboard)/layout.tsx @@ -11,6 +11,7 @@ import LazyCommandPalette from '@/components/common/LazyCommandPalette' import { SettingsHotkey } from '@/components/settings/SettingsHotkey' import { SessionTimeoutController } from '@/components/auth/SessionTimeoutController' import { SandboxBanner } from '@/components/dashboard/SandboxBanner' +import TrialExpiredDialog from '@/components/billing/TrialExpiredDialog' import { getExtensionNavItems } from '@/lib/extensions/sectors' import { CompanyProvider } from '@/contexts/CompanyContext' import { getCompanyEntitlements } from '@/lib/entitlements/has-capability' @@ -107,6 +108,8 @@ export default async function DashboardLayout({ isSandbox: false, capabilities: [], trialEndsAt: null, + entitlementState: 'none' as const, + trialExpiredAt: null, }} > @@ -231,6 +234,8 @@ export default async function DashboardLayout({ isSandbox: false, capabilities: [], trialEndsAt: null, + entitlementState: 'none' as const, + trialExpiredAt: null, } return ( @@ -314,6 +319,8 @@ export default async function DashboardLayout({ isSandbox, capabilities: entitlements.capabilities, trialEndsAt: entitlements.trialEndsAt, + entitlementState: entitlements.entitlementState, + trialExpiredAt: entitlements.trialExpiredAt, } return ( @@ -361,6 +368,19 @@ export default async function DashboardLayout({ {children}