155 lines
5.9 KiB
TypeScript
155 lines
5.9 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import axios from "axios";
|
|
import { useRouter } from "next/navigation";
|
|
import { ArrowLeft, Calendar, Clock, MapPin, AlertCircle, CheckCircle, XCircle, Key } from "lucide-react";
|
|
|
|
interface Booking {
|
|
booking_id: string;
|
|
room: {
|
|
name: string;
|
|
floor: string;
|
|
};
|
|
start_time: string;
|
|
end_time: string;
|
|
purpose: string;
|
|
status: string;
|
|
redeem_code: 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 {
|
|
// PERBAIKAN: Ubah /api/bookings menjadi /api/my-bookings
|
|
const response = await axios.get("http://172.17.110.6:8080/api/my-bookings", {
|
|
headers: { Authorization: `Bearer ${token}` },
|
|
});
|
|
setBookings(response.data.data || []);
|
|
} catch (error) {
|
|
console.error("Gagal ambil history:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
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"
|
|
});
|
|
};
|
|
|
|
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:
|
|
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 font-bold text-gray-500">Memuat riwayat...</div>;
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-50">
|
|
<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>
|
|
|
|
{item.status === "Approved" && item.redeem_code && (
|
|
<div className="mt-4 bg-indigo-50 border border-indigo-200 rounded-lg p-4">
|
|
<div className="flex items-center gap-2 text-indigo-800 mb-2">
|
|
<Key size={16} />
|
|
<p className="text-sm font-bold">Kode Akses Ruangan</p>
|
|
</div>
|
|
<div className="bg-white border border-indigo-100 rounded text-center py-2 shadow-inner">
|
|
<p className="text-2xl font-mono font-bold text-indigo-700 tracking-[0.2em]">
|
|
{item.redeem_code}
|
|
</p>
|
|
</div>
|
|
<p className="text-xs text-indigo-500 mt-2 text-center">
|
|
*Masukkan kode ini pada layar ruangan.
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</main>
|
|
</div>
|
|
);
|
|
} |