Clean up revenue, AOV, brands & categories dialogs
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"folder-color.pathColors": [
|
||||
{
|
||||
"folderPath": "Dev/dashboard/",
|
||||
"badge": "🔵"
|
||||
},
|
||||
{
|
||||
"folderPath": "Dev/dashboard-server/",
|
||||
"badge": "🟣"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -159,7 +159,7 @@ const DetailDialog = ({
|
||||
children
|
||||
}) => (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-2xl max-h-[80vh] overflow-auto">
|
||||
<DialogContent className="max-w-2xl max-h-[80vh] overflow-hidden">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
</DialogHeader>
|
||||
@@ -180,54 +180,15 @@ const RevenueDetails = ({ data }) => {
|
||||
date: DateTime.fromISO(day.timestamp).toFormat('LLL d')
|
||||
})).sort((a, b) => DateTime.fromISO(a.timestamp) - DateTime.fromISO(b.timestamp));
|
||||
|
||||
// Calculate summary statistics
|
||||
const totalRevenue = chartData.reduce((sum, day) => sum + day.revenue, 0);
|
||||
const averageDailyRevenue = totalRevenue / chartData.length;
|
||||
const maxRevenue = Math.max(...chartData.map(day => day.revenue));
|
||||
const minRevenue = Math.min(...chartData.map(day => day.revenue));
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<TimeSeriesChart
|
||||
data={chartData}
|
||||
dataKey="revenue"
|
||||
name="Revenue"
|
||||
color="hsl(142.1 76.2% 36.3%)"
|
||||
valueFormatter={(value) => formatCurrency(value)}
|
||||
height={400}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<StatCard
|
||||
title="Total Revenue"
|
||||
value={formatCurrency(totalRevenue)}
|
||||
description={`${chartData.length} days`}
|
||||
colorClass="text-green-600 dark:text-green-400"
|
||||
icon={DollarSign}
|
||||
/>
|
||||
<StatCard
|
||||
title="Average Daily Revenue"
|
||||
value={formatCurrency(averageDailyRevenue)}
|
||||
description="per day"
|
||||
colorClass="text-green-600 dark:text-green-400"
|
||||
icon={TrendingUp}
|
||||
/>
|
||||
<StatCard
|
||||
title="Highest Day"
|
||||
value={formatCurrency(maxRevenue)}
|
||||
description={chartData.find(d => d.revenue === maxRevenue)?.date}
|
||||
colorClass="text-green-600 dark:text-green-400"
|
||||
icon={ArrowUp}
|
||||
/>
|
||||
<StatCard
|
||||
title="Lowest Day"
|
||||
value={formatCurrency(minRevenue)}
|
||||
description={chartData.find(d => d.revenue === minRevenue)?.date}
|
||||
colorClass="text-green-600 dark:text-green-400"
|
||||
icon={ArrowDown}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<TimeSeriesChart
|
||||
data={chartData}
|
||||
dataKey="revenue"
|
||||
name="Revenue"
|
||||
color="hsl(142.1 76.2% 36.3%)"
|
||||
valueFormatter={(value) => formatCurrency(value)}
|
||||
height={400}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -300,31 +261,13 @@ const AverageOrderDetails = ({ data, orderCount }) => {
|
||||
if (!data?.length) return <div className="text-muted-foreground">No data available for the selected time range.</div>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<TimeSeriesChart
|
||||
data={data}
|
||||
dataKey="averageOrderValue"
|
||||
name="Average Order Value"
|
||||
color="hsl(262.1 83.3% 57.8%)"
|
||||
valueFormatter={(value) => formatCurrency(value)}
|
||||
/>
|
||||
<div className="mt-8">
|
||||
<StatGrid
|
||||
stats={[
|
||||
{
|
||||
label: "Average Items per Order",
|
||||
value: data[0]?.averageItemsPerOrder?.toFixed(1) || "0",
|
||||
description: "items"
|
||||
},
|
||||
{
|
||||
label: "Total Orders",
|
||||
value: orderCount?.toLocaleString() || "0",
|
||||
description: "orders"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
<TimeSeriesChart
|
||||
data={data}
|
||||
dataKey="averageOrderValue"
|
||||
name="Average Order Value"
|
||||
color="hsl(262.1 83.3% 57.8%)"
|
||||
valueFormatter={(value) => formatCurrency(value)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -419,81 +362,65 @@ const CancellationsDetails = ({ data }) => {
|
||||
const BrandsCategoriesDetails = ({ data }) => {
|
||||
if (!data?.length) return <div className="text-muted-foreground">No data available for the selected time range.</div>;
|
||||
|
||||
const brandData = data[0]?.brands?.list?.map(brand => ({
|
||||
name: brand.name,
|
||||
count: brand.count,
|
||||
revenue: brand.revenue,
|
||||
percentage: (brand.count / data[0]?.itemCount * 100) || 0
|
||||
}))?.sort((a, b) => b.count - a.count) || [];
|
||||
|
||||
const categoryData = data[0]?.categories?.list?.map(category => ({
|
||||
name: category.name,
|
||||
count: category.count,
|
||||
revenue: category.revenue,
|
||||
percentage: (category.count / data[0]?.itemCount * 100) || 0
|
||||
}))?.sort((a, b) => b.count - a.count) || [];
|
||||
|
||||
if (!brandData.length && !categoryData.length) {
|
||||
return <div className="text-muted-foreground">No brands or categories data available.</div>;
|
||||
}
|
||||
const stats = data[0];
|
||||
const brandsList = stats?.brands?.list || [];
|
||||
const categoriesList = stats?.categories?.list || [];
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{brandData.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-lg font-medium mb-4">All Brands</h3>
|
||||
<div className="rounded-lg border bg-card max-h-[400px] overflow-auto">
|
||||
<Table>
|
||||
<TableHeader className="sticky top-0 bg-background z-10">
|
||||
<TableRow>
|
||||
<TableHead>Brand</TableHead>
|
||||
<TableHead className="text-right">Items</TableHead>
|
||||
<TableHead className="text-right">Revenue</TableHead>
|
||||
<TableHead className="text-right">% of Total</TableHead>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div className="flex flex-col h-[30vh] sm:h-[60vh]">
|
||||
<h3 className="text-md font-medium mb-4 flex items-center gap-2">
|
||||
<Star className="h-5 w-5 text-yellow-500" />
|
||||
Brands ({stats?.brands?.total || 0})
|
||||
</h3>
|
||||
<div className="rounded-md border overflow-auto">
|
||||
<Table>
|
||||
<TableHeader className="sticky top-0 bg-background z-10">
|
||||
<TableRow>
|
||||
<TableHead>Brand</TableHead>
|
||||
<TableHead className="text-right">Items</TableHead>
|
||||
<TableHead className="text-right">Revenue</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{brandsList.map((brand) => (
|
||||
<TableRow key={brand.name}>
|
||||
<TableCell className="font-medium">{brand.name}</TableCell>
|
||||
<TableCell className="text-right">{brand.count?.toLocaleString()}</TableCell>
|
||||
<TableCell className="text-right">${brand.revenue?.toFixed(2)}</TableCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{brandData.map((brand, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="font-medium">{brand.name}</TableCell>
|
||||
<TableCell className="text-right">{brand.count.toLocaleString()}</TableCell>
|
||||
<TableCell className="text-right">{formatCurrency(brand.revenue)}</TableCell>
|
||||
<TableCell className="text-right">{brand.percentage.toFixed(1)}%</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{categoryData.length > 0 && (
|
||||
<div>
|
||||
<h3 className="text-lg font-medium mb-4">All Categories</h3>
|
||||
<div className="rounded-lg border bg-card max-h-[400px] overflow-auto">
|
||||
<Table>
|
||||
<TableHeader className="sticky top-0 bg-background z-10">
|
||||
<TableRow>
|
||||
<TableHead>Category</TableHead>
|
||||
<TableHead className="text-right">Items</TableHead>
|
||||
<TableHead className="text-right">Revenue</TableHead>
|
||||
<TableHead className="text-right">% of Total</TableHead>
|
||||
<div className="flex flex-col h-[30vh] md:h-[60vh]">
|
||||
<h3 className="text-md font-medium mb-4 flex items-center gap-2">
|
||||
<Tags className="h-5 w-5 text-indigo-500" />
|
||||
Categories ({stats?.categories?.total || 0})
|
||||
</h3>
|
||||
<div className="rounded-md border overflow-auto">
|
||||
<Table>
|
||||
<TableHeader className="sticky top-0 bg-background z-10">
|
||||
<TableRow>
|
||||
<TableHead>Category</TableHead>
|
||||
<TableHead className="text-right">Items</TableHead>
|
||||
<TableHead className="text-right">Revenue</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{categoriesList.map((category) => (
|
||||
<TableRow key={category.name}>
|
||||
<TableCell className="font-medium">{category.name}</TableCell>
|
||||
<TableCell className="text-right">{category.count?.toLocaleString()}</TableCell>
|
||||
<TableCell className="text-right">${category.revenue?.toFixed(2)}</TableCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{categoryData.map((category, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="font-medium">{category.name}</TableCell>
|
||||
<TableCell className="text-right">{category.count.toLocaleString()}</TableCell>
|
||||
<TableCell className="text-right">{formatCurrency(category.revenue)}</TableCell>
|
||||
<TableCell className="text-right">{category.percentage.toFixed(1)}%</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user