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 && ( +
+
+ + + +

{error}

+
+
+ )} + + {/* Budget Alerts */} + {budgetAlerts.map((alert, index) => ( +
+
+ {alert.message} +
+
+ ))} + {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}% + @@ -588,4 +622,4 @@ const faqs = [ ); }; -export default ContactUs; +export default ContactUs; \ No newline at end of file diff --git a/frontend/src/pages/ReceiptsPage.jsx b/frontend/src/pages/ReceiptsPage.jsx index 7082bf9..dc38190 100644 --- a/frontend/src/pages/ReceiptsPage.jsx +++ b/frontend/src/pages/ReceiptsPage.jsx @@ -52,6 +52,19 @@ const ReceiptsPage = () => { return; } + // Validate file type + const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'application/pdf']; + if (!validTypes.includes(file.type)) { + setError("Please upload JPG, PNG, or PDF files only."); + return; + } + + // Validate file size (5MB max) + if (file.size > 5 * 1024 * 1024) { + setError("File size must be less than 5MB."); + return; + } + const formData = new FormData(); formData.append("receipt", file); @@ -68,7 +81,13 @@ const ReceiptsPage = () => { // Open the modal to allow user to edit the extracted data setOpenEditReceiptResult(true); } catch (err) { - setError("Upload failed. Please try again."); + if (err.response?.status === 413) { + setError("File too large. Please upload a smaller image."); + } else if (err.response?.status === 400) { + setError("Invalid file type. Please upload JPG, PNG, or PDF."); + } else { + setError("Upload failed. Please try again."); + } console.error(err); } finally { setUploading(false); @@ -158,7 +177,7 @@ const ReceiptsPage = () => {
{
- Uploaded Receipt + {receiptResult.fileUrl && ( + Uploaded Receipt + )} ) : (

@@ -288,4 +306,4 @@ const ReceiptsPage = () => { ); }; -export default ReceiptsPage; +export default ReceiptsPage; \ No newline at end of file