first commit

This commit is contained in:
akbarguci.pkl 2026-05-19 08:41:03 +07:00
parent 4518db6940
commit e8f3995cea
1 changed files with 261 additions and 58 deletions

View File

@ -29,10 +29,11 @@ export default function TransaksiBAST() {
======================================================= */
useEffect(() => {
setLoading(true);
axios
.get("http://localhost:5000/api/bast")
.then((res) => {
// PENGAMAN KREDENSIAL: Memastikan data yang masuk ke state murni bertipe Array
if (res.data && res.data.success && Array.isArray(res.data.data)) {
setDataBAST(res.data.data);
} else if (Array.isArray(res.data)) {
@ -51,23 +52,30 @@ export default function TransaksiBAST() {
}, []);
/* =======================================================
DOWNLOAD PDF (Sinkronisasi Properti Sesuai Database)
PREVIEW PDF
======================================================= */
const downloadPDF = async (item: any) => {
const doc = new jsPDF();
/* =========================
QR CODE EXTRACTION
========================= */
let qrImage = "";
try {
// Menggunakan fallback jika qr_image sudah ada di dalam object barang
const qrRes = await axios.get(
`http://localhost:5000/api/qrcode/${item.bast_id}`
);
qrImage = qrRes.data.qr;
} catch (err) {
console.log("QR Code endpoint not found, menggunakan QR default jika tersedia.");
console.log(
"QR Code endpoint not found, menggunakan QR default jika tersedia."
);
}
if (qrImage) {
@ -78,7 +86,9 @@ export default function TransaksiBAST() {
KODE DOKUMEN BAST
========================= */
doc.rect(140, 50, 50, 10);
doc.setFontSize(10);
doc.text(`BAST-#${item.bast_id}`, 143, 57);
/* =========================
@ -86,34 +96,70 @@ export default function TransaksiBAST() {
========================= */
doc.setFont("helvetica", "bold");
doc.setFontSize(15);
doc.text("BERITA ACARA", 105, 80, { align: "center" });
doc.text("SERAH TERIMA BARANG/ASSET", 105, 92, { align: "center" });
doc.text("BERITA ACARA", 105, 80, {
align: "center",
});
doc.text("SERAH TERIMA BARANG/ASSET", 105, 92, {
align: "center",
});
/* =========================
ISI SURAT KONTRAK BAST
========================= */
doc.setFont("helvetica", "normal");
doc.setFontSize(11);
// Konversi tanggal sql ke format readable lokal jika ada
const waktuBuat = item.dibuat_pada ? new Date(item.dibuat_pada).toLocaleDateString("id-ID") : "-";
doc.text(`Pada hari ini, tanggal ${waktuBuat},`, 20, 115);
doc.text(`telah dilakukan serah terima barang secara sah antara pihak:`, 20, 128);
const waktuBuat = item.dibuat_pada
? new Date(item.dibuat_pada).toLocaleDateString("id-ID")
: "-";
doc.text(
`Pada hari ini, tanggal ${waktuBuat},`,
20,
115
);
doc.text(
`telah dilakukan serah terima barang secara sah antara pihak:`,
20,
128
);
doc.setFont("helvetica", "bold");
doc.text(`[Penyerah] ${item.nama_penyerah || "-"}`, 25, 138);
doc.text(`[Penerima] ${item.nama_penerima || "-"}`, 25, 146);
doc.text(
`[Penyerah] ${item.nama_penyerah || "-"}`,
25,
138
);
doc.text(
`[Penerima] ${item.nama_penerima || "-"}`,
25,
146
);
doc.setFont("helvetica", "normal");
doc.text("Adapun rincian barang/aset CIFO yang diserahterimakan sebagai berikut:", 20, 160);
doc.text(
"Adapun rincian barang/aset CIFO yang diserahterimakan sebagai berikut:",
20,
160
);
/* =========================
TABEL RINCIAN ASET UKK
TABEL RINCIAN ASET
========================= */
autoTable(doc, {
startY: 168,
margin: { left: 20, right: 20 },
margin: {
left: 20,
right: 20,
},
styles: {
fontSize: 10,
halign: "center",
@ -121,164 +167,321 @@ export default function TransaksiBAST() {
lineWidth: 0.3,
lineColor: [0, 0, 0],
},
headStyles: {
fillColor: [255, 255, 255],
textColor: [0, 0, 0],
fontStyle: "bold",
},
bodyStyles: {
textColor: [0, 0, 0],
},
head: [["No.", "Kode Barang", "Nama Barang", "Kondisi Serah", "Kondisi Terima"]],
head: [[
"No.",
"Kode Barang",
"Nama Barang",
"Kondisi Serah",
"Kondisi Terima"
]],
body: [[
"1",
item.kode_barang || "-",
item.nama_barang || "-",
item.status_serah || "Menunggu",
"1",
item.kode_barang || "-",
item.nama_barang || "-",
item.status_serah || "Menunggu",
item.status_terima || "Menunggu"
]],
});
/* =========================
DOKUMEN KLAUSUL PENUTUP
DOKUMEN PENUTUP
========================= */
doc.setFont("helvetica", "normal");
doc.setFontSize(11);
doc.text("Demikian Berita Acara Serah Terima ini dibuat dengan sebenar-benarnya dan", 20, 220);
doc.text("dipergunakan sebagaimana mestinya untuk keperluan inventarisasi sekolah.", 20, 232);
doc.text(
"Demikian Berita Acara Serah Terima ini dibuat dengan sebenar-benarnya dan",
20,
220
);
doc.text(
"dipergunakan sebagaimana mestinya untuk keperluan inventarisasi sekolah.",
20,
232
);
/* =========================
TANDA TANGAN (TTD)
TANDA TANGAN
========================= */
doc.setFont("helvetica", "bold");
doc.text("Yang Menyerahkan,", 20, 255);
doc.text("Yang Menerima,", 125, 255);
doc.text(
"Yang Menyerahkan,",
20,
255
);
doc.text(
"Yang Menerima,",
125,
255
);
doc.setFont("helvetica", "normal");
doc.text(item.nama_penyerah || "-", 20, 285);
doc.text(item.nama_penerima || "-", 125, 285);
doc.text(
item.nama_penyerah || "-",
20,
285
);
doc.text(
item.nama_penerima || "-",
125,
285
);
/* =========================
SAVE EKSPOR FILE PDF
PREVIEW PDF DI TAB BARU
========================= */
doc.save(`BAST_CIFO_${item.bast_id}.pdf`);
const pdfBlob = doc.output("blob");
const pdfUrl = URL.createObjectURL(pdfBlob);
window.open(pdfUrl, "_blank");
};
return (
<div className="p-8 min-h-screen bg-gray-100 dark:bg-gray-900 transition-all duration-300">
{/* HEADER UTAMA */}
{/* HEADER */}
<div className="flex justify-between items-center mb-8">
<div>
<h1 className="text-4xl font-bold dark:text-white">
BAST
</h1>
<p className="text-gray-500 dark:text-gray-300 mt-2">
Data serah terima barang logistik resmi perusahaan & sekolah.
</p>
</div>
</div>
{/* RENDER LAYOUT KONDISIONAL */}
{/* RENDER DATA */}
{loading ? (
<div className="p-12 text-center bg-white dark:bg-gray-800 rounded-3xl text-blue-600 font-medium shadow-md animate-pulse">
Sedang menyinkronkan data dokumen BAST dari server CIFO... ⏳
</div>
) : dataBAST && dataBAST.length > 0 ? (
<div className="space-y-5">
{dataBAST.map((item) => (
<div
key={item.bast_id}
className="bg-white dark:bg-gray-800 rounded-3xl shadow-md p-6 border border-gray-200 dark:border-gray-700 hover:shadow-lg transition-all duration-300"
>
<div className="flex flex-col sm:flex-row justify-between items-start gap-4">
<div className="space-y-2.5 flex-1">
{/* ID BAST */}
{/* ID */}
<div className="flex items-center gap-2">
<FileText size={20} className="text-blue-600 shrink-0" />
<FileText
size={20}
className="text-blue-600 shrink-0"
/>
<h2 className="text-xl font-extrabold dark:text-white">
Dokumen BAST #{item.bast_id}
</h2>
</div>
{/* INFO UTAMA BARANG */}
{/* BARANG */}
<div>
<span className="text-xs font-mono font-bold px-2 py-0.5 bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 rounded">
{item.kode_barang || "KODE-BLANK"}
</span>
<p className="text-xl font-bold text-blue-600 dark:text-blue-400 mt-1">
{item.nama_barang}
</p>
</div>
{/* KATEGORI & LOKASI ATRIBUT */}
{/* KATEGORI */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-1.5 pt-1 text-sm text-gray-600 dark:text-gray-300">
<div className="flex items-center gap-2">
<Layers size={16} className="text-gray-400" />
<span>Kategori: <strong className="text-gray-800 dark:text-gray-100">{item.nama_kategori || "-"}</strong></span>
<Layers
size={16}
className="text-gray-400"
/>
<span>
Kategori:
<strong className="text-gray-800 dark:text-gray-100">
{" "}
{item.nama_kategori || "-"}
</strong>
</span>
</div>
<div className="flex items-center gap-2">
<MapPin size={16} className="text-gray-400" />
<span>Lokasi: <strong className="text-gray-800 dark:text-gray-100">{item.nama_lokasi || "-"}</strong></span>
<MapPin
size={16}
className="text-gray-400"
/>
<span>
Lokasi:
<strong className="text-gray-800 dark:text-gray-100">
{" "}
{item.nama_lokasi || "-"}
</strong>
</span>
</div>
</div>
{/* PETUGAS OTORISASI (PENYERAH & PENERIMA) */}
{/* USER */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-1.5 pt-1 border-t border-gray-100 dark:border-gray-700 text-sm text-gray-600 dark:text-gray-300">
<div className="flex items-center gap-2">
<BadgeCheck size={16} className="text-green-500" />
<span>Penyerah: <strong className="text-gray-800 dark:text-gray-100">{item.nama_penyerah || "-"}</strong></span>
<BadgeCheck
size={16}
className="text-green-500"
/>
<span>
Penyerah:
<strong className="text-gray-800 dark:text-gray-100">
{" "}
{item.nama_penyerah || "-"}
</strong>
</span>
</div>
<div className="flex items-center gap-2">
<User size={16} className="text-blue-500" />
<span>Penerima: <strong className="text-gray-800 dark:text-gray-100">{item.nama_penerima || "-"}</strong></span>
<User
size={16}
className="text-blue-500"
/>
<span>
Penerima:
<strong className="text-gray-800 dark:text-gray-100">
{" "}
{item.nama_penerima || "-"}
</strong>
</span>
</div>
</div>
{/* TRACKING REALTIME TIME STAMP */}
{/* TANGGAL */}
<div className="flex items-center gap-2 text-xs text-gray-400 pt-1">
<Clock3 size={14} />
<span>Dibuat Pada: {item.dibuat_pada ? new Date(item.dibuat_pada).toLocaleString("id-ID") : "-"}</span>
<span>
Dibuat Pada:{" "}
{item.dibuat_pada
? new Date(item.dibuat_pada).toLocaleString("id-ID")
: "-"}
</span>
</div>
</div>
{/* SISI KANAN CONTROL STATUS & CETAK PDF */}
{/* KANAN */}
<div className="flex sm:flex-col items-end justify-between sm:justify-start w-full sm:w-auto gap-3 self-stretch sm:self-start border-t sm:border-t-0 pt-4 sm:pt-0 border-gray-100 dark:border-gray-700">
<div className="flex flex-col gap-1 items-end">
<span className="text-[10px] uppercase font-bold text-gray-400 tracking-wider">Status Dokumen</span>
<span className="text-[10px] uppercase font-bold text-gray-400 tracking-wider">
Status Dokumen
</span>
<div className="flex gap-1.5">
<span className={`text-xs font-bold px-3 py-1 rounded-full ${
item.status_serah === "Approved" ? "bg-green-100 text-green-700" : "bg-yellow-100 text-yellow-700"
}`}>
<span
className={`text-xs font-bold px-3 py-1 rounded-full ${
item.status_serah === "Approved"
? "bg-green-100 text-green-700"
: "bg-yellow-100 text-yellow-700"
}`}
>
Serah: {item.status_serah}
</span>
<span className={`text-xs font-bold px-3 py-1 rounded-full ${
item.status_terima === "Approved" ? "bg-green-100 text-green-700" : "bg-yellow-100 text-yellow-700"
}`}>
<span
className={`text-xs font-bold px-3 py-1 rounded-full ${
item.status_terima === "Approved"
? "bg-green-100 text-green-700"
: "bg-yellow-100 text-yellow-700"
}`}
>
Terima: {item.status_terima}
</span>
</div>
</div>
<button
onClick={() => downloadPDF(item)}
className="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white font-semibold text-sm px-4 py-2.5 rounded-xl shadow-md hover:shadow-lg transition duration-200"
>
<Download size={16} />
Cetak PDF
Preview PDF
</button>
</div>
</div>
</div>
))}
</div>
) : (
<div className="p-12 text-center bg-white dark:bg-gray-800 rounded-3xl border border-gray-200 dark:border-gray-700 text-gray-400 font-medium">
Belum ada riwayat dokumen transaksi BAST terdaftar, Bang! 📦
</div>
)}
</div>
);
}