diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index 6250ae5..df6c765 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -2,6 +2,9 @@ namespace App\Http\Controllers; +use App\Enums\OrderChannel; +use App\Enums\OrderStatus; +use App\Enums\PaymentMethod; use App\Models\Expense; use App\Models\Order; use App\Models\OrderItem; @@ -63,9 +66,48 @@ public function __invoke() ]; }); + $paymentMethods = DB::table('orders') + ->select('payment_method as name', DB::raw('COUNT(*) as total')) + ->groupBy('payment_method') + ->get() + ->map(function ($item) { + if ($item->name) { + $item->name = PaymentMethod::tryFrom($item->name)?->label() ?? $item->name; + } + + return $item; + }); + + $orderStatuses = DB::table('orders') + ->select('order_status as name', DB::raw('COUNT(*) as total')) + ->groupBy('order_status') + ->get() + ->map(function ($item) { + if ($item->name) { + $item->name = OrderStatus::tryFrom($item->name)?->label() ?? $item->name; + } + + return $item; + }); + + $orderChannels = DB::table('orders') + ->select('order_channel as name', DB::raw('COUNT(*) as total')) + ->groupBy('order_channel') + ->get() + ->map(function ($item) { + if ($item->name) { + $item->name = OrderChannel::tryFrom($item->name)?->label() ?? $item->name; + } + + return $item; + }); + return Inertia::render('dashboard', [ 'stats' => $stats, 'salesByHour' => $salesByHour, + 'paymentMethods' => $paymentMethods, + 'orderStatuses' => $orderStatuses, + 'orderChannels' => $orderChannels, ]); } diff --git a/resources/js/components/charts/pie-chart.tsx b/resources/js/components/charts/pie-chart.tsx new file mode 100644 index 0000000..b4980d7 --- /dev/null +++ b/resources/js/components/charts/pie-chart.tsx @@ -0,0 +1,80 @@ +"use client" + +import React, { useMemo } from "react" +import { Pie, PieChart } from "recharts" + +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card" +import { + ChartContainer, + ChartTooltip, + ChartTooltipContent, + type ChartConfig, +} from "@/components/ui/chart" + +export const CustomPieChart = React.memo(function CustomPieChart({ + title, + description, + data, + colorOffset = 0, +}: { + title: string + description: string + data: any[] + colorOffset?: number +}) { + 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, '_') + + // Generate distinct HSL color based on index and chart-level offset + // 137.5 is the golden angle, which distributes colors nicely + const hue = Math.floor((index * 137.5 + colorOffset) % 360) + + cfg[key] = { + label: String(item.name), + color: `hsl(${hue}, 70%, 50%)`, + } + return { + ...item, + fill: `var(--color-${key})`, + name: String(item.name), + } + }) + return { config: cfg, chartData: formattedData } + }, [data, colorOffset]) + + return ( + + + {title} + {description} + + + + + } /> + + + + + + ) +}) diff --git a/resources/js/pages/dashboard.tsx b/resources/js/pages/dashboard.tsx index 1797a55..2a9a561 100644 --- a/resources/js/pages/dashboard.tsx +++ b/resources/js/pages/dashboard.tsx @@ -5,9 +5,10 @@ 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 { CustomPieChart } from '../components/charts/pie-chart'; export default function Dashboard() { - const { auth, stats, salesByHour } = usePage().props; + const { auth, stats, salesByHour, paymentMethods, orderStatuses, orderChannels } = usePage().props; const [time, setTime] = useState(new Date()); useEffect(() => { @@ -101,6 +102,27 @@ export default function Dashboard() { + +
+ + + +
);