TIFNJK_E41222887/resources/js/pages/dashboard.tsx

202 lines
10 KiB
TypeScript

import { Head, Link } from '@inertiajs/react';
import AppLayout from '@/layouts/app-layout';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Users, Building2, Briefcase, UserPlus, Banknote } from 'lucide-react';
import {
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip as RechartsTooltip, ResponsiveContainer,
PieChart, Pie, Cell, Legend
} from 'recharts';
interface DashboardProps {
stats: {
total_employees: number;
total_departments: number;
total_positions: number;
payroll_realisasi: number;
};
genderData: { name: string; value: number; fill: string }[];
deptData: { name: string; employees: number }[];
latestEmployees: {
id: number;
join_date: string;
user: { name: string };
position: { name: string };
department: { name: string };
}[];
currentPeriod: string;
}
function formatRupiah(value: number): string {
return new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
minimumFractionDigits: 0,
}).format(value);
}
function formatPeriod(period: string): string {
const [year, month] = period.split('-');
return new Date(Number(year), Number(month) - 1, 1)
.toLocaleDateString('id-ID', { month: 'long', year: 'numeric' });
}
export default function Dashboard({ stats, genderData, deptData, latestEmployees, currentPeriod }: DashboardProps) {
return (
<AppLayout breadcrumbs={[{ title: 'Dashboard', href: '/dashboard' }]}>
<Head title="Dashboard" />
<div className="flex flex-1 flex-col gap-4 p-4 md:p-8 pt-0">
{/* CARD STATISTIK */}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Karyawan</CardTitle>
<Users className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{stats.total_employees}</div>
<p className="text-xs text-muted-foreground">Pegawai aktif saat ini</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Departemen</CardTitle>
<Building2 className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{stats.total_departments}</div>
<p className="text-xs text-muted-foreground">Divisi terdaftar</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Jabatan</CardTitle>
<Briefcase className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{stats.total_positions}</div>
<p className="text-xs text-muted-foreground">Posisi pekerjaan</p>
</CardContent>
</Card>
{/* Card Realisasi Gaji */}
<Card className="border-emerald-200 bg-emerald-50/50 dark:bg-emerald-950/20 dark:border-emerald-900">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-emerald-800 dark:text-emerald-300">
Realisasi Gaji Bulan Ini
</CardTitle>
<Banknote className="h-4 w-4 text-emerald-600" />
</CardHeader>
<CardContent>
<div className="text-xl font-bold text-emerald-700 dark:text-emerald-400 leading-tight">
{formatRupiah(stats.payroll_realisasi)}
</div>
<p className="text-xs text-emerald-600/80 dark:text-emerald-500 mt-1">
Status &quot;Telah Dikirim&quot; · {formatPeriod(currentPeriod)}
</p>
</CardContent>
</Card>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-7">
{/* Grafik Batang */}
<Card className="col-span-4">
<CardHeader>
<CardTitle>Karyawan per Departemen</CardTitle>
</CardHeader>
<CardContent className="pl-2">
<div className="h-[300px] w-full">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={deptData}>
<CartesianGrid strokeDasharray="3 3" vertical={false} />
<XAxis
dataKey="name"
stroke="#888888"
fontSize={12}
tickLine={false}
axisLine={false}
/>
<YAxis
stroke="#888888"
fontSize={12}
tickLine={false}
axisLine={false}
tickFormatter={(value) => `${value}`}
/>
<RechartsTooltip
cursor={{ fill: 'transparent' }}
contentStyle={{ borderRadius: '8px', border: 'none', boxShadow: '0 4px 12px rgba(0,0,0,0.1)' }}
/>
<Bar dataKey="employees" fill="#0f172a" radius={[4, 4, 0, 0]} barSize={40} />
</BarChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>
{/* Grafik Donat*/}
<Card className="col-span-3">
<CardHeader>
<CardTitle>Komposisi Gender</CardTitle>
</CardHeader>
<CardContent>
<div className="h-[300px] w-full">
<ResponsiveContainer width="100%" height="100%">
<PieChart>
<Pie
data={genderData}
cx="50%"
cy="50%"
innerRadius={60}
outerRadius={80}
paddingAngle={5}
dataKey="value"
>
{genderData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.fill} />
))}
</Pie>
<RechartsTooltip />
<Legend verticalAlign="bottom" height={36}/>
</PieChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>
</div>
{/* TABEL KARYAWAN TERBARU */}
<Card>
<CardHeader className="flex flex-row items-center justify-between">
<CardTitle>Karyawan Terbaru Bergabung</CardTitle>
<Link href="/admin/employees" className="text-sm text-blue-600 hover:underline">
Lihat Semua
</Link>
</CardHeader>
<CardContent>
<div className="space-y-8">
{latestEmployees.length === 0 ? (
<p className="text-sm text-center text-muted-foreground py-4">Belum ada data karyawan.</p>
) : latestEmployees.map((emp) => (
<div key={emp.id} className="flex items-center">
<div className="h-9 w-9 rounded-full bg-slate-100 flex items-center justify-center border">
<UserPlus className="h-5 w-5 text-slate-500" />
</div>
<div className="ml-4 space-y-1">
<p className="text-sm font-medium leading-none">{emp.user?.name ?? '-'}</p>
<p className="text-xs text-muted-foreground">{emp.position?.name ?? '-'} {emp.department?.name ?? '-'}</p>
</div>
<div className="ml-auto font-medium text-xs text-muted-foreground">
{new Date(emp.join_date).toLocaleDateString('id-ID', { day: 'numeric', month: 'short', year: 'numeric' })}
</div>
</div>
))}
</div>
</CardContent>
</Card>
</div>
</AppLayout>
);
}