From ad7208f8a8c0882ae4fc3541045c017520f7c42f Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Thu, 31 Jul 2025 16:00:43 +0000 Subject: [PATCH 1/4] fix: only check team status for team plans MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't check team status API for non-team products (free, starter, pro, max). Now properly checks billing status first to determine if user has team plan before fetching team status, matching the pattern used in other components. Fixes #169 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Anthony --- frontend/src/routes/index.tsx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx index f902daebe..4abac7ba4 100644 --- a/frontend/src/routes/index.tsx +++ b/frontend/src/routes/index.tsx @@ -17,6 +17,7 @@ import { TeamManagementDialog } from "@/components/team/TeamManagementDialog"; import { useQuery } from "@tanstack/react-query"; import { getBillingService } from "@/billing/billingService"; import type { TeamStatus } from "@/types/team"; +import type { BillingStatus as BillingStatusType } from "@/billing/billingApi"; const homeVariants = cva("grid h-full w-full overflow-hidden", { variants: { @@ -69,14 +70,27 @@ function Index() { const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [teamDialogOpen, setTeamDialogOpen] = useState(false); - // Fetch team status for the dialog + // Fetch billing status first to check if user has team plan + const { data: billingStatus } = useQuery({ + queryKey: ["billingStatus"], + queryFn: async () => { + const billingService = getBillingService(); + return await billingService.getBillingStatus(); + }, + enabled: !!os.auth.user + }); + + // Check if user has team plan + const isTeamPlan = billingStatus?.product_name?.toLowerCase().includes("team"); + + // Fetch team status only if user has team plan const { data: teamStatus } = useQuery({ queryKey: ["teamStatus"], queryFn: async () => { const billingService = getBillingService(); return await billingService.getTeamStatus(); }, - enabled: !!os.auth.user + enabled: isTeamPlan && !!os.auth.user && !!billingStatus }); // Auto-open team dialog if team_setup is true From ac655cfa7244578ace89f60e61c4fefe1ba003c0 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 17:39:37 +0000 Subject: [PATCH 2/4] fix: ensure BillingStatus component only queries team status for team plans Previously the BillingStatus component's billing status query wasn't conditional on user authentication, and now also ensures consistency with the home route's conditional team status logic. Co-authored-by: Anthony --- frontend/src/components/BillingStatus.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/BillingStatus.tsx b/frontend/src/components/BillingStatus.tsx index 9ff291df1..8858a625c 100644 --- a/frontend/src/components/BillingStatus.tsx +++ b/frontend/src/components/BillingStatus.tsx @@ -20,13 +20,14 @@ export function BillingStatus() { const status = await billingService.getBillingStatus(); setBillingStatus(status); return status; - } + }, + enabled: !!os.auth.user }); // Check if user has team plan const isTeamPlan = billingStatus?.product_name?.toLowerCase().includes("team"); - // Fetch team status if user has team plan + // Fetch team status only if user has team plan const { data: teamStatus } = useQuery({ queryKey: ["teamStatus"], queryFn: async () => { From 261d515c5bb2e5a63d9b9ee5f41969dc1ba4c11c Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 17:48:57 +0000 Subject: [PATCH 3/4] fix: AccountMenu now fetches billing status directly instead of using stale local state The AccountMenu component was relying on billingStatus from local state, which could be null or stale when users first log in. This caused the team status API to still be called in some cases due to timing issues with React Query's enabled condition. Now AccountMenu fetches its own billing status using useQuery, matching the pattern used in BillingStatus component. This ensures the isTeamPlan check is accurate and prevents unnecessary team status API calls for non-team plans. Co-authored-by: Anthony --- frontend/src/components/AccountMenu.tsx | 13 +++++++++++-- frontend/src/routes/_auth.chat.$chatId.tsx | 20 +++++++++++--------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/AccountMenu.tsx b/frontend/src/components/AccountMenu.tsx index 5f8271e2e..82b75d926 100644 --- a/frontend/src/components/AccountMenu.tsx +++ b/frontend/src/components/AccountMenu.tsx @@ -79,10 +79,19 @@ function ConfirmDeleteDialog() { export function AccountMenu() { const os = useOpenSecret(); const router = useRouter(); - const { billingStatus } = useLocalState(); const [isPortalLoading, setIsPortalLoading] = useState(false); const [isTeamDialogOpen, setIsTeamDialogOpen] = useState(false); + // Fetch billing status directly instead of relying on local state + const { data: billingStatus } = useQuery({ + queryKey: ["billingStatus"], + queryFn: async () => { + const billingService = getBillingService(); + return await billingService.getBillingStatus(); + }, + enabled: !!os.auth.user + }); + const hasStripeAccount = billingStatus?.stripe_customer_id !== null; const productName = billingStatus?.product_name || ""; const isPro = productName.toLowerCase().includes("pro"); @@ -92,7 +101,7 @@ export function AccountMenu() { const showUpgrade = !isMax && !isTeamPlan; const showManage = (isPro || isMax || isStarter || isTeamPlan) && hasStripeAccount; - // Fetch team status if user has team plan + // Fetch team status only if user has team plan const { data: teamStatus } = useQuery({ queryKey: ["teamStatus"], queryFn: async () => { diff --git a/frontend/src/routes/_auth.chat.$chatId.tsx b/frontend/src/routes/_auth.chat.$chatId.tsx index 7edd3a4da..3ca03827e 100644 --- a/frontend/src/routes/_auth.chat.$chatId.tsx +++ b/frontend/src/routes/_auth.chat.$chatId.tsx @@ -233,16 +233,18 @@ function ChatComponent() { setUserImages([]); // Send message with system prompt as separate parameter - appendUserMessage(prompt, images, undefined, undefined, sysPrompt || undefined).catch((error) => { - // Only reset if it wasn't an abort - if (!(error instanceof Error) || error.message !== "Stream aborted") { - console.error("[ChatComponent] Failed to append message:", error); - setUserPrompt(prompt); - setSystemPrompt(sysPrompt); - setUserImages(images); - initialPromptProcessedRef.current = false; + appendUserMessage(prompt, images, undefined, undefined, sysPrompt || undefined).catch( + (error) => { + // Only reset if it wasn't an abort + if (!(error instanceof Error) || error.message !== "Stream aborted") { + console.error("[ChatComponent] Failed to append message:", error); + setUserPrompt(prompt); + setSystemPrompt(sysPrompt); + setUserImages(images); + initialPromptProcessedRef.current = false; + } } - }); + ); } }, [ userPrompt, From 5e155f5433ca81d12b7b3bc86a06908d060292e8 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 17:58:10 +0000 Subject: [PATCH 4/4] fix: ensure isTeamPlan boolean coercion prevents undefined values Fix boolean coercion issue where isTeamPlan could be undefined instead of false when billingStatus is undefined during initial load. This was causing React Query enabled conditions to behave unexpectedly and potentially trigger team status API calls for non-team users. Co-authored-by: Anthony --- frontend/src/components/AccountMenu.tsx | 2 +- frontend/src/components/BillingStatus.tsx | 2 +- frontend/src/routes/index.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/AccountMenu.tsx b/frontend/src/components/AccountMenu.tsx index 82b75d926..1955746db 100644 --- a/frontend/src/components/AccountMenu.tsx +++ b/frontend/src/components/AccountMenu.tsx @@ -97,7 +97,7 @@ export function AccountMenu() { const isPro = productName.toLowerCase().includes("pro"); const isMax = productName.toLowerCase().includes("max"); const isStarter = productName.toLowerCase().includes("starter"); - const isTeamPlan = productName.toLowerCase().includes("team"); + const isTeamPlan = productName.toLowerCase().includes("team") ?? false; const showUpgrade = !isMax && !isTeamPlan; const showManage = (isPro || isMax || isStarter || isTeamPlan) && hasStripeAccount; diff --git a/frontend/src/components/BillingStatus.tsx b/frontend/src/components/BillingStatus.tsx index 8858a625c..0924cc5b3 100644 --- a/frontend/src/components/BillingStatus.tsx +++ b/frontend/src/components/BillingStatus.tsx @@ -25,7 +25,7 @@ export function BillingStatus() { }); // Check if user has team plan - const isTeamPlan = billingStatus?.product_name?.toLowerCase().includes("team"); + const isTeamPlan = billingStatus?.product_name?.toLowerCase().includes("team") ?? false; // Fetch team status only if user has team plan const { data: teamStatus } = useQuery({ diff --git a/frontend/src/routes/index.tsx b/frontend/src/routes/index.tsx index 4abac7ba4..5b58f9c2d 100644 --- a/frontend/src/routes/index.tsx +++ b/frontend/src/routes/index.tsx @@ -81,7 +81,7 @@ function Index() { }); // Check if user has team plan - const isTeamPlan = billingStatus?.product_name?.toLowerCase().includes("team"); + const isTeamPlan = billingStatus?.product_name?.toLowerCase().includes("team") ?? false; // Fetch team status only if user has team plan const { data: teamStatus } = useQuery({