first commit

This commit is contained in:
akbarguci.pkl 2026-05-19 08:56:45 +07:00
parent e8f3995cea
commit 03229101d5
3 changed files with 251 additions and 42 deletions

10
package-lock.json generated
View File

@ -14,6 +14,7 @@
"lucide-react": "^1.14.0",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"react-icons": "^5.6.0",
"react-router-dom": "^7.14.2"
},
"devDependencies": {
@ -3521,6 +3522,15 @@
"react": "^19.2.5"
}
},
"node_modules/react-icons": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz",
"integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==",
"license": "MIT",
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-router": {
"version": "7.14.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.2.tgz",

View File

@ -16,6 +16,7 @@
"lucide-react": "^1.14.0",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"react-icons": "^5.6.0",
"react-router-dom": "^7.14.2"
},
"devDependencies": {

View File

@ -2,15 +2,25 @@ import { useState } from "react";
import axios from "axios";
import type { UserData } from "../../routes/ManajementUsers";
import { FaEdit } from "react-icons/fa";
import { MdDelete } from "react-icons/md";
interface TableProps {
dataUsers: UserData[];
onHapusUser: (user_id: number) => void;
onUserDiedit: () => void;
}
export default function UsersTable({ dataUsers, onHapusUser, onUserDiedit }: TableProps) {
export default function UsersTable({
dataUsers,
onHapusUser,
onUserDiedit,
}: TableProps) {
const [isEditOpen, setIsEditOpen] = useState(false);
const [targetId, setTargetId] = useState<number | null>(null);
const [editFormData, setEditFormData] = useState({
nama_lengkap: "",
username: "",
@ -18,11 +28,14 @@ export default function UsersTable({ dataUsers, onHapusUser, onUserDiedit }: Tab
email: "",
lembaga: "",
});
const [loading, setLoading] = useState(false);
// Buka Modal Edit dan set data default-nya ke form
// BUKA MODAL EDIT
const bukaModalEdit = (user: UserData) => {
setTargetId(user.user_id);
setEditFormData({
nama_lengkap: user.nama_lengkap,
username: user.username,
@ -30,60 +43,133 @@ export default function UsersTable({ dataUsers, onHapusUser, onUserDiedit }: Tab
email: user.email || "",
lembaga: user.lembaga || "",
});
setIsEditOpen(true);
};
const handleUpdate = async (e: React.FormEvent) => {
// UPDATE USER
const handleUpdate = async (
e: React.FormEvent
) => {
e.preventDefault();
if (!targetId) return;
setLoading(true);
try {
const respon = await axios.put(`http://localhost:5000/api/users/${targetId}`, editFormData);
const respon = await axios.put(
`http://localhost:5000/api/users/${targetId}`,
editFormData
);
if (respon.data.success) {
alert(respon.data.message);
setIsEditOpen(false);
onUserDiedit(); // Memicu refresh state di rute induk
onUserDiedit();
}
} catch (error: any) {
console.error(error);
alert(error.response?.data?.message || "Gagal memperbarui data user!");
alert(
error.response?.data?.message ||
"Gagal memperbarui data user!"
);
} finally {
setLoading(false);
}
};
return (
<div className="bg-white rounded-3xl shadow-sm border border-blue-100 overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full text-left border-collapse">
<thead>
<tr className="bg-blue-50 border-b border-blue-100 text-blue-800 font-bold text-sm">
<th className="p-4 pl-6">ID Pengguna</th>
<th className="p-4">Nama Lengkap</th>
<th className="p-4">Username</th>
<th className="p-4">Hak Akses / Role</th>
<th className="p-4">Email</th>
<th className="p-4">Lembaga / Instansi</th>
<th className="p-4 text-center pr-6">Aksi Operasional</th>
<th className="p-4 pl-6">
ID Pengguna
</th>
<th className="p-4">
Nama Lengkap
</th>
<th className="p-4">
Username
</th>
<th className="p-4">
Hak Akses / Role
</th>
<th className="p-4">
Email
</th>
<th className="p-4">
Lembaga / Instansi
</th>
<th className="p-4 text-center pr-6">
Aksi Operasional
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-100 text-sm text-gray-700">
{dataUsers.length === 0 ? (
<tr>
<td colSpan={7} className="p-8 text-center text-gray-400 font-medium">
<td
colSpan={7}
className="p-8 text-center text-gray-400 font-medium"
>
Belum ada data pengguna terdaftar atau kata kunci salah, Bang! 📭
</td>
</tr>
) : (
dataUsers.map((user) => (
<tr key={user.user_id} className="hover:bg-blue-50/40 transition">
<td className="p-4 pl-6 font-semibold text-gray-500">#{user.user_id}</td>
<td className="p-4 font-bold text-gray-900">{user.nama_lengkap}</td>
<tr
key={user.user_id}
className="hover:bg-blue-50/40 transition"
>
<td className="p-4 pl-6 font-semibold text-gray-500">
#{user.user_id}
</td>
<td className="p-4 font-bold text-gray-900">
{user.nama_lengkap}
</td>
<td className="p-4 font-mono text-gray-600 bg-gray-50/50 rounded-lg px-2 py-0.5 inline-block mt-3">
{user.username}
</td>
<td className="p-4">
<span
className={`px-3 py-1 rounded-full text-xs font-bold capitalize ${
user.role === "Admin"
@ -95,117 +181,229 @@ export default function UsersTable({ dataUsers, onHapusUser, onUserDiedit }: Tab
>
{user.role}
</span>
</td>
<td className="p-4 text-gray-500">{user.email || "-"}</td>
<td className="p-4 text-gray-600 font-medium">{user.lembaga || "-"}</td>
<td className="p-4 text-gray-500">
{user.email || "-"}
</td>
<td className="p-4 text-gray-600 font-medium">
{user.lembaga || "-"}
</td>
<td className="p-4 pr-6 text-center">
<div className="flex justify-center items-center gap-2">
{/* BUTTON EDIT */}
<button
onClick={() => bukaModalEdit(user)}
className="bg-amber-50 hover:bg-amber-100 text-amber-700 border border-amber-200 px-3 py-1.5 rounded-xl font-semibold transition"
className="flex items-center gap-2 bg-amber-50 hover:bg-amber-100 text-amber-700 border border-amber-200 px-3 py-1.5 rounded-xl font-semibold transition"
>
✏️ Edit
<FaEdit size={14} />
Edit
</button>
{/* BUTTON HAPUS */}
<button
onClick={() => onHapusUser(user.user_id)}
className="bg-red-50 hover:bg-red-100 text-red-600 border border-red-200 px-3 py-1.5 rounded-xl font-semibold transition"
onClick={() =>
onHapusUser(user.user_id)
}
className="flex items-center gap-2 bg-red-50 hover:bg-red-100 text-red-600 border border-red-200 px-3 py-1.5 rounded-xl font-semibold transition"
>
🗑️ Hapus
<MdDelete size={16} />
Hapus
</button>
</div>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
{/* MODAL POP UP FORM EDIT USER */}
{/* MODAL EDIT */}
{isEditOpen && (
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex justify-center items-center z-50 p-4">
<div className="bg-white rounded-3xl p-8 max-w-md w-full shadow-2xl border border-gray-100 text-gray-800 animate-in fade-in zoom-in-95 duration-200">
<h2 className="text-2xl font-bold text-gray-900 mb-6 flex items-center gap-2 text-amber-600">
✏️ Sunting Data Pengguna
</h2>
<form onSubmit={handleUpdate} className="space-y-4">
<form
onSubmit={handleUpdate}
className="space-y-4"
>
<div>
<label className="block text-sm font-semibold text-gray-700 mb-1">Nama Lengkap *</label>
<label className="block text-sm font-semibold text-gray-700 mb-1">
Nama Lengkap *
</label>
<input
type="text"
required
value={editFormData.nama_lengkap}
onChange={(e) => setEditFormData({ ...editFormData, nama_lengkap: e.target.value })}
onChange={(e) =>
setEditFormData({
...editFormData,
nama_lengkap: e.target.value,
})
}
className="w-full px-4 py-2.5 rounded-xl border border-gray-300 focus:ring-2 focus:ring-amber-500 focus:border-amber-500 outline-none transition"
/>
</div>
<div>
<label className="block text-sm font-semibold text-gray-700 mb-1">Username *</label>
<label className="block text-sm font-semibold text-gray-700 mb-1">
Username *
</label>
<input
type="text"
required
value={editFormData.username}
onChange={(e) => setEditFormData({ ...editFormData, username: e.target.value })}
onChange={(e) =>
setEditFormData({
...editFormData,
username: e.target.value,
})
}
className="w-full px-4 py-2.5 rounded-xl border border-gray-300 focus:ring-2 focus:ring-amber-500 focus:border-amber-500 outline-none transition bg-gray-50"
/>
</div>
<div>
<label className="block text-sm font-semibold text-gray-700 mb-1">Role / Hak Akses</label>
<label className="block text-sm font-semibold text-gray-700 mb-1">
Role / Hak Akses
</label>
<select
value={editFormData.role}
onChange={(e) => setEditFormData({ ...editFormData, role: e.target.value })}
onChange={(e) =>
setEditFormData({
...editFormData,
role: e.target.value,
})
}
className="w-full px-4 py-2.5 rounded-xl border border-gray-300 focus:ring-2 focus:ring-amber-500 focus:border-amber-500 outline-none transition bg-white"
>
<option value="Admin">Admin</option>
<option value="Petugas">Petugas</option>
<option value="Siswa">Siswa</option>
<option value="User">User Biasa</option>
<option value="Admin">
Admin
</option>
<option value="Petugas">
Petugas
</option>
<option value="Siswa">
Siswa
</option>
<option value="User">
User Biasa
</option>
</select>
</div>
<div>
<label className="block text-sm font-semibold text-gray-700 mb-1">Email</label>
<label className="block text-sm font-semibold text-gray-700 mb-1">
Email
</label>
<input
type="email"
value={editFormData.email}
onChange={(e) => setEditFormData({ ...editFormData, email: e.target.value })}
onChange={(e) =>
setEditFormData({
...editFormData,
email: e.target.value,
})
}
className="w-full px-4 py-2.5 rounded-xl border border-gray-300 focus:ring-2 focus:ring-amber-500 focus:border-amber-500 outline-none transition"
/>
</div>
<div>
<label className="block text-sm font-semibold text-gray-700 mb-1">Instansi / Lembaga</label>
<label className="block text-sm font-semibold text-gray-700 mb-1">
Instansi / Lembaga
</label>
<input
type="text"
value={editFormData.lembaga}
onChange={(e) => setEditFormData({ ...editFormData, lembaga: e.target.value })}
onChange={(e) =>
setEditFormData({
...editFormData,
lembaga: e.target.value,
})
}
className="w-full px-4 py-2.5 rounded-xl border border-gray-300 focus:ring-2 focus:ring-amber-500 focus:border-amber-500 outline-none transition"
/>
</div>
<div className="flex justify-end gap-3 pt-4 border-t border-gray-100 mt-6">
<button
type="button"
onClick={() => setIsEditOpen(false)}
onClick={() =>
setIsEditOpen(false)
}
className="px-5 py-2.5 rounded-xl border border-gray-300 font-semibold text-gray-600 hover:bg-gray-50 transition"
>
Batal
</button>
<button
type="submit"
disabled={loading}
className="px-5 py-2.5 rounded-xl bg-amber-600 font-semibold text-white hover:bg-amber-700 shadow transition disabled:opacity-50"
>
{loading ? "Memproses..." : "Perbarui Pengguna"}
{loading
? "Memproses..."
: "Perbarui Pengguna"}
</button>
</div>
</form>
</div>
</div>
)}
</div>
);
}