first commit

This commit is contained in:
akbarguci.pkl 2026-05-20 10:14:14 +07:00
parent 527ad45ab8
commit 8bb8fa23f4
3 changed files with 1141 additions and 243 deletions

View File

@ -5,6 +5,9 @@ import {
Moon, Moon,
Sun, Sun,
X, X,
User,
Mail,
Shield,
} from "lucide-react"; } from "lucide-react";
import { import {
@ -12,6 +15,8 @@ import {
useState, useState,
} from "react"; } from "react";
import axios from "axios";
export default function SettingsPage() { export default function SettingsPage() {
const [darkMode, setDarkMode] = const [darkMode, setDarkMode] =
@ -20,6 +25,17 @@ export default function SettingsPage() {
const [showAbout, setShowAbout] = const [showAbout, setShowAbout] =
useState(false); useState(false);
const [showProfile, setShowProfile] =
useState(false);
const [userData, setUserData] =
useState({
nama: "-",
username: "-",
role: "-",
gmail: "-",
});
useEffect(() => { useEffect(() => {
const savedTheme = 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 = () => { const toggleTheme = () => {
if (darkMode) { if (darkMode) {
@ -67,6 +133,9 @@ export default function SettingsPage() {
}; };
// =========================
// MENU SETTINGS
// =========================
const settingsMenu = [ const settingsMenu = [
{ {
title: "Ganti Password", title: "Ganti Password",
@ -74,6 +143,15 @@ export default function SettingsPage() {
icon: <Lock size={26} />, icon: <Lock size={26} />,
}, },
{
title: "Tentang Anda",
desc:
"Informasi akun pengguna",
icon: <User size={26} />,
action: () =>
setShowProfile(true),
},
{ {
title: "Tentang Aplikasi", title: "Tentang Aplikasi",
desc: desc:
@ -194,9 +272,9 @@ export default function SettingsPage() {
</div> </div>
{/* LOGOUT */} {/* LOGOUT */}
<div className="mt-10"> <div className="mt-10 flex justify-start">
<div <button
onClick={() => { onClick={() => {
localStorage.clear(); localStorage.clear();
@ -205,53 +283,241 @@ export default function SettingsPage() {
}} }}
className=" className="
flex
items-center
gap-3
bg-red-500 bg-red-500
hover:bg-red-600 hover:bg-red-600
text-white text-white
rounded-3xl px-5
p-6 py-3
shadow-lg rounded-2xl
cursor-pointer shadow-md
transition-all transition-all
duration-300 duration-300
w-full hover:scale-105
md:w-[320px]
" "
> >
<div className="flex items-center gap-4"> <LogOut size={20} />
<span className="font-semibold">
Logout
</span>
</button>
</div>
{/* ========================= */}
{/* MODAL PROFILE */}
{/* ========================= */}
{showProfile && (
<div className=" <div className="
w-14 fixed
h-14 inset-0
rounded-2xl bg-black/50
bg-white/20
flex flex
items-center items-center
justify-center justify-center
z-50
"> ">
<LogOut size={26} />
<div className="
bg-white
dark:bg-gray-800
w-[90%]
md:w-[500px]
rounded-3xl
p-8
shadow-2xl
relative
">
{/* CLOSE */}
<button
onClick={() =>
setShowProfile(false)
}
className="
absolute
top-5
right-5
text-gray-500
hover:text-red-500
"
>
<X size={24} />
</button>
{/* PROFILE */}
<div className="flex flex-col items-center">
<div
className="
w-32
h-32
rounded-full
bg-blue-600
flex
items-center
justify-center
shadow-xl
border-4
border-blue-300
"
>
<User
size={60}
className="text-white"
/>
</div> </div>
<div> <h1 className="
text-3xl
font-bold
text-gray-800
dark:text-white
mt-4
">
{userData.nama}
</h1>
<h2 className="text-2xl font-bold"> <p className="
Logout text-gray-500
</h2> dark:text-gray-300
mt-1
<p className="text-red-100 text-sm mt-1"> ">
Keluar dari aplikasi @{userData.username}
</p> </p>
</div> </div>
{/* INFO */}
<div className="mt-8 space-y-5">
{/* NAMA */}
<div className="
flex
items-center
gap-4
bg-gray-100
dark:bg-gray-700
p-4
rounded-2xl
">
<User
size={22}
className="text-blue-600"
/>
<div>
<p className="
text-sm
text-gray-500
dark:text-gray-300
">
Nama
</p>
<h2 className="
font-semibold
text-gray-800
dark:text-white
">
{userData.nama}
</h2>
</div>
</div>
{/* ROLE */}
<div className="
flex
items-center
gap-4
bg-gray-100
dark:bg-gray-700
p-4
rounded-2xl
">
<Shield
size={22}
className="text-green-600"
/>
<div>
<p className="
text-sm
text-gray-500
dark:text-gray-300
">
Role
</p>
<h2 className="
font-semibold
text-gray-800
dark:text-white
">
{userData.role}
</h2>
</div>
</div>
{/* EMAIL */}
<div className="
flex
items-center
gap-4
bg-gray-100
dark:bg-gray-700
p-4
rounded-2xl
">
<Mail
size={22}
className="text-red-500"
/>
<div>
<p className="
text-sm
text-gray-500
dark:text-gray-300
">
Email
</p>
<h2 className="
font-semibold
text-gray-800
dark:text-white
">
{userData.gmail}
</h2>
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
)}
{/* ========================= */}
{/* MODAL ABOUT */} {/* MODAL ABOUT */}
{/* ========================= */}
{showAbout && ( {showAbout && (
<div className=" <div className="
@ -291,7 +557,6 @@ export default function SettingsPage() {
<X size={24} /> <X size={24} />
</button> </button>
{/* TITLE */}
<h1 className=" <h1 className="
text-3xl text-3xl
font-bold font-bold
@ -301,7 +566,6 @@ export default function SettingsPage() {
Tentang Aplikasi Tentang Aplikasi
</h1> </h1>
{/* CONTENT */}
<p className=" <p className="
text-gray-700 text-gray-700
dark:text-gray-300 dark:text-gray-300
@ -311,45 +575,25 @@ export default function SettingsPage() {
<span className="font-bold"> <span className="font-bold">
School Loan School Loan
</span>{" "} </span>{" "}
adalah aplikasi peminjaman dan adalah aplikasi pengelolaan barang
pengelolaan barang sekolah berbasis dan transaksi BAST berbasis web.
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.
<br /><br /> <br /><br />
Aplikasi ini dibuat untuk Dibuat menggunakan:
mempermudah petugas sekolah dalam <br />
mengelola data peminjaman barang
seperti laptop, buku, proyektor, • React JS
alat tulis, dan perlengkapan <br />
lainnya secara modern dan • Express JS
terintegrasi. <br />
• MySQL
<br />
• Tailwind CSS
<br /><br /> <br /><br />
<span className="font-semibold"> Versi 1.0
Versi:
</span>{" "}
1.0
<br />
<span className="font-semibold">
Developer:
</span>{" "}
Akbar
<br />
<span className="font-semibold">
Framework:
</span>{" "}
React JS, Express JS, MySQL
</p> </p>

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,9 @@ export default function MasterData() {
const [showTambah, setShowTambah] = const [showTambah, setShowTambah] =
useState(false); useState(false);
const [previewQR, setPreviewQR] =
useState<string | null>(null);
const [barangList, setBarangList] = const [barangList, setBarangList] =
useState<BarangType[]>([]); useState<BarangType[]>([]);
@ -226,35 +229,6 @@ export default function MasterData() {
} }
alt={barang.nama_barang} alt={barang.nama_barang}
className="w-full h-full object-cover" 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 =
'<span class="text-4xl mb-1"><FaBox /></span><span class="text-xs font-medium">No Image</span>';
parent.appendChild(
fallback
);
}
}}
/> />
) : ( ) : (
@ -324,12 +298,34 @@ export default function MasterData() {
{/* QR */} {/* QR */}
{barang.qr_image && ( {barang.qr_image && (
<div className="shrink-0">
<img <img
src={barang.qr_image} src={barang.qr_image}
alt="QR" alt="QR"
className="w-12 h-12 rounded-lg border p-0.5 bg-white shadow-sm" onClick={() =>
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
"
/> />
</div>
)} )}
{/* STOK */} {/* STOK */}
@ -395,6 +391,76 @@ export default function MasterData() {
)} )}
{/* MODAL PREVIEW QR */}
{previewQR && (
<div
className="
fixed
inset-0
z-50
bg-black/70
flex
items-center
justify-center
p-4
"
onClick={() =>
setPreviewQR(null)
}
>
<div
className="
bg-white
p-5
rounded-3xl
shadow-2xl
relative
"
onClick={(e) =>
e.stopPropagation()
}
>
{/* BUTTON CLOSE */}
<button
onClick={() =>
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
"
>
×
</button>
{/* QR BESAR */}
<img
src={previewQR}
alt="Preview QR"
className="
w-80
h-80
object-contain
"
/>
</div>
</div>
)}
</div> </div>
); );
} }