refactor: consolidate hourly chart components and update sales-by-hour data logic
This commit is contained in:
parent
b1eb893cfe
commit
a280eb8a64
@ -114,7 +114,7 @@ public function __invoke()
|
|||||||
->limit(5)
|
->limit(5)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$ordersByHourRaw = DB::table('orders')
|
$salesByHourRaw = DB::table('orders')
|
||||||
->select(
|
->select(
|
||||||
DB::raw('HOUR(created_at) as hour'),
|
DB::raw('HOUR(created_at) as hour'),
|
||||||
DB::raw('COUNT(*) as total')
|
DB::raw('COUNT(*) as total')
|
||||||
@ -122,8 +122,8 @@ public function __invoke()
|
|||||||
->groupBy(DB::raw('HOUR(created_at)'))
|
->groupBy(DB::raw('HOUR(created_at)'))
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$ordersByHour = collect(range(0, 23))->map(function ($hour) use ($ordersByHourRaw) {
|
$salesByHour = collect(range(0, 23))->map(function ($hour) use ($salesByHourRaw) {
|
||||||
$found = $ordersByHourRaw->firstWhere('hour', $hour);
|
$found = $salesByHourRaw->firstWhere('hour', $hour);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'hour' => str_pad($hour, 2, '0', STR_PAD_LEFT).':00',
|
'hour' => str_pad($hour, 2, '0', STR_PAD_LEFT).':00',
|
||||||
@ -134,7 +134,7 @@ public function __invoke()
|
|||||||
return Inertia::render('analysis', [
|
return Inertia::render('analysis', [
|
||||||
'stats' => $stats,
|
'stats' => $stats,
|
||||||
'salesByMonth' => $salesByMonth,
|
'salesByMonth' => $salesByMonth,
|
||||||
'ordersByHour' => $ordersByHour,
|
'salesByHour' => $salesByHour,
|
||||||
'paymentMethods' => $paymentMethods,
|
'paymentMethods' => $paymentMethods,
|
||||||
'orderStatuses' => $orderStatuses,
|
'orderStatuses' => $orderStatuses,
|
||||||
'orderChannels' => $orderChannels,
|
'orderChannels' => $orderChannels,
|
||||||
|
|||||||
@ -1,84 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/components/ui/card";
|
|
||||||
import type { ChartConfig } from "@/components/ui/chart";
|
|
||||||
import {
|
|
||||||
ChartContainer,
|
|
||||||
ChartLegend,
|
|
||||||
ChartLegendContent,
|
|
||||||
ChartTooltip,
|
|
||||||
ChartTooltipContent
|
|
||||||
} from "@/components/ui/chart";
|
|
||||||
|
|
||||||
export const description = "Menampilkan aktivitas toko berdasarkan pesanan yang masuk per jam."
|
|
||||||
|
|
||||||
const chartConfig = {
|
|
||||||
total: {
|
|
||||||
label: "Total Pesanan",
|
|
||||||
color: "var(--chart-1)",
|
|
||||||
},
|
|
||||||
} satisfies ChartConfig
|
|
||||||
|
|
||||||
export function OrdersByHourChart({ data }: { data: any[] }) {
|
|
||||||
return (
|
|
||||||
<Card className="pt-0">
|
|
||||||
<CardHeader className="flex items-center gap-2 space-y-0 border-b py-5 sm:flex-row">
|
|
||||||
<div className="grid flex-1 gap-1">
|
|
||||||
<CardTitle>Jam Sibuk</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Menampilkan aktivitas toko berdasarkan pesanan yang masuk per jam.
|
|
||||||
</CardDescription>
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="px-2 pt-4 sm:px-6 sm:pt-6">
|
|
||||||
<ChartContainer
|
|
||||||
config={chartConfig}
|
|
||||||
className="aspect-auto h-[250px] w-full"
|
|
||||||
>
|
|
||||||
<AreaChart data={data}>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="fillTotal" x1="0" y1="0" x2="0" y2="1">
|
|
||||||
<stop
|
|
||||||
offset="5%"
|
|
||||||
stopColor="var(--color-total)"
|
|
||||||
stopOpacity={0.8}
|
|
||||||
/>
|
|
||||||
<stop
|
|
||||||
offset="95%"
|
|
||||||
stopColor="var(--color-total)"
|
|
||||||
stopOpacity={0.1}
|
|
||||||
/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<CartesianGrid vertical={false} />
|
|
||||||
<XAxis
|
|
||||||
dataKey="hour"
|
|
||||||
tickLine={false}
|
|
||||||
axisLine={false}
|
|
||||||
tickMargin={8}
|
|
||||||
/>
|
|
||||||
<ChartTooltip
|
|
||||||
cursor={false}
|
|
||||||
content={<ChartTooltipContent indicator="dot" />}
|
|
||||||
/>
|
|
||||||
<Area
|
|
||||||
dataKey="total"
|
|
||||||
type="natural"
|
|
||||||
fill="url(#fillTotal)"
|
|
||||||
stroke="var(--color-total)"
|
|
||||||
/>
|
|
||||||
<ChartLegend content={<ChartLegendContent />} />
|
|
||||||
</AreaChart>
|
|
||||||
</ChartContainer>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -19,9 +19,7 @@ import {
|
|||||||
} from "@/components/ui/chart"
|
} from "@/components/ui/chart"
|
||||||
import type { ChartConfig } from "@/components/ui/chart";
|
import type { ChartConfig } from "@/components/ui/chart";
|
||||||
|
|
||||||
export const description = "Menampilkan aktivitas toko berdasarkan pesanan yang masuk."
|
const defaultChartConfig = {
|
||||||
|
|
||||||
const chartConfig = {
|
|
||||||
today: {
|
today: {
|
||||||
label: "Hari Ini",
|
label: "Hari Ini",
|
||||||
color: "var(--chart-1)",
|
color: "var(--chart-1)",
|
||||||
@ -32,48 +30,50 @@ const chartConfig = {
|
|||||||
},
|
},
|
||||||
} satisfies ChartConfig
|
} satisfies ChartConfig
|
||||||
|
|
||||||
export function SalesByHourChart({ data }: { data: any[] }) {
|
interface SalesByHourChartProps {
|
||||||
|
data: any[];
|
||||||
|
config?: ChartConfig;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SalesByHourChart({
|
||||||
|
data,
|
||||||
|
config = defaultChartConfig,
|
||||||
|
title = "Jam Sibuk",
|
||||||
|
description = "Menampilkan aktivitas toko berdasarkan pesanan yang masuk per jam."
|
||||||
|
}: SalesByHourChartProps) {
|
||||||
|
const keys = Object.keys(config);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="pt-0">
|
<Card className="pt-0">
|
||||||
<CardHeader className="flex items-center gap-2 space-y-0 border-b py-5 sm:flex-row">
|
<CardHeader className="flex items-center gap-2 space-y-0 border-b py-5 sm:flex-row">
|
||||||
<div className="grid flex-1 gap-1">
|
<div className="grid flex-1 gap-1">
|
||||||
<CardTitle>Jam Sibuk</CardTitle>
|
<CardTitle>{title}</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>{description}</CardDescription>
|
||||||
Menampilkan aktivitas toko berdasarkan pesanan yang masuk.
|
|
||||||
</CardDescription>
|
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="px-2 pt-4 sm:px-6 sm:pt-6">
|
<CardContent className="px-2 pt-4 sm:px-6 sm:pt-6">
|
||||||
<ChartContainer
|
<ChartContainer
|
||||||
config={chartConfig}
|
config={config}
|
||||||
className="aspect-auto h-[250px] w-full"
|
className="aspect-auto h-[250px] w-full"
|
||||||
>
|
>
|
||||||
<AreaChart data={data}>
|
<AreaChart data={data}>
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="fillToday" x1="0" y1="0" x2="0" y2="1">
|
{keys.map((key) => (
|
||||||
|
<linearGradient key={key} id={`fill${key}`} x1="0" y1="0" x2="0" y2="1">
|
||||||
<stop
|
<stop
|
||||||
offset="5%"
|
offset="5%"
|
||||||
stopColor="var(--color-today)"
|
stopColor={`var(--color-${key})`}
|
||||||
stopOpacity={0.8}
|
stopOpacity={0.8}
|
||||||
/>
|
/>
|
||||||
<stop
|
<stop
|
||||||
offset="95%"
|
offset="95%"
|
||||||
stopColor="var(--color-today)"
|
stopColor={`var(--color-${key})`}
|
||||||
stopOpacity={0.1}
|
|
||||||
/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="fillYesterday" x1="0" y1="0" x2="0" y2="1">
|
|
||||||
<stop
|
|
||||||
offset="5%"
|
|
||||||
stopColor="var(--color-yesterday)"
|
|
||||||
stopOpacity={0.8}
|
|
||||||
/>
|
|
||||||
<stop
|
|
||||||
offset="95%"
|
|
||||||
stopColor="var(--color-yesterday)"
|
|
||||||
stopOpacity={0.1}
|
stopOpacity={0.1}
|
||||||
/>
|
/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
|
))}
|
||||||
</defs>
|
</defs>
|
||||||
<CartesianGrid vertical={false} />
|
<CartesianGrid vertical={false} />
|
||||||
<XAxis
|
<XAxis
|
||||||
@ -86,18 +86,17 @@ export function SalesByHourChart({ data }: { data: any[] }) {
|
|||||||
cursor={false}
|
cursor={false}
|
||||||
content={<ChartTooltipContent indicator="dot" />}
|
content={<ChartTooltipContent indicator="dot" />}
|
||||||
/>
|
/>
|
||||||
|
{/* Render Areas in reverse order so first key in config is on top */}
|
||||||
|
{[...keys].reverse().map((key) => (
|
||||||
<Area
|
<Area
|
||||||
dataKey="yesterday"
|
key={key}
|
||||||
|
dataKey={key}
|
||||||
type="natural"
|
type="natural"
|
||||||
fill="url(#fillYesterday)"
|
fill={`url(#fill${key})`}
|
||||||
stroke="var(--color-yesterday)"
|
stroke={`var(--color-${key})`}
|
||||||
/>
|
isAnimationActive={true}
|
||||||
<Area
|
|
||||||
dataKey="today"
|
|
||||||
type="natural"
|
|
||||||
fill="url(#fillToday)"
|
|
||||||
stroke="var(--color-today)"
|
|
||||||
/>
|
/>
|
||||||
|
))}
|
||||||
<ChartLegend content={<ChartLegendContent />} />
|
<ChartLegend content={<ChartLegendContent />} />
|
||||||
</AreaChart>
|
</AreaChart>
|
||||||
</ChartContainer>
|
</ChartContainer>
|
||||||
|
|||||||
@ -4,11 +4,11 @@ import type { AnalysisPageProps } from '@/types';
|
|||||||
import { Head, usePage } from '@inertiajs/react';
|
import { Head, usePage } from '@inertiajs/react';
|
||||||
import { CustomBarChart } from '../components/charts/bar-chart';
|
import { CustomBarChart } from '../components/charts/bar-chart';
|
||||||
import { CustomPieChart } from '../components/charts/pie-chart';
|
import { CustomPieChart } from '../components/charts/pie-chart';
|
||||||
import { OrdersByHourChart } from '../components/charts/orders-by-hour-chart';
|
import { SalesByHourChart } from '../components/charts/sales-by-hour-chart';
|
||||||
import { RevenueVsPurchasesChart } from '../components/charts/revenue-vs-purchases-chart';
|
import { RevenueVsPurchasesChart } from '../components/charts/revenue-vs-purchases-chart';
|
||||||
|
|
||||||
export default function Analisa() {
|
export default function Analisa() {
|
||||||
const { stats, salesByMonth, ordersByHour, paymentMethods, orderStatuses, orderChannels, topProducts, topCustomers } =
|
const { stats, salesByMonth, salesByHour, paymentMethods, orderStatuses, orderChannels, topProducts, topCustomers } =
|
||||||
usePage<AnalysisPageProps>().props;
|
usePage<AnalysisPageProps>().props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -51,7 +51,15 @@ export default function Analisa() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<RevenueVsPurchasesChart data={salesByMonth} />
|
<RevenueVsPurchasesChart data={salesByMonth} />
|
||||||
<OrdersByHourChart data={ordersByHour} />
|
<SalesByHourChart
|
||||||
|
data={salesByHour}
|
||||||
|
config={{
|
||||||
|
total: {
|
||||||
|
label: "Total Penjualan",
|
||||||
|
color: "var(--chart-1)",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3">
|
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3">
|
||||||
<CustomPieChart
|
<CustomPieChart
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user