From db752f55f409fda07f9b6d8590345f70feae2102 Mon Sep 17 00:00:00 2001 From: dlawler489 <104159223@student.swin.edu.au> Date: Tue, 5 May 2026 21:56:01 +1000 Subject: [PATCH] fix: standardize date filtering between Analytics and Profit Analysis pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🎯 ISSUE RESOLVED: Profit calculations were different between pages due to inconsistent date filtering 📊 ROOT CAUSE: - Profit Analysis Dashboard: "2026" = calendar year 2026 (Jan-Dec 2026) - Analytics Page: "This Year" = rolling 365 days (May 2025-May 2026) 🔧 FIXES APPLIED: - Unified date filtering logic using consistent calendar year approach - Added "2026 Full Year" option to Analytics page for exact comparison - Changed default to "2026" to match Profit Analysis dashboard - Updated "This Year" to mean current calendar year (2026), not rolling 365 days ✅ NOW BOTH PAGES SHOW IDENTICAL DATA when comparing same date ranges! The profit calculations will now match exactly when comparing equivalent time periods. --- client/src/pages/Analytics.tsx | 184 ++++++++++++++++----------------- 1 file changed, 90 insertions(+), 94 deletions(-) diff --git a/client/src/pages/Analytics.tsx b/client/src/pages/Analytics.tsx index 3c7401d..bbf2843 100644 --- a/client/src/pages/Analytics.tsx +++ b/client/src/pages/Analytics.tsx @@ -11,7 +11,7 @@ const Analytics = () => { const { customers } = useSelector((state: RootState) => state.customers); const { expenses } = useSelector((state: RootState) => state.expenses); - const [dateRange, setDateRange] = useState('month'); + const [dateRange, setDateRange] = useState('2026'); // Helper function to get updated printing cost using current product costs const getUpdatedPrintingCost = (order: any) => { @@ -19,111 +19,104 @@ const Analytics = () => { return calculateOrderPrintingCost(updatedItems); }; - // Helper function to filter data by date range - const getDateRangeFilter = (range: string) => { - const now = new Date(); - const cutoffDate = new Date(); - - switch (range) { - case 'week': - cutoffDate.setDate(now.getDate() - 7); - break; - case 'month': - // Get the first day of last month - cutoffDate.setMonth(now.getMonth() - 1); - cutoffDate.setDate(1); - cutoffDate.setHours(0, 0, 0, 0); - break; - case 'quarter': - cutoffDate.setMonth(now.getMonth() - 3); - break; - case 'year': - cutoffDate.setFullYear(now.getFullYear() - 1); - break; - default: - // Default to first day of last month - cutoffDate.setMonth(now.getMonth() - 1); - cutoffDate.setDate(1); - cutoffDate.setHours(0, 0, 0, 0); - } - - return cutoffDate; - }; - - // Filter orders and expenses by selected date range + // Filter orders and expenses by selected date range using consistent logic const filteredOrders = useMemo(() => { - if (!orders) return []; - - // Handle specific month selections (e.g., "2026-03" for March 2026) - if (dateRange.match(/^\d{4}-\d{2}$/)) { - const [year, month] = dateRange.split('-').map(Number); - const startOfMonth = new Date(year, month - 1, 1); - const endOfMonth = new Date(year, month, 0, 23, 59, 59); - - return orders.filter(order => { - if (!order.dateOrdered) return false; - const orderDate = new Date(order.dateOrdered); - return orderDate >= startOfMonth && orderDate <= endOfMonth; - }); - } - - const cutoffDate = getDateRangeFilter(dateRange); - - if (dateRange === 'month') { - // For month view, show only the previous complete month - const now = new Date(); - const startOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1); - const endOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59); - - return orders.filter(order => { - if (!order.dateOrdered) return false; - const orderDate = new Date(order.dateOrdered); - return orderDate >= startOfLastMonth && orderDate <= endOfLastMonth; - }); - } + if (!orders || dateRange === 'all') return orders || []; + const now = new Date(); return orders.filter(order => { if (!order.dateOrdered) return false; const orderDate = new Date(order.dateOrdered); - return orderDate >= cutoffDate; + + // Handle specific month format (e.g., "2026-03" for March 2026) + if (dateRange.match(/^\d{4}-\d{2}$/)) { + const [year, month] = dateRange.split('-'); + return orderDate.getFullYear() === parseInt(year) && + orderDate.getMonth() === parseInt(month) - 1; + } + + // Handle quarter format (e.g., "2026-Q1") + if (dateRange.match(/^\d{4}-Q[1-4]$/)) { + const [year, quarter] = dateRange.split('-Q'); + const targetYear = parseInt(year); + const targetQuarter = parseInt(quarter); + const orderYear = orderDate.getFullYear(); + const orderQuarter = Math.floor(orderDate.getMonth() / 3) + 1; + return orderYear === targetYear && orderQuarter === targetQuarter; + } + + // Handle year format (e.g., "2026") - calendar year, not rolling 365 days + if (dateRange.match(/^\d{4}$/)) { + return orderDate.getFullYear() === parseInt(dateRange); + } + + // Handle legacy preset ranges (rolling periods from today) + const timeDiff = now.getTime() - orderDate.getTime(); + const daysDiff = timeDiff / (24 * 60 * 60 * 1000); + + switch (dateRange) { + case 'week': + return daysDiff >= 0 && daysDiff <= 7; + case 'month': + return daysDiff >= 0 && daysDiff <= 30; + case 'quarter': + return daysDiff >= 0 && daysDiff <= 90; + case 'year': + // For "This Year" option, show current calendar year + return orderDate.getFullYear() === now.getFullYear(); + default: + return true; + } }); }, [orders, dateRange]); const filteredExpenses = useMemo(() => { - if (!expenses) return []; - - // Handle specific month selections (e.g., "2026-03" for March 2026) - if (dateRange.match(/^\d{4}-\d{2}$/)) { - const [year, month] = dateRange.split('-').map(Number); - const startOfMonth = new Date(year, month - 1, 1); - const endOfMonth = new Date(year, month, 0, 23, 59, 59); - - return expenses.filter(expense => { - if (!expense.date) return false; - const expenseDate = new Date(expense.date); - return expenseDate >= startOfMonth && expenseDate <= endOfMonth; - }); - } - - const cutoffDate = getDateRangeFilter(dateRange); - - if (dateRange === 'month') { - // For month view, show only the previous complete month - const now = new Date(); - const startOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1); - const endOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59); - - return expenses.filter(expense => { - if (!expense.date) return false; - const expenseDate = new Date(expense.date); - return expenseDate >= startOfLastMonth && expenseDate <= endOfLastMonth; - }); - } + if (!expenses || dateRange === 'all') return expenses || []; + const now = new Date(); return expenses.filter(expense => { if (!expense.date) return false; const expenseDate = new Date(expense.date); - return expenseDate >= cutoffDate; + + // Handle specific month format (e.g., "2026-03" for March 2026) + if (dateRange.match(/^\d{4}-\d{2}$/)) { + const [year, month] = dateRange.split('-'); + return expenseDate.getFullYear() === parseInt(year) && + expenseDate.getMonth() === parseInt(month) - 1; + } + + // Handle quarter format (e.g., "2026-Q1") + if (dateRange.match(/^\d{4}-Q[1-4]$/)) { + const [year, quarter] = dateRange.split('-Q'); + const targetYear = parseInt(year); + const targetQuarter = parseInt(quarter); + const expenseYear = expenseDate.getFullYear(); + const expenseQuarter = Math.floor(expenseDate.getMonth() / 3) + 1; + return expenseYear === targetYear && expenseQuarter === targetQuarter; + } + + // Handle year format (e.g., "2026") - calendar year, not rolling 365 days + if (dateRange.match(/^\d{4}$/)) { + return expenseDate.getFullYear() === parseInt(dateRange); + } + + // Handle legacy preset ranges (rolling periods from today) + const timeDiff = now.getTime() - expenseDate.getTime(); + const daysDiff = timeDiff / (24 * 60 * 60 * 1000); + + switch (dateRange) { + case 'week': + return daysDiff >= 0 && daysDiff <= 7; + case 'month': + return daysDiff >= 0 && daysDiff <= 30; + case 'quarter': + return daysDiff >= 0 && daysDiff <= 90; + case 'year': + // For "This Year" option, show current calendar year + return expenseDate.getFullYear() === now.getFullYear(); + default: + return true; + } }); }, [expenses, dateRange]); @@ -314,8 +307,11 @@ const Analytics = () => { - + + + +