diff --git a/frontend/src/pages/Budgets.jsx b/frontend/src/pages/Budgets.jsx
index b2402bc..8a97e05 100644
--- a/frontend/src/pages/Budgets.jsx
+++ b/frontend/src/pages/Budgets.jsx
@@ -13,9 +13,12 @@ const Budgets = () => {
const { currency } = useCurrency();
const [isBudgetModalOpen, setIsBudgetModalOpen] = useState(false);
const [editingBudget, setEditingBudget] = useState(null);
+ const [error, setError] = useState('');
+ const [budgetAlerts, setBudgetAlerts] = useState([]);
const fetchData = useCallback(async () => {
setLoading(true);
+ setError('');
try {
const [budgetsRes, categoriesRes, transactionsRes] = await Promise.all([
api.get('/budgets'),
@@ -27,6 +30,7 @@ const Budgets = () => {
setTransactions(transactionsRes.data.transactions || []);
} catch (error) {
console.error('Failed to fetch budgets or transactions', error);
+ setError('Failed to load budgets. Please check your connection.');
} finally {
setLoading(false);
}
@@ -36,6 +40,29 @@ const Budgets = () => {
fetchData();
}, [fetchData]);
+ // Budget alerts - auto calculates
+ useEffect(() => {
+ const alerts = budgets.map(budget => {
+ const spent = calculateSpent(budget);
+ const percent = (spent / budget.amount) * 100;
+ if (percent > 100) {
+ return {
+ message: `🚨 OVER BUDGET: ${budget.category} exceeded by ${(percent-100).toFixed(1)}%`,
+ type: 'error'
+ };
+ }
+ if (percent > 80) {
+ return {
+ message: `⚠️ WARNING: ${budget.category} at ${percent.toFixed(1)}% of budget`,
+ type: 'warning'
+ };
+ }
+ return null;
+ }).filter(alert => alert);
+
+ setBudgetAlerts(alerts);
+ }, [budgets, transactions]);
+
const handleOpenBudgetModal = (budget = null) => {
setEditingBudget(budget);
setIsBudgetModalOpen(true);
@@ -48,22 +75,26 @@ const Budgets = () => {
const handleFormSubmit = async (formData, id) => {
try {
+ setError('');
if (id) await api.put(`/budgets/${id}`, formData);
else await api.post('/budgets', formData);
fetchData();
handleCloseBudgetModal();
} catch (error) {
console.error('Failed to save budget', error);
+ setError('Failed to save budget. Please try again.');
}
};
const handleDeleteBudget = async (id) => {
if (window.confirm('Are you sure you want to delete this budget?')) {
try {
+ setError('');
await api.delete(`/budgets/${id}`);
fetchData();
} catch (error) {
console.error('Failed to delete budget', error);
+ setError('Failed to delete budget. Please try again.');
}
}
};
@@ -95,6 +126,34 @@ const Budgets = () => {
+ {/* Error Display */}
+ {error && (
+
+ )}
+
+ {/* Budget Alerts */}
+ {budgetAlerts.map((alert, index) => (
+
+ ))}
+
{loading ? (
) : budgets.length > 0 ? (
@@ -129,9 +188,7 @@ const Budgets = () => {
{budgets.map((b) => {
const spent = calculateSpent(b);
const remaining = b.amount - spent;
- const percent = Math.min((spent / b.amount) * 100, 100).toFixed(
- 1
- );
+ const percent = Math.min((spent / b.amount) * 100, 100).toFixed(1);
return (
@@ -177,6 +234,12 @@ const Budgets = () => {
{percent}%
|
+
|