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(),
},
],
};