All dates now use day-first ordering (14 Mar 2025) and 24-hour time
(14:30) via a single formatDate.ts module, replacing scattered inline
toLocaleDateString/toLocaleTimeString calls across 12 files.
Show the free tier (€0, 1 site, 5k pageviews, 6 months retention)
as the first card on the pricing page. Enforce a 1-site limit for
free plan users in the frontend.
The modal was rendered inside <main> which is a sibling of the
fixed header. Browser compositing didn't apply backdrop-blur
across the header's separate GPU layer. Using createPortal to
render at document.body matches how the delete account modal
works (rendered as sibling to header via SettingsModalWrapper).
Header uses fixed z-50 with backdrop-blur which creates its own
stacking context. z-[60] wasn't enough, z-[100] matches the
pattern used by VerificationModal.
Modal now says "Delete Ciphera?" instead of generic "Delete Organization?".
Subscription cancellation card now shows for any paid plan (solo/team/business)
regardless of subscription_status, which could be 'active' or 'trialing'.
Red border, frosted overlay, destruction items as prominent
red-bordered cards with warning icons instead of small bullet
points. Button text changed to "Delete Forever".
The delete button fired loadSubscription() without awaiting it,
so the modal opened with subscription=null and the destruction
summary (sites count, active subscription) never rendered.
Lists what will be deleted: site count with analytics data,
member count, active subscription, and notification settings.
Replaces the generic warning with specific impact details.
Switch from blocklist (strip known-bad params) to allowlist (only keep
UTM/attribution params). Eliminates cache-busters like _t and _ from
page paths without maintaining an ever-growing blocklist.
Swap the old dot grid overlay inside the Recharts SVG for a GridPattern
component rendered behind the chart card. Uses a vertical mask gradient
to fade edges for a cleaner look.
Smooth out the jarring visual pop when loading skeletons are replaced
by real content. Only animates after an actual skeleton was shown —
cached data still renders instantly with no delay.
Replace manual useState/useEffect fetch patterns with SWR hooks so
cached data renders instantly on tab revisit. Skeleton loading now
only appears on the initial cold load, not every navigation.
New hooks: useFunnels, useUptimeStatus, useGoals, useReportSchedules,
useSubscription — all with background revalidation.
After refreshing the base token, call switch-context to get an
org-scoped token. This prevents 403 errors on Pulse API requests
when the access token is refreshed mid-session.
Token refresh race condition: when multiple requests got 401 simultaneously,
queued retries reused stale headers and the initiator fell through without
throwing on retry failure. Now retries regenerate headers (fresh request ID
and CSRF token), and both retry failure and refresh failure throw explicitly.
SWR cache is now invalidated after token refresh so stale error responses
are not served from cache.