diff --git a/dashboard-server/klaviyo-server/services/events.service.js b/dashboard-server/klaviyo-server/services/events.service.js index 3e79dee..409313b 100644 --- a/dashboard-server/klaviyo-server/services/events.service.js +++ b/dashboard-server/klaviyo-server/services/events.service.js @@ -1417,503 +1417,45 @@ export class EventsService { const { metric, daily = false } = params; // Get period dates - let periodStart, periodEnd; + let periodStart, periodEnd, prevPeriodStart, prevPeriodEnd; if (params.startDate && params.endDate) { periodStart = this.timeManager.toDateTime(params.startDate); periodEnd = this.timeManager.toDateTime(params.endDate); + const duration = periodEnd.diff(periodStart); + prevPeriodEnd = periodStart.minus({ milliseconds: 1 }); + prevPeriodStart = prevPeriodEnd.minus(duration); } else if (params.timeRange) { - const range = this.timeManager.getDateRange(params.timeRange); + // Normalize time range to use 'last' prefix instead of 'previous' + const normalizedTimeRange = params.timeRange.replace('previous', 'last'); + const range = this.timeManager.getDateRange(normalizedTimeRange); + const prevRange = this.timeManager.getPreviousPeriod(normalizedTimeRange); + if (!range || !prevRange) { + throw new Error('Invalid time range specified'); + } periodStart = range.start; periodEnd = range.end; + prevPeriodStart = prevRange.start; + prevPeriodEnd = prevRange.end; } - // Load data based on metric type - let mainData, orderData; - switch (metric) { - case 'revenue': - case 'orders': - case 'average_order': - mainData = await this.getEvents({ - ...params, - metricId: METRIC_IDS.PLACED_ORDER - }); - break; - case 'refunds': - // Get refund events - mainData = await this.getEvents({ - ...params, - metricId: METRIC_IDS.PAYMENT_REFUNDED - }); - - const refundEvents = this._transformEvents(mainData.data); - const dailyRefundStats = new Map(); - let currentDate = periodStart; - - // Initialize daily stats for the entire date range - while (currentDate <= periodEnd) { - const dateKey = currentDate.toFormat('yyyy-MM-dd'); - dailyRefundStats.set(dateKey, { - date: currentDate.toISO(), - timestamp: dateKey, - total: 0, - count: 0, - reasons: {}, - items: [] - }); - currentDate = currentDate.plus({ days: 1 }); - } - - // Process refund events - refundEvents.forEach(event => { - const datetime = this.timeManager.toDateTime(event.attributes?.datetime); - if (!datetime) return; - - const dateKey = datetime.toFormat('yyyy-MM-dd'); - if (!dailyRefundStats.has(dateKey)) return; - - const stats = dailyRefundStats.get(dateKey); - const props = event.event_properties || {}; - const amount = Number(props.PaymentAmount || 0); - const reason = props.CancelReason || props.OrderMessage || 'No reason provided'; - - stats.total += amount; - stats.count++; - stats.reasons[reason] = (stats.reasons[reason] || 0) + 1; - stats.items.push({ - orderId: props.OrderId, - amount, - reason, - datetime: datetime.toISO() - }); - - // Add these properties to match what RefundDetails component expects - stats.refunds = { - total: stats.total, - count: stats.count, - reasons: stats.reasons - }; - }); - - // Format daily stats - const formattedRefundStats = Array.from(dailyRefundStats.values()) - .sort((a, b) => a.date.localeCompare(b.date)); - - // Calculate total stats - const totalRefunds = refundEvents.reduce((sum, event) => { - const props = event.event_properties || {}; - return sum + Number(props.PaymentAmount || 0); - }, 0); - - const totalReasons = refundEvents.reduce((acc, event) => { - const props = event.event_properties || {}; - const reason = props.CancelReason || props.OrderMessage || 'No reason provided'; - acc[reason] = (acc[reason] || 0) + 1; - return acc; - }, {}); - - // Store totals in both meta and first day - const totalStats = { - meta: { - refunds: { - total: totalRefunds, - count: refundEvents.length, - reasons: totalReasons - } - } - }; - - // Add totals to first day for the details view - if (formattedRefundStats.length > 0) { - formattedRefundStats[0] = { - ...formattedRefundStats[0], - refunds: { - ...formattedRefundStats[0].refunds, - total: totalRefunds, - count: refundEvents.length, - reasons: totalReasons - } - }; - } - - // Return array with meta property - return Object.assign(formattedRefundStats, totalStats); - case 'shipping': - // Get shipped orders - mainData = await this.getEvents({ - ...params, - metricId: METRIC_IDS.SHIPPED_ORDER - }); - - // Transform shipped orders data with proper error handling - if (mainData?.data) { - try { - const transformedShipped = this._transformEvents(mainData.data).filter(event => { - const props = event.event_properties || {}; - // Log each event's shipping properties - console.log('[EventsService] Shipping properties for event:', { - id: event.id, - orderId: props.OrderId, - shipMethod: props.ShipMethod, - shippingMethod: props.ShippingMethod, - state: props.ShippingState, - country: props.ShippingCountry, - isValid: !!(props.OrderId && (props.ShipMethod || props.ShippingMethod)) - }); - // Ensure we have basic shipping data - return props.OrderId && (props.ShipMethod || props.ShippingMethod); - }); - - const shippedCount = transformedShipped.length; - console.log('[EventsService] Processing shipping data:', { - totalEvents: mainData.data.length, - validEvents: shippedCount - }); - - // Track shipping methods and locations with error handling - const methodMap = new Map(); - const stateMap = new Map(); - const countryMap = new Map(); - - transformedShipped.forEach(shipped => { - const props = shipped.event_properties || {}; - - // Track shipping methods with normalization - const method = (props.ShipMethod || props.ShippingMethod || 'Unknown').trim(); - methodMap.set(method, (methodMap.get(method) || 0) + 1); - - // Track locations with proper validation - const state = props.ShippingState?.trim(); - const country = props.ShippingCountry?.trim(); - - // For international orders, use country as the "state" - const locationKey = state || country; - if (locationKey) { - if (!stateMap.has(locationKey)) { - stateMap.set(locationKey, { count: 0, country: country || 'Unknown' }); - } - stateMap.get(locationKey).count++; - - // Track by country - if (country) { - if (!countryMap.has(country)) { - countryMap.set(country, { count: 0, states: new Set() }); - } - const countryStats = countryMap.get(country); - countryStats.count++; - countryStats.states.add(locationKey); - } - } - }); - - // Format the data for the frontend with validation - const methodStats = Array.from(methodMap.entries()) - .map(([method, count]) => ({ - name: method, - value: count, - percentage: shippedCount > 0 ? (count / shippedCount) * 100 : 0 - })) - .sort((a, b) => b.value - a.value); - - mainData.meta = { - ...mainData.meta, - shipping: { - shippedCount, - methods: Object.fromEntries(methodMap), - methodStats, - locations: { - total: stateMap.size, - byState: Array.from(stateMap.entries()) - .map(([location, data]) => ({ - state: location, - country: data.country, - count: data.count, - percentage: shippedCount > 0 ? (data.count / shippedCount) * 100 : 0 - })) - .sort((a, b) => b.count - a.count), - byCountry: Array.from(countryMap.entries()) - .map(([country, data]) => ({ - country, - count: data.count, - states: Array.from(data.states), - percentage: shippedCount > 0 ? (data.count / shippedCount) * 100 : 0 - })) - .sort((a, b) => b.count - a.count) - } - } - } - - // Log shipping stats for debugging - console.log('[EventsService] Shipping stats:', { - shippedCount, - methodCount: methodStats.length, - stateCount: stateMap.size, - countryCount: countryMap.size, - methods: methodStats, - states: mainData.meta.shipping.locations.byState.length, - countries: mainData.meta.shipping.locations.byCountry.length - }); - - } catch (error) { - console.error('[EventsService] Error processing shipping data:', error); - // Provide empty but valid data structure on error - mainData.meta = { - ...mainData.meta, - shipping: { - shippedCount: 0, - methods: {}, - methodStats: [], - locations: { - total: 0, - byState: [], - byCountry: [] - } - } - }; - } - } - break; - case 'order_range': - mainData = await this.getEvents({ - ...params, - metricId: METRIC_IDS.PLACED_ORDER - }); - - const orderEvents = this._transformEvents(mainData.data); - const dailyOrderStats = new Map(); - let orderCurrentDate = periodStart; - - // Initialize daily stats for the entire date range - while (orderCurrentDate <= periodEnd) { - const dateKey = orderCurrentDate.toFormat('yyyy-MM-dd'); - dailyOrderStats.set(dateKey, { - date: orderCurrentDate.toISO(), - timestamp: dateKey, - orderValueRange: { - largest: { value: 0, orderId: null }, - smallest: { value: Infinity, orderId: null }, - distribution: { - under25: { count: 0, total: 0 }, - under50: { count: 0, total: 0 }, - under100: { count: 0, total: 0 }, - under200: { count: 0, total: 0 }, - over200: { count: 0, total: 0 } - } - } - }); - orderCurrentDate = orderCurrentDate.plus({ days: 1 }); - } - - // Process order events - orderEvents.forEach(event => { - const datetime = this.timeManager.toDateTime(event.attributes?.datetime); - if (!datetime) return; - - const dateKey = datetime.toFormat('yyyy-MM-dd'); - if (!dailyOrderStats.has(dateKey)) return; - - const stats = dailyOrderStats.get(dateKey); - const props = event.event_properties || {}; - const amount = Number(props.TotalAmount || 0); - const orderId = props.OrderId; - - // Update largest/smallest - if (amount > stats.orderValueRange.largest.value) { - stats.orderValueRange.largest = { value: amount, orderId }; - } - if (amount < stats.orderValueRange.smallest.value) { - stats.orderValueRange.smallest = { value: amount, orderId }; - } - - // Update distribution - if (amount <= 25) { - stats.orderValueRange.distribution.under25.count++; - stats.orderValueRange.distribution.under25.total += amount; - } else if (amount <= 50) { - stats.orderValueRange.distribution.under50.count++; - stats.orderValueRange.distribution.under50.total += amount; - } else if (amount <= 100) { - stats.orderValueRange.distribution.under100.count++; - stats.orderValueRange.distribution.under100.total += amount; - } else if (amount <= 200) { - stats.orderValueRange.distribution.under200.count++; - stats.orderValueRange.distribution.under200.total += amount; - } else { - stats.orderValueRange.distribution.over200.count++; - stats.orderValueRange.distribution.over200.total += amount; - } - }); - - // Format daily stats - const formattedOrderStats = Array.from(dailyOrderStats.values()) - .sort((a, b) => a.date.localeCompare(b.date)); - - // Calculate period totals - const periodStats = { - largest: { value: 0, orderId: null }, - smallest: { value: Infinity, orderId: null }, - distribution: { - under25: { count: 0, total: 0 }, - under50: { count: 0, total: 0 }, - under100: { count: 0, total: 0 }, - under200: { count: 0, total: 0 }, - over200: { count: 0, total: 0 } - } - }; - - // Aggregate all daily stats - orderEvents.forEach(event => { - const props = event.event_properties || {}; - const amount = Number(props.TotalAmount || 0); - const orderId = props.OrderId; - - if (amount > periodStats.largest.value) { - periodStats.largest = { value: amount, orderId }; - } - if (amount < periodStats.smallest.value) { - periodStats.smallest = { value: amount, orderId }; - } - - if (amount <= 25) { - periodStats.distribution.under25.count++; - periodStats.distribution.under25.total += amount; - } else if (amount <= 50) { - periodStats.distribution.under50.count++; - periodStats.distribution.under50.total += amount; - } else if (amount <= 100) { - periodStats.distribution.under100.count++; - periodStats.distribution.under100.total += amount; - } else if (amount <= 200) { - periodStats.distribution.under200.count++; - periodStats.distribution.under200.total += amount; - } else { - periodStats.distribution.over200.count++; - periodStats.distribution.over200.total += amount; - } - }); - - // Add period totals to first day and meta - if (formattedOrderStats.length > 0) { - formattedOrderStats[0].orderValueRange = { - ...formattedOrderStats[0].orderValueRange, - ...periodStats - }; - } - - return Object.assign(formattedOrderStats, { meta: { orderValueRange: periodStats } }); - case 'refunds': - mainData = await this.getEvents({ - ...params, - metricId: METRIC_IDS.PAYMENT_REFUNDED - }); - break; - case 'cancellations': - // Get cancellation events - mainData = await this.getEvents({ - ...params, - metricId: METRIC_IDS.CANCELED_ORDER - }); - - const cancelEvents = this._transformEvents(mainData.data); - const dailyCancelStats = new Map(); - let cancelCurrentDate = periodStart; - - // Initialize daily stats for the entire date range - while (cancelCurrentDate <= periodEnd) { - const dateKey = cancelCurrentDate.toFormat('yyyy-MM-dd'); - dailyCancelStats.set(dateKey, { - date: cancelCurrentDate.toISO(), - timestamp: dateKey, - total: 0, - count: 0, - reasons: {}, - items: [] - }); - cancelCurrentDate = cancelCurrentDate.plus({ days: 1 }); - } - - // Process cancellation events - cancelEvents.forEach(event => { - const datetime = this.timeManager.toDateTime(event.attributes?.datetime); - if (!datetime) return; - - const dateKey = datetime.toFormat('yyyy-MM-dd'); - if (!dailyCancelStats.has(dateKey)) return; - - const stats = dailyCancelStats.get(dateKey); - const props = event.event_properties || {}; - const amount = Number(props.TotalAmount || 0); - const reason = props.CancelReason || props.OrderMessage || 'No reason provided'; - - stats.total += amount; - stats.count++; - stats.reasons[reason] = (stats.reasons[reason] || 0) + 1; - stats.items.push({ - orderId: props.OrderId, - amount, - reason, - datetime: datetime.toISO() - }); - - // Add these properties to match what CancellationsDetails component expects - stats.canceledOrders = { - total: stats.total, - count: stats.count, - reasons: stats.reasons - }; - }); - - // Format daily stats - const formattedCancelStats = Array.from(dailyCancelStats.values()) - .sort((a, b) => a.date.localeCompare(b.date)); - - // Calculate total stats - const cancelTotalAmount = cancelEvents.reduce((sum, event) => { - const props = event.event_properties || {}; - return sum + Number(props.TotalAmount || 0); - }, 0); - - const cancelReasonTotals = cancelEvents.reduce((acc, event) => { - const props = event.event_properties || {}; - const reason = props.CancelReason || props.OrderMessage || 'No reason provided'; - acc[reason] = (acc[reason] || 0) + 1; - return acc; - }, {}); - - // Store totals in both meta and first day - const cancelTotalStats = { - meta: { - canceledOrders: { - total: cancelTotalAmount, - count: cancelEvents.length, - reasons: cancelReasonTotals - } - } - }; - - // Add totals to first day for the details view - if (formattedCancelStats.length > 0) { - formattedCancelStats[0] = { - ...formattedCancelStats[0], - canceledOrders: { - ...formattedCancelStats[0].canceledOrders, - total: cancelTotalAmount, - count: cancelEvents.length, - reasons: cancelReasonTotals - } - }; - } - - // Return array with meta property - return Object.assign(formattedCancelStats, cancelTotalStats); - default: - mainData = await this.getEvents({ - ...params, - metricId: METRIC_IDS.PLACED_ORDER - }); - } + // Load both current and previous period data + const [currentResponse, prevResponse] = await Promise.all([ + this.getEvents({ + ...params, + startDate: periodStart.toISO(), + endDate: periodEnd.toISO(), + metricId: METRIC_IDS.PLACED_ORDER + }), + this.getEvents({ + metricId: METRIC_IDS.PLACED_ORDER, + startDate: prevPeriodStart.toISO(), + endDate: prevPeriodEnd.toISO() + }) + ]); // Transform events - const transformedData = this._transformEvents(mainData.data); + const currentEvents = this._transformEvents(currentResponse.data || []); + const prevEvents = this._transformEvents(prevResponse.data || []); // Initialize daily stats map with all dates in range const dailyStats = new Map(); @@ -1928,19 +1470,15 @@ export class EventsService { itemCount: 0, averageOrderValue: 0, averageItemsPerOrder: 0, - refunds: { total: 0, count: 0, reasons: {} }, - shipping: { count: 0, locations: [], methods: {} }, - orderTypes: { - preOrders: { count: 0, value: 0, percentage: 0 }, - localPickup: { count: 0, value: 0, percentage: 0 }, - heldItems: { count: 0, value: 0, percentage: 0 } - } + prevRevenue: 0, + prevOrders: 0, + prevAvgOrderValue: 0 }); currentDate = currentDate.plus({ days: 1 }); } - // Process events into daily stats - for (const event of transformedData) { + // Process current period events + for (const event of currentEvents) { const datetime = this.timeManager.toDateTime(event.attributes?.datetime); if (!datetime) continue; @@ -1949,106 +1487,42 @@ export class EventsService { const dayStats = dailyStats.get(dateKey); const props = event.event_properties || {}; + const totalAmount = Number(props.TotalAmount || 0); + const items = props.Items || []; - switch (metric) { - case 'revenue': - case 'orders': - case 'average_order': - case 'order_range': - dayStats.revenue += Number(props.TotalAmount || 0); - dayStats.orders++; - dayStats.itemCount += (props.Items || []).length; - // Calculate daily averages immediately - dayStats.averageOrderValue = dayStats.revenue / dayStats.orders; - dayStats.averageItemsPerOrder = dayStats.itemCount / dayStats.orders; - break; - case 'shipping': - const shippingMethod = props.ShipMethod || props.ShippingMethod || 'Unknown'; - dayStats.shipping.count++; - dayStats.shipping.methods[shippingMethod] = (dayStats.shipping.methods[shippingMethod] || 0) + 1; - - if (props.ShippingState) { - dayStats.shipping.locations.push({ - state: props.ShippingState, - country: props.ShippingCountry || 'Unknown', - count: 1 - }); - } - break; - case 'pre_orders': - case 'local_pickup': - case 'on_hold': - const typeKey = metric === 'pre_orders' ? 'preOrders' : - metric === 'local_pickup' ? 'localPickup' : 'heldItems'; - const isPreOrder = metric === 'pre_orders' && props.HasPreorder; - const isLocalPickup = metric === 'local_pickup' && props.LocalPickup; - const isOnHold = metric === 'on_hold' && props.IsOnHold; - - if (isPreOrder || isLocalPickup || isOnHold) { - const amount = Number(props.TotalAmount || 0); - dayStats.orderTypes[typeKey].count++; - dayStats.orderTypes[typeKey].value += amount; - dayStats.orderTypes[typeKey].percentage = (dayStats.orderTypes[typeKey].count / dayStats.orders) * 100; - } - break; - } - - dailyStats.set(dateKey, dayStats); + dayStats.revenue += totalAmount; + dayStats.orders++; + dayStats.itemCount += items.length; + dayStats.averageOrderValue = dayStats.revenue / dayStats.orders; + dayStats.averageItemsPerOrder = dayStats.itemCount / dayStats.orders; } - // Calculate period totals separately after processing daily stats - const totalOrders = transformedData.length; - const totalRevenue = transformedData.reduce((sum, event) => { + // Process previous period events and map them to corresponding current period dates + const daysDiff = periodEnd.diff(periodStart, 'days').days; + 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'); + + if (!dailyStats.has(dateKey)) continue; + + const dayStats = dailyStats.get(dateKey); const props = event.event_properties || {}; - return sum + Number(props.TotalAmount || 0); - }, 0); - const totalItems = transformedData.reduce((sum, event) => { - const props = event.event_properties || {}; - return sum + (props.Items || []).length; - }, 0); + const totalAmount = Number(props.TotalAmount || 0); + + dayStats.prevRevenue += totalAmount; + dayStats.prevOrders++; + dayStats.prevAvgOrderValue = dayStats.prevRevenue / dayStats.prevOrders; + } // Convert to array and sort by date const stats = Array.from(dailyStats.values()) .sort((a, b) => a.date.localeCompare(b.date)); - // If not requesting daily data, aggregate the stats - if (!daily) { - const aggregated = { - totalOrders, - totalRevenue, - totalItems, - averageOrderValue: totalOrders > 0 ? totalRevenue / totalOrders : 0, - averageItemsPerOrder: totalOrders > 0 ? totalItems / totalOrders : 0, - orderCount: totalOrders - }; - - // Add meta data for specific metrics - if (metric === 'shipping' && mainData.meta?.shipping) { - aggregated.shipping = mainData.meta.shipping; - } else if (['pre_orders', 'local_pickup', 'on_hold'].includes(metric) && mainData.meta?.orderType) { - const typeKey = metric === 'pre_orders' ? 'preOrders' : - metric === 'local_pickup' ? 'localPickup' : 'heldItems'; - aggregated.orderTypes = { - [typeKey]: mainData.meta.orderType - }; - } - - // For average_order metric, return array with daily stats included - if (metric === 'average_order') { - return [{ - orderCount: totalOrders, - averageOrderValue: totalOrders > 0 ? totalRevenue / totalOrders : 0, - averageItemsPerOrder: totalOrders > 0 ? totalItems / totalOrders : 0, - totalRevenue: totalRevenue, - totalItems: totalItems, - orders: totalOrders, - dailyStats: stats // Include the daily stats - }]; - } - - return aggregated; - } - return stats; } catch (error) { console.error('[EventsService] Error calculating detailed stats:', error); diff --git a/dashboard-server/klaviyo-server/utils/time.utils.js b/dashboard-server/klaviyo-server/utils/time.utils.js index d26aace..566a693 100644 --- a/dashboard-server/klaviyo-server/utils/time.utils.js +++ b/dashboard-server/klaviyo-server/utils/time.utils.js @@ -310,71 +310,80 @@ export class TimeManager { end: this.getDayEnd(twoDaysAgo) }; } - case 'last7days': { - // If current period is days 0-6 (e.g., 12/11 1am - 12/17 12:59am) - // Previous should be days 7-13 (e.g., 12/4 1am - 12/11 12:59am) + case 'last7days': + case 'previous7days': { const dayStart = this.getDayStart(now); - const currentStart = dayStart.minus({ days: 6 }); // Start of current period - const prevStart = currentStart.minus({ days: 7 }); // Start 7 days before current start + const currentStart = dayStart.minus({ days: 6 }); + const prevEnd = currentStart.minus({ milliseconds: 1 }); + const prevStart = prevEnd.minus({ days: 6 }); return { start: prevStart, - end: this.getDayEnd(currentStart.minus({ days: 1 })) // End right before current start + end: prevEnd }; } - case 'last30days': { - // If current period is days 0-29, previous should be days 30-59 + case 'last30days': + case 'previous30days': { const dayStart = this.getDayStart(now); - const currentStart = dayStart.minus({ days: 29 }); // Start of current period - const prevStart = currentStart.minus({ days: 30 }); // Start 30 days before current start + const currentStart = dayStart.minus({ days: 29 }); + const prevEnd = currentStart.minus({ milliseconds: 1 }); + const prevStart = prevEnd.minus({ days: 29 }); return { start: prevStart, - end: this.getDayEnd(currentStart.minus({ days: 1 })) // End right before current start + end: prevEnd }; } - case 'last90days': { - // If current period is days 0-89, previous should be days 90-179 + case 'last90days': + case 'previous90days': { const dayStart = this.getDayStart(now); - const currentStart = dayStart.minus({ days: 89 }); // Start of current period - const prevStart = currentStart.minus({ days: 90 }); // Start 90 days before current start + const currentStart = dayStart.minus({ days: 89 }); + const prevEnd = currentStart.minus({ milliseconds: 1 }); + const prevStart = prevEnd.minus({ days: 89 }); return { start: prevStart, - end: this.getDayEnd(currentStart.minus({ days: 1 })) // End right before current start + end: prevEnd }; } case 'thisWeek': { - const lastWeek = now.minus({ weeks: 1 }); - const weekStart = this.getWeekStart(lastWeek); - const weekEnd = weekStart.plus({ days: 6 }); + const weekStart = this.getWeekStart(now); + const prevEnd = weekStart.minus({ milliseconds: 1 }); + const prevStart = this.getWeekStart(prevEnd); return { - start: weekStart, - end: this.getDayEnd(weekEnd) + start: prevStart, + end: prevEnd }; } case 'lastWeek': { - const twoWeeksAgo = now.minus({ weeks: 2 }); - const weekStart = this.getWeekStart(twoWeeksAgo); - const weekEnd = weekStart.plus({ days: 6 }); + const lastWeekStart = this.getWeekStart(now.minus({ weeks: 1 })); + const prevEnd = lastWeekStart.minus({ milliseconds: 1 }); + const prevStart = this.getWeekStart(prevEnd); return { - start: weekStart, - end: this.getDayEnd(weekEnd) + start: prevStart, + end: prevEnd }; } case 'thisMonth': { - const lastMonth = now.minus({ months: 1 }); - const monthStart = lastMonth.startOf('month').set({ hour: this.dayStartHour }); - const monthEnd = monthStart.plus({ months: 1 }).minus({ days: 1 }); + const monthStart = now.startOf('month').set({ hour: this.dayStartHour }); + const prevEnd = monthStart.minus({ milliseconds: 1 }); + const prevStart = prevEnd.startOf('month').set({ hour: this.dayStartHour }); return { - start: monthStart, - end: this.getDayEnd(monthEnd) + start: prevStart, + end: prevEnd }; } case 'lastMonth': { - const twoMonthsAgo = now.minus({ months: 2 }); - const monthStart = twoMonthsAgo.startOf('month').set({ hour: this.dayStartHour }); - const monthEnd = monthStart.plus({ months: 1 }).minus({ days: 1 }); + const lastMonthStart = now.minus({ months: 1 }).startOf('month').set({ hour: this.dayStartHour }); + const prevEnd = lastMonthStart.minus({ milliseconds: 1 }); + const prevStart = prevEnd.startOf('month').set({ hour: this.dayStartHour }); return { - start: monthStart, - end: this.getDayEnd(monthEnd) + start: prevStart, + end: prevEnd + }; + } + case 'twoDaysAgo': { + const twoDaysAgo = now.minus({ days: 2 }); + return { + start: this.getDayStart(twoDaysAgo), + end: this.getDayEnd(twoDaysAgo) }; } default: diff --git a/dashboard/src/components/dashboard/KlaviyoStats.jsx b/dashboard/src/components/dashboard/KlaviyoStats.jsx index 848e10e..0b9dc78 100644 --- a/dashboard/src/components/dashboard/KlaviyoStats.jsx +++ b/dashboard/src/components/dashboard/KlaviyoStats.jsx @@ -91,10 +91,10 @@ const formatPercent = (value, total) => { const getPreviousPeriod = (timeRange) => { switch (timeRange) { case 'today': return 'yesterday'; - case 'yesterday': return 'last2days'; - case 'last7days': return 'previous7days'; - case 'last30days': return 'previous30days'; - case 'last90days': return 'previous90days'; + case 'yesterday': return 'twoDaysAgo'; + case 'last7days': return 'last7days'; + case 'last30days': return 'last30days'; + case 'last90days': return 'last90days'; default: return timeRange; } }; diff --git a/dashboard/src/components/dashboard/SalesChart.jsx b/dashboard/src/components/dashboard/SalesChart.jsx index d80b0e1..57812de 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: 'previous7days', - last30days: 'previous30days', - last90days: 'previous90days', + last7days: 'last7days', + last30days: 'last30days', + last90days: 'last90days', yesterday: 'twoDaysAgo' };