feat: enhance billing dashboard with historical data & security hardening
- Fix usage tracking zero-value bug with self-healing logic - Add month selector for historical usage views - Implement start-of-month graceful initialization - Merge PR #372: Harden user-scoped access in subscription routes - Fix UI bugs in UsageDashboard component
This commit is contained in:
@@ -61,13 +61,16 @@ const EnhancedStrategyActivationButton: React.FC<EnhancedStrategyActivationButto
|
||||
|
||||
const handleSetupMonitoring = async (monitoringPlan: any) => {
|
||||
setIsLoading(true);
|
||||
// try {
|
||||
setActivationProgress(10);
|
||||
|
||||
try {
|
||||
console.log('🎯 EnhancedStrategyActivationButton: handleSetupMonitoring called');
|
||||
|
||||
// Get strategy ID
|
||||
const strategyId = strategyData?.id || 1;
|
||||
|
||||
// Step 1: Generate monitoring plan if not provided
|
||||
setActivationProgress(30);
|
||||
let finalMonitoringPlan = monitoringPlan;
|
||||
if (!finalMonitoringPlan) {
|
||||
console.log('🎯 Generating monitoring plan...');
|
||||
@@ -85,6 +88,8 @@ const EnhancedStrategyActivationButton: React.FC<EnhancedStrategyActivationButto
|
||||
}
|
||||
}
|
||||
|
||||
setActivationProgress(60);
|
||||
|
||||
// Step 2: Activate strategy with monitoring plan
|
||||
console.log('🎯 Activating strategy with monitoring...');
|
||||
try {
|
||||
@@ -98,19 +103,31 @@ const EnhancedStrategyActivationButton: React.FC<EnhancedStrategyActivationButto
|
||||
} catch (error) {
|
||||
console.warn('Could not activate strategy with monitoring:', error);
|
||||
// Continue with local activation only
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
setActivationProgress(80);
|
||||
|
||||
// Step 3: Call the local confirmation function
|
||||
console.log('🎯 EnhancedStrategyActivationButton: Calling onConfirmStrategy()');
|
||||
await onConfirmStrategy();
|
||||
console.log('🎯 EnhancedStrategyActivationButton: onConfirmStrategy() completed');
|
||||
// } catch (error) {
|
||||
// setIsLoading(false);
|
||||
// console.error('Strategy activation failed:', error);
|
||||
// throw error;
|
||||
// }
|
||||
|
||||
setActivationProgress(100);
|
||||
setIsSuccess(true);
|
||||
setShowSuccessMessage(true);
|
||||
|
||||
// Reset success state after a delay
|
||||
setTimeout(() => {
|
||||
setIsSuccess(false);
|
||||
}, 3000);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Strategy activation failed:', error);
|
||||
// Optional: Show error message
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
setActivationProgress(0);
|
||||
}
|
||||
};
|
||||
|
||||
/* const setupAnalyticsAndMonitoring = async (strategyId: number, monitoringPlan: any) => {
|
||||
@@ -212,6 +229,8 @@ const EnhancedStrategyActivationButton: React.FC<EnhancedStrategyActivationButto
|
||||
size={20}
|
||||
sx={{ color: 'white' }}
|
||||
/>
|
||||
) : isSuccess ? (
|
||||
<CheckIcon />
|
||||
) : strategyConfirmed ? (
|
||||
<AutoAwesomeIcon />
|
||||
) : (
|
||||
@@ -289,13 +308,24 @@ const EnhancedStrategyActivationButton: React.FC<EnhancedStrategyActivationButto
|
||||
) : isSuccess ? (
|
||||
<motion.div
|
||||
key="success"
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
variants={successVariants}
|
||||
initial="initial"
|
||||
animate="animate"
|
||||
exit="exit"
|
||||
>
|
||||
<Typography variant="button" sx={{ fontWeight: 600 }}>
|
||||
Strategy Activated! 🎉
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<CelebrationIcon />
|
||||
<Typography variant="button" sx={{ fontWeight: 600 }}>
|
||||
Strategy Activated!
|
||||
</Typography>
|
||||
<motion.span
|
||||
variants={confettiVariants}
|
||||
initial="initial"
|
||||
animate="animate"
|
||||
>
|
||||
🎉
|
||||
</motion.span>
|
||||
</Box>
|
||||
</motion.div>
|
||||
) : strategyConfirmed ? (
|
||||
<motion.div
|
||||
|
||||
@@ -85,17 +85,37 @@ export const useAgentHuddleFeed = (options?: { detailTier?: 'summary' | 'detaile
|
||||
useEffect(() => {
|
||||
stopRef.current = false;
|
||||
let pollingTimer: ReturnType<typeof setInterval> | null = null;
|
||||
let backoffMs = BASE_BACKOFF_MS;
|
||||
|
||||
// If polling fails repeatedly, try to reconnect SSE
|
||||
const handlePollingError = () => {
|
||||
if (connectionMode === 'polling' && reconnectAttemptRef.current < 5) {
|
||||
connect();
|
||||
}
|
||||
};
|
||||
|
||||
const startPolling = () => {
|
||||
setConnectionMode('polling');
|
||||
if (pollingTimer) clearInterval(pollingTimer);
|
||||
pollingTimer = setInterval(async () => {
|
||||
|
||||
const poll = async () => {
|
||||
if (document.hidden) return;
|
||||
try {
|
||||
await loadSnapshot(cursorRef.current);
|
||||
} catch {
|
||||
// no-op
|
||||
backoffMs = BASE_BACKOFF_MS;
|
||||
} catch (err: any) {
|
||||
if (err?.response?.status === 429) {
|
||||
// Exponential backoff for 429s
|
||||
backoffMs = Math.min(backoffMs * 2, 60000);
|
||||
clearInterval(pollingTimer!);
|
||||
pollingTimer = setInterval(poll, backoffMs);
|
||||
} else {
|
||||
handlePollingError();
|
||||
}
|
||||
}
|
||||
}, 7000);
|
||||
};
|
||||
|
||||
pollingTimer = setInterval(poll, 10000);
|
||||
};
|
||||
|
||||
const connect = async () => {
|
||||
|
||||
Reference in New Issue
Block a user