From 8bb8fa23f426644082640b7ad78ea2f00152b7a9 Mon Sep 17 00:00:00 2001 From: "akbarguci.pkl" Date: Wed, 20 May 2026 10:14:14 +0700 Subject: [PATCH] first commit --- src/pages/SettingsPage.tsx | 392 ++++++++++++---- src/pages/TransaksiBAST.tsx | 858 ++++++++++++++++++++++++++++++------ src/routes/MasterData.tsx | 134 ++++-- 3 files changed, 1141 insertions(+), 243 deletions(-) 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 */} +
-
{ + + +
+ + {/* ========================= */} + {/* MODAL PROFILE */} + {/* ========================= */} + {showProfile && ( + +
+ +
+ p-8 + shadow-2xl + relative + "> -
+ {/* CLOSE */} + + + {/* 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.

+ +
- {/* 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} +
+ +
+ + {/* FORM */} +
+ +
+ + {[ + { + label: + "Kode Barang", + name: + "kode_barang", + }, + + { + label: + "Nama Barang", + name: + "nama_barang", + }, + + { + label: + "Kategori", + name: + "nama_kategori", + }, + + { + label: + "Lokasi", + name: + "nama_lokasi", + }, + + { + label: + "Nama Penyerah", + name: + "nama_penyerah", + }, + + { + label: + "Nama Penerima", + name: + "nama_penerima", + }, + + ].map((field) => ( + +
+ + + + + +
+ + ))} + +
+ + {/* BUTTON */} +
+ + + + + +
+ +
+ +
+
)}
+ ); } \ 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 && ( - QR +
+ + QR + 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 */} + + + {/* QR BESAR */} + Preview QR + +
+ +
+ + )} +
); } \ No newline at end of file