diff --git a/dashboard-server/klaviyo-server/services/events.service.js b/dashboard-server/klaviyo-server/services/events.service.js index 409313b..fb4e5af 100644 --- a/dashboard-server/klaviyo-server/services/events.service.js +++ b/dashboard-server/klaviyo-server/services/events.service.js @@ -1415,6 +1415,7 @@ export class EventsService { async calculateDetailedStats(params = {}) { try { const { metric, daily = false } = params; + console.log('[EventsService] Request params:', params); // Get period dates let periodStart, periodEnd, prevPeriodStart, prevPeriodEnd; @@ -1425,20 +1426,64 @@ export class EventsService { prevPeriodEnd = periodStart.minus({ milliseconds: 1 }); prevPeriodStart = prevPeriodEnd.minus(duration); } else if (params.timeRange) { - // Normalize time range to use 'last' prefix instead of 'previous' - const normalizedTimeRange = params.timeRange.replace('previous', 'last'); + // Handle both current and previous period time ranges + const timeRange = params.timeRange; + const isPreviousPeriod = timeRange.startsWith('previous'); + const normalizedTimeRange = isPreviousPeriod ? timeRange.replace('previous', 'last') : timeRange; + + console.log('[EventsService] Time range details:', { + originalTimeRange: timeRange, + isPreviousPeriod, + normalizedTimeRange + }); + + // Get current period range const range = this.timeManager.getDateRange(normalizedTimeRange); - const prevRange = this.timeManager.getPreviousPeriod(normalizedTimeRange); - if (!range || !prevRange) { - throw new Error('Invalid time range specified'); + if (!range) { + throw new Error(`Invalid time range specified: ${timeRange}`); } + + // Get previous period range using TimeManager + const prevRange = this.timeManager.getPreviousPeriod(normalizedTimeRange); + if (!prevRange) { + throw new Error(`Could not calculate previous period for: ${timeRange}`); + } + periodStart = range.start; periodEnd = range.end; prevPeriodStart = prevRange.start; prevPeriodEnd = prevRange.end; + + console.log('[EventsService] Calculated date ranges:', { + timeRange, + current: { + start: periodStart.toISO(), + end: periodEnd.toISO(), + duration: periodEnd.diff(periodStart).as('days') + }, + previous: { + start: prevPeriodStart.toISO(), + end: prevPeriodEnd.toISO(), + duration: prevPeriodEnd.diff(prevPeriodStart).as('days') + } + }); } // Load both current and previous period data + console.log('[EventsService] Fetching events with params:', { + current: { + startDate: periodStart.toISO(), + endDate: periodEnd.toISO(), + metricId: METRIC_IDS.PLACED_ORDER, + ...params + }, + previous: { + startDate: prevPeriodStart.toISO(), + endDate: prevPeriodEnd.toISO(), + metricId: METRIC_IDS.PLACED_ORDER + } + }); + const [currentResponse, prevResponse] = await Promise.all([ this.getEvents({ ...params, @@ -1457,6 +1502,17 @@ export class EventsService { const currentEvents = this._transformEvents(currentResponse.data || []); const prevEvents = this._transformEvents(prevResponse.data || []); + console.log('[EventsService] Transformed events:', { + current: { + count: currentEvents.length, + revenue: currentEvents.reduce((sum, event) => sum + (Number(event.event_properties?.TotalAmount) || 0), 0) + }, + previous: { + count: prevEvents.length, + revenue: prevEvents.reduce((sum, event) => sum + (Number(event.event_properties?.TotalAmount) || 0), 0) + } + }); + // Initialize daily stats map with all dates in range const dailyStats = new Map(); let currentDate = periodStart; @@ -1472,6 +1528,7 @@ export class EventsService { averageItemsPerOrder: 0, prevRevenue: 0, prevOrders: 0, + prevItemCount: 0, prevAvgOrderValue: 0 }); currentDate = currentDate.plus({ days: 1 }); @@ -1497,31 +1554,78 @@ export class EventsService { dayStats.averageItemsPerOrder = dayStats.itemCount / dayStats.orders; } - // Process previous period events and map them to corresponding current period dates - const daysDiff = periodEnd.diff(periodStart, 'days').days; + // Process previous period events + const prevDailyStats = new Map(); + let prevDate = prevPeriodStart; + while (prevDate <= prevPeriodEnd) { + const dateKey = prevDate.toFormat('yyyy-MM-dd'); + prevDailyStats.set(dateKey, { + date: prevDate.toISO(), + revenue: 0, + orders: 0, + itemCount: 0 + }); + prevDate = prevDate.plus({ days: 1 }); + } + + // Aggregate previous period data for (const event of prevEvents) { const datetime = this.timeManager.toDateTime(event.attributes?.datetime); if (!datetime) continue; - // Calculate the corresponding date in the current period - const daysFromStart = datetime.diff(prevPeriodStart, 'days').days; - const correspondingDate = periodStart.plus({ days: daysFromStart }); - const dateKey = correspondingDate.toFormat('yyyy-MM-dd'); + const dateKey = datetime.toFormat('yyyy-MM-dd'); + if (!prevDailyStats.has(dateKey)) continue; - if (!dailyStats.has(dateKey)) continue; - - const dayStats = dailyStats.get(dateKey); + const dayStats = prevDailyStats.get(dateKey); const props = event.event_properties || {}; const totalAmount = Number(props.TotalAmount || 0); + const items = props.Items || []; - dayStats.prevRevenue += totalAmount; - dayStats.prevOrders++; - dayStats.prevAvgOrderValue = dayStats.prevRevenue / dayStats.prevOrders; + dayStats.revenue += totalAmount; + dayStats.orders++; + dayStats.itemCount += items.length; } + // Map previous period data to current period days based on relative position + const prevPeriodDays = Array.from(prevDailyStats.values()); + const currentPeriodDays = Array.from(dailyStats.values()); + const daysInPeriod = currentPeriodDays.length; + + for (let i = 0; i < daysInPeriod; i++) { + const currentDayStats = currentPeriodDays[i]; + const prevDayStats = prevPeriodDays[i]; + + if (prevDayStats) { + const dayStats = dailyStats.get(currentDayStats.timestamp); + dayStats.prevRevenue = prevDayStats.revenue; + dayStats.prevOrders = prevDayStats.orders; + dayStats.prevItemCount = prevDayStats.itemCount; + dayStats.prevAvgOrderValue = prevDayStats.orders > 0 ? prevDayStats.revenue / prevDayStats.orders : 0; + } + } + + // Log the final daily stats before returning + console.log('[EventsService] Final daily stats sample:', { + totalDays: dailyStats.size, + firstDay: Array.from(dailyStats.values())[0], + lastDay: Array.from(dailyStats.values())[dailyStats.size - 1] + }); + // Convert to array and sort by date const stats = Array.from(dailyStats.values()) - .sort((a, b) => a.date.localeCompare(b.date)); + .sort((a, b) => a.date.localeCompare(b.date)) + .map(day => ({ + ...day, + revenue: Number(day.revenue || 0), + orders: Number(day.orders || 0), + itemCount: Number(day.itemCount || 0), + averageOrderValue: Number(day.averageOrderValue || 0), + averageItemsPerOrder: Number(day.averageItemsPerOrder || 0), + prevRevenue: Number(day.prevRevenue || 0), + prevOrders: Number(day.prevOrders || 0), + prevItemCount: Number(day.prevItemCount || 0), + prevAvgOrderValue: Number(day.prevAvgOrderValue || 0) + })); return stats; } catch (error) { diff --git a/dashboard-server/klaviyo-server/utils/time.utils.js b/dashboard-server/klaviyo-server/utils/time.utils.js index 566a693..6a6a467 100644 --- a/dashboard-server/klaviyo-server/utils/time.utils.js +++ b/dashboard-server/klaviyo-server/utils/time.utils.js @@ -153,7 +153,10 @@ export class TimeManager { getDateRange(period) { const now = this.getNow(); - switch (period) { + // Normalize period to handle both 'last' and 'previous' prefixes + const normalizedPeriod = period.startsWith('previous') ? period.replace('previous', 'last') : period; + + switch (normalizedPeriod) { case 'custom': { // Custom ranges are handled separately via getCustomRange console.warn('[TimeManager] Custom ranges should use getCustomRange method'); diff --git a/dashboard/src/components/dashboard/SalesChart.jsx b/dashboard/src/components/dashboard/SalesChart.jsx index 57812de..d80b0e1 100644 --- a/dashboard/src/components/dashboard/SalesChart.jsx +++ b/dashboard/src/components/dashboard/SalesChart.jsx @@ -60,9 +60,9 @@ const PREVIOUS_PERIOD_MAP = { today: 'yesterday', thisWeek: 'lastWeek', thisMonth: 'lastMonth', - last7days: 'last7days', - last30days: 'last30days', - last90days: 'last90days', + last7days: 'previous7days', + last30days: 'previous30days', + last90days: 'previous90days', yesterday: 'twoDaysAgo' };