Skip to content

Commit fac57b1

Browse files
fix(dashboard): extract shared getBudgetSettings — single source of truth
Argus found getBudgetSettings() duplicated in CostPage and BudgetAlertBanner with different implementations. Now extracted to utils/budgetSettings.ts: - Single getBudgetSettings() with Number()/Boolean() coercion - Single STORAGE_KEY constant ('aegis:settings') - Both CostPage and BudgetAlertBanner import from shared module - No more duplicate logic divergence risk 1265 tests pass, tsc clean, build clean. Refs: #3183 (review)
1 parent 3f71bf6 commit fac57b1

3 files changed

Lines changed: 31 additions & 42 deletions

File tree

dashboard/src/components/shared/BudgetAlertBanner.tsx

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,14 @@
1010
import { useState, useEffect, useCallback } from 'react';
1111
import { AlertTriangle, X } from 'lucide-react';
1212
import { formatCurrency } from '../../utils/formatNumber';
13-
14-
interface BudgetSettings {
15-
budgetDailyCapUsd: number;
16-
budgetMonthlyCapUsd: number;
17-
budgetAlertEnabled: boolean;
18-
}
13+
import { getBudgetSettings } from '../../utils/budgetSettings';
1914

2015
interface AlertState {
2116
level: 'warning' | 'critical';
2217
message: string;
2318
dismissed: boolean;
2419
}
2520

26-
function getBudgetSettings(): BudgetSettings {
27-
try {
28-
const raw = localStorage.getItem('aegis:settings');
29-
if (raw) {
30-
const parsed = JSON.parse(raw);
31-
return {
32-
budgetDailyCapUsd: parsed.budgetDailyCapUsd ?? 0,
33-
budgetMonthlyCapUsd: parsed.budgetMonthlyCapUsd ?? 0,
34-
budgetAlertEnabled: parsed.budgetAlertEnabled ?? false,
35-
};
36-
}
37-
} catch { /* ignore */ }
38-
return { budgetDailyCapUsd: 0, budgetMonthlyCapUsd: 0, budgetAlertEnabled: false };
39-
}
40-
4121
const DISMISS_KEY = 'aegis:budget-alert-dismissed';
4222

4323
export function BudgetAlertBanner() {

dashboard/src/pages/CostPage.tsx

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import type { AnalyticsCostsResponse } from '../types';
3030
import { BudgetProgressBar } from '../components/shared/BudgetProgressBar';
3131
import { SpendSummary } from '../components/cost/SpendSummary';
3232
import { ForecastChart } from '../components/cost/ForecastChart';
33+
import { getBudgetSettings, type BudgetSettings } from '../utils/budgetSettings';
3334

3435
const MODEL_COLORS: Record<string, string> = {
3536
'claude-sonnet-4.6': 'var(--color-accent-cyan)',
@@ -63,27 +64,6 @@ function CustomTooltip({ active, payload, label }: {
6364
}
6465

6566

66-
interface BudgetSettings {
67-
budgetDailyCapUsd: number;
68-
budgetMonthlyCapUsd: number;
69-
budgetAlertEnabled: boolean;
70-
}
71-
72-
function getBudgetSettings(): BudgetSettings {
73-
try {
74-
const raw = localStorage.getItem('aegis:settings');
75-
if (raw) {
76-
const parsed = JSON.parse(raw);
77-
return {
78-
budgetDailyCapUsd: Number(parsed.budgetDailyCapUsd) || 0,
79-
budgetMonthlyCapUsd: Number(parsed.budgetMonthlyCapUsd) || 0,
80-
budgetAlertEnabled: Boolean(parsed.budgetAlertEnabled),
81-
};
82-
}
83-
} catch { /* ignore parse errors */ }
84-
return { budgetDailyCapUsd: 0, budgetMonthlyCapUsd: 0, budgetAlertEnabled: false };
85-
}
86-
8767

8868
interface BudgetOverviewProps {
8969
dailyData: Array<{ date: string; estimatedCostUsd: number; sessions: number }>;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* budgetSettings.ts — Shared budget settings reader from localStorage.
3+
*
4+
* Single source of truth for budget configuration parsing.
5+
* Used by CostPage, BudgetAlertBanner, and any future budget-aware components.
6+
*/
7+
8+
export interface BudgetSettings {
9+
budgetDailyCapUsd: number;
10+
budgetMonthlyCapUsd: number;
11+
budgetAlertEnabled: boolean;
12+
}
13+
14+
const STORAGE_KEY = 'aegis:settings';
15+
16+
export function getBudgetSettings(): BudgetSettings {
17+
try {
18+
const raw = localStorage.getItem(STORAGE_KEY);
19+
if (raw) {
20+
const parsed = JSON.parse(raw);
21+
return {
22+
budgetDailyCapUsd: Number(parsed.budgetDailyCapUsd) || 0,
23+
budgetMonthlyCapUsd: Number(parsed.budgetMonthlyCapUsd) || 0,
24+
budgetAlertEnabled: Boolean(parsed.budgetAlertEnabled),
25+
};
26+
}
27+
} catch { /* ignore parse errors */ }
28+
return { budgetDailyCapUsd: 0, budgetMonthlyCapUsd: 0, budgetAlertEnabled: false };
29+
}

0 commit comments

Comments
 (0)