feat: add sales by hour statistics to dashboard with interactive chart component
This commit is contained in:
parent
80b7705b3f
commit
7ee44c8fd9
@ -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,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
106
resources/js/components/charts/sales-by-hour-chart.tsx
Normal file
106
resources/js/components/charts/sales-by-hour-chart.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@ -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>
|
||||
</>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user