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,
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: <Lock size={26} />,
},
{
title: "Tentang Anda",
desc:
"Informasi akun pengguna",
icon: <User size={26} />,
action: () =>
setShowProfile(true),
},
{
title: "Tentang Aplikasi",
desc:
@ -194,9 +272,9 @@ export default function SettingsPage() {
</div>
{/* LOGOUT */}
<div className="mt-10">
<div className="mt-10 flex justify-start">
<div
<button
onClick={() => {
localStorage.clear();
@ -205,53 +283,241 @@ export default function SettingsPage() {
}}
className="
flex
items-center
gap-3
bg-red-500
hover:bg-red-600
text-white
rounded-3xl
p-6
shadow-lg
cursor-pointer
px-5
py-3
rounded-2xl
shadow-md
transition-all
duration-300
w-full
md:w-[320px]
hover:scale-105
"
>
<div className="flex items-center gap-4">
<LogOut size={20} />
<span className="font-semibold">
Logout
</span>
</button>
</div>
{/* ========================= */}
{/* MODAL PROFILE */}
{/* ========================= */}
{showProfile && (
<div className="
w-14
h-14
rounded-2xl
bg-white/20
fixed
inset-0
bg-black/50
flex
items-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>
<h1 className="
text-3xl
font-bold
text-gray-800
dark:text-white
mt-4
">
{userData.nama}
</h1>
<h2 className="text-2xl font-bold">
Logout
</h2>
<p className="text-red-100 text-sm mt-1">
Keluar dari aplikasi
<p className="
text-gray-500
dark:text-gray-300
mt-1
">
@{userData.username}
</p>
</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>
)}
{/* ========================= */}
{/* MODAL ABOUT */}
{/* ========================= */}
{showAbout && (
<div className="
@ -291,7 +557,6 @@ export default function SettingsPage() {
<X size={24} />
</button>
{/* TITLE */}
<h1 className="
text-3xl
font-bold
@ -301,7 +566,6 @@ export default function SettingsPage() {
Tentang Aplikasi
</h1>
{/* CONTENT */}
<p className="
text-gray-700
dark:text-gray-300
@ -311,45 +575,25 @@ export default function SettingsPage() {
<span className="font-bold">
School Loan
</span>{" "}
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.
<br /><br />
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:
<br />
• React JS
<br />
• Express JS
<br />
• MySQL
<br />
• Tailwind CSS
<br /><br />
<span className="font-semibold">
Versi:
</span>{" "}
1.0
<br />
<span className="font-semibold">
Developer:
</span>{" "}
Akbar
<br />
<span className="font-semibold">
Framework:
</span>{" "}
React JS, Express JS, MySQL
Versi 1.0
</p>

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,9 @@ export default function MasterData() {
const [showTambah, setShowTambah] =
useState(false);
const [previewQR, setPreviewQR] =
useState<string | null>(null);
const [barangList, setBarangList] =
useState<BarangType[]>([]);
@ -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 =
'<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 */}
{barang.qr_image && (
<div className="shrink-0">
<img
src={barang.qr_image}
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 */}
@ -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>
);
}