diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php
index df6c765..f9a5dfb 100644
--- a/app/Http/Controllers/DashboardController.php
+++ b/app/Http/Controllers/DashboardController.php
@@ -102,12 +102,33 @@ public function __invoke()
return $item;
});
+ $topProducts = DB::table('order_items')
+ ->join('products', 'order_items.product_id', '=', 'products.id')
+ ->join('orders', 'order_items.order_id', '=', 'orders.id')
+ ->select('products.name as name', DB::raw('SUM(order_items.qty) as total'))
+ ->whereDate('orders.created_at', $today)
+ ->groupBy('products.name')
+ ->orderByDesc('total')
+ ->limit(5)
+ ->get();
+
+ $topCustomers = DB::table('orders')
+ ->select('customer_name as name', DB::raw('SUM(total) as total'))
+ ->whereDate('created_at', $today)
+ ->whereNotNull('customer_name')
+ ->groupBy('customer_name')
+ ->orderByDesc('total')
+ ->limit(5)
+ ->get();
+
return Inertia::render('dashboard', [
'stats' => $stats,
'salesByHour' => $salesByHour,
'paymentMethods' => $paymentMethods,
'orderStatuses' => $orderStatuses,
'orderChannels' => $orderChannels,
+ 'topProducts' => $topProducts,
+ 'topCustomers' => $topCustomers,
]);
}
diff --git a/resources/js/components/charts/bar-chart.tsx b/resources/js/components/charts/bar-chart.tsx
new file mode 100644
index 0000000..d69266e
--- /dev/null
+++ b/resources/js/components/charts/bar-chart.tsx
@@ -0,0 +1,116 @@
+"use client"
+
+import React, { useMemo } from "react"
+import { Bar, BarChart, CartesianGrid, LabelList, XAxis, YAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/components/ui/chart"
+
+export const CustomBarChart = React.memo(function CustomBarChart({
+ title,
+ description,
+ data,
+ colorOffset = 0,
+ isCurrency = false
+}: {
+ title: string
+ description: string
+ data: any[]
+ colorOffset?: number
+ isCurrency?: boolean
+}) {
+ const { config, chartData } = useMemo(() => {
+ const cfg: ChartConfig = {
+ total: { label: "Total" },
+ }
+
+ const formattedData = data.map((item, index) => {
+ const key = String(item.name).toLowerCase().replace(/[^a-z0-9]/g, '_')
+
+ const hue = Math.floor((index * 137.5 + colorOffset) % 360)
+
+ cfg[key] = {
+ label: String(item.name),
+ color: `hsl(${hue}, 60%, 65%)`,
+ }
+ return {
+ ...item,
+ fill: `var(--color-${key})`,
+ name: String(item.name),
+ total: Number(item.total)
+ }
+ })
+ return { config: cfg, chartData: formattedData }
+ }, [data, colorOffset])
+
+ const formatValue = (val: any) => {
+ if (!isCurrency) return val;
+ return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(val);
+ };
+
+ return (
+
+
+ {title}
+ {description}
+
+
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+ )
+})
diff --git a/resources/js/pages/dashboard.tsx b/resources/js/pages/dashboard.tsx
index 2a9a561..179fd9f 100644
--- a/resources/js/pages/dashboard.tsx
+++ b/resources/js/pages/dashboard.tsx
@@ -5,10 +5,11 @@ 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';
+import { CustomBarChart } from '../components/charts/bar-chart';
import { CustomPieChart } from '../components/charts/pie-chart';
export default function Dashboard() {
- const { auth, stats, salesByHour, paymentMethods, orderStatuses, orderChannels } = usePage().props;
+ const { auth, stats, salesByHour, paymentMethods, orderStatuses, orderChannels, topProducts, topCustomers } = usePage().props;
const [time, setTime] = useState(new Date());
useEffect(() => {
@@ -123,6 +124,22 @@ export default function Dashboard() {
colorOffset={240}
/>
+
+
+
+
+
>
);