228 lines
8.4 KiB
TypeScript
228 lines
8.4 KiB
TypeScript
// src/routes/BAST.tsx
|
|
import React, { useState, useEffect } from "react";
|
|
import axios from "axios";
|
|
import { useNavigate } from "react-router-dom"; // Kita pakai untuk redirect user biasa
|
|
import { FileText, ArrowRight, ClipboardList, User, ShieldAlert } from "lucide-react";
|
|
|
|
interface UserType {
|
|
user_id: number;
|
|
nama_lengkap: string;
|
|
role: string;
|
|
}
|
|
|
|
interface BarangType {
|
|
barang_id: number;
|
|
nama_barang: string;
|
|
}
|
|
|
|
export default function BAST() {
|
|
const [daftarUser, setDaftarUser] = useState<UserType[]>([]);
|
|
const [daftarBarang, setDaftarBarang] = useState<BarangType[]>([]);
|
|
|
|
// State Input Form
|
|
const [barangId, setBarangId] = useState("");
|
|
const [userSerahId, setUserSerahId] = useState(""); // Ditambahkan sesuai soal: Admin bebas pilih User Serah
|
|
const [userTerimaId, setUserTerimaId] = useState(""); // Ditambahkan sesuai soal: Admin bebas pilih User Terima
|
|
const [loadingSubmit, setLoadingSubmit] = useState(false);
|
|
|
|
// State Otentikasi Role
|
|
const [currentAdmin, setCurrentAdmin] = useState<UserType | null>(null);
|
|
const [isAuthorized, setIsAuthorized] = useState(false);
|
|
|
|
const navigate = useNavigate();
|
|
|
|
useEffect(() => {
|
|
// 1. CEK SESSION LOGIN DAN ROLE NYA
|
|
const savedUser = sessionStorage.getItem("user");
|
|
if (!savedUser) {
|
|
alert("Silakan login terlebih dahulu!");
|
|
navigate("/login");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const parsed = JSON.parse(savedUser);
|
|
|
|
// KUNCI UTAMA SESUAI SOAL UKK: JIKA BUKAN ADMIN, BLOKIR AKSESNYA!
|
|
if (parsed.role !== "Admin") {
|
|
setIsAuthorized(false);
|
|
alert("Akses Ditolak! Berdasarkan soal UKK, hanya Admin yang boleh membuat dokumen BAST.");
|
|
navigate("/userDashboard"); // Buang user biasa ke dashboard mereka sendiri
|
|
return;
|
|
}
|
|
|
|
// Jika dia Admin, izinkan akses halaman ini
|
|
setCurrentAdmin(parsed);
|
|
setIsAuthorized(true);
|
|
} catch (e) {
|
|
console.error("Gagal membaca session:", e);
|
|
navigate("/login");
|
|
return;
|
|
}
|
|
|
|
// 2. AMBIL DATA PILIHAN USER (UNTUK DROPDOWN SERAH & TERIMA)
|
|
axios.get("http://localhost:5000/api/users")
|
|
.then((res) => {
|
|
if (res.data.success) setDaftarUser(res.data.data);
|
|
})
|
|
.catch((err) => console.error("Gagal memuat list user:", err));
|
|
|
|
// 3. AMBIL DATA BARANG DARI BACKEND
|
|
axios.get("http://localhost:5000/api/barang")
|
|
.then((res) => {
|
|
if (res.data.success) setDaftarBarang(res.data.data);
|
|
})
|
|
.catch((err) => console.error("Gagal memuat list barang:", err));
|
|
}, [navigate]);
|
|
|
|
// PROSES KIRIM FORM (ADMIN YANG MEMBUATKAN)
|
|
const handleSubmitBAST = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
if (!barangId || !userSerahId || !userTerimaId) {
|
|
alert("Harap lengkapi semua pilihan barang, pihak penyerah, dan pihak penerima!");
|
|
return;
|
|
}
|
|
|
|
if (userSerahId === userTerimaId) {
|
|
alert("Pihak Penyerah dan Pihak Penerima tidak boleh orang yang sama!");
|
|
return;
|
|
}
|
|
|
|
setLoadingSubmit(true);
|
|
|
|
try {
|
|
const response = await axios.post("http://localhost:5000/api/bast", {
|
|
barang_id: Number(barangId),
|
|
user_serah_id: Number(userSerahId), // Input dinamis pilihan Admin
|
|
user_terima_id: Number(userTerimaId), // Input dinamis pilihan Admin
|
|
});
|
|
|
|
if (response.data.success) {
|
|
alert("Dokumen BAST Baru Berhasil Diterbitkan! Status otomatis 'Menunggu Approval' dari kedua belah pihak.");
|
|
// Reset pilihan form
|
|
setBarangId("");
|
|
setUserSerahId("");
|
|
setUserTerimaId("");
|
|
} else {
|
|
alert("Gagal menerbitkan BAST: " + response.data.message);
|
|
}
|
|
} catch (error: any) {
|
|
console.error(error);
|
|
alert(error.response?.data?.message || "Terjadi kendala pada sistem backend.");
|
|
} finally {
|
|
setLoadingSubmit(false);
|
|
}
|
|
};
|
|
|
|
// Guarding UI biar tidak bocor sekilas saat loading validasi role
|
|
if (!isAuthorized) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-gray-900 text-white p-4">
|
|
<div className="text-center space-y-3">
|
|
<ShieldAlert className="mx-auto text-red-500 animate-pulse" size={48} />
|
|
<h2 className="text-xl font-bold">Memvalidasi Otoritas Akun...</h2>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 p-6 flex items-center justify-center">
|
|
<div className="max-w-2xl w-full bg-white border border-gray-200 rounded-3xl shadow-xl overflow-hidden">
|
|
|
|
{/* HEADER FORM (KHUSUS PORTAL ADMIN) */}
|
|
<div className="bg-gradient-to-r from-blue-700 to-indigo-800 p-6 text-white flex items-center gap-4">
|
|
<div className="p-3 bg-white/10 rounded-2xl">
|
|
<FileText size={28} />
|
|
</div>
|
|
<div>
|
|
<h1 className="text-2xl font-bold">Penerbitan BAST (Admin)</h1>
|
|
<p className="text-blue-100 text-xs mt-0.5">Petugas: {currentAdmin?.nama_lengkap} (Manajemen Inventaris)</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* BODY FORM */}
|
|
<form onSubmit={handleSubmitBAST} className="p-6 space-y-6">
|
|
|
|
{/* SELEKSI BARANG */}
|
|
<div className="space-y-2">
|
|
<label className="text-sm font-semibold text-gray-700 flex items-center gap-2">
|
|
<ClipboardList size={16} className="text-gray-400" />
|
|
1. Pilih Inventaris Barang Sekolah
|
|
</label>
|
|
<select
|
|
value={barangId}
|
|
onChange={(e) => setBarangId(e.target.value)}
|
|
className="w-full bg-white border border-gray-300 rounded-xl px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
>
|
|
<option value="">-- Cari / Pilih Barang --</option>
|
|
{daftarBarang.map((b) => (
|
|
<option key={b.barang_id} value={b.barang_id}>
|
|
{b.nama_barang}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
{/* SELEKSI PIHAK 1 (PENYERAH) */}
|
|
<div className="space-y-2">
|
|
<label className="text-sm font-semibold text-gray-700 flex items-center gap-2">
|
|
<User size={16} className="text-green-600" />
|
|
2. Pihak 1 (User Penyerah)
|
|
</label>
|
|
<select
|
|
value={userSerahId}
|
|
onChange={(e) => setUserSerahId(e.target.value)}
|
|
className="w-full bg-white border border-gray-300 rounded-xl px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
>
|
|
<option value="">-- Pilih Penyerah --</option>
|
|
{daftarUser.map((u) => (
|
|
<option key={u.user_id} value={u.user_id}>
|
|
{u.nama_lengkap} ({u.role})
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
{/* SELEKSI PIHAK 2 (PENERIMA) */}
|
|
<div className="space-y-2">
|
|
<label className="text-sm font-semibold text-gray-700 flex items-center gap-2">
|
|
<User size={16} className="text-orange-500" />
|
|
3. Pihak 2 (User Penerima)
|
|
</label>
|
|
<select
|
|
value={userTerimaId}
|
|
onChange={(e) => setUserTerimaId(e.target.value)}
|
|
className="w-full bg-white border border-gray-300 rounded-xl px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
>
|
|
<option value="">-- Pilih Penerima --</option>
|
|
{daftarUser.map((u) => (
|
|
<option key={u.user_id} value={u.user_id}>
|
|
{u.nama_lengkap} ({u.role})
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* BUTTON PEMBUATAN DOKUMEN */}
|
|
<button
|
|
type="submit"
|
|
disabled={loadingSubmit}
|
|
className="w-full bg-indigo-600 hover:bg-indigo-700 disabled:bg-indigo-300 text-white font-bold py-3 px-4 rounded-xl shadow-lg shadow-indigo-500/10 flex items-center justify-center gap-2 transition active:scale-[0.99]"
|
|
>
|
|
{loadingSubmit ? "Sedang Mendaftarkan Dokumen..." : (
|
|
<>
|
|
Terbitkan Dokumen BAST Resmi
|
|
<ArrowRight size={18} />
|
|
</>
|
|
)}
|
|
</button>
|
|
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |