diff --git a/dashboard/src/components/dashboard/MiniSalesChart.jsx b/dashboard/src/components/dashboard/MiniSalesChart.jsx index 24d5ab3..193f1ec 100644 --- a/dashboard/src/components/dashboard/MiniSalesChart.jsx +++ b/dashboard/src/components/dashboard/MiniSalesChart.jsx @@ -165,11 +165,26 @@ const MiniSalesChart = ({ className = "" }) => { totalOrders: 0, prevRevenue: 0, prevOrders: 0, - growth: { - revenue: 0, - orders: 0 - } + periodProgress: 100 }); + const [projection, setProjection] = useState(null); + const [projectionLoading, setProjectionLoading] = useState(false); + + const fetchProjection = useCallback(async () => { + if (summaryStats.periodProgress >= 100) return; + + try { + setProjectionLoading(true); + const response = await axios.get("/api/klaviyo/events/projection", { + params: { timeRange: "last30days" } + }); + setProjection(response.data); + } catch (error) { + console.error("Error loading projection:", error); + } finally { + setProjectionLoading(false); + } + }, [summaryStats.periodProgress]); const fetchData = useCallback(async () => { try { @@ -200,33 +215,30 @@ const MiniSalesChart = ({ className = "" }) => { totalOrders: acc.totalOrders + (Number(day.orders) || 0), prevRevenue: acc.prevRevenue + (Number(day.prevRevenue) || 0), prevOrders: acc.prevOrders + (Number(day.prevOrders) || 0), + periodProgress: day.periodProgress || 100, }), { totalRevenue: 0, totalOrders: 0, prevRevenue: 0, - prevOrders: 0 + prevOrders: 0, + periodProgress: 100 }); - // Calculate growth percentages - const growth = { - revenue: totals.prevRevenue > 0 - ? ((totals.totalRevenue - totals.prevRevenue) / totals.prevRevenue) * 100 - : 0, - orders: totals.prevOrders > 0 - ? ((totals.totalOrders - totals.prevOrders) / totals.prevOrders) * 100 - : 0 - }; - setData(processedData); - setSummaryStats({ ...totals, growth }); + setSummaryStats(totals); setError(null); + + // Fetch projection if needed + if (totals.periodProgress < 100) { + fetchProjection(); + } } catch (error) { console.error("Error fetching data:", error); setError(error.message); } finally { setLoading(false); } - }, []); + }, [fetchProjection]); useEffect(() => { fetchData(); @@ -294,8 +306,16 @@ const MiniSalesChart = ({ className = "" }) => { title="30 Days Revenue" value={formatCurrency(summaryStats.totalRevenue, false)} previousValue={formatCurrency(summaryStats.prevRevenue, false)} - trend={summaryStats.growth.revenue >= 0 ? "up" : "down"} - trendValue={`${Math.abs(Math.round(summaryStats.growth.revenue))}%`} + trend={ + summaryStats.periodProgress < 100 + ? ((projection?.projectedRevenue || summaryStats.totalRevenue) >= summaryStats.prevRevenue ? "up" : "down") + : (summaryStats.totalRevenue >= summaryStats.prevRevenue ? "up" : "down") + } + trendValue={ + summaryStats.periodProgress < 100 + ? `${Math.abs(Math.round(((projection?.projectedRevenue || summaryStats.totalRevenue) - summaryStats.prevRevenue) / summaryStats.prevRevenue * 100))}%` + : `${Math.abs(Math.round(((summaryStats.totalRevenue - summaryStats.prevRevenue) / summaryStats.prevRevenue) * 100))}%` + } colorClass="text-emerald-300" titleClass="text-emerald-300 font-bold text-md" descriptionClass="text-emerald-300 text-md font-semibold pb-1" @@ -309,8 +329,16 @@ const MiniSalesChart = ({ className = "" }) => { title="30 Days Orders" value={summaryStats.totalOrders.toLocaleString()} previousValue={summaryStats.prevOrders.toLocaleString()} - trend={summaryStats.growth.orders >= 0 ? "up" : "down"} - trendValue={`${Math.abs(Math.round(summaryStats.growth.orders))}%`} + trend={ + summaryStats.periodProgress < 100 + ? ((Math.round(summaryStats.totalOrders * (100 / summaryStats.periodProgress))) >= summaryStats.prevOrders ? "up" : "down") + : (summaryStats.totalOrders >= summaryStats.prevOrders ? "up" : "down") + } + trendValue={ + summaryStats.periodProgress < 100 + ? `${Math.abs(Math.round(((Math.round(summaryStats.totalOrders * (100 / summaryStats.periodProgress))) - summaryStats.prevOrders) / summaryStats.prevOrders * 100))}%` + : `${Math.abs(Math.round(((summaryStats.totalOrders - summaryStats.prevOrders) / summaryStats.prevOrders) * 100))}%` + } colorClass="text-blue-300" titleClass="text-blue-300 font-bold text-md" descriptionClass="text-blue-300 text-md font-semibold pb-1" diff --git a/dashboard/src/components/dashboard/SalesChart.jsx b/dashboard/src/components/dashboard/SalesChart.jsx index 98bbedb..22f456f 100644 --- a/dashboard/src/components/dashboard/SalesChart.jsx +++ b/dashboard/src/components/dashboard/SalesChart.jsx @@ -342,16 +342,8 @@ const calculateSummaryStats = (data = []) => { return best; }, null); - // Calculate growth percentages - const growth = { - revenue: prevRevenue - ? ((totalRevenue - prevRevenue) / prevRevenue) * 100 - : 0, - orders: prevOrders ? ((totalOrders - prevOrders) / prevOrders) * 100 : 0, - avgOrderValue: prevAvgOrderValue - ? ((avgOrderValue - prevAvgOrderValue) / prevAvgOrderValue) * 100 - : 0, - }; + // Get period progress from the last day + const periodProgress = data[data.length - 1]?.periodProgress || 100; return { totalRevenue, @@ -361,7 +353,7 @@ const calculateSummaryStats = (data = []) => { prevRevenue, prevOrders, prevAvgOrderValue, - growth, + periodProgress, movingAverages: { revenue: data[data.length - 1]?.movingAverage || 0, orders: data[data.length - 1]?.orderMovingAverage || 0, @@ -371,7 +363,7 @@ const calculateSummaryStats = (data = []) => { }; // Add memoized SummaryStats component -const SummaryStats = memo(({ stats = {} }) => { +const SummaryStats = memo(({ stats = {}, projection = null, projectionLoading = false }) => { const { totalRevenue = 0, totalOrders = 0, @@ -380,17 +372,39 @@ const SummaryStats = memo(({ stats = {} }) => { prevRevenue = 0, prevOrders = 0, prevAvgOrderValue = 0, - growth = { revenue: 0, orders: 0, avgOrderValue: 0 }, + periodProgress = 100 } = stats; + // Calculate projected values when period is incomplete + const currentRevenue = periodProgress < 100 ? (projection?.projectedRevenue || totalRevenue) : totalRevenue; + const revenueTrend = currentRevenue >= prevRevenue ? "up" : "down"; + const revenueDiff = Math.abs(currentRevenue - prevRevenue); + const revenuePercentage = (revenueDiff / prevRevenue) * 100; + + // Calculate order trends + const currentOrders = periodProgress < 100 ? (projection?.projectedOrders || totalOrders) : totalOrders; + const ordersTrend = currentOrders >= prevOrders ? "up" : "down"; + const ordersDiff = Math.abs(currentOrders - prevOrders); + const ordersPercentage = (ordersDiff / prevOrders) * 100; + + // Calculate AOV trends + const currentAOV = currentOrders ? currentRevenue / currentOrders : avgOrderValue; + const aovTrend = currentAOV >= prevAvgOrderValue ? "up" : "down"; + const aovDiff = Math.abs(currentAOV - prevAvgOrderValue); + const aovPercentage = (aovDiff / prevAvgOrderValue) * 100; + return (