154 lines
5.8 KiB
TypeScript
154 lines
5.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import axios from "axios";
|
|
import { useRouter } from "next/navigation";
|
|
import { LogOut, CheckCircle, XCircle, Clock } from "lucide-react";
|
|
|
|
export default function AdminDashboard() {
|
|
const router = useRouter();
|
|
const [admin, setAdmin] = useState<any>(null);
|
|
const [bookings, setBookings] = useState<any[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const token = localStorage.getItem("token");
|
|
const userData = localStorage.getItem("user");
|
|
|
|
if (!token || !userData) {
|
|
router.push("/login");
|
|
return;
|
|
}
|
|
|
|
const userObj = JSON.parse(userData);
|
|
// Tendang kalau bukan admin yang masuk
|
|
if (userObj.role !== "admin") {
|
|
router.push("/dashboard");
|
|
return;
|
|
}
|
|
|
|
setAdmin(userObj);
|
|
fetchBookings(token);
|
|
}, []);
|
|
|
|
const fetchBookings = async (token: string) => {
|
|
try {
|
|
const response = await axios.get("http://localhost:8080/api/bookings", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
setBookings(response.data.data || []);
|
|
} catch (error) {
|
|
console.error("Gagal ambil data booking:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleUpdateStatus = async (bookingId: string, newStatus: string) => {
|
|
const token = localStorage.getItem("token");
|
|
try {
|
|
await axios.patch(
|
|
`http://localhost:8080/api/bookings/${bookingId}`,
|
|
{ status: newStatus },
|
|
{ headers: { Authorization: `Bearer ${token}` } }
|
|
);
|
|
alert(`Booking berhasil di-${newStatus}!`);
|
|
fetchBookings(token as string); // Refresh data setelah update
|
|
} catch (error) {
|
|
alert("Gagal mengubah status booking.");
|
|
console.error(error);
|
|
}
|
|
};
|
|
|
|
const handleLogout = () => {
|
|
localStorage.removeItem("token");
|
|
localStorage.removeItem("user");
|
|
router.push("/login");
|
|
};
|
|
|
|
if (loading) return <div className="p-10 text-center">Memuat panel admin...</div>;
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-100">
|
|
{/* Navbar Admin */}
|
|
<nav className="bg-slate-800 shadow-sm px-6 py-4 flex justify-between items-center text-white sticky top-0 z-10">
|
|
<div>
|
|
<h1 className="text-xl font-bold text-blue-400">S-CLASS | ADMIN PANEL</h1>
|
|
<p className="text-xs text-gray-300">Welcome, {admin?.full_name}</p>
|
|
</div>
|
|
<button
|
|
onClick={handleLogout}
|
|
className="flex items-center gap-2 text-sm bg-red-500 hover:bg-red-600 px-4 py-2 rounded-lg transition"
|
|
>
|
|
<LogOut size={16} /> Keluar
|
|
</button>
|
|
</nav>
|
|
|
|
{/* Konten Utama */}
|
|
<main className="max-w-6xl mx-auto p-6">
|
|
<h2 className="text-2xl font-bold text-gray-800 mb-6">Manajemen Peminjaman Ruangan</h2>
|
|
|
|
<div className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
|
<table className="w-full text-left border-collapse">
|
|
<thead>
|
|
<tr className="bg-gray-50 text-gray-600 text-sm border-b">
|
|
<th className="p-4">Ruangan</th>
|
|
<th className="p-4">Peminjam</th>
|
|
<th className="p-4">Waktu</th>
|
|
<th className="p-4">Keperluan</th>
|
|
<th className="p-4">Status</th>
|
|
<th className="p-4 text-center">Aksi</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{bookings.length === 0 ? (
|
|
<tr>
|
|
<td colSpan={6} className="text-center p-8 text-gray-500">Belum ada pengajuan peminjaman.</td>
|
|
</tr>
|
|
) : (
|
|
bookings.map((b) => (
|
|
<tr key={b.booking_id} className="border-b hover:bg-gray-50">
|
|
<td className="p-4 font-semibold text-gray-800">{b.room?.name}</td>
|
|
<td className="p-4 text-sm text-gray-600">ID User: {b.user_id.substring(0,8)}...</td>
|
|
<td className="p-4 text-sm text-gray-600">
|
|
<div>Mulai: {new Date(b.start_time).toLocaleString('id-ID')}</div>
|
|
<div>Selesai: {new Date(b.end_time).toLocaleString('id-ID')}</div>
|
|
</td>
|
|
<td className="p-4 text-sm text-gray-600">{b.purpose}</td>
|
|
<td className="p-4">
|
|
<span className={`px-2 py-1 text-xs font-bold rounded-full ${
|
|
b.status === 'Approved' ? 'bg-green-100 text-green-700' :
|
|
b.status === 'Rejected' ? 'bg-red-100 text-red-700' :
|
|
'bg-yellow-100 text-yellow-700'
|
|
}`}>
|
|
{b.status}
|
|
</span>
|
|
</td>
|
|
<td className="p-4">
|
|
{b.status === 'Pending' ? (
|
|
<div className="flex justify-center gap-2">
|
|
<button
|
|
onClick={() => handleUpdateStatus(b.booking_id, 'Approved')}
|
|
className="bg-green-500 hover:bg-green-600 text-white p-2 rounded-lg" title="Setujui">
|
|
<CheckCircle size={18} />
|
|
</button>
|
|
<button
|
|
onClick={() => handleUpdateStatus(b.booking_id, 'Rejected')}
|
|
className="bg-red-500 hover:bg-red-600 text-white p-2 rounded-lg" title="Tolak">
|
|
<XCircle size={18} />
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<div className="text-center text-gray-400 text-xs">- Selesai -</div>
|
|
)}
|
|
</td>
|
|
</tr>
|
|
))
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
} |