feat: add transaction volume and AOV chart to analysis dashboard
This commit is contained in:
parent
51f2e65bd2
commit
4d417fa08b
@ -137,12 +137,13 @@ public function __invoke()
|
||||
];
|
||||
});
|
||||
|
||||
// Revenue & Profit per month
|
||||
// Revenue & Profit & Volume per month
|
||||
$ordersByMonth = DB::table('orders')
|
||||
->select(
|
||||
DB::raw('MONTH(created_at) as month'),
|
||||
DB::raw('SUM(total) as revenue'),
|
||||
DB::raw('SUM(hpp) as cogs')
|
||||
DB::raw('SUM(hpp) as cogs'),
|
||||
DB::raw('COUNT(*) as count')
|
||||
)
|
||||
->groupBy(DB::raw('MONTH(created_at)'))
|
||||
->get();
|
||||
@ -182,10 +183,24 @@ public function __invoke()
|
||||
];
|
||||
});
|
||||
|
||||
$transactionVolume = collect(range(1, 12))->map(function ($month) use ($ordersByMonth, $monthNames) {
|
||||
$found = $ordersByMonth->firstWhere('month', $month);
|
||||
$count = $found ? (int) $found->count : 0;
|
||||
$revenue = $found ? (float) $found->revenue : 0;
|
||||
$aov = $count > 0 ? $revenue / $count : 0;
|
||||
|
||||
return [
|
||||
'month' => $monthNames[$month - 1],
|
||||
'count' => $count,
|
||||
'aov' => $aov,
|
||||
];
|
||||
});
|
||||
|
||||
return Inertia::render('analysis', [
|
||||
'stats' => $stats,
|
||||
'salesByMonth' => $salesByMonth,
|
||||
'revenueVsProfit' => $revenueVsProfit,
|
||||
'transactionVolume' => $transactionVolume,
|
||||
'salesByHour' => $salesByHour,
|
||||
'paymentMethods' => $paymentMethods,
|
||||
'orderStatuses' => $orderStatuses,
|
||||
|
||||
124
resources/js/components/charts/transaction-volume-chart.tsx
Normal file
124
resources/js/components/charts/transaction-volume-chart.tsx
Normal file
@ -0,0 +1,124 @@
|
||||
"use client"
|
||||
|
||||
import { GitCommitVertical, TrendingUp } from "lucide-react"
|
||||
import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card"
|
||||
import {
|
||||
ChartContainer,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
type ChartConfig,
|
||||
} from "@/components/ui/chart"
|
||||
import { formatCurrency } from "@/lib/formatters"
|
||||
|
||||
export const description = "Menampilkan volume transaksi per bulan."
|
||||
|
||||
const chartConfig = {
|
||||
count: {
|
||||
label: "Jumlah Transaksi",
|
||||
color: "var(--chart-1)",
|
||||
},
|
||||
aov: {
|
||||
label: "AOV",
|
||||
color: "var(--chart-2)",
|
||||
},
|
||||
} satisfies ChartConfig
|
||||
|
||||
export function TransactionVolumeChart({ data }: { data: any[] }) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Volume Transaksi</CardTitle>
|
||||
<CardDescription>Total pesanan masuk per bulan</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer config={chartConfig} className="aspect-auto h-[300px] w-full">
|
||||
<LineChart
|
||||
accessibilityLayer
|
||||
data={data}
|
||||
margin={{
|
||||
left: 12,
|
||||
right: 12,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid vertical={false} />
|
||||
<XAxis
|
||||
dataKey="month"
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
tickFormatter={(value) => value.slice(0, 3)}
|
||||
/>
|
||||
<ChartTooltip
|
||||
cursor={false}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
formatter={(value, name, item) => {
|
||||
if (name === "count") {
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-2 w-2 rounded-full bg-[var(--color-count)]" />
|
||||
<span className="text-muted-foreground">Transaksi:</span>
|
||||
<span className="font-bold">{value}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-2 w-2 rounded-full bg-[var(--color-aov)]" />
|
||||
<span className="text-muted-foreground">AOV:</span>
|
||||
<span className="font-bold">{formatCurrency(item.payload.aov)}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Line
|
||||
dataKey="count"
|
||||
type="natural"
|
||||
stroke="var(--color-count)"
|
||||
strokeWidth={2}
|
||||
dot={({ cx, cy, payload }) => {
|
||||
if (cx == null || cy == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
const r = 24
|
||||
|
||||
return (
|
||||
<GitCommitVertical
|
||||
key={payload.month}
|
||||
x={cx - r / 2}
|
||||
y={cy - r / 2}
|
||||
width={r}
|
||||
height={r}
|
||||
fill="hsl(var(--background))"
|
||||
stroke="var(--color-count)"
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</LineChart>
|
||||
</ChartContainer>
|
||||
</CardContent>
|
||||
<CardFooter className="flex-col items-start gap-2 text-sm">
|
||||
<div className="flex gap-2 leading-none font-medium">
|
||||
Menampilkan tren aktivitas transaksi <TrendingUp className="h-4 w-4" />
|
||||
</div>
|
||||
<div className="leading-none text-muted-foreground">
|
||||
Berdasarkan total pesanan dari Januari - Desember
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@ -5,11 +5,12 @@ import { Head, usePage } from '@inertiajs/react';
|
||||
import { CustomBarChart } from '../components/charts/bar-chart';
|
||||
import { CustomPieChart } from '../components/charts/pie-chart';
|
||||
import { RevenueVsProfitChart } from '../components/charts/revenue-vs-profit-chart';
|
||||
import { TransactionVolumeChart } from '../components/charts/transaction-volume-chart';
|
||||
import { RevenueVsPurchasesChart } from '../components/charts/revenue-vs-purchases-chart';
|
||||
import { SalesByHourChart } from '../components/charts/sales-by-hour-chart';
|
||||
|
||||
export default function Analisa() {
|
||||
const { stats, salesByMonth, revenueVsProfit, salesByHour, paymentMethods, orderStatuses, orderChannels, topProducts, topCustomers } =
|
||||
const { stats, salesByMonth, revenueVsProfit, transactionVolume, salesByHour, paymentMethods, orderStatuses, orderChannels, topProducts, topCustomers } =
|
||||
usePage<AnalysisPageProps>().props;
|
||||
|
||||
return (
|
||||
@ -56,6 +57,8 @@ export default function Analisa() {
|
||||
|
||||
<RevenueVsProfitChart data={revenueVsProfit} />
|
||||
|
||||
<TransactionVolumeChart data={transactionVolume} />
|
||||
|
||||
<SalesByHourChart
|
||||
data={salesByHour}
|
||||
config={{
|
||||
|
||||
@ -33,6 +33,7 @@ export interface AnalysisPageProps {
|
||||
stats: DashboardStats;
|
||||
salesByMonth: any[];
|
||||
revenueVsProfit: any[];
|
||||
transactionVolume: any[];
|
||||
salesByHour: any[];
|
||||
paymentMethods: any[];
|
||||
orderStatuses: any[];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user