diff --git a/frontend/src/components/billing/CompactBillingDashboard/hooks/useCompactBillingData.ts b/frontend/src/components/billing/CompactBillingDashboard/hooks/useCompactBillingData.ts index b0dfca9c..dfbb910e 100644 --- a/frontend/src/components/billing/CompactBillingDashboard/hooks/useCompactBillingData.ts +++ b/frontend/src/components/billing/CompactBillingDashboard/hooks/useCompactBillingData.ts @@ -24,7 +24,7 @@ interface UseCompactBillingDataReturn { /** * Custom hook for managing CompactBillingDashboard data fetching and state */ -export const useCompactBillingData = (userId?: string): UseCompactBillingDataReturn => { +export const useCompactBillingData = (userId: string): UseCompactBillingDataReturn => { const [dashboardData, setDashboardData] = useState(null); const [systemHealth, setSystemHealth] = useState(null); const [loading, setLoading] = useState(true); diff --git a/frontend/src/components/billing/CompactBillingDashboard/index.tsx b/frontend/src/components/billing/CompactBillingDashboard/index.tsx index 427621fa..b63318fd 100644 --- a/frontend/src/components/billing/CompactBillingDashboard/index.tsx +++ b/frontend/src/components/billing/CompactBillingDashboard/index.tsx @@ -21,7 +21,7 @@ import { MonthlyBudgetUsage } from './components/MonthlyBudgetUsage'; import { AlertsSection } from './components/AlertsSection'; interface CompactBillingDashboardProps { - userId?: string; + userId: string; terminalTheme?: boolean; } diff --git a/frontend/src/components/billing/EnhancedBillingDashboard.tsx b/frontend/src/components/billing/EnhancedBillingDashboard.tsx index 740e9a30..fb380bc0 100644 --- a/frontend/src/components/billing/EnhancedBillingDashboard.tsx +++ b/frontend/src/components/billing/EnhancedBillingDashboard.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +import { useAuth } from '@clerk/clerk-react'; import { Box, Container, @@ -64,6 +65,8 @@ const EnhancedBillingDashboard: React.FC = ({ use // Conditional component selection based on terminal theme const TypographyComponent = terminalTheme ? TerminalTypography : Typography; const AlertComponent = terminalTheme ? TerminalAlert : Alert; + const { userId: authUserId } = useAuth(); + const effectiveUserId = userId || authUserId; const [dashboardData, setDashboardData] = useState(null); const [systemHealth, setSystemHealth] = useState(null); const [loading, setLoading] = useState(true); @@ -73,10 +76,16 @@ const EnhancedBillingDashboard: React.FC = ({ use const [healthError, setHealthError] = useState(null); const fetchDashboardData = async (showSuccessToast: boolean = false) => { + if (!effectiveUserId) { + setLoading(false); + setError('Unable to load billing data: missing authenticated user context.'); + return; + } + try { // Use Promise.allSettled to prevent health check timeout from blocking dashboard const results = await Promise.allSettled([ - billingService.getDashboardData(), + billingService.getDashboardData(effectiveUserId), monitoringService.getSystemHealth() ]); @@ -147,14 +156,16 @@ const EnhancedBillingDashboard: React.FC = ({ use }; useEffect(() => { + if (!effectiveUserId) return; fetchDashboardData(); - }, [userId]); + }, [effectiveUserId]); // Event-driven refresh: refresh only when non-billing/monitoring APIs complete useEffect(() => { const unsubscribe = onApiEvent((detail) => { if (detail.source && detail.source !== 'other') return; - Promise.allSettled([billingService.getDashboardData(), monitoringService.getSystemHealth()]) + if (!effectiveUserId) return; + Promise.allSettled([billingService.getDashboardData(effectiveUserId), monitoringService.getSystemHealth()]) .then((results) => { if (results[0].status === 'fulfilled') { setDashboardData(results[0].value); @@ -176,25 +187,26 @@ const EnhancedBillingDashboard: React.FC = ({ use .catch(() => {/* ignore */}); }); return unsubscribe; - }, []); + }, [effectiveUserId]); // Refetch when tab becomes visible again (cheap, avoids polling) useEffect(() => { const onVisible = () => { - if (document.visibilityState === 'visible') { + if (document.visibilityState === 'visible' && effectiveUserId) { fetchDashboardData(); } }; document.addEventListener('visibilitychange', onVisible); return () => document.removeEventListener('visibilitychange', onVisible); - }, []); + }, [effectiveUserId]); // Listen for billing refresh requests (e.g., when subscription limits are exceeded) useEffect(() => { const handleBillingRefresh = () => { console.log('EnhancedBillingDashboard: Billing refresh requested, refreshing data...'); // Use allSettled to prevent health check from blocking refresh - Promise.allSettled([billingService.getDashboardData(), monitoringService.getSystemHealth()]) + if (!effectiveUserId) return; + Promise.allSettled([billingService.getDashboardData(effectiveUserId), monitoringService.getSystemHealth()]) .then((results) => { if (results[0].status === 'fulfilled') { setDashboardData(results[0].value); @@ -233,7 +245,7 @@ const EnhancedBillingDashboard: React.FC = ({ use return () => { window.removeEventListener('billing-refresh-requested', handleBillingRefresh); }; - }, []); // Empty deps - handler doesn't depend on component state + }, [effectiveUserId]); // Empty deps - handler doesn't depend on component state const handleViewModeChange = ( event: React.MouseEvent, @@ -463,7 +475,11 @@ const EnhancedBillingDashboard: React.FC = ({ use exit={{ opacity: 0, x: 20 }} transition={{ duration: 0.3 }} > - + {effectiveUserId ? ( + + ) : ( + Unable to load billing data: missing authenticated user context. + )} ) : ( { const { subscription, checkSubscription } = useSubscription(); + const { userId } = useAuth(); // Monitor subscription status and show toast notifications useEffect(() => { @@ -232,14 +234,18 @@ const BillingPage: React.FC = () => { {/* Billing Dashboard Content */} - + {userId ? ( + + ) : null} {/* Subscription Renewal History Section */} - + initialLimit={20} + /> + ) : null} {/* Usage Logs Section */} diff --git a/frontend/src/services/billingService.ts b/frontend/src/services/billingService.ts index 8cb10384..1aeace8b 100644 --- a/frontend/src/services/billingService.ts +++ b/frontend/src/services/billingService.ts @@ -294,9 +294,9 @@ export const billingService = { /** * Get comprehensive dashboard data for a user */ - getDashboardData: async (userId?: string): Promise => { + getDashboardData: async (userId: string): Promise => { try { - const actualUserId = userId || localStorage.getItem('user_id') || 'demo-user'; + const actualUserId = userId; // Debug logs removed to reduce console noise const response = await billingAPI.get(`/dashboard/${actualUserId}`); @@ -383,9 +383,9 @@ export const billingService = { /** * Get current usage statistics for a user */ - getUsageStats: async (userId?: string, period?: string): Promise => { + getUsageStats: async (userId: string, period?: string): Promise => { try { - const actualUserId = userId || localStorage.getItem('user_id') || 'demo-user'; + const actualUserId = userId; const params = period ? { billing_period: period } : {}; const response = await billingAPI.get(`/usage/${actualUserId}`, { params }); @@ -409,9 +409,9 @@ export const billingService = { /** * Get usage trends over time */ - getUsageTrends: async (userId?: string, months: number = 6): Promise => { + getUsageTrends: async (userId: string, months: number = 6): Promise => { try { - const actualUserId = userId || localStorage.getItem('user_id') || 'demo-user'; + const actualUserId = userId; const response = await billingAPI.get(`/usage/${actualUserId}/trends`, { params: { months } }); @@ -469,9 +469,9 @@ export const billingService = { /** * Get usage alerts for a user */ - getUsageAlerts: async (userId?: string, unreadOnly: boolean = false): Promise => { + getUsageAlerts: async (userId: string, unreadOnly: boolean = false): Promise => { try { - const actualUserId = userId || localStorage.getItem('user_id') || 'demo-user'; + const actualUserId = userId; const response = await billingAPI.get(`/alerts/${actualUserId}`, { params: { unread_only: unreadOnly } }); @@ -507,9 +507,9 @@ export const billingService = { /** * Get user's current subscription information */ - getUserSubscription: async (userId?: string) => { + getUserSubscription: async (userId: string) => { try { - const actualUserId = userId || localStorage.getItem('user_id') || 'demo-user'; + const actualUserId = userId; const response = await billingAPI.get(`/user/${actualUserId}/subscription`); if (!response.data.success) { @@ -555,12 +555,12 @@ export const billingService = { * Get subscription renewal history for the current user */ getRenewalHistory: async ( - userId?: string, + userId: string, limit: number = 50, offset: number = 0 ): Promise => { try { - const actualUserId = userId || localStorage.getItem('user_id') || 'demo-user'; + const actualUserId = userId; const params: any = { limit, offset }; const response = await billingAPI.get(