diff --git a/CHANGELOG.md b/CHANGELOG.md index b9c956b..f9ec287 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,26 +8,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [0.11.0-alpha] - 2026-02-22 -### Changed +### Added -- **Smoother loading experience.** Pages now show a subtle preview of the layout while data loads instead of a blank screen or spinner. This applies everywhere โ€” dashboards, settings, uptime, funnels, notifications, billing, and detail modals. -- **No more loading flicker.** Fast-loading pages no longer flash a loading state for a split second before showing content. -- **Clearer error messages.** When something goes wrong, the error message now tells you what failed (e.g. "Failed to load uptime monitors") instead of a generic "Failed to load data". -- **Faster favicon loading.** Site icons in the dashboard, referrers, and campaigns now use Next.js image optimization for better caching and lazy loading. - **Better page titles.** Browser tabs now show which site and page you're on (e.g. "Uptime ยท example.com | Pulse") instead of the same generic title everywhere. - **Link previews for public dashboards.** Sharing a public dashboard link on social media now shows a proper preview with the site name and description. - **Faster login redirects.** If you're not signed in and try to open a dashboard or settings page, you're redirected to login immediately instead of seeing a blank page first. Already-signed-in users who visit the login page are sent straight to the dashboard. - **Graceful error recovery.** If a page crashes, you now see a friendly error screen with a "Try again" button instead of a blank white page. Each section of the app has its own error message so you know exactly what went wrong. - **Security headers.** All pages now include clickjacking protection, MIME-sniffing prevention, a strict referrer policy, and HSTS. Browser APIs like camera and microphone are explicitly disabled. - **Better form experience.** Forms now auto-focus the first field when they open, text inputs enforce character limits with a visible counter when you're close, and the settings page warns you before navigating away with unsaved changes. +- **Accessibility improvements.** The notification bell, workspace switcher, and all dashboard tabs are now fully keyboard-navigable. Screen readers announce unread counts, active organizations, and tab changes correctly. Decorative icons are hidden from assistive technology. +- **Smooth organization switching.** Switching between organizations now shows a branded loading screen instead of a blank flash while the page reloads. + +### Changed + +- **Smoother loading experience.** Pages now show a subtle preview of the layout while data loads instead of a blank screen or spinner. This applies everywhere โ€” dashboards, settings, uptime, funnels, notifications, billing, and detail modals. +- **Clearer error messages.** When something goes wrong, the error message now tells you what failed (e.g. "Failed to load uptime monitors") instead of a generic "Failed to load data". +- **Faster favicon loading.** Site icons in the dashboard, referrers, and campaigns now use Next.js image optimization for better caching and lazy loading. - **Tighter name limits.** Site, funnel, and monitor names are now capped at 100 characters instead of 255 โ€” long enough for any real name, short enough to not break the UI. +- **Stricter type safety.** Eliminated all `any` types and `@ts-ignore` suppressions across the codebase, so the TypeScript compiler catches more bugs at build time. ### Fixed +- **No more loading flicker.** Fast-loading pages no longer flash a loading state for a split second before showing content. - **Organization context switch.** Switching away from a deleted organization now stores the session correctly instead of using an insecure fallback. - **Removed debug logs.** Auth and organization-switching details no longer leak into the browser console in production. -- **Stricter type safety.** Eliminated all `any` types and `@ts-ignore` suppressions across the codebase, so the TypeScript compiler catches more bugs at build time. -- **Accessibility improvements.** The notification bell, workspace switcher, and all dashboard tabs are now fully keyboard-navigable. Screen readers announce unread counts, active organizations, and tab changes correctly. Decorative icons are hidden from assistive technology. ## [0.10.0-alpha] - 2026-02-21 diff --git a/app/layout-content.tsx b/app/layout-content.tsx index 1f50e03..14d3b49 100644 --- a/app/layout-content.tsx +++ b/app/layout-content.tsx @@ -10,14 +10,30 @@ import Link from 'next/link' import { useEffect, useState } from 'react' import { getUserOrganizations, switchContext } from '@/lib/api/organization' import { setSessionAction } from '@/app/actions/auth' +import { LoadingOverlay } from '@ciphera-net/ui' import { useRouter } from 'next/navigation' +const ORG_SWITCH_KEY = 'pulse_switching_org' + export default function LayoutContent({ children }: { children: React.ReactNode }) { const auth = useAuth() const router = useRouter() const isOnline = useOnlineStatus() const [orgs, setOrgs] = useState([]) - + const [isSwitchingOrg, setIsSwitchingOrg] = useState(() => { + if (typeof window === 'undefined') return false + return sessionStorage.getItem(ORG_SWITCH_KEY) === 'true' + }) + + // * Clear the switching flag once the page has settled after reload + useEffect(() => { + if (isSwitchingOrg) { + sessionStorage.removeItem(ORG_SWITCH_KEY) + const timer = setTimeout(() => setIsSwitchingOrg(false), 600) + return () => clearTimeout(timer) + } + }, [isSwitchingOrg]) + // * Fetch organizations for the header organization switcher useEffect(() => { if (auth.user) { @@ -32,6 +48,7 @@ export default function LayoutContent({ children }: { children: React.ReactNode try { const { access_token } = await switchContext(orgId) await setSessionAction(access_token) + sessionStorage.setItem(ORG_SWITCH_KEY, 'true') window.location.reload() } catch (err) { console.error('Failed to switch organization', err) @@ -47,6 +64,10 @@ export default function LayoutContent({ children }: { children: React.ReactNode const headerHeightRem = 6; const mainTopPaddingRem = barHeightRem + headerHeightRem; + if (isSwitchingOrg) { + return + } + return ( <> {auth.user && } diff --git a/components/WorkspaceSwitcher.tsx b/components/WorkspaceSwitcher.tsx index 81b828c..4610e44 100644 --- a/components/WorkspaceSwitcher.tsx +++ b/components/WorkspaceSwitcher.tsx @@ -33,8 +33,8 @@ export default function OrganizationSwitcher({ orgs, activeOrgId }: { orgs: Orga // * Note: switchContext only returns access_token, we keep existing refresh token await setSessionAction(access_token) - // Force reload to pick up new permissions - window.location.reload() + sessionStorage.setItem('pulse_switching_org', 'true') + window.location.reload() } catch (err) { console.error('Failed to switch organization', err) diff --git a/components/settings/OrganizationSettings.tsx b/components/settings/OrganizationSettings.tsx index 9c6a691..b122f45 100644 --- a/components/settings/OrganizationSettings.tsx +++ b/components/settings/OrganizationSettings.tsx @@ -419,10 +419,11 @@ export default function OrganizationSettings() { try { const { access_token } = await switchContext(null) await setSessionAction(access_token) + sessionStorage.setItem('pulse_switching_org', 'true') window.location.href = '/' } catch (switchErr) { console.error('Failed to switch to personal context after delete:', switchErr) - // Fallback: reload and let backend handle invalid token if any + sessionStorage.setItem('pulse_switching_org', 'true') window.location.href = '/' }