Tutorial #14: Dashboard Admin – Manajemen Pengguna (CRUD) dengan React
Halo para admin bank! Setelah berhasil login di tutorial #13, sekarang saatnya kita membangun fitur CRUD (Create, Read, Update, Delete) untuk mengelola data admin dan petugas langsung dari dashboard. Admin bisa menambah petugas baru, mengubah data, atau menghapus akun. Siap jadi bos user?
Yang akan kita lakukan:
- ✅ Menampilkan daftar user (admin & petugas) dalam tabel Bootstrap.
- ✅ Membuat modal form untuk menambah user baru (dengan pilihan role).
- ✅ Membuat modal form untuk mengedit user (data terisi otomatis).
- ✅ Menambahkan tombol hapus dengan konfirmasi SweetAlert.
- ✅ Menangani error dari API dan menampilkan notifikasi.
- ✅ Memperbarui tabel setelah operasi CRUD.
Langkah 1: Menyiapkan State dan Fungsi di AdminDashboard
Buka file src/pages/AdminDashboard.js. Kita akan menambahkan state untuk mengelola data form, modal, dan loading.
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import Swal from 'sweetalert2';
function AdminDashboard() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [showModal, setShowModal] = useState(false);
const [editingUser, setEditingUser] = useState(null); // null = tambah, berisi data = edit
const [formData, setFormData] = useState({
name: '',
email: '',
password: '',
role_id: 2 // default petugas (2)
});
const [submitting, setSubmitting] = useState(false);
// Ambil data users saat komponen dimuat
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
const token = localStorage.getItem('token');
try {
const response = await axios.get('http://127.0.0.1:8000/api/users', {
headers: { Authorization: `Bearer ${token}` }
});
setUsers(response.data);
} catch (error) {
Swal.fire('Error', 'Gagal memuat data pengguna', 'error');
} finally {
setLoading(false);
}
};
// Hitung jumlah user berdasarkan role
const adminCount = users.filter(u => u.role.name === 'Admin').length;
const petugasCount = users.filter(u => u.role.name === 'Petugas').length;
const nasabahCount = users.filter(u => u.role.name === 'Nasabah').length;
// ... (render akan kita buat nanti)
}
Langkah 2: Fungsi untuk Membuka Modal Tambah/Edit
const handleAddClick = () => {
setEditingUser(null);
setFormData({
name: '',
email: '',
password: '',
role_id: 2
});
setShowModal(true);
};
const handleEditClick = (user) => {
setEditingUser(user);
setFormData({
name: user.name,
email: user.email,
password: '', // kosong, tidak perlu diisi ulang
role_id: user.role_id
});
setShowModal(true);
};
const handleCloseModal = () => {
setShowModal(false);
setEditingUser(null);
};
Langkah 3: Fungsi Submit (Tambah/Edit)
const handleSubmit = async (e) => {
e.preventDefault();
setSubmitting(true);
const token = localStorage.getItem('token');
try {
if (editingUser) {
// Update user
await axios.put(`http://127.0.0.1:8000/api/users/${editingUser.id}`, formData, {
headers: { Authorization: `Bearer ${token}` }
});
Swal.fire('Berhasil', 'Data user diperbarui', 'success');
} else {
// Create user
await axios.post('http://127.0.0.1:8000/api/users', formData, {
headers: { Authorization: `Bearer ${token}` }
});
Swal.fire('Berhasil', 'User baru ditambahkan', 'success');
}
handleCloseModal();
fetchUsers(); // refresh tabel
} catch (error) {
const message = error.response?.data?.message || 'Terjadi kesalahan';
Swal.fire('Gagal', message, 'error');
} finally {
setSubmitting(false);
}
};
Langkah 4: Fungsi Hapus dengan Konfirmasi
const handleDelete = (user) => {
Swal.fire({
title: 'Yakin hapus?',
text: `User ${user.name} (${user.role.name}) akan dihapus permanen.`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, hapus!',
cancelButtonText: 'Batal'
}).then(async (result) => {
if (result.isConfirmed) {
const token = localStorage.getItem('token');
try {
await axios.delete(`http://127.0.0.1:8000/api/users/${user.id}`, {
headers: { Authorization: `Bearer ${token}` }
});
Swal.fire('Terhapus!', 'User berhasil dihapus.', 'success');
fetchUsers(); // refresh
} catch (error) {
Swal.fire('Gagal', error.response?.data?.message || 'Tidak bisa dihapus', 'error');
}
}
});
};
Langkah 5: Membuat Modal Form dengan Bootstrap
Tambahkan komponen modal di dalam return (setelah tabel). Gunakan Bootstrap modal.
// Modal Form (taruh di bawah tabel)
<div className={`modal fade ${showModal ? 'show d-block' : ''}`} style={{ backgroundColor: showModal ? 'rgba(0,0,0,0.5)' : 'transparent' }} tabIndex="-1">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header bg-primary text-white">
<h5 className="modal-title">{editingUser ? 'Edit User' : 'Tambah User'}</h5>
<button type="button" className="btn-close" onClick={handleCloseModal}></button>
</div>
<form onSubmit={handleSubmit}>
<div className="modal-body">
<div className="mb-3">
<label className="form-label">Nama</label>
<input
type="text"
className="form-control"
value={formData.name}
onChange={(e) => setFormData({...formData, name: e.target.value})}
required
/>
</div>
<div className="mb-3">
<label className="form-label">Email</label>
<input
type="email"
className="form-control"
value={formData.email}
onChange={(e) => setFormData({...formData, email: e.target.value})}
required
/>
</div>
<div className="mb-3">
<label className="form-label">Password {editingUser && '(kosongkan jika tidak diubah)'}</label>
<input
type="password"
className="form-control"
value={formData.password}
onChange={(e) => setFormData({...formData, password: e.target.value})}
required={!editingUser}
/>
</div>
<div className="mb-3">
<label className="form-label">Role</label>
<select
className="form-select"
value={formData.role_id}
onChange={(e) => setFormData({...formData, role_id: parseInt(e.target.value)})}
>
<option value="1">Admin</option>
<option value="2">Petugas</option>
</select>
</div>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-secondary" onClick={handleCloseModal}>Batal</button>
<button type="submit" className="btn btn-primary" disabled={submitting}>
{submitting ? 'Menyimpan...' : 'Simpan'}
</button>
</div>
</form>
</div>
</div>
</div>
show d-block serta background overlay manual. Atau bisa juga pakai library react-bootstrap, tapi manual lebih sederhana.
Langkah 6: Memperbarui Tabel dengan Tombol Aksi
Di bagian tabel, ganti tombol edit dan hapus dengan fungsi yang sudah dibuat.
<tbody>
{users.filter(u => u.role.name !== 'Nasabah').map(user => (
<tr key={user.id}>
<td>{user.id}</td>
<td>{user.name}</td>
<td>{user.email}</td>
<td>
<span className={`badge ${user.role.name === 'Admin' ? 'bg-danger' : 'bg-success'}`}>
{user.role.name}
</span>
</td>
<td>
<button className="btn btn-sm btn-warning me-2" onClick={() => handleEditClick(user)}>Edit</button>
<button className="btn btn-sm btn-danger" onClick={() => handleDelete(user)}>Hapus</button>
</td>
</tr>
))}
</tbody>
Langkah 7: Uji Coba CRUD
- Login sebagai admin.
- Klik tombol "Tambah User" (kita tambahkan di atas tabel).
- Isi form dengan data petugas baru, submit. Muncul notifikasi sukses dan tabel bertambah.
- Klik edit pada salah satu user, ubah data, submit. Data berubah.
- Klik hapus, konfirmasi, user hilang dari tabel.
Kesimpulan
- ✅ Admin dapat mengelola user (admin & petugas) dengan CRUD penuh.
- ✅ Form modal Bootstrap memudahkan input data.
- ✅ SweetAlert memberikan konfirmasi dan notifikasi yang manis.
- ✅ Data selalu terbarui setelah operasi CRUD.
Di tutorial selanjutnya (#15: Dashboard Petugas – Melihat Mutasi Nasabah) kita akan menambahkan fitur bagi petugas untuk melihat mutasi nasabah tertentu. Sampai jumpa!
Daftar Tutorial Lanjutan
- Tutorial #15: Dashboard Petugas – Melihat Mutasi Nasabah
- Tutorial #16: Fitur Ubah Password untuk Semua Level Pengguna
- Tutorial #17: Menambahkan Fitur Profil dan Edit Profil
- Tutorial #18: Laporan Transaksi (Admin) – Filter dan Export ke Excel/PDF
- Tutorial #19: Finalisasi dan Penyempurnaan Aplikasi