import { Head, usePage } from '@inertiajs/react'; import { useEffect, useState } from 'react'; import { cn } from '@/lib/utils'; import { PlaceholderPattern } from '@/components/ui/placeholder-pattern'; import { dashboard } from '@/routes'; import { Auth } from '@/types'; import { Sun, Moon, Cloud, Trees, CloudRain, Stars, Bird, TrendingUp, TrendingDown, ShoppingBag, DollarSign, Wallet, Receipt, Percent, BarChart3, CreditCard, ArrowUpRight, ArrowDownRight, Package, Users, ShoppingCart } from 'lucide-react'; import { Card, CardHeader, CardTitle, CardDescription, CardAction, CardFooter, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, ResponsiveContainer, AreaChart, Area, PieChart, Pie, Cell, LabelList, LineChart, Line, Sector, Label } from 'recharts'; import { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, type ChartConfig } from '@/components/ui/chart'; interface StatItem { value: number; yesterday: number; change: number; } interface DashboardStats { total_penjualan: StatItem; total_pendapatan: StatItem; total_pengeluaran: StatItem; hpp: StatItem; aov: StatItem; profit_margin: StatItem; total_diskon: StatItem; laba_kotor: StatItem; laba_bersih: StatItem; total_pembelian: StatItem; produk_terjual: StatItem; total_pelanggan: StatItem; charts: { jam_sibuk: Array<{ hour: string; today: number; yesterday: number }>; order_by_payment: Array<{ name: string; value: number }>; order_by_status: Array<{ name: string; value: number }>; order_by_channel: Array<{ name: string; value: number }>; top_products: Array<{ name: string; qty: number }>; }; } interface PageProps { auth: Auth; stats: DashboardStats; [key: string]: any; } const DynamicScene = ({ hour }: { hour: number }) => { if (hour >= 5 && hour < 11) { return (
); } if (hour >= 11 && hour < 15) { return (
); } if (hour >= 15 && hour < 19) { return (
); } return (
); }; export default function Dashboard() { const { auth, stats } = usePage().props; const [time, setTime] = useState(new Date()); useEffect(() => { const timer = setInterval(() => setTime(new Date()), 1000); return () => clearInterval(timer); }, []); const getTimeTheme = () => { const hour = time.getHours(); if (hour >= 5 && hour < 11) return { greeting: 'Selamat Pagi', class: 'bg-gradient-to-br from-amber-50 to-orange-100 dark:from-amber-950/40 dark:to-orange-900/40 border-amber-200 dark:border-amber-800/30 shadow-sm', text: 'text-amber-950 dark:text-amber-50', subtext: 'text-amber-900/70 dark:text-amber-200/70', decoration: 'bg-amber-500/10 dark:bg-amber-500/20', pageBackground: 'bg-amber-50/50 dark:bg-amber-950/10', card: 'bg-white/80 dark:bg-amber-900/10 backdrop-blur-sm border-amber-200/50 dark:border-amber-800/20', placeholder: 'stroke-amber-500/20 dark:stroke-amber-400/20' }; if (hour >= 11 && hour < 15) return { greeting: 'Selamat Siang', class: 'bg-gradient-to-br from-sky-50 to-blue-100 dark:from-sky-950/40 dark:to-blue-900/40 border-sky-200 dark:border-sky-800/30 shadow-sm', text: 'text-sky-950 dark:text-sky-50', subtext: 'text-sky-900/70 dark:text-sky-200/70', decoration: 'bg-sky-500/10 dark:bg-sky-500/20', pageBackground: 'bg-sky-50/50 dark:bg-sky-950/10', card: 'bg-white/80 dark:bg-sky-900/10 backdrop-blur-sm border-sky-200/50 dark:border-sky-800/20', placeholder: 'stroke-sky-500/20 dark:stroke-sky-400/20' }; if (hour >= 15 && hour < 19) return { greeting: 'Selamat Sore', class: 'bg-gradient-to-br from-orange-50 to-purple-100 dark:from-orange-950/40 dark:to-purple-900/40 border-orange-200 dark:border-orange-800/30 shadow-sm', text: 'text-orange-950 dark:text-orange-50', subtext: 'text-orange-900/70 dark:text-orange-200/70', decoration: 'bg-orange-500/10 dark:bg-orange-500/20', pageBackground: 'bg-orange-50/50 dark:bg-orange-950/10', card: 'bg-white/80 dark:bg-orange-900/10 backdrop-blur-sm border-orange-200/50 dark:border-orange-800/20', placeholder: 'stroke-orange-500/20 dark:stroke-orange-400/20' }; return { greeting: 'Selamat Malam', class: 'bg-gradient-to-br from-slate-900 via-indigo-950 to-black text-white border-indigo-500/20 shadow-indigo-950', text: 'text-indigo-50 drop-shadow-md', subtext: 'text-indigo-200/70 drop-shadow-sm', decoration: 'bg-indigo-500/20', pageBackground: 'bg-slate-100 dark:bg-slate-950', card: 'bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-800', placeholder: 'stroke-slate-300 dark:stroke-slate-700' }; }; const theme = getTimeTheme(); const hour = time.getHours(); const formattedTime = time.toLocaleTimeString('id-ID', { hour: '2-digit', minute: '2-digit', second: '2-digit', }); const formattedDate = time.toLocaleDateString('id-ID', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric', }); const formatCurrency = (value: number) => { return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0, }).format(value); }; const formatNumber = (value: number) => { return new Intl.NumberFormat('id-ID').format(value); }; const StatCard = ({ title, stat, icon: Icon, isCurrency = true, isPercentage = false, className }: { title: string; stat: StatItem; icon: any; isCurrency?: boolean; isPercentage?: boolean; className?: string; }) => { const isPositive = stat.change >= 0; return (
{title}
{isPercentage ? `${stat.value}%` : (isCurrency ? formatCurrency(stat.value) : formatNumber(stat.value))} {isPositive ? : } {Math.abs(stat.change)}%
{isPositive ? ( Meningkat ) : ( Menurun )} vs kemarin
Kemarin: {isPercentage ? `${stat.yesterday}%` : (isCurrency ? formatCurrency(stat.yesterday) : formatNumber(stat.yesterday))}
); }; return ( <>
{/* Welcome Section */}
{/* Decorative Elements */}

{theme.greeting}, {auth.user.name}!

Senang melihat Anda kembali. Berikut adalah ringkasan bisnis Anda hari ini.

{formattedTime}
{formattedDate}
{/* Stats Grid */}
{[ { title: "Total Penjualan", stat: stats.total_penjualan, icon: ShoppingBag, isCurrency: false }, { title: "Total Pendapatan", stat: stats.total_pendapatan, icon: DollarSign }, { title: "Total Pengeluaran", stat: stats.total_pengeluaran, icon: Wallet }, { title: "HPP (Modal)", stat: stats.hpp, icon: Receipt }, { title: "AOV (Avg Order Value)", stat: stats.aov, icon: CreditCard }, { title: "Profit Margin", stat: stats.profit_margin, icon: Percent, isPercentage: true }, { title: "Total Diskon", stat: stats.total_diskon, icon: Percent }, { title: "Laba Kotor", stat: stats.laba_kotor, icon: BarChart3 }, { title: "Laba Bersih", stat: stats.laba_bersih, icon: TrendingUp }, { title: "Total Pembelian", stat: stats.total_pembelian, icon: ShoppingCart }, { title: "Produk Terjual", stat: stats.produk_terjual, icon: Package, isCurrency: false }, { title: "Total Pelanggan", stat: stats.total_pelanggan, icon: Users, isCurrency: false }, ].map((item, index) => (
))}
{/* Charts Section */}
{/* Jam Sibuk - Multiple Line Chart Pattern */} Jam Sibuk (Pesanan per Jam) Perbandingan aktivitas hari ini vs kemarin } /> } />
Menampilkan data pesanan per jam untuk mengidentifikasi waktu operasional tersibuk.
{/* Order Distribution - Pie Chart Label List Pattern */} Metode Pembayaran Distribusi transaksi hari ini [ item.name.toLowerCase().replace(/[^a-z0-h]/g, ''), { label: item.name, color: `var(--chart-${(i % 5) + 1})` } ]))} className="mx-auto aspect-square max-h-[300px] [&_.recharts-text]:fill-foreground" > } /> ({ ...item, fill: `var(--color-${item.name.toLowerCase().replace(/[^a-z0-h]/g, '')})` }))} dataKey="value" nameKey="name" >
Distribusi pembayaran real-time
{/* Order Channel - Donut Active Pattern */} Saluran Pesanan Sumber pesanan masuk [ item.name.toLowerCase().replace(/[^a-z0-h]/g, ''), { label: item.name, color: `var(--chart-${((i + 2) % 5) + 1})` } ]))} className="mx-auto aspect-square max-h-[300px]" > } /> ({ ...item, fill: `var(--color-${item.name.toLowerCase().replace(/[^a-z0-h]/g, '')})` }))} dataKey="value" nameKey="name" innerRadius={60} strokeWidth={5} activeShape={({ outerRadius = 0, ...props }: any) => ( )} />
Performa saluran penjualan
{/* Top Products - Multiple Bar Pattern (Adapted) */} Top 5 Produk Terlaris Berdasarkan kuantitas yang terjual hari ini value.length > 15 ? value.slice(0, 15) + '...' : value} /> } />
); } Dashboard.layout = { breadcrumbs: [ { title: 'Dasbor', href: dashboard(), }, ], };