Tutorial Laravel & ReactJS #16: Dashboard Petugas – Melihat Mutasi Nasabah

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?

😂 Joke petugas: "Kenapa petugas perlu lihat mutasi? Biar tahu nasabah rajin nabung atau suka jajan!" 🍭

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>
);
💡 Catatan: Struktur data dari API bisa berbeda. Sesuaikan properti (misal 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

  1. Login sebagai petugas.
  2. Klik tab "Lihat Mutasi Nasabah".
  3. Masukkan ID rekening yang valid (misal 1) dan klik tombol.
  4. Jika data ditemukan, muncul card informasi rekening dan tabel mutasi.
  5. Coba masukkan ID yang tidak valid, muncul SweetAlert error.
  6. Uji dengan rekening yang belum punya transaksi, muncul pesan "Belum ada transaksi".
😆 "Dengan fitur ini, petugas bisa jadi detektif keuangan: melacak kemana uang nasabah pergi!" 🕵️‍♀️

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
Lebih baru Lebih lama

نموذج الاتصال