first commit
This commit is contained in:
parent
e8f3995cea
commit
03229101d5
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue