This commit is contained in:
Iliyan Angelov
2025-12-01 06:50:10 +02:00
parent 91f51bc6fe
commit 62c1fe5951
4682 changed files with 544807 additions and 31208 deletions

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useRef } from 'react';
import {
CreditCard,
Receipt,
@@ -20,6 +20,8 @@ import { formatDate } from '../../shared/utils/format';
import { useFormatCurrency } from '../../features/payments/hooks/useFormatCurrency';
import { useAsync } from '../../shared/hooks/useAsync';
import { useNavigate } from 'react-router-dom';
import { logger } from '../../shared/utils/logger';
import { getPaymentStatusColor } from '../../shared/utils/paymentUtils';
const AccountantDashboardPage: React.FC = () => {
const { formatCurrency } = useFormatCurrency();
@@ -32,6 +34,8 @@ const AccountantDashboardPage: React.FC = () => {
const [recentInvoices, setRecentInvoices] = useState<Invoice[]>([]);
const [loadingPayments, setLoadingPayments] = useState(false);
const [loadingInvoices, setLoadingInvoices] = useState(false);
const paymentsAbortRef = useRef<AbortController | null>(null);
const invoicesAbortRef = useRef<AbortController | null>(null);
const [financialSummary, setFinancialSummary] = useState({
totalRevenue: 0,
totalPayments: 0,
@@ -64,6 +68,14 @@ const AccountantDashboardPage: React.FC = () => {
}, [dateRange]);
useEffect(() => {
// Cancel previous request if exists
if (paymentsAbortRef.current) {
paymentsAbortRef.current.abort();
}
// Create new abort controller
paymentsAbortRef.current = new AbortController();
const fetchPayments = async () => {
try {
setLoadingPayments(true);
@@ -71,7 +83,7 @@ const AccountantDashboardPage: React.FC = () => {
if (response.success && response.data?.payments) {
setRecentPayments(response.data.payments);
// Calculate financial summary
const completedPayments = response.data.payments.filter((p: Payment) => p.payment_status === 'completed');
const completedPayments = response.data.payments.filter((p: Payment) => p.payment_status === 'completed' || p.payment_status === 'paid');
const pendingPayments = response.data.payments.filter((p: Payment) => p.payment_status === 'pending');
const totalRevenue = completedPayments.reduce((sum: number, p: Payment) => sum + (p.amount || 0), 0);
@@ -83,15 +95,34 @@ const AccountantDashboardPage: React.FC = () => {
}));
}
} catch (err: any) {
console.error('Error fetching payments:', err);
// Handle AbortError silently
if (err.name === 'AbortError') {
return;
}
logger.error('Error fetching payments', err);
} finally {
setLoadingPayments(false);
}
};
fetchPayments();
// Cleanup: abort request on unmount
return () => {
if (paymentsAbortRef.current) {
paymentsAbortRef.current.abort();
}
};
}, []);
useEffect(() => {
// Cancel previous request if exists
if (invoicesAbortRef.current) {
invoicesAbortRef.current.abort();
}
// Create new abort controller
invoicesAbortRef.current = new AbortController();
const fetchInvoices = async () => {
try {
setLoadingInvoices(true);
@@ -110,32 +141,29 @@ const AccountantDashboardPage: React.FC = () => {
}));
}
} catch (err: any) {
console.error('Error fetching invoices:', err);
// Handle AbortError silently
if (err.name === 'AbortError') {
return;
}
logger.error('Error fetching invoices', err);
} finally {
setLoadingInvoices(false);
}
};
fetchInvoices();
// Cleanup: abort request on unmount
return () => {
if (invoicesAbortRef.current) {
invoicesAbortRef.current.abort();
}
};
}, []);
const handleRefresh = () => {
execute();
};
const getPaymentStatusColor = (status: string) => {
switch (status) {
case 'completed':
return 'bg-gradient-to-r from-emerald-50 to-green-50 text-emerald-800 border-emerald-200';
case 'pending':
return 'bg-gradient-to-r from-amber-50 to-yellow-50 text-amber-800 border-amber-200';
case 'failed':
return 'bg-gradient-to-r from-rose-50 to-red-50 text-rose-800 border-rose-200';
case 'refunded':
return 'bg-gradient-to-r from-slate-50 to-gray-50 text-slate-700 border-slate-200';
default:
return 'bg-gradient-to-r from-slate-50 to-gray-50 text-slate-700 border-slate-200';
}
};
const getInvoiceStatusColor = (status: string) => {
switch (status) {