From eddc54c6c00eda549e5e7735d18796a5e6376304 Mon Sep 17 00:00:00 2001 From: ConnorYoh <40631091+ConnorYoh@users.noreply.github.com> Date: Thu, 11 Jun 2026 21:42:59 +0100 Subject: [PATCH] fix(payg): land usage-limit modal CTAs on the Plan section (#6630) --- .../saas/components/shared/AppConfigModal.tsx | 17 ++++++++++++++++- .../components/shared/FreeLimitReachedModal.tsx | 8 ++------ .../components/shared/SpendCapReachedModal.tsx | 8 ++------ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/frontend/editor/src/saas/components/shared/AppConfigModal.tsx b/frontend/editor/src/saas/components/shared/AppConfigModal.tsx index 4f9a60a7c..c226cab70 100644 --- a/frontend/editor/src/saas/components/shared/AppConfigModal.tsx +++ b/frontend/editor/src/saas/components/shared/AppConfigModal.tsx @@ -8,7 +8,7 @@ import LocalIcon from "@app/components/shared/LocalIcon"; import Overview from "@app/components/shared/config/configSections/Overview"; import { createSaasConfigNavSections } from "@app/components/shared/config/saasConfigNavSections"; import { NavKey } from "@app/components/shared/config/types"; -import { withBasePath } from "@app/constants/app"; +import { stripBasePath, withBasePath } from "@app/constants/app"; import { COOKIE_CONSENT_SCROLL_SHARD } from "@app/hooks/useCookieConsent"; import "@app/components/shared/AppConfigModal.css"; import { @@ -47,6 +47,21 @@ const AppConfigModal: React.FC = ({ opened, onClose }) => { ); }, []); + // When the modal opens via a /settings/
deep link (navigateToSettings — e.g. the + // usage-limit modal CTAs, which need to land on the Plan section), select that section. The + // opener (QuickAccessBar) opens the modal whenever the path is /settings/*, but doesn't carry + // the section, and `active` defaults to "overview" — so without this a deep link would open on + // Overview rather than the linked section. + useEffect(() => { + if (!opened) return; + const match = stripBasePath(window.location.pathname).match( + /^\/settings\/([^/?#]+)/, + ); + if (match) { + setActive(match[1] as NavKey); + } + }, [opened]); + // Listen for notice updates (e.g., "Not enough credits..." next to Plan title) useEffect(() => { const handler = (ev: Event) => { diff --git a/frontend/editor/src/saas/components/shared/FreeLimitReachedModal.tsx b/frontend/editor/src/saas/components/shared/FreeLimitReachedModal.tsx index 802583771..7db30887e 100644 --- a/frontend/editor/src/saas/components/shared/FreeLimitReachedModal.tsx +++ b/frontend/editor/src/saas/components/shared/FreeLimitReachedModal.tsx @@ -5,7 +5,7 @@ import CelebrationIcon from "@mui/icons-material/CelebrationOutlined"; import AnimatedSlideBackground from "@app/components/onboarding/slides/AnimatedSlideBackground"; import styles from "@app/components/onboarding/InitialOnboardingModal/InitialOnboardingModal.module.css"; import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from "@app/styles/zIndex"; -import { openPlanSettings } from "@app/utils/appSettings"; +import { navigateToSettings } from "@app/utils/settingsNavigation"; import { FreeMeterPanel, freeSnapshotFromWallet, @@ -69,11 +69,7 @@ export function FreeLimitReachedModal({ onClose }: FreeLimitReachedModalProps) { const handleUpgrade = () => { onClose(); - // Open the App Config modal AND select the Plan section. navigateToSettings("plan") only - // pushes a /settings/plan URL, which in SaaS opens config at its default section rather than - // the Plan page; openPlanSettings dispatches the appConfig:open + appConfig:navigate events the - // config modal actually listens for. - openPlanSettings(); + navigateToSettings("plan"); }; return ( diff --git a/frontend/editor/src/saas/components/shared/SpendCapReachedModal.tsx b/frontend/editor/src/saas/components/shared/SpendCapReachedModal.tsx index fbb54bc04..c124b9bae 100644 --- a/frontend/editor/src/saas/components/shared/SpendCapReachedModal.tsx +++ b/frontend/editor/src/saas/components/shared/SpendCapReachedModal.tsx @@ -5,7 +5,7 @@ import TrendingUpIcon from "@mui/icons-material/TrendingUpOutlined"; import AnimatedSlideBackground from "@app/components/onboarding/slides/AnimatedSlideBackground"; import styles from "@app/components/onboarding/InitialOnboardingModal/InitialOnboardingModal.module.css"; import { Z_INDEX_OVER_FULLSCREEN_SURFACE } from "@app/styles/zIndex"; -import { openPlanSettings } from "@app/utils/appSettings"; +import { navigateToSettings } from "@app/utils/settingsNavigation"; import { SpendCapMeterPanel, spendCapSnapshotFromWallet, @@ -69,11 +69,7 @@ export function SpendCapReachedModal({ onClose }: SpendCapReachedModalProps) { const handleRaiseCap = () => { onClose(); - // Open the App Config modal AND select the Plan section. navigateToSettings("plan") only - // pushes a /settings/plan URL, which in SaaS opens config at its default section rather than - // the Plan page; openPlanSettings dispatches the appConfig:open + appConfig:navigate events the - // config modal actually listens for. - openPlanSettings(); + navigateToSettings("plan"); }; return (