Refactor billing flows to require authenticated user IDs

This commit is contained in:
ي
2026-03-04 20:41:07 +05:30
parent 2318fd8a48
commit 5a7b9e6c6b
7 changed files with 54 additions and 33 deletions

View File

@@ -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<DashboardData | null>(null);
const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null);
const [loading, setLoading] = useState(true);

View File

@@ -21,7 +21,7 @@ import { MonthlyBudgetUsage } from './components/MonthlyBudgetUsage';
import { AlertsSection } from './components/AlertsSection';
interface CompactBillingDashboardProps {
userId?: string;
userId: string;
terminalTheme?: boolean;
}

View File

@@ -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<EnhancedBillingDashboardProps> = ({ 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<DashboardData | null>(null);
const [systemHealth, setSystemHealth] = useState<SystemHealth | null>(null);
const [loading, setLoading] = useState(true);
@@ -73,10 +76,16 @@ const EnhancedBillingDashboard: React.FC<EnhancedBillingDashboardProps> = ({ use
const [healthError, setHealthError] = useState<string | null>(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<EnhancedBillingDashboardProps> = ({ 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<EnhancedBillingDashboardProps> = ({ 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<EnhancedBillingDashboardProps> = ({ 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<HTMLElement>,
@@ -463,7 +475,11 @@ const EnhancedBillingDashboard: React.FC<EnhancedBillingDashboardProps> = ({ use
exit={{ opacity: 0, x: 20 }}
transition={{ duration: 0.3 }}
>
<CompactBillingDashboard userId={userId} terminalTheme={terminalTheme} />
{effectiveUserId ? (
<CompactBillingDashboard userId={effectiveUserId} terminalTheme={terminalTheme} />
) : (
<AlertComponent severity="warning">Unable to load billing data: missing authenticated user context.</AlertComponent>
)}
</motion.div>
) : (
<motion.div

View File

@@ -42,7 +42,7 @@ import {
import { showToastNotification } from '../../utils/toastNotifications';
interface SubscriptionRenewalHistoryProps {
userId?: string;
userId: string;
terminalTheme?: boolean;
initialLimit?: number;
}