first commit
This commit is contained in:
parent
527ad45ab8
commit
8bb8fa23f4
|
|
@ -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
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue