From aeb8f47c87c7eade7df4c49809415a5b5ec18f3b Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 18:23:53 +0000 Subject: [PATCH 1/8] Add user-visible error handling for Manage Subscription failures When getPortalUrl() fails, the error was only logged to console with no feedback to the user. Now shows an alert with a message to retry or contact support. Also prevents the pricing page from silently falling through to a new checkout flow when the portal URL fails to load for already-subscribed users. Fixes #450 Co-Authored-By: tony@opensecret.cloud --- frontend/src/components/AccountMenu.tsx | 3 +++ frontend/src/components/team/TeamInviteDialog.tsx | 3 +++ frontend/src/routes/pricing.tsx | 9 +++++++++ 3 files changed, 15 insertions(+) diff --git a/frontend/src/components/AccountMenu.tsx b/frontend/src/components/AccountMenu.tsx index 5f8c0e75..f36b49bc 100644 --- a/frontend/src/components/AccountMenu.tsx +++ b/frontend/src/components/AccountMenu.tsx @@ -211,6 +211,9 @@ export function AccountMenu() { window.open(url, "_blank"); } catch (error) { console.error("Error fetching portal URL:", error); + alert( + "Unable to open subscription management. Please try again or contact support@opensecret.cloud." + ); } finally { setIsPortalLoading(false); } diff --git a/frontend/src/components/team/TeamInviteDialog.tsx b/frontend/src/components/team/TeamInviteDialog.tsx index e655f7ad..fad1e5c6 100644 --- a/frontend/src/components/team/TeamInviteDialog.tsx +++ b/frontend/src/components/team/TeamInviteDialog.tsx @@ -71,6 +71,9 @@ export function TeamInviteDialog({ open, onOpenChange, teamStatus }: TeamInviteD window.open(url, "_blank", "noopener,noreferrer"); } catch (error) { console.error("Failed to open billing portal:", error); + alert( + "Unable to open subscription management. Please try again or contact support@opensecret.cloud." + ); } finally { setIsPortalLoading(false); } diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index 3c12de9f..c21c723d 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -612,6 +612,15 @@ function PricingPage() { return; } + // If the user is already on a paid plan (including team) and portal URL failed to load, + // show an error instead of silently falling through to checkout + if (isCurrentPlan) { + alert( + "Unable to open subscription management. Please try again or contact support@opensecret.cloud." + ); + return; + } + // If no portal URL exists and it's not a free plan user upgrading, // create checkout session // For team plans, show seat selection dialog first From 24a694ef2c826058b755b0dc7aa539230a2caccf Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 18:50:19 +0000 Subject: [PATCH 2/8] Replace alert() with useNotification for polished error UX Use the existing NotificationContext/GlobalNotification system instead of native browser alert() for a more consistent and polished error experience. The notification appears as a styled toast in the top-right corner with an error icon, title, message, and dismiss button. Co-Authored-By: tony@opensecret.cloud --- frontend/src/components/AccountMenu.tsx | 11 ++++++++--- frontend/src/components/team/TeamInviteDialog.tsx | 11 ++++++++--- frontend/src/routes/pricing.tsx | 12 +++++++++--- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/AccountMenu.tsx b/frontend/src/components/AccountMenu.tsx index f36b49bc..1c2004a8 100644 --- a/frontend/src/components/AccountMenu.tsx +++ b/frontend/src/components/AccountMenu.tsx @@ -49,6 +49,7 @@ import { Link } from "@tanstack/react-router"; import { getBillingService } from "@/billing/billingService"; import { useState } from "react"; import type { TeamStatus } from "@/types/team"; +import { useNotification } from "@/contexts/NotificationContext"; import { TeamManagementDialog } from "@/components/team/TeamManagementDialog"; import { ApiKeyManagementDialog } from "@/components/apikeys/ApiKeyManagementDialog"; import packageJson from "../../package.json"; @@ -110,6 +111,7 @@ export function AccountMenu() { const [isTeamDialogOpen, setIsTeamDialogOpen] = useState(false); const [isApiKeyDialogOpen, setIsApiKeyDialogOpen] = useState(false); const [showAboutMenu, setShowAboutMenu] = useState(false); + const { showNotification } = useNotification(); const hasStripeAccount = billingStatus?.stripe_customer_id !== null; const productName = billingStatus?.product_name || ""; @@ -211,9 +213,12 @@ export function AccountMenu() { window.open(url, "_blank"); } catch (error) { console.error("Error fetching portal URL:", error); - alert( - "Unable to open subscription management. Please try again or contact support@opensecret.cloud." - ); + showNotification({ + type: "error", + title: "Unable to open subscription management", + message: "Please try again or contact support@opensecret.cloud.", + duration: 0 + }); } finally { setIsPortalLoading(false); } diff --git a/frontend/src/components/team/TeamInviteDialog.tsx b/frontend/src/components/team/TeamInviteDialog.tsx index fad1e5c6..b32c4130 100644 --- a/frontend/src/components/team/TeamInviteDialog.tsx +++ b/frontend/src/components/team/TeamInviteDialog.tsx @@ -17,6 +17,7 @@ import { getBillingService } from "@/billing/billingService"; import { useLocalState } from "@/state/useLocalState"; import { isTauri } from "@/utils/platform"; import type { TeamStatus } from "@/types/team"; +import { useNotification } from "@/contexts/NotificationContext"; interface TeamInviteDialogProps { open: boolean; @@ -32,6 +33,7 @@ export function TeamInviteDialog({ open, onOpenChange, teamStatus }: TeamInviteD const [isPortalLoading, setIsPortalLoading] = useState(false); const queryClient = useQueryClient(); const { billingStatus } = useLocalState(); + const { showNotification } = useNotification(); const seatsAvailable = teamStatus?.seats_available || 0; const hasStripeAccount = billingStatus?.stripe_customer_id !== null; @@ -71,9 +73,12 @@ export function TeamInviteDialog({ open, onOpenChange, teamStatus }: TeamInviteD window.open(url, "_blank", "noopener,noreferrer"); } catch (error) { console.error("Failed to open billing portal:", error); - alert( - "Unable to open subscription management. Please try again or contact support@opensecret.cloud." - ); + showNotification({ + type: "error", + title: "Unable to open subscription management", + message: "Please try again or contact support@opensecret.cloud.", + duration: 0 + }); } finally { setIsPortalLoading(false); } diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index c21c723d..3b7ebd02 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -10,6 +10,7 @@ import { Loader2, Check, AlertTriangle, Bitcoin, Tag } from "lucide-react"; import type { DiscountResponse } from "@/billing/billingApi"; import { Badge } from "@/components/ui/badge"; import { useLocalState } from "@/state/useLocalState"; +import { useNotification } from "@/contexts/NotificationContext"; import { Button } from "@/components/ui/button"; import { Switch } from "@/components/ui/switch"; import { PRICING_PLANS } from "@/config/pricingConfig"; @@ -205,6 +206,7 @@ function PricingPage() { const navigate = useNavigate(); const os = useOpenSecret(); const { setBillingStatus } = useLocalState(); + const { showNotification } = useNotification(); const isLoggedIn = !!os.auth.user; const isGuestUser = os.auth.user?.user.login_method?.toLowerCase() === "guest"; const { selected_plan } = Route.useSearch(); @@ -615,9 +617,12 @@ function PricingPage() { // If the user is already on a paid plan (including team) and portal URL failed to load, // show an error instead of silently falling through to checkout if (isCurrentPlan) { - alert( - "Unable to open subscription management. Please try again or contact support@opensecret.cloud." - ); + showNotification({ + type: "error", + title: "Unable to open subscription management", + message: "Please try again or contact support@opensecret.cloud.", + duration: 0 + }); return; } @@ -637,6 +642,7 @@ function PricingPage() { navigate, portalUrl, newHandleSubscribe, + showNotification, isIOSPlatform, isMobilePlatform ] From ca1c94e32d66e2ea57725f113a6c83f81ddf6314 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 18:56:42 +0000 Subject: [PATCH 3/8] Revert "Replace alert() with useNotification for polished error UX" This reverts commit 24a694ef2c826058b755b0dc7aa539230a2caccf. --- frontend/src/components/AccountMenu.tsx | 11 +++-------- frontend/src/components/team/TeamInviteDialog.tsx | 11 +++-------- frontend/src/routes/pricing.tsx | 12 +++--------- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/frontend/src/components/AccountMenu.tsx b/frontend/src/components/AccountMenu.tsx index 1c2004a8..f36b49bc 100644 --- a/frontend/src/components/AccountMenu.tsx +++ b/frontend/src/components/AccountMenu.tsx @@ -49,7 +49,6 @@ import { Link } from "@tanstack/react-router"; import { getBillingService } from "@/billing/billingService"; import { useState } from "react"; import type { TeamStatus } from "@/types/team"; -import { useNotification } from "@/contexts/NotificationContext"; import { TeamManagementDialog } from "@/components/team/TeamManagementDialog"; import { ApiKeyManagementDialog } from "@/components/apikeys/ApiKeyManagementDialog"; import packageJson from "../../package.json"; @@ -111,7 +110,6 @@ export function AccountMenu() { const [isTeamDialogOpen, setIsTeamDialogOpen] = useState(false); const [isApiKeyDialogOpen, setIsApiKeyDialogOpen] = useState(false); const [showAboutMenu, setShowAboutMenu] = useState(false); - const { showNotification } = useNotification(); const hasStripeAccount = billingStatus?.stripe_customer_id !== null; const productName = billingStatus?.product_name || ""; @@ -213,12 +211,9 @@ export function AccountMenu() { window.open(url, "_blank"); } catch (error) { console.error("Error fetching portal URL:", error); - showNotification({ - type: "error", - title: "Unable to open subscription management", - message: "Please try again or contact support@opensecret.cloud.", - duration: 0 - }); + alert( + "Unable to open subscription management. Please try again or contact support@opensecret.cloud." + ); } finally { setIsPortalLoading(false); } diff --git a/frontend/src/components/team/TeamInviteDialog.tsx b/frontend/src/components/team/TeamInviteDialog.tsx index b32c4130..fad1e5c6 100644 --- a/frontend/src/components/team/TeamInviteDialog.tsx +++ b/frontend/src/components/team/TeamInviteDialog.tsx @@ -17,7 +17,6 @@ import { getBillingService } from "@/billing/billingService"; import { useLocalState } from "@/state/useLocalState"; import { isTauri } from "@/utils/platform"; import type { TeamStatus } from "@/types/team"; -import { useNotification } from "@/contexts/NotificationContext"; interface TeamInviteDialogProps { open: boolean; @@ -33,7 +32,6 @@ export function TeamInviteDialog({ open, onOpenChange, teamStatus }: TeamInviteD const [isPortalLoading, setIsPortalLoading] = useState(false); const queryClient = useQueryClient(); const { billingStatus } = useLocalState(); - const { showNotification } = useNotification(); const seatsAvailable = teamStatus?.seats_available || 0; const hasStripeAccount = billingStatus?.stripe_customer_id !== null; @@ -73,12 +71,9 @@ export function TeamInviteDialog({ open, onOpenChange, teamStatus }: TeamInviteD window.open(url, "_blank", "noopener,noreferrer"); } catch (error) { console.error("Failed to open billing portal:", error); - showNotification({ - type: "error", - title: "Unable to open subscription management", - message: "Please try again or contact support@opensecret.cloud.", - duration: 0 - }); + alert( + "Unable to open subscription management. Please try again or contact support@opensecret.cloud." + ); } finally { setIsPortalLoading(false); } diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index 3b7ebd02..c21c723d 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -10,7 +10,6 @@ import { Loader2, Check, AlertTriangle, Bitcoin, Tag } from "lucide-react"; import type { DiscountResponse } from "@/billing/billingApi"; import { Badge } from "@/components/ui/badge"; import { useLocalState } from "@/state/useLocalState"; -import { useNotification } from "@/contexts/NotificationContext"; import { Button } from "@/components/ui/button"; import { Switch } from "@/components/ui/switch"; import { PRICING_PLANS } from "@/config/pricingConfig"; @@ -206,7 +205,6 @@ function PricingPage() { const navigate = useNavigate(); const os = useOpenSecret(); const { setBillingStatus } = useLocalState(); - const { showNotification } = useNotification(); const isLoggedIn = !!os.auth.user; const isGuestUser = os.auth.user?.user.login_method?.toLowerCase() === "guest"; const { selected_plan } = Route.useSearch(); @@ -617,12 +615,9 @@ function PricingPage() { // If the user is already on a paid plan (including team) and portal URL failed to load, // show an error instead of silently falling through to checkout if (isCurrentPlan) { - showNotification({ - type: "error", - title: "Unable to open subscription management", - message: "Please try again or contact support@opensecret.cloud.", - duration: 0 - }); + alert( + "Unable to open subscription management. Please try again or contact support@opensecret.cloud." + ); return; } @@ -642,7 +637,6 @@ function PricingPage() { navigate, portalUrl, newHandleSubscribe, - showNotification, isIOSPlatform, isMobilePlatform ] From 93f9f65e96b79b2e61f62c7603bc35a5a1e6b3c8 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:10:18 +0000 Subject: [PATCH 4/8] Revert "Revert "Replace alert() with useNotification for polished error UX"" This reverts commit ca1c94e32d66e2ea57725f113a6c83f81ddf6314. --- frontend/src/components/AccountMenu.tsx | 11 ++++++++--- frontend/src/components/team/TeamInviteDialog.tsx | 11 ++++++++--- frontend/src/routes/pricing.tsx | 12 +++++++++--- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/AccountMenu.tsx b/frontend/src/components/AccountMenu.tsx index f36b49bc..1c2004a8 100644 --- a/frontend/src/components/AccountMenu.tsx +++ b/frontend/src/components/AccountMenu.tsx @@ -49,6 +49,7 @@ import { Link } from "@tanstack/react-router"; import { getBillingService } from "@/billing/billingService"; import { useState } from "react"; import type { TeamStatus } from "@/types/team"; +import { useNotification } from "@/contexts/NotificationContext"; import { TeamManagementDialog } from "@/components/team/TeamManagementDialog"; import { ApiKeyManagementDialog } from "@/components/apikeys/ApiKeyManagementDialog"; import packageJson from "../../package.json"; @@ -110,6 +111,7 @@ export function AccountMenu() { const [isTeamDialogOpen, setIsTeamDialogOpen] = useState(false); const [isApiKeyDialogOpen, setIsApiKeyDialogOpen] = useState(false); const [showAboutMenu, setShowAboutMenu] = useState(false); + const { showNotification } = useNotification(); const hasStripeAccount = billingStatus?.stripe_customer_id !== null; const productName = billingStatus?.product_name || ""; @@ -211,9 +213,12 @@ export function AccountMenu() { window.open(url, "_blank"); } catch (error) { console.error("Error fetching portal URL:", error); - alert( - "Unable to open subscription management. Please try again or contact support@opensecret.cloud." - ); + showNotification({ + type: "error", + title: "Unable to open subscription management", + message: "Please try again or contact support@opensecret.cloud.", + duration: 0 + }); } finally { setIsPortalLoading(false); } diff --git a/frontend/src/components/team/TeamInviteDialog.tsx b/frontend/src/components/team/TeamInviteDialog.tsx index fad1e5c6..b32c4130 100644 --- a/frontend/src/components/team/TeamInviteDialog.tsx +++ b/frontend/src/components/team/TeamInviteDialog.tsx @@ -17,6 +17,7 @@ import { getBillingService } from "@/billing/billingService"; import { useLocalState } from "@/state/useLocalState"; import { isTauri } from "@/utils/platform"; import type { TeamStatus } from "@/types/team"; +import { useNotification } from "@/contexts/NotificationContext"; interface TeamInviteDialogProps { open: boolean; @@ -32,6 +33,7 @@ export function TeamInviteDialog({ open, onOpenChange, teamStatus }: TeamInviteD const [isPortalLoading, setIsPortalLoading] = useState(false); const queryClient = useQueryClient(); const { billingStatus } = useLocalState(); + const { showNotification } = useNotification(); const seatsAvailable = teamStatus?.seats_available || 0; const hasStripeAccount = billingStatus?.stripe_customer_id !== null; @@ -71,9 +73,12 @@ export function TeamInviteDialog({ open, onOpenChange, teamStatus }: TeamInviteD window.open(url, "_blank", "noopener,noreferrer"); } catch (error) { console.error("Failed to open billing portal:", error); - alert( - "Unable to open subscription management. Please try again or contact support@opensecret.cloud." - ); + showNotification({ + type: "error", + title: "Unable to open subscription management", + message: "Please try again or contact support@opensecret.cloud.", + duration: 0 + }); } finally { setIsPortalLoading(false); } diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index c21c723d..3b7ebd02 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -10,6 +10,7 @@ import { Loader2, Check, AlertTriangle, Bitcoin, Tag } from "lucide-react"; import type { DiscountResponse } from "@/billing/billingApi"; import { Badge } from "@/components/ui/badge"; import { useLocalState } from "@/state/useLocalState"; +import { useNotification } from "@/contexts/NotificationContext"; import { Button } from "@/components/ui/button"; import { Switch } from "@/components/ui/switch"; import { PRICING_PLANS } from "@/config/pricingConfig"; @@ -205,6 +206,7 @@ function PricingPage() { const navigate = useNavigate(); const os = useOpenSecret(); const { setBillingStatus } = useLocalState(); + const { showNotification } = useNotification(); const isLoggedIn = !!os.auth.user; const isGuestUser = os.auth.user?.user.login_method?.toLowerCase() === "guest"; const { selected_plan } = Route.useSearch(); @@ -615,9 +617,12 @@ function PricingPage() { // If the user is already on a paid plan (including team) and portal URL failed to load, // show an error instead of silently falling through to checkout if (isCurrentPlan) { - alert( - "Unable to open subscription management. Please try again or contact support@opensecret.cloud." - ); + showNotification({ + type: "error", + title: "Unable to open subscription management", + message: "Please try again or contact support@opensecret.cloud.", + duration: 0 + }); return; } @@ -637,6 +642,7 @@ function PricingPage() { navigate, portalUrl, newHandleSubscribe, + showNotification, isIOSPlatform, isMobilePlatform ] From ee8a04b3330ca3cc3c509b7eb021cda0bc62f069 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:13:28 +0000 Subject: [PATCH 5/8] Use inline Alert destructive error boxes instead of alert() or notifications Co-Authored-By: tony@opensecret.cloud --- frontend/src/components/AccountMenu.tsx | 20 +++++++++++-------- .../src/components/team/TeamInviteDialog.tsx | 12 +++-------- frontend/src/routes/pricing.tsx | 12 +++-------- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/frontend/src/components/AccountMenu.tsx b/frontend/src/components/AccountMenu.tsx index 1c2004a8..03ffb22e 100644 --- a/frontend/src/components/AccountMenu.tsx +++ b/frontend/src/components/AccountMenu.tsx @@ -49,7 +49,7 @@ import { Link } from "@tanstack/react-router"; import { getBillingService } from "@/billing/billingService"; import { useState } from "react"; import type { TeamStatus } from "@/types/team"; -import { useNotification } from "@/contexts/NotificationContext"; +import { Alert, AlertDescription } from "@/components/ui/alert"; import { TeamManagementDialog } from "@/components/team/TeamManagementDialog"; import { ApiKeyManagementDialog } from "@/components/apikeys/ApiKeyManagementDialog"; import packageJson from "../../package.json"; @@ -111,7 +111,7 @@ export function AccountMenu() { const [isTeamDialogOpen, setIsTeamDialogOpen] = useState(false); const [isApiKeyDialogOpen, setIsApiKeyDialogOpen] = useState(false); const [showAboutMenu, setShowAboutMenu] = useState(false); - const { showNotification } = useNotification(); + const [portalError, setPortalError] = useState(null); const hasStripeAccount = billingStatus?.stripe_customer_id !== null; const productName = billingStatus?.product_name || ""; @@ -178,6 +178,7 @@ export function AccountMenu() { try { setIsPortalLoading(true); + setPortalError(null); const billingService = getBillingService(); const url = await billingService.getPortalUrl(); @@ -213,12 +214,9 @@ export function AccountMenu() { window.open(url, "_blank"); } catch (error) { console.error("Error fetching portal URL:", error); - showNotification({ - type: "error", - title: "Unable to open subscription management", - message: "Please try again or contact support@opensecret.cloud.", - duration: 0 - }); + setPortalError( + "Unable to open subscription management. Please try again or contact support@opensecret.cloud." + ); } finally { setIsPortalLoading(false); } @@ -456,6 +454,12 @@ export function AccountMenu() { + {portalError && ( + + + {portalError} + + )} Date: Fri, 27 Feb 2026 19:18:58 +0000 Subject: [PATCH 6/8] Fix pricing page: use inline Alert instead of setCheckoutError for portal errors setCheckoutError was replacing the entire pricing page with a misleading 'Unable to Load Pricing' full-page error screen. Now uses a separate portalError state with an inline Alert that appears above the plan cards, keeping the pricing page visible and functional. Co-Authored-By: tony@opensecret.cloud --- frontend/src/routes/pricing.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index 1581bbbe..651f8eea 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -6,7 +6,8 @@ import { FullPageMain } from "@/components/FullPageMain"; import { getBillingService } from "@/billing/billingService"; import { useQuery } from "@tanstack/react-query"; import { MarketingHeader } from "@/components/MarketingHeader"; -import { Loader2, Check, AlertTriangle, Bitcoin, Tag } from "lucide-react"; +import { Loader2, Check, AlertTriangle, AlertCircle, Bitcoin, Tag } from "lucide-react"; +import { Alert, AlertDescription } from "@/components/ui/alert"; import type { DiscountResponse } from "@/billing/billingApi"; import { Badge } from "@/components/ui/badge"; import { useLocalState } from "@/state/useLocalState"; @@ -198,6 +199,7 @@ function PricingFAQ() { function PricingPage() { const [checkoutError, setCheckoutError] = useState(""); + const [portalError, setPortalError] = useState(null); const [loadingProductId, setLoadingProductId] = useState(null); const [useBitcoin, setUseBitcoin] = useState(false); const [showTeamSeatDialog, setShowTeamSeatDialog] = useState(false); @@ -615,7 +617,7 @@ function PricingPage() { // If the user is already on a paid plan (including team) and portal URL failed to load, // show an error instead of silently falling through to checkout if (isCurrentPlan) { - setCheckoutError( + setPortalError( "Unable to open subscription management. Please try again or contact support@opensecret.cloud." ); return; @@ -794,6 +796,16 @@ function PricingPage() { )} + {/* Portal Error Message */} + {portalError && ( +
+ + + {portalError} + +
+ )} + {/* Promotion Banner */} {discount?.active && (
From 5823c59b7de2eddd62e4c6c0eef729ebfc86d5be Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:23:09 +0000 Subject: [PATCH 7/8] Clear portalError on plan button click in pricing page Co-Authored-By: tony@opensecret.cloud --- frontend/src/routes/pricing.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/routes/pricing.tsx b/frontend/src/routes/pricing.tsx index 651f8eea..19427f58 100644 --- a/frontend/src/routes/pricing.tsx +++ b/frontend/src/routes/pricing.tsx @@ -478,6 +478,7 @@ function PricingPage() { const handleButtonClick = useCallback( (product: Product) => { + setPortalError(null); const targetPlanName = product.name.toLowerCase(); const isFreeplan = targetPlanName.includes("free"); const isTeamPlan = targetPlanName.includes("team"); From 553eb4ae0b93ed3701959db6ac0a486b1f2b6d49 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:27:19 +0000 Subject: [PATCH 8/8] Clear error state at start of handleManageSubscription in TeamInviteDialog Co-Authored-By: tony@opensecret.cloud --- frontend/src/components/team/TeamInviteDialog.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/team/TeamInviteDialog.tsx b/frontend/src/components/team/TeamInviteDialog.tsx index 9fb39e26..659e73c2 100644 --- a/frontend/src/components/team/TeamInviteDialog.tsx +++ b/frontend/src/components/team/TeamInviteDialog.tsx @@ -39,6 +39,7 @@ export function TeamInviteDialog({ open, onOpenChange, teamStatus }: TeamInviteD if (!hasStripeAccount) return; try { + setError(null); setIsPortalLoading(true); const billingService = getBillingService(); const url = await billingService.getPortalUrl();