Frontend
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ArrowLeft, Calendar, Clock, MapPin, AlertCircle, CheckCircle, XCircle } from "lucide-react";
|
||||
|
||||
interface Booking {
|
||||
booking_id: string;
|
||||
room: {
|
||||
name: string;
|
||||
floor: string;
|
||||
};
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
purpose: string;
|
||||
status: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export default function HistoryPage() {
|
||||
const router = useRouter();
|
||||
const [bookings, setBookings] = useState<Booking[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
const token = localStorage.getItem("token");
|
||||
if (!token) {
|
||||
router.push("/login");
|
||||
return;
|
||||
}
|
||||
fetchHistory(token);
|
||||
}, []);
|
||||
|
||||
const fetchHistory = 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 history:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Fungsi untuk memformat tanggal agar enak dibaca (Contoh: 10 Feb 2026, 08:00)
|
||||
const formatDate = (isoString: string) => {
|
||||
const date = new Date(isoString);
|
||||
return date.toLocaleDateString("id-ID", {
|
||||
day: "numeric", month: "short", year: "numeric",
|
||||
hour: "2-digit", minute: "2-digit"
|
||||
});
|
||||
};
|
||||
|
||||
// Fungsi menentukan warna status
|
||||
const getStatusBadge = (status: string) => {
|
||||
switch (status) {
|
||||
case "Approved":
|
||||
return <span className="flex items-center gap-1 bg-green-100 text-green-700 px-3 py-1 rounded-full text-xs font-bold"><CheckCircle size={14}/> Disetujui</span>;
|
||||
case "Rejected":
|
||||
return <span className="flex items-center gap-1 bg-red-100 text-red-700 px-3 py-1 rounded-full text-xs font-bold"><XCircle size={14}/> Ditolak</span>;
|
||||
default: // Pending
|
||||
return <span className="flex items-center gap-1 bg-yellow-100 text-yellow-700 px-3 py-1 rounded-full text-xs font-bold"><AlertCircle size={14}/> Menunggu</span>;
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <div className="p-10 text-center">Memuat riwayat...</div>;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
{/* Navbar Header */}
|
||||
<nav className="bg-white shadow-sm px-6 py-4 sticky top-0 z-10">
|
||||
<div className="max-w-4xl mx-auto flex items-center gap-4">
|
||||
<button
|
||||
onClick={() => router.push("/dashboard")}
|
||||
className="p-2 hover:bg-gray-100 rounded-full transition"
|
||||
>
|
||||
<ArrowLeft className="text-gray-600" />
|
||||
</button>
|
||||
<h1 className="text-xl font-bold text-gray-800">Riwayat Peminjaman</h1>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main className="max-w-4xl mx-auto p-6">
|
||||
{bookings.length === 0 ? (
|
||||
<div className="text-center py-20 text-gray-500">
|
||||
<Calendar size={48} className="mx-auto mb-4 text-gray-300" />
|
||||
<p>Belum ada riwayat booking.</p>
|
||||
<button onClick={() => router.push("/dashboard")} className="text-blue-600 mt-2 hover:underline">
|
||||
Yuk booking sekarang!
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{bookings.map((item) => (
|
||||
<div key={item.booking_id} className="bg-white p-5 rounded-xl shadow-sm border border-gray-100 hover:shadow-md transition">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div>
|
||||
<h3 className="text-lg font-bold text-gray-800">{item.room.name}</h3>
|
||||
<div className="flex items-center gap-1 text-xs text-gray-500 mt-1">
|
||||
<MapPin size={12} /> {item.room.floor}
|
||||
</div>
|
||||
</div>
|
||||
{getStatusBadge(item.status)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm text-gray-600 bg-gray-50 p-3 rounded-lg">
|
||||
<div>
|
||||
<p className="text-xs text-gray-400 mb-1">Waktu Mulai</p>
|
||||
<div className="flex items-center gap-2 font-medium">
|
||||
<Calendar size={14} className="text-blue-500"/>
|
||||
{formatDate(item.start_time)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-400 mb-1">Waktu Selesai</p>
|
||||
<div className="flex items-center gap-2 font-medium">
|
||||
<Clock size={14} className="text-orange-500"/>
|
||||
{formatDate(item.end_time)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 pt-3 border-t border-gray-100">
|
||||
<p className="text-xs text-gray-400">Keperluan:</p>
|
||||
<p className="text-sm text-gray-800 font-medium">{item.purpose}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user