feat: add sales by hour statistics to dashboard with interactive chart component

This commit is contained in:
Yoga Pangestu 2026-04-23 13:01:58 +07:00
parent 80b7705b3f
commit 7ee44c8fd9
3 changed files with 142 additions and 2 deletions

View File

@ -7,6 +7,7 @@
use App\Models\OrderItem;
use App\Models\Purchase;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
class DashboardController extends Controller
@ -32,8 +33,39 @@ public function __invoke()
$stats['net_profit'] = $this->calculateDiff($stats['gross_profit'], $stats['total_expenses']);
$stats['profit_margin'] = $this->calculateMargin($stats['net_profit'], $stats['total_revenue']);
// Get sales by hour
$todayOrders = DB::table('orders')
->whereDate('created_at', $today)
->select(
DB::raw('HOUR(created_at) as hour'),
DB::raw('COUNT(*) as total')
)
->groupBy(DB::raw('HOUR(created_at)'))
->get();
$yesterdayOrders = DB::table('orders')
->whereDate('created_at', $yesterday)
->select(
DB::raw('HOUR(created_at) as hour'),
DB::raw('COUNT(*) as total')
)
->groupBy(DB::raw('HOUR(created_at)'))
->get();
$salesByHour = collect(range(0, 23))->map(function ($hour) use ($todayOrders, $yesterdayOrders) {
$todayFound = $todayOrders->firstWhere('hour', $hour);
$yesterdayFound = $yesterdayOrders->firstWhere('hour', $hour);
return [
'hour' => str_pad($hour, 2, '0', STR_PAD_LEFT).':00',
'today' => $todayFound ? (int) $todayFound->total : 0,
'yesterday' => $yesterdayFound ? (int) $yesterdayFound->total : 0,
];
});
return Inertia::render('dashboard', [
'stats' => $stats,
'salesByHour' => $salesByHour,
]);
}

View File

@ -0,0 +1,106 @@
"use client"
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import {
ChartContainer,
ChartLegend,
ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
type ChartConfig,
} from "@/components/ui/chart"
export const description = "Menampilkan aktivitas toko berdasarkan pesanan yang masuk."
const chartConfig = {
today: {
label: "Hari Ini",
color: "var(--chart-1)",
},
yesterday: {
label: "Kemarin",
color: "var(--chart-2)",
},
} satisfies ChartConfig
export function SalesByHourChart({ 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.
</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="fillToday" x1="0" y1="0" x2="0" y2="1">
<stop
offset="5%"
stopColor="var(--color-today)"
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor="var(--color-today)"
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}
/>
</linearGradient>
</defs>
<CartesianGrid vertical={false} />
<XAxis
dataKey="hour"
tickLine={false}
axisLine={false}
tickMargin={8}
/>
<ChartTooltip
cursor={false}
content={<ChartTooltipContent indicator="dot" />}
/>
<Area
dataKey="yesterday"
type="natural"
fill="url(#fillYesterday)"
stroke="var(--color-yesterday)"
/>
<Area
dataKey="today"
type="natural"
fill="url(#fillToday)"
stroke="var(--color-today)"
/>
<ChartLegend content={<ChartLegendContent />} />
</AreaChart>
</ChartContainer>
</CardContent>
</Card>
)
}

View File

@ -4,10 +4,10 @@ import { dashboard } from '@/routes';
import { DashboardPageProps } from '@/types';
import { StatCard } from '@/components/cards/stat-card';
import { WelcomeCard } from '@/components/cards/welcome-card';
import { SalesByHourChart } from '../components/charts/sales-by-hour-chart';
export default function Dashboard() {
const { auth, stats } = usePage<DashboardPageProps>().props;
const { auth, stats, salesByHour } = usePage<DashboardPageProps & { salesByHour: any[] }>().props;
const [time, setTime] = useState(new Date());
useEffect(() => {
@ -99,6 +99,8 @@ export default function Dashboard() {
</div>
))}
</div>
<SalesByHourChart data={salesByHour} />
</div>
</>
);