Tutorial #16: Dashboard Petugas – Melihat Mutasi Nasabah
Halo para petugas bank yang teliti! Setelah bisa melakukan transaksi setor/tarik di tutorial #15, sekarang kita akan menambahkan fitur untuk melihat mutasi (riwayat transaksi) nasabah. Petugas bisa memasukkan nomor rekening atau ID, lalu melihat semua transaksi yang pernah dilakukan nasabah tersebut. Siap jadi detektif keuangan?
Yang akan kita lakukan:
- ✅ Menambahkan tab baru di dashboard petugas: "Lihat Mutasi".
- ✅ Membuat form input untuk ID rekening atau nomor rekening.
- ✅ Mengambil data mutasi dari API (endpoint yang sudah dibuat di tutorial #6).
- ✅ Menampilkan informasi rekening (nama, nomor, saldo) dalam card.
- ✅ Menampilkan tabel mutasi dengan format yang rapi (tanggal, jenis, jumlah, keterangan, petugas).
- ✅ Menambahkan loading state dan error handling dengan SweetAlert.
Langkah 1: Menyiapkan Tab Navigasi di PetugasDashboard
Kita akan menggunakan Bootstrap tabs untuk memisahkan antara form transaksi (tutorial #15) dan fitur lihat mutasi. Modifikasi PetugasDashboard.js dengan menambahkan navigasi tab.
import React, { useState } from 'react';
import TransaksiTab from './petugas/TransaksiTab';
import MutasiTab from './petugas/MutasiTab';
function PetugasDashboard() {
const [activeTab, setActiveTab] = useState('transaksi');
return (
<div className="container mt-4">
<h2 className="mb-4">Dashboard Petugas</h2>
<ul className="nav nav-tabs">
<li className="nav-item">
<button
className={`nav-link ${activeTab === 'transaksi' ? 'active' : ''}`}
onClick={() => setActiveTab('transaksi')}
>
Transaksi Setor/Tarik
</button>
</li>
<li className="nav-item">
<button
className={`nav-link ${activeTab === 'mutasi' ? 'active' : ''}`}
onClick={() => setActiveTab('mutasi')}
>
Lihat Mutasi Nasabah
</button>
</li>
</ul>
<div className="tab-content mt-4">
{activeTab === 'transaksi' && <TransaksiTab />}
{activeTab === 'mutasi' && <MutasiTab />}
</div>
</div>
);
}
export default PetugasDashboard;
Kita akan memisahkan kode transaksi ke komponen terpisah agar lebih rapi. Buat folder src/pages/petugas/ dan dua file: TransaksiTab.js (isi dari tutorial #15) dan MutasiTab.js (yang akan kita buat sekarang).
Langkah 2: Membuat Komponen MutasiTab
Buat file src/pages/petugas/MutasiTab.js dengan struktur berikut:
import React, { useState } from 'react';
import axios from 'axios';
import Swal from 'sweetalert2';
function MutasiTab() {
const [accountId, setAccountId] = useState('');
const [accountData, setAccountData] = useState(null);
const [transactions, setTransactions] = useState([]);
const [loading, setLoading] = useState(false);
const fetchMutasi = async () => {
if (!accountId) {
Swal.fire('Peringatan', 'Masukkan ID rekening', 'warning');
return;
}
setLoading(true);
const token = localStorage.getItem('token');
try {
// Endpoint untuk mengambil mutasi berdasarkan account_id
const response = await axios.get(`http://127.0.0.1:8000/api/mutasi/${accountId}`, {
headers: { Authorization: `Bearer ${token}` }
});
// Asumsikan response berisi data rekening dan transaksi
setAccountData(response.data.account || response.data);
setTransactions(response.data.transactions || response.data.mutasi || []);
} catch (error) {
Swal.fire('Error', error.response?.data?.message || 'Gagal mengambil data', 'error');
setAccountData(null);
setTransactions([]);
} finally {
setLoading(false);
}
};
const formatRupiah = (angka) => {
return new Intl.NumberFormat('id-ID', {
style: 'currency',
currency: 'IDR',
minimumFractionDigits: 0
}).format(angka);
};
// ... render akan di bawah
}
Catatan: Endpoint /api/mutasi/{account_id} perlu dibuat di backend. Jika belum ada, buat di routes/api.php (dalam grup middleware auth:sanctum dan isPetugas) dan controller method yang sesuai. Kita asumsikan sudah ada dari tutorial #6 (bonus).
Langkah 3: Membuat Form Pencarian dan Tampilan Data
return (
<div>
<div className="card mb-4">
<div className="card-header bg-info text-white">
<h5 className="mb-0">Cari Mutasi Nasabah</h5>
</div>
<div className="card-body">
<div className="row">
<div className="col-md-8">
<input
type="number"
className="form-control"
placeholder="Masukkan ID Rekening (contoh: 1)"
value={accountId}
onChange={(e) => setAccountId(e.target.value)}
/>
</div>
<div className="col-md-4">
<button
className="btn btn-info w-100"
onClick={fetchMutasi}
disabled={loading}
>
{loading ? 'Mencari...' : 'Lihat Mutasi'}
</button>
</div>
</div>
</div>
</div>
{accountData && (
<div className="card mb-4">
<div className="card-header bg-primary text-white">
<h5 className="mb-0">Informasi Rekening</h5>
</div>
<div className="card-body">
<div className="row">
<div className="col-md-6">
<p><strong>Nama Nasabah:</strong> {accountData.nasabah || accountData.user?.name}</p>
<p><strong>No. Rekening:</strong> {accountData.account_number || accountData.no_rekening}</p>
</div>
<div className="col-md-6">
<p><strong>Saldo Saat Ini:</strong></p>
<h3 className="text-success">{formatRupiah(accountData.balance || accountData.saldo)}</h3>
</div>
</div>
</div>
</div>
)}
{transactions.length > 0 ? (
<div className="card">
<div className="card-header bg-secondary text-white">
<h5 className="mb-0">Riwayat Transaksi</h5>
</div>
<div className="card-body">
<div className="table-responsive">
<table className="table table-striped table-hover">
<thead>
<tr>
<th>Tanggal</th>
<th>Jenis</th>
<th>Jumlah</th>
<th>Keterangan</th>
<th>Petugas</th>
</tr>
</thead>
<tbody>
{transactions.map((trx, idx) => (
<tr key={idx}>
<td>{trx.tanggal || new Date(trx.created_at).toLocaleString('id-ID')}</td>
<td>
<span className={`badge ${trx.type === 'debit' || trx.jenis === 'Setor' ? 'bg-success' : 'bg-danger'}`}>
{trx.type === 'debit' ? 'Setor' : trx.type === 'kredit' ? 'Tarik' : trx.jenis}
</span>
</td>
<td className={trx.type === 'debit' || trx.jenis === 'Setor' ? 'text-success' : 'text-danger'}>
{formatRupiah(trx.amount || trx.jumlah)}
</td>
<td>{trx.description || trx.keterangan}</td>
<td>{trx.petugas?.name || trx.petugas}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
) : accountData && (
<div className="alert alert-info">Belum ada transaksi untuk rekening ini.</div>
)}
</div>
);
accountData.user.name atau accountData.nasabah) dengan response API yang sebenarnya. Di tutorial #6 kita membuat endpoint yang mengembalikan nasabah, no_rekening, saldo, dan mutasi.
Langkah 4: (Opsional) Membuat Endpoint di Backend Jika Belum Ada
Jika endpoint /api/mutasi/{account_id} belum ada, kita perlu membuatnya. Buka routes/api.php dan tambahkan:
Route::middleware(['auth:sanctum', 'isPetugas'])->group(function () {
Route::get('/mutasi/{account_id}', [App\Http\Controllers\Api\TransactionController::class, 'mutasiPetugas']);
});
Kemudian di TransactionController.php, tambahkan method:
public function mutasiPetugas($account_id)
{
$account = Account::with('user')->findOrFail($account_id);
$transactions = $account->transactions()->with('petugas')->latest()->get();
return response()->json([
'nasabah' => $account->user->name,
'no_rekening' => $account->account_number,
'saldo' => $account->balance,
'mutasi' => $transactions->map(function ($trx) {
return [
'tanggal' => $trx->created_at->format('d-m-Y H:i'),
'jenis' => $trx->type == 'debit' ? 'Setor' : 'Tarik',
'jumlah' => $trx->amount,
'keterangan' => $trx->description,
'petugas' => $trx->petugas->name ?? 'Unknown',
];
})
]);
}
Langkah 5: Uji Coba Fitur Mutasi
- Login sebagai petugas.
- Klik tab "Lihat Mutasi Nasabah".
- Masukkan ID rekening yang valid (misal 1) dan klik tombol.
- Jika data ditemukan, muncul card informasi rekening dan tabel mutasi.
- Coba masukkan ID yang tidak valid, muncul SweetAlert error.
- Uji dengan rekening yang belum punya transaksi, muncul pesan "Belum ada transaksi".
Kesimpulan
- ✅ Petugas dapat melihat mutasi nasabah dengan mudah.
- ✅ Informasi rekening dan saldo ditampilkan secara jelas.
- ✅ Tabel mutasi menggunakan Bootstrap dan badge warna.
- ✅ Error handling dan loading state membuat pengalaman lebih baik.
Di tutorial selanjutnya (#17: Fitur Ubah Password untuk Semua Level Pengguna) kita akan menambahkan halaman pengaturan password agar semua pengguna (nasabah, petugas, admin) bisa mengganti password mereka sendiri. Sampai jumpa!
Daftar Tutorial Lanjutan
- Tutorial #17: Fitur Ubah Password untuk Semua Level Pengguna
- Tutorial #18: Menambahkan Fitur Profil dan Edit Profil
- Tutorial #19: Laporan Transaksi (Admin) – Filter dan Export ke Excel/PDF
- Tutorial #20: Finalisasi dan Penyempurnaan Aplikasi