diff --git a/dashboard-server/klaviyo-server/services/events.service.js b/dashboard-server/klaviyo-server/services/events.service.js index fb4e5af..ba0ed0b 100644 --- a/dashboard-server/klaviyo-server/services/events.service.js +++ b/dashboard-server/klaviyo-server/services/events.service.js @@ -1,6 +1,7 @@ import fetch from 'node-fetch'; import { TimeManager } from '../utils/time.utils.js'; import { RedisService } from './redis.service.js'; +import _ from 'lodash'; const METRIC_IDS = { PLACED_ORDER: 'Y8cqcF', @@ -225,12 +226,24 @@ export class EventsService { metricId: METRIC_IDS.CANCELED_ORDER }), this.getEvents({ + // Only pass through non-date related params for previous period + ..._.omit(params, ['timeRange', 'startDate', 'endDate']), metricId: METRIC_IDS.PLACED_ORDER, startDate: prevPeriodStart.toISO(), endDate: prevPeriodEnd.toISO() }) ]); + // Add debug logging + console.log('[EventsService] Previous period request:', { + params: _.omit(params, ['timeRange', 'startDate', 'endDate']), + dates: { + start: prevPeriodStart.toISO(), + end: prevPeriodEnd.toISO() + }, + responseLength: prevPeriodData?.data?.length + }); + // Transform all data const transformedOrders = this._transformEvents(orderData.data); const transformedShipped = this._transformEvents(shippedData.data); @@ -1489,15 +1502,51 @@ export class EventsService { ...params, startDate: periodStart.toISO(), endDate: periodEnd.toISO(), - metricId: METRIC_IDS.PLACED_ORDER + metricId: METRIC_IDS.PLACED_ORDER, + customFilters: params.metric === 'pre_orders' ? ['equals(event_properties.HasPreorder,true)'] : + params.metric === 'local_pickup' ? ['equals(event_properties.LocalPickup,true)'] : + params.metric === 'on_hold' ? ['equals(event_properties.IsOnHold,true)'] : undefined }), this.getEvents({ - metricId: METRIC_IDS.PLACED_ORDER, + ..._.omit(params, ['timeRange', 'startDate', 'endDate']), startDate: prevPeriodStart.toISO(), - endDate: prevPeriodEnd.toISO() + endDate: prevPeriodEnd.toISO(), + metricId: METRIC_IDS.PLACED_ORDER, + timeRange: undefined, + isPreviousPeriod: true, + cacheKey: `prev_${prevPeriodStart.toISO()}_${prevPeriodEnd.toISO()}`, + customFilters: params.metric === 'pre_orders' ? ['equals(event_properties.HasPreorder,true)'] : + params.metric === 'local_pickup' ? ['equals(event_properties.LocalPickup,true)'] : + params.metric === 'on_hold' ? ['equals(event_properties.IsOnHold,true)'] : undefined }) ]); + // Add debug logging for request params and filters + console.log('[EventsService] Request details with filters:', { + current: { + params: { + ...params, + startDate: periodStart.toISO(), + endDate: periodEnd.toISO(), + customFilters: params.metric === 'pre_orders' ? ['equals(event_properties.HasPreorder,true)'] : + params.metric === 'local_pickup' ? ['equals(event_properties.LocalPickup,true)'] : + params.metric === 'on_hold' ? ['equals(event_properties.IsOnHold,true)'] : undefined + }, + responseLength: currentResponse?.data?.length + }, + previous: { + params: { + ..._.omit(params, ['timeRange', 'startDate', 'endDate']), + startDate: prevPeriodStart.toISO(), + endDate: prevPeriodEnd.toISO(), + customFilters: params.metric === 'pre_orders' ? ['equals(event_properties.HasPreorder,true)'] : + params.metric === 'local_pickup' ? ['equals(event_properties.LocalPickup,true)'] : + params.metric === 'on_hold' ? ['equals(event_properties.IsOnHold,true)'] : undefined + }, + responseLength: prevResponse?.data?.length + } + }); + // Transform events const currentEvents = this._transformEvents(currentResponse.data || []); const prevEvents = this._transformEvents(prevResponse.data || []); @@ -1561,6 +1610,7 @@ export class EventsService { const dateKey = prevDate.toFormat('yyyy-MM-dd'); prevDailyStats.set(dateKey, { date: prevDate.toISO(), + timestamp: dateKey, revenue: 0, orders: 0, itemCount: 0 @@ -1584,31 +1634,43 @@ export class EventsService { dayStats.revenue += totalAmount; dayStats.orders++; dayStats.itemCount += items.length; + prevDailyStats.set(dateKey, dayStats); } - // 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; + // Map previous period data to current period days + const prevPeriodDays = Array.from(prevDailyStats.values()).sort((a, b) => a.date.localeCompare(b.date)); + const currentPeriodDays = Array.from(dailyStats.values()).sort((a, b) => a.date.localeCompare(b.date)); - for (let i = 0; i < daysInPeriod; i++) { + // Add debug logging for data before mapping + console.log('[EventsService] Data before mapping:', { + currentPeriod: currentPeriodDays.slice(0, 3), + previousPeriod: prevPeriodDays.slice(0, 3), + currentLength: currentPeriodDays.length, + prevLength: prevPeriodDays.length + }); + + // Map the data using array indices + for (let i = 0; i < currentPeriodDays.length && i < prevPeriodDays.length; i++) { const currentDayStats = currentPeriodDays[i]; const prevDayStats = prevPeriodDays[i]; - - if (prevDayStats) { + + if (prevDayStats && currentDayStats) { 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; + if (dayStats) { + dayStats.prevRevenue = prevDayStats.revenue; + dayStats.prevOrders = prevDayStats.orders; + dayStats.prevItemCount = prevDayStats.itemCount; + dayStats.prevAvgOrderValue = prevDayStats.orders > 0 ? prevDayStats.revenue / prevDayStats.orders : 0; + dailyStats.set(currentDayStats.timestamp, dayStats); + } } } - // 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] + // Add debug logging for mapped data + console.log('[EventsService] Sample of mapped data:', { + firstDay: dailyStats.get(currentPeriodDays[0]?.timestamp), + lastDay: dailyStats.get(currentPeriodDays[currentPeriodDays.length - 1]?.timestamp), + totalDays: dailyStats.size }); // Convert to array and sort by date diff --git a/dashboard-server/klaviyo-server/services/redis.service.js b/dashboard-server/klaviyo-server/services/redis.service.js index 0c933e9..9a5409b 100644 --- a/dashboard-server/klaviyo-server/services/redis.service.js +++ b/dashboard-server/klaviyo-server/services/redis.service.js @@ -131,24 +131,47 @@ export class RedisService { } } - // Helper to generate cache keys - _getCacheKey(type, params = {}) { - const { timeRange, startDate, endDate, metricId, metric, daily } = params; - let key = `klaviyo:${type}`; +// Helper to generate cache keys +_getCacheKey(type, params = {}) { + const { + timeRange, + startDate, + endDate, + metricId, + metric, + daily, + cacheKey, + isPreviousPeriod + } = params; - if (type === 'stats:details') { - key += `:${metric}${daily ? ':daily' : ''}`; - } + let key = `klaviyo:${type}`; - if (timeRange) { - key += `:${timeRange}${metricId ? `:${metricId}` : ''}`; - } else if (startDate && endDate) { - key += `:custom:${startDate}:${endDate}${metricId ? `:${metricId}` : ''}`; - } - - return key; + // Handle "stats:details" for daily or metric-based keys + if (type === 'stats:details') { + key += `:${metric}${daily ? ':daily' : ''}`; } + // If a specific cache key is provided, use it (highest priority) + if (cacheKey) { + key += `:${cacheKey}`; + } + // Otherwise, build a default cache key + else if (timeRange) { + key += `:${timeRange}${metricId ? `:${metricId}` : ''}`; + if (isPreviousPeriod) { + key += ':prev'; + } + } else if (startDate && endDate) { + key += `:custom:${startDate}:${endDate}${metricId ? `:${metricId}` : ''}`; + if (isPreviousPeriod) { + key += ':prev'; + } + } + + return key; +} + + // Get TTL based on time range _getTTL(timeRange) { const TTL_MAP = { diff --git a/dashboard/src/components/dashboard/SalesChart.jsx b/dashboard/src/components/dashboard/SalesChart.jsx index d80b0e1..b558c24 100644 --- a/dashboard/src/components/dashboard/SalesChart.jsx +++ b/dashboard/src/components/dashboard/SalesChart.jsx @@ -523,53 +523,24 @@ const SalesChart = ({ setLoading(true); setError(null); - // Get previous period params - const prevPeriodParams = calculatePreviousPeriodDates( - params.timeRange, - params.startDate, - params.endDate - ); + // Fetch data + const response = await axios.get('/api/klaviyo/events/stats/details', { + params: { + ...params, + metric: 'revenue', + daily: true + } + }); - // Fetch both current and previous period data - const [currentResponse, prevResponse] = await Promise.all([ - axios.get('/api/klaviyo/events/stats/details', { - params: { - ...params, - metric: 'revenue', - daily: true - } - }), - axios.get('/api/klaviyo/events/stats/details', { - params: { - metric: 'revenue', - daily: true, - ...(prevPeriodParams || {}) - } - }) - ]); - - if (!currentResponse.data) { + if (!response.data) { throw new Error('Invalid response format'); } // Process the data - const currentStats = Array.isArray(currentResponse.data) ? currentResponse.data : currentResponse.data.stats || []; - const prevStats = Array.isArray(prevResponse.data) ? prevResponse.data : (prevResponse.data?.stats || []); + const currentStats = Array.isArray(response.data) ? response.data : response.data.stats || []; - // Map previous period data to current period dates - const processedStats = currentStats.map((day, index) => { - // Find the corresponding previous period day - const prevDay = prevStats[index] || {}; - - return { - ...day, - prevRevenue: Number(prevDay.revenue || 0), - prevOrders: Number(prevDay.orders || 0), - prevAvgOrderValue: Number(prevDay.averageOrderValue || (prevDay.orders > 0 ? prevDay.revenue / prevDay.orders : 0)) - }; - }); - - const processedData = processData(processedStats); + // Process the data directly without remapping + const processedData = processData(currentStats); const stats = calculateSummaryStats(processedData); setData(processedData);