From 7ee44c8fd919b530f3ca0c6042852d3709950f2c Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Thu, 23 Apr 2026 13:01:58 +0700 Subject: [PATCH] feat: add sales by hour statistics to dashboard with interactive chart component --- app/Http/Controllers/DashboardController.php | 32 ++++++ .../components/charts/sales-by-hour-chart.tsx | 106 ++++++++++++++++++ resources/js/pages/dashboard.tsx | 6 +- 3 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 resources/js/components/charts/sales-by-hour-chart.tsx diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index d020473..6250ae5 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -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, ]); } diff --git a/resources/js/components/charts/sales-by-hour-chart.tsx b/resources/js/components/charts/sales-by-hour-chart.tsx new file mode 100644 index 0000000..a13087a --- /dev/null +++ b/resources/js/components/charts/sales-by-hour-chart.tsx @@ -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 ( + + +
+ Jam Sibuk + + Menampilkan aktivitas toko berdasarkan pesanan yang masuk. + +
+
+ + + + + + + + + + + + + + + + } + /> + + + } /> + + + +
+ ) +} diff --git a/resources/js/pages/dashboard.tsx b/resources/js/pages/dashboard.tsx index 9cf08c4..1797a55 100644 --- a/resources/js/pages/dashboard.tsx +++ b/resources/js/pages/dashboard.tsx @@ -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().props; + const { auth, stats, salesByHour } = usePage().props; const [time, setTime] = useState(new Date()); useEffect(() => { @@ -99,6 +99,8 @@ export default function Dashboard() { ))} + + );