diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx
index c91aa00..5910b3c 100644
--- a/src/pages/SettingsPage.tsx
+++ b/src/pages/SettingsPage.tsx
@@ -5,6 +5,9 @@ import {
Moon,
Sun,
X,
+ User,
+ Mail,
+ Shield,
} from "lucide-react";
import {
@@ -12,6 +15,8 @@ import {
useState,
} from "react";
+import axios from "axios";
+
export default function SettingsPage() {
const [darkMode, setDarkMode] =
@@ -20,6 +25,17 @@ export default function SettingsPage() {
const [showAbout, setShowAbout] =
useState(false);
+ const [showProfile, setShowProfile] =
+ useState(false);
+
+ const [userData, setUserData] =
+ useState({
+ nama: "-",
+ username: "-",
+ role: "-",
+ gmail: "-",
+ });
+
useEffect(() => {
const savedTheme =
@@ -35,8 +51,58 @@ export default function SettingsPage() {
}
+ ambilDataUser();
+
}, []);
+ // =========================
+ // AMBIL DATA USER LOGIN
+ // =========================
+ const ambilDataUser = async () => {
+
+ try {
+
+ const userId =
+ localStorage.getItem("user_id");
+
+ if (!userId) return;
+
+ const response =
+ await axios.get(
+ `http://localhost:5000/api/user/${userId}`
+ );
+
+ const user =
+ response.data.data;
+
+ setUserData({
+ nama:
+ user.nama_lengkap || "-",
+
+ username:
+ user.username || "-",
+
+ role:
+ user.role || "-",
+
+ gmail:
+ user.email || "-",
+ });
+
+ } catch (error) {
+
+ console.error(
+ "Gagal ambil data user:",
+ error
+ );
+
+ }
+
+ };
+
+ // =========================
+ // DARK MODE
+ // =========================
const toggleTheme = () => {
if (darkMode) {
@@ -67,6 +133,9 @@ export default function SettingsPage() {
};
+ // =========================
+ // MENU SETTINGS
+ // =========================
const settingsMenu = [
{
title: "Ganti Password",
@@ -74,6 +143,15 @@ export default function SettingsPage() {
icon: ,
},
+ {
+ title: "Tentang Anda",
+ desc:
+ "Informasi akun pengguna",
+ icon: ,
+ action: () =>
+ setShowProfile(true),
+ },
+
{
title: "Tentang Aplikasi",
desc:
@@ -193,55 +271,241 @@ export default function SettingsPage() {
- {/* LOGOUT */}
-
+{/* LOGOUT */}
+
-
{
+ {
- localStorage.clear();
+ localStorage.clear();
- window.location.href = "/";
+ window.location.href = "/";
- }}
- className="
- bg-red-500
- hover:bg-red-600
- text-white
+ }}
+ className="
+ flex
+ items-center
+ gap-3
+ bg-red-500
+ hover:bg-red-600
+ text-white
+ px-5
+ py-3
+ rounded-2xl
+ shadow-md
+ transition-all
+ duration-300
+ hover:scale-105
+ "
+ >
+
+
+
+
+ Logout
+
+
+
+
+
+
+ {/* ========================= */}
+ {/* MODAL PROFILE */}
+ {/* ========================= */}
+ {showProfile && (
+
+
+
+
+ p-8
+ shadow-2xl
+ relative
+ ">
-
+ {/* CLOSE */}
+
+ setShowProfile(false)
+ }
+ className="
+ absolute
+ top-5
+ right-5
+ text-gray-500
+ hover:text-red-500
+ "
+ >
+
+
+
+ {/* PROFILE */}
+
+
+
+
+
+
+
+
+
+ {userData.nama}
+
+
+
+ @{userData.username}
+
-
-
-
+ {/* INFO */}
+
-
- Logout
-
+ {/* NAMA */}
+
-
- Keluar dari aplikasi
-
+
+
+
+
+ Nama
+
+
+
+ {userData.nama}
+
+
+
+
+
+ {/* ROLE */}
+
+
+
+
+
+
+ Role
+
+
+
+ {userData.role}
+
+
+
+
+
+ {/* EMAIL */}
+
+
+
+
+
+
+ Email
+
+
+
+ {userData.gmail}
+
+
+
+
@@ -249,9 +513,11 @@ export default function SettingsPage() {
-
+ )}
+ {/* ========================= */}
{/* MODAL ABOUT */}
+ {/* ========================= */}
{showAbout && (
- {/* CONTENT */}
School Loan
{" "}
- adalah aplikasi peminjaman dan
- pengelolaan barang sekolah berbasis
- web yang digunakan untuk membantu
- proses transaksi BAST (Berita Acara
- Serah Terima), pengelolaan data
- barang, monitoring stok barang,
- serta pembuatan laporan secara
- cepat, akurat, dan efisien.
+ adalah aplikasi pengelolaan barang
+ dan transaksi BAST berbasis web.
- Aplikasi ini dibuat untuk
- mempermudah petugas sekolah dalam
- mengelola data peminjaman barang
- seperti laptop, buku, proyektor,
- alat tulis, dan perlengkapan
- lainnya secara modern dan
- terintegrasi.
+ Dibuat menggunakan:
+
+
+ • React JS
+
+ • Express JS
+
+ • MySQL
+
+ • Tailwind CSS
-
- Versi:
- {" "}
- 1.0
-
-
-
-
- Developer:
- {" "}
- Akbar
-
-
-
-
- Framework:
- {" "}
- React JS, Express JS, MySQL
+ Versi 1.0
diff --git a/src/pages/TransaksiBAST.tsx b/src/pages/TransaksiBAST.tsx
index f021a82..e7f9ca2 100644
--- a/src/pages/TransaksiBAST.tsx
+++ b/src/pages/TransaksiBAST.tsx
@@ -7,10 +7,13 @@ import {
BadgeCheck,
Download,
MapPin,
- Layers,
+ Layers,
+ Plus,
+ X,
} from "lucide-react";
import { useEffect, useState } from "react";
+
import axios from "axios";
import jsPDF from "jspdf";
@@ -22,101 +25,257 @@ import { FaHourglassEnd } from "react-icons/fa";
export default function TransaksiBAST() {
/* =======================================================
- STATE DATA DATABASE (Diberikan default array kosong [])
+ STATE
======================================================= */
- const [dataBAST, setDataBAST] = useState
([]);
- const [loading, setLoading] = useState(true);
+ const [dataBAST, setDataBAST] =
+ useState([]);
+
+ const [loading, setLoading] =
+ useState(true);
+
+ const [showModal, setShowModal] =
+ useState(false);
+
+ const [isSubmitting, setIsSubmitting] =
+ useState(false);
+
+ const [formData, setFormData] =
+ useState({
+ kode_barang: "",
+ nama_barang: "",
+ nama_kategori: "",
+ nama_lokasi: "",
+ nama_penyerah: "",
+ nama_penerima: "",
+ });
/* =======================================================
- GET DATA DARI API (Ekstraksi Jalur Data Aman)
+ FETCH DATA
======================================================= */
- useEffect(() => {
+ const fetchBAST = () => {
+
setLoading(true);
axios
.get("http://localhost:5000/api/bast")
+
.then((res) => {
- if (res.data && res.data.success && Array.isArray(res.data.data)) {
+ if (
+ res.data &&
+ res.data.success &&
+ Array.isArray(res.data.data)
+ ) {
+
setDataBAST(res.data.data);
- } else if (Array.isArray(res.data)) {
+
+ } else if (
+ Array.isArray(res.data)
+ ) {
+
setDataBAST(res.data);
+
} else {
+
setDataBAST([]);
+
}
+
})
+
.catch((err) => {
- console.error("Gagal memuat data transaksi BAST:", err);
+
+ console.error(
+ "Gagal memuat data transaksi BAST:",
+ err
+ );
+
setDataBAST([]);
+
})
+
.finally(() => {
+
setLoading(false);
+
});
+
+ };
+
+ useEffect(() => {
+
+ fetchBAST();
+
}, []);
/* =======================================================
- PREVIEW PDF
+ HANDLE INPUT
======================================================= */
- const downloadPDF = async (item: any) => {
+ const handleInputChange = (
+ e: React.ChangeEvent
+ ) => {
+
+ setFormData({
+ ...formData,
+ [e.target.name]:
+ e.target.value,
+ });
+
+ };
+
+ /* =======================================================
+ SUBMIT BAST
+ ======================================================= */
+ const handleSubmit = async (
+ e: React.FormEvent
+ ) => {
+
+ e.preventDefault();
+
+ setIsSubmitting(true);
+
+ try {
+
+ await axios.post(
+ "http://localhost:5000/api/bast",
+ formData
+ );
+
+ setShowModal(false);
+
+ setFormData({
+ kode_barang: "",
+ nama_barang: "",
+ nama_kategori: "",
+ nama_lokasi: "",
+ nama_penyerah: "",
+ nama_penerima: "",
+ });
+
+ fetchBAST();
+
+ } catch (error) {
+
+ console.error(
+ "Gagal membuat BAST:",
+ error
+ );
+
+ alert(
+ "Gagal membuat BAST"
+ );
+
+ } finally {
+
+ setIsSubmitting(false);
+
+ }
+
+ };
+
+ /* =======================================================
+ DOWNLOAD PDF
+ ======================================================= */
+ const downloadPDF = async (
+ item: any
+ ) => {
const doc = new jsPDF();
- /* =========================
- QR CODE EXTRACTION
- ========================= */
let qrImage = "";
try {
- const qrRes = await axios.get(
- `http://localhost:5000/api/qrcode/${item.bast_id}`
- );
+ const qrRes =
+ await axios.get(
+ `http://localhost:5000/api/qrcode/${item.bast_id}`
+ );
- qrImage = qrRes.data.qr;
+ qrImage =
+ qrRes.data.qr;
} catch (err) {
console.log(
- "QR Code endpoint not found, menggunakan QR default jika tersedia."
+ "QR Code endpoint not found."
);
+
}
if (qrImage) {
- doc.addImage(qrImage, "PNG", 160, 15, 30, 30);
+
+ doc.addImage(
+ qrImage,
+ "PNG",
+ 160,
+ 15,
+ 30,
+ 30
+ );
+
}
/* =========================
- KODE DOKUMEN BAST
+ HEADER
========================= */
- doc.rect(140, 50, 50, 10);
+ doc.rect(
+ 140,
+ 50,
+ 50,
+ 10
+ );
doc.setFontSize(10);
- doc.text(`BAST-#${item.bast_id}`, 143, 57);
+ doc.text(
+ `BAST-#${item.bast_id}`,
+ 143,
+ 57
+ );
+
+ doc.setFont(
+ "helvetica",
+ "bold"
+ );
- /* =========================
- JUDUL DOKUMEN
- ========================= */
- doc.setFont("helvetica", "bold");
doc.setFontSize(15);
- doc.text("BERITA ACARA", 105, 80, {
- align: "center",
- });
+ doc.text(
+ "BERITA ACARA",
+ 105,
+ 80,
+ {
+ align: "center",
+ }
+ );
- doc.text("SERAH TERIMA BARANG/ASSET", 105, 92, {
- align: "center",
- });
+ doc.text(
+ "SERAH TERIMA BARANG/ASSET",
+ 105,
+ 92,
+ {
+ align: "center",
+ }
+ );
/* =========================
- ISI SURAT KONTRAK BAST
+ ISI
========================= */
- doc.setFont("helvetica", "normal");
+ doc.setFont(
+ "helvetica",
+ "normal"
+ );
+
doc.setFontSize(11);
- const waktuBuat = item.dibuat_pada
- ? new Date(item.dibuat_pada).toLocaleDateString("id-ID")
- : "-";
+ const waktuBuat =
+ item.dibuat_pada
+ ? new Date(
+ item.dibuat_pada
+ ).toLocaleDateString(
+ "id-ID"
+ )
+ : "-";
doc.text(
`Pada hari ini, tanggal ${waktuBuat},`,
@@ -130,7 +289,10 @@ export default function TransaksiBAST() {
128
);
- doc.setFont("helvetica", "bold");
+ doc.setFont(
+ "helvetica",
+ "bold"
+ );
doc.text(
`[Penyerah] ${item.nama_penyerah || "-"}`,
@@ -144,7 +306,10 @@ export default function TransaksiBAST() {
146
);
- doc.setFont("helvetica", "normal");
+ doc.setFont(
+ "helvetica",
+ "normal"
+ );
doc.text(
"Adapun rincian barang/aset CIFO yang diserahterimakan sebagai berikut:",
@@ -153,7 +318,7 @@ export default function TransaksiBAST() {
);
/* =========================
- TABEL RINCIAN ASET
+ TABLE
========================= */
autoTable(doc, {
startY: 168,
@@ -172,13 +337,27 @@ export default function TransaksiBAST() {
},
headStyles: {
- fillColor: [255, 255, 255],
- textColor: [0, 0, 0],
+ fillColor: [
+ 255,
+ 255,
+ 255,
+ ],
+
+ textColor: [
+ 0,
+ 0,
+ 0,
+ ],
+
fontStyle: "bold",
},
bodyStyles: {
- textColor: [0, 0, 0],
+ textColor: [
+ 0,
+ 0,
+ 0,
+ ],
},
head: [[
@@ -186,7 +365,7 @@ export default function TransaksiBAST() {
"Kode Barang",
"Nama Barang",
"Kondisi Serah",
- "Kondisi Terima"
+ "Kondisi Terima",
]],
body: [[
@@ -194,15 +373,13 @@ export default function TransaksiBAST() {
item.kode_barang || "-",
item.nama_barang || "-",
item.status_serah || "Menunggu",
- item.status_terima || "Menunggu"
+ item.status_terima || "Menunggu",
]],
});
/* =========================
- DOKUMEN PENUTUP
+ FOOTER
========================= */
- doc.setFont("helvetica", "normal");
-
doc.setFontSize(11);
doc.text(
@@ -217,10 +394,10 @@ export default function TransaksiBAST() {
232
);
- /* =========================
- TANDA TANGAN
- ========================= */
- doc.setFont("helvetica", "bold");
+ doc.setFont(
+ "helvetica",
+ "bold"
+ );
doc.text(
"Yang Menyerahkan,",
@@ -234,7 +411,10 @@ export default function TransaksiBAST() {
255
);
- doc.setFont("helvetica", "normal");
+ doc.setFont(
+ "helvetica",
+ "normal"
+ );
doc.text(
item.nama_penyerah || "-",
@@ -248,44 +428,111 @@ export default function TransaksiBAST() {
285
);
- /* =========================
- PREVIEW PDF DI TAB BARU
- ========================= */
- const pdfBlob = doc.output("blob");
+ const pdfBlob =
+ doc.output("blob");
- const pdfUrl = URL.createObjectURL(pdfBlob);
+ const pdfUrl =
+ URL.createObjectURL(
+ pdfBlob
+ );
+
+ window.open(
+ pdfUrl,
+ "_blank"
+ );
- window.open(pdfUrl, "_blank");
};
return (
-
+
+
{/* HEADER */}
-
+
-
+
BAST
-
+
Data serah terima barang logistik resmi perusahaan & sekolah.
+
+ setShowModal(true)
+ }
+ className="
+ flex
+ items-center
+ gap-2
+ bg-blue-600
+ hover:bg-blue-700
+ text-white
+ font-semibold
+ px-5
+ py-2.5
+ rounded-xl
+ shadow-md
+ transition-all
+ "
+ >
+
+
+
+ Buat BAST
+
+
+
- {/* RENDER DATA */}
+ {/* LOADING */}
{loading ? (
-
- Sedang menyinkronkan data dokumen BAST dari server CIFO...
+
+
+ Sedang menyinkronkan data dokumen BAST dari server CIFO...
+
+
+
- ) : dataBAST && dataBAST.length > 0 ? (
+ ) : dataBAST &&
+ dataBAST.length > 0 ? (
@@ -293,72 +540,132 @@ export default function TransaksiBAST() {
-
+
-
+ {/* KIRI */}
+
- {/* ID */}
-
+
-
- Dokumen BAST #{item.bast_id}
+
+ Dokumen BAST #
+ {item.bast_id}
- {/* BARANG */}
-
- {item.kode_barang || "KODE-BLANK"}
+
+
+ {item.kode_barang ||
+ "KODE-BLANK"}
+
-
+
{item.nama_barang}
- {/* KATEGORI */}
-
+
-
+
Kategori:
-
+
{" "}
- {item.nama_kategori || "-"}
+ {item.nama_kategori}
-
+
Lokasi:
-
+
{" "}
- {item.nama_lokasi || "-"}
+ {item.nama_lokasi}
@@ -366,38 +673,57 @@ export default function TransaksiBAST() {
- {/* USER */}
-
+
-
+
Penyerah:
-
+
{" "}
- {item.nama_penyerah || "-"}
+ {item.nama_penyerah}
-
+
Penerima:
-
+
{" "}
- {item.nama_penerima || "-"}
+ {item.nama_penerima}
@@ -405,15 +731,25 @@ export default function TransaksiBAST() {
- {/* TANGGAL */}
-
+
- Dibuat Pada:{" "}
+ Dibuat Pada:
+ {" "}
{item.dibuat_pada
- ? new Date(item.dibuat_pada).toLocaleString("id-ID")
+ ? new Date(
+ item.dibuat_pada
+ ).toLocaleString(
+ "id-ID"
+ )
: "-"}
@@ -422,43 +758,74 @@ export default function TransaksiBAST() {
{/* KANAN */}
-
+
-
+
-
- Status Dokumen
+
+ Serah:
+ {" "}
+ {item.status_serah}
-
-
-
- Serah: {item.status_serah}
-
-
-
- Terima: {item.status_terima}
-
-
-
+
+ Terima:
+ {" "}
+ {item.status_terima}
+
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"
+ 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
+ "
>
@@ -479,12 +846,233 @@ export default function TransaksiBAST() {
) : (
-
- Belum ada riwayat dokumen transaksi BAST terdaftar, Bang!
+
+
+ Belum ada riwayat dokumen transaksi BAST terdaftar, Bang!
+
+
+
+
+
+ )}
+
+ {/* MODAL */}
+ {showModal && (
+
+
+
+
+
+ {/* HEADER */}
+
+
+
+ Buat BAST Baru
+
+
+
+ setShowModal(false)
+ }
+ >
+
+
+
+
+
+
+
+ {/* FORM */}
+
+
+
+
)}
+
);
}
\ No newline at end of file
diff --git a/src/routes/MasterData.tsx b/src/routes/MasterData.tsx
index 41ce602..bf85b52 100644
--- a/src/routes/MasterData.tsx
+++ b/src/routes/MasterData.tsx
@@ -36,6 +36,9 @@ export default function MasterData() {
const [showTambah, setShowTambah] =
useState(false);
+ const [previewQR, setPreviewQR] =
+ useState(null);
+
const [barangList, setBarangList] =
useState([]);
@@ -226,35 +229,6 @@ export default function MasterData() {
}
alt={barang.nama_barang}
className="w-full h-full object-cover"
- onError={(e) => {
-
- const target =
- e.target as HTMLImageElement;
-
- target.style.display =
- "none";
-
- const parent =
- target.parentElement;
-
- if (parent) {
-
- const fallback =
- document.createElement(
- "div"
- );
-
- fallback.className =
- "flex flex-col items-center justify-center text-gray-400";
-
- fallback.innerHTML =
- 'No Image ';
-
- parent.appendChild(
- fallback
- );
- }
- }}
/>
) : (
@@ -324,11 +298,33 @@ export default function MasterData() {
{/* QR */}
{barang.qr_image && (
-
+
+
+
+ setPreviewQR(
+ barang.qr_image
+ )
+ }
+ className="
+ w-16
+ h-16
+ rounded-xl
+ border
+ border-gray-200
+ bg-white
+ p-1
+ shadow-sm
+ cursor-pointer
+ hover:scale-110
+ transition-all
+ duration-200
+ "
+ />
+
+
)}
@@ -395,6 +391,76 @@ export default function MasterData() {
)}
+ {/* MODAL PREVIEW QR */}
+ {previewQR && (
+
+
+ setPreviewQR(null)
+ }
+ >
+
+
+ e.stopPropagation()
+ }
+ >
+
+ {/* BUTTON CLOSE */}
+
+ setPreviewQR(null)
+ }
+ className="
+ absolute
+ top-3
+ right-3
+ bg-red-500
+ hover:bg-red-600
+ text-white
+ w-8
+ h-8
+ rounded-full
+ font-bold
+ "
+ >
+ ×
+
+
+ {/* QR BESAR */}
+
+
+
+
+
+
+ )}
+
);
}
\ No newline at end of file