Add average order value to analytics chart
Enhanced the AdminAnalytics component to display average order value (AOV) alongside revenue and orders in the analytics chart. Updated data structures and chart rendering to support and visualize AOV, providing more detailed business insights.
This commit is contained in:
@@ -41,7 +41,7 @@ interface AnalyticsData {
|
||||
total?: number;
|
||||
today?: number;
|
||||
thisWeek?: number;
|
||||
dailyRevenue?: { date: string; amount: number }[];
|
||||
dailyRevenue?: { date: string; amount: number; orders?: number; avgOrderValue?: number }[];
|
||||
};
|
||||
engagement?: {
|
||||
totalChats?: number;
|
||||
@@ -130,14 +130,17 @@ export default function AdminAnalytics() {
|
||||
};
|
||||
|
||||
// Helper to combine orders and revenue data for dual-axis chart
|
||||
const combineOrdersAndRevenue = (orders: Array<{ date: string; count: number }>, revenue: Array<{ date: string; amount: number }>) => {
|
||||
const combineOrdersAndRevenue = (orders: Array<{ date: string; count: number }>, revenue: Array<{ date: string; amount: number; orders?: number; avgOrderValue?: number }>) => {
|
||||
if (!orders || orders.length === 0) return [];
|
||||
|
||||
// Create a map of revenue by date for quick lookup
|
||||
const revenueMap = new Map<string, number>();
|
||||
// Create a map of revenue and AOV by date for quick lookup
|
||||
const revenueMap = new Map<string, { amount: number; avgOrderValue: number }>();
|
||||
if (revenue && revenue.length > 0) {
|
||||
revenue.forEach(r => {
|
||||
revenueMap.set(r.date, r.amount || 0);
|
||||
revenueMap.set(r.date, {
|
||||
amount: r.amount || 0,
|
||||
avgOrderValue: r.avgOrderValue || 0
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -152,11 +155,14 @@ export default function AdminAnalytics() {
|
||||
const dayOfWeek = date.toLocaleDateString('en-GB', { weekday: 'short' });
|
||||
const monthDay = date.toLocaleDateString('en-GB', { month: 'short', day: 'numeric' });
|
||||
|
||||
const revenueData = revenueMap.get(dateStr) || { amount: 0, avgOrderValue: 0 };
|
||||
|
||||
return {
|
||||
date: dateStr,
|
||||
formattedDate: `${dayOfWeek}, ${monthDay}`,
|
||||
orders: order.count || 0,
|
||||
revenue: revenueMap.get(dateStr) || 0
|
||||
revenue: revenueData.amount,
|
||||
avgOrderValue: revenueData.avgOrderValue
|
||||
};
|
||||
});
|
||||
};
|
||||
@@ -185,6 +191,11 @@ export default function AdminAnalytics() {
|
||||
<p className="text-sm text-green-600">
|
||||
Revenue: <span className="font-semibold">{formatGBP(data.revenue)}</span>
|
||||
</p>
|
||||
{data.avgOrderValue !== undefined && data.avgOrderValue > 0 && (
|
||||
<p className="text-sm text-purple-600">
|
||||
Avg Order Value: <span className="font-semibold">{formatGBP(data.avgOrderValue)}</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-primary">
|
||||
@@ -601,11 +612,12 @@ export default function AdminAnalytics() {
|
||||
orientation="right"
|
||||
tick={{ fontSize: 12 }}
|
||||
tickFormatter={(value) => `£${(value / 1000).toFixed(0)}k`}
|
||||
label={{ value: 'Revenue', angle: 90, position: 'insideRight' }}
|
||||
label={{ value: 'Revenue / AOV', angle: 90, position: 'insideRight' }}
|
||||
/>
|
||||
<Tooltip content={<CustomTooltip />} />
|
||||
<Bar yAxisId="left" dataKey="orders" fill="#3b82f6" radius={[2, 2, 0, 0]} name="Orders" />
|
||||
<Line yAxisId="right" type="monotone" dataKey="revenue" stroke="#10b981" strokeWidth={2} dot={{ fill: '#10b981', r: 4 }} name="Revenue" />
|
||||
<Line yAxisId="right" type="monotone" dataKey="avgOrderValue" stroke="#a855f7" strokeWidth={2} strokeDasharray="5 5" dot={{ fill: '#a855f7', r: 3 }} name="Avg Order Value" />
|
||||
</ComposedChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"commitHash": "4ef0fd1",
|
||||
"buildTime": "2025-11-30T15:39:38.047Z"
|
||||
"commitHash": "93214ce",
|
||||
"buildTime": "2025-12-01T13:19:53.318Z"
|
||||
}
|
||||
Reference in New Issue
Block a user