164 lines
5.5 KiB
TypeScript
164 lines
5.5 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import axios from 'axios';
|
|
|
|
interface DashboardStats {
|
|
permintaanHariIni: number;
|
|
menungguPersetujuan: number;
|
|
totalBarang: number;
|
|
penggunaAktif: number;
|
|
stokHampirHabis: number;
|
|
}
|
|
|
|
export default function Hero() {
|
|
// 1. Ambil data profil dari LocalStorage hasil login sebelumnya
|
|
const userName = localStorage.getItem('user_name') || 'User';
|
|
const userRole = localStorage.getItem('user_role') || 'Siswa';
|
|
|
|
// State untuk menampung angka statistik dari database
|
|
const [stats, setStats] = useState<DashboardStats>({
|
|
permintaanHariIni: 0,
|
|
menungguPersetujuan: 0,
|
|
totalBarang: 0,
|
|
penggunaAktif: 0,
|
|
stokHampirHabis: 0
|
|
});
|
|
|
|
// 2. Fungsi membuat inisial nama (Contoh: "Bu Sari" -> "BS", "Admin" -> "AD")
|
|
const dapatkanInisial = (nama: string) => {
|
|
const kata = nama.split(' ');
|
|
if (kata.length >= 2) {
|
|
return (kata[0][0] + kata[1][0]).toUpperCase();
|
|
}
|
|
return nama.substring(0, 2).toUpperCase();
|
|
};
|
|
|
|
// 3. Efek untuk mengambil data counter statistik dari backend pas halaman dibuka
|
|
useEffect(() => {
|
|
const ambilStatistikDashboard = async () => {
|
|
try {
|
|
// Silahkan buat rute /api/dashboard-stats di backend jika ingin memisahkan kueri count-nya
|
|
// Sebagai alternatif awal, kita ambil total barang & total bast dari rute yang sudah ada
|
|
const resBarang = await axios.get('http://localhost:5000/api/barang');
|
|
const resBast = await axios.get('http://localhost:5000/api/bast');
|
|
const resUser = await axios.get('http://localhost:5000/api/users');
|
|
|
|
// Hitung total data berdasarkan respon database
|
|
const totalBarang = resBarang.data.success ? resBarang.data.data.length : 0;
|
|
const listBast = resBast.data.success ? resBast.data.data : [];
|
|
const totalUser = resUser.data.success ? resUser.data.data.length : 0;
|
|
|
|
// Filter data untuk counter spesifik dokumen BAST
|
|
const menunggu = listBast.filter((b: any) => b.status_serah === 'Menunggu' || b.status_terima === 'Menunggu').length;
|
|
|
|
// Simulasi hitung permintaan hari ini dari tanggal dibuat_pada (UKK basic)
|
|
const hariIniFormat = new Date().toISOString().split('T')[0];
|
|
const hariIni = listBast.filter((b: any) => b.dibuat_pada && b.dibuat_pada.startsWith(hariIniFormat)).length;
|
|
|
|
// Hitung barang dengan stok kritis (di bawah 5 buah)
|
|
const kritis = resBarang.data.success
|
|
? resBarang.data.data.filter((brg: any) => brg.stok <= 3).length
|
|
: 0;
|
|
|
|
setStats({
|
|
permintaanHariIni: hariIni,
|
|
menungguPersetujuan: menunggu,
|
|
totalBarang: totalBarang,
|
|
penggunaAktif: totalUser,
|
|
stokHampirHabis: kritis
|
|
});
|
|
} catch (error) {
|
|
console.error("Gagal memuat statistik dashboard:", error);
|
|
}
|
|
};
|
|
|
|
ambilStatistikDashboard();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="bg-blue-50 rounded-3xl p-8 shadow-sm border border-blue-100 mb-8">
|
|
{/* HEADER */}
|
|
<div className="flex justify-between items-start mb-8">
|
|
<div>
|
|
<p className="text-gray-500 text-lg">
|
|
Selamat datang kembali 👋
|
|
</p>
|
|
|
|
<h1 className="text-5xl font-extrabold text-blue-700 mt-2">
|
|
Dashboard
|
|
</h1>
|
|
</div>
|
|
|
|
{/* PROFILE */}
|
|
<div className="flex items-center gap-3 bg-white rounded-full px-4 py-2 shadow border">
|
|
<div className="w-12 h-12 bg-blue-500 rounded-full flex items-center justify-center text-white font-bold">
|
|
{dapatkanInisial(userName)}
|
|
</div>
|
|
|
|
<div>
|
|
<h3 className="font-semibold text-black">
|
|
{userName}
|
|
</h3>
|
|
<p className="text-gray-500 text-sm capitalize">
|
|
{userRole}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* STAT CARDS */}
|
|
<div className="grid grid-cols-4 gap-6">
|
|
{/* CARD 1 */}
|
|
<div className="bg-white rounded-2xl p-6 shadow border">
|
|
<p className="text-gray-500 mb-2">
|
|
Permintaan Hari Ini
|
|
</p>
|
|
<h2 className="text-4xl font-bold text-blue-700">
|
|
{stats.permintaanHariIni}
|
|
</h2>
|
|
<p className="text-green-600 mt-2 text-sm">
|
|
Dokumen baru masuk
|
|
</p>
|
|
</div>
|
|
|
|
{/* CARD 2 */}
|
|
<div className="bg-white rounded-2xl p-6 shadow border">
|
|
<p className="text-gray-500 mb-2">
|
|
Menunggu Persetujuan
|
|
</p>
|
|
<h2 className="text-4xl font-bold text-blue-700">
|
|
{stats.menungguPersetujuan}
|
|
</h2>
|
|
<p className="text-yellow-600 mt-2 text-sm">
|
|
Perlu tindakan segera
|
|
</p>
|
|
</div>
|
|
|
|
{/* CARD 3 */}
|
|
<div className="bg-white rounded-2xl p-6 shadow border">
|
|
<p className="text-gray-500 mb-2">
|
|
Total Ragam Barang
|
|
</p>
|
|
<h2 className="text-4xl font-bold text-blue-700">
|
|
{stats.totalBarang}
|
|
</h2>
|
|
<p className="text-red-500 mt-2 text-sm">
|
|
{stats.stokHampirHabis} stok hampir habis
|
|
</p>
|
|
</div>
|
|
|
|
{/* CARD 4 */}
|
|
<div className="bg-white rounded-2xl p-6 shadow border">
|
|
<p className="text-gray-500 mb-2">
|
|
Total Terdaftar
|
|
</p>
|
|
<h2 className="text-4xl font-bold text-blue-700">
|
|
{stats.penggunaAktif}
|
|
</h2>
|
|
<p className="text-green-600 mt-2 text-sm">
|
|
Siswa & petugas aktif
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |