Tutorial Laravel: Membuat Sistem Pengaduan Masyarakat (Tahap 7: Manajemen Admin, Verifikasi & Tanggapan)

Setelah fitur pengajuan pengaduan dari sisi pengguna (Masyarakat) selesai pada Tahap 6, saatnya kita menyelesaikan modul terakhir yaitu Fitur Manajemen Admin.
Pada Tahap 7 ini, Admin dapat melihat seluruh laporan masuk dari masyarakat, memeriksa foto bukti, memperbarui status pengaduan (Pending, Diproses, Selesai, Ditolak), serta memberikan tanggapan/catatan penanganan.
📋 Ringkasan Tahap 7
- Tujuan: Membangun antarmuka pengelolaan pengaduan untuk Admin, merubah status laporan, dan menyajikan tanggapan balasan ke layar User.
- Fitur Utama: Update status laporan, penyampaian tanggapan Admin, serta visualisasi balasan pada riwayat pengaduan pengguna.
- Estimasi Waktu: 7 – 10 Menit.
🛠️ Langkah-Langkah Eksekusi
1. Membuat AdminComplaintController
Kita butuh controller khusus yang menangani daftar laporan masuk serta pembaruan status & tanggapan oleh Admin.
Jalankan perintah ini di terminal:
php artisan make:controller AdminComplaintController
Buka file app/Http/Controllers/AdminComplaintController.php dan isi kodenya sebagai berikut:
<?php
namespace App\Http\Controllers;
use App\Models\Complaint;
use Illuminate\Http\Request;
class AdminComplaintController extends Controller
{
// 1. Menampilkan seluruh daftar pengaduan masyarakat
public function index()
{
$complaints = Complaint::with('user')->latest()->get();
return view('admin.complaints.index', compact('complaints'));
}
// 2. Menampilkan rincian detail laporan & form tanggapan
public function show($id)
{
$complaint = Complaint::with('user')->findOrFail($id);
return view('admin.complaints.show', compact('complaint'));
}
// 3. Memperbarui status & menyimpan tanggapan dari admin
public function update(Request $request, $id)
{
$request->validate([
'status' => 'required|in:pending,processing,resolved,rejected',
'admin_response' => 'nullable|string',
]);
$complaint = Complaint::findOrFail($id);
$complaint->update([
'status' => $request->status,
'admin_response' => $request->admin_response,
]);
return redirect()->route('admin.complaints.index')
->with('success', 'Status pengaduan dan tanggapan berhasil diperbarui!');
}
}
2. Mengatur Route Admin (routes/web.php)
Buka file routes/web.php dan sesuaikan grup route khusus Admin agar terhubung ke AdminComplaintController:
use App\Http\Controllers\AdminComplaintController;
// Kelompok Route khusus Admin
Route::middleware(['auth', 'role:admin'])->prefix('admin')->group(function () {
Route::get('/dashboard', [AdminComplaintController::class, 'index'])->name('admin.dashboard');
Route::get('/complaints', [AdminComplaintController::class, 'index'])->name('admin.complaints.index');
Route::get('/complaints/{id}', [AdminComplaintController::class, 'show'])->name('admin.complaints.show');
Route::put('/complaints/{id}', [AdminComplaintController::class, 'update'])->name('admin.complaints.update');
});
3. Membuat View Daftar Pengaduan (admin/complaints/index.blade.php)
Buat folder complaints di dalam folder resources/views/admin/, lalu buat file index.blade.php:
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Admin - Kelola Pengaduan</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<!-- Navbar Navigation Admin -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="#">Admin Panel - Pengaduan</a>
<div class="d-flex align-items-center text-white">
<span class="me-3">Admin: <strong>{{ Auth::user()->name }}</strong></span>
<form action="{{ route('logout') }}" method="POST">
@csrf
<button type="submit" class="btn btn-danger btn-sm">Logout</button>
</form>
</div>
</div>
</nav>
<!-- Content Table Pengaduan -->
<div class="container mt-4 mb-5">
<h4 class="mb-3 fw-bold">Daftar Laporan Masuk Masyarakat</h4>
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show">
{{ session('success') }}
</div>
@endif
<div class="card shadow-sm border-0">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-dark">
<tr>
<th>No</th>
<th>Pelapor</th>
<th>Judul Laporan</th>
<th>Status</th>
<th>Tanggal</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($complaints as $index => $item)
<tr>
<td>{{ $index + 1 }}</td>
<td>
<strong>{{ $item->user->name }}</strong><br>
<small class="text-muted">{{ $item->user->email }}</small>
</td>
<td>{{ $item->title }}</td>
<td>
@if($item->status == 'pending')
<span class="badge bg-warning text-dark">Pending</span>
@elseif($item->status == 'processing')
<span class="badge bg-info">Diproses</span>
@elseif($item->status == 'resolved')
<span class="badge bg-success">Selesai</span>
@else
<span class="badge bg-danger">Ditolak</span>
@endif
</td>
<td>{{ $item->created_at->format('d M Y, H:i') }} WIB</td>
<td>
<a href="{{ route('admin.complaints.show', $item->id) }}" class="btn btn-primary btn-sm">Tanggapi & Detail</a>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center py-4 text-muted">Belum ada laporan pengaduan yang masuk.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</html>
4. Membuat View Detail & Form Tanggapan (admin/complaints/show.blade.php)
Buat file show.blade.php di dalam folder resources/views/admin/complaints/:
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Detail & Tanggapan Pengaduan</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-4 mb-5">
<a href="{{ route('admin.complaints.index') }}" class="btn btn-secondary mb-3">← Kembali ke Daftar</a>
<div class="row">
<!-- Kolom Detail Laporan Pengaduan -->
<div class="col-md-7 mb-4">
<div class="card shadow-sm border-0">
<div class="card-header bg-white py-3">
<h5 class="mb-0 fw-bold">Detail Laporan #{{ $complaint->id }}</h5>
</div>
<div class="card-body">
<table class="table table-borderless">
<tr>
<th width="30%">Nama Pelapor</th>
<td>: {{ $complaint->user->name }} ({{ $complaint->user->email }})</td>
</tr>
<tr>
<th>Judul Laporan</th>
<td>: {{ $complaint->title }}</td>
</tr>
<tr>
<th>Waktu Pengiriman</th>
<td>: {{ $complaint->created_at->format('d F Y, H:i') }} WIB</td>
</tr>
<tr>
<th>Isi Pengaduan</th>
<td>: {{ $complaint->description }}</td>
</tr>
</table>
@if($complaint->image)
<div class="mt-3">
<h6 class="fw-bold">Foto Bukti Lampiran:</h6>
<img src="{{ asset('storage/' . $complaint->image) }}" class="img-fluid rounded border shadow-sm" style="max-height: 350px;">
</div>
@endif
</div>
</div>
</div>
<!-- Kolom Form Tanggapan Admin -->
<div class="col-md-5">
<div class="card shadow-sm border-0">
<div class="card-header bg-primary text-white py-3">
<h5 class="mb-0 fw-bold">Proses & Beri Tanggapan</h5>
</div>
<div class="card-body">
<form action="{{ route('admin.complaints.update', $complaint->id) }}" method="POST">
@csrf
@method('PUT')
<div class="mb-3">
<label class="form-label fw-bold">Status Laporan</label>
<select name="status" class="form-select" required>
<option value="pending" {{ $complaint->status == 'pending' ? 'selected' : '' }}>Pending (Menunggu)</option>
<option value="processing" {{ $complaint->status == 'processing' ? 'selected' : '' }}>Diproses</option>
<option value="resolved" {{ $complaint->status == 'resolved' ? 'selected' : '' }}>Selesai</option>
<option value="rejected" {{ $complaint->status == 'rejected' ? 'selected' : '' }}>Ditolak</option>
</select>
</div>
<div class="mb-3">
<label class="form-label fw-bold">Catatan / Tanggapan Admin</label>
<textarea name="admin_response" class="form-control" rows="5" placeholder="Tuliskan balasan atau instruksi tindak lanjut di sini...">{{ $complaint->admin_response }}</textarea>
</div>
<button type="submit" class="btn btn-success w-100">Simpan Perubahan & Balas</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
5. Memperbarui Tampilan User (user/dashboard.blade.php)
Agar pengguna (Masyarakat) dapat melihat balasan dari Admin, buka kembali file resources/views/user/dashboard.blade.php lalu tambahkan kolom Tanggapan Admin pada tabel riwayat laporan pengguna:
<!-- Tambahkan <th>Tanggapan Admin</th> di bagian <thead> -->
<thead class="table-light">
<tr>
<th>Judul</th>
<th>Status</th>
<th>Foto</th>
<th>Tanggapan Admin</th> <!-- Tambahkan ini -->
<th>Tanggal</th>
</tr>
</thead>
<!-- Tambahkan <td> untuk menampilkan tanggapan di <tbody> -->
<tbody>
@forelse($complaints as $complaint)
<tr>
<td class="fw-bold">{{ $complaint->title }}</td>
<td>
@if($complaint->status == 'pending')
<span class="badge bg-warning text-dark">Pending</span>
@elseif($complaint->status == 'processing')
<span class="badge bg-info">Diproses</span>
@elseif($complaint->status == 'resolved')
<span class="badge bg-success">Selesai</span>
@else
<span class="badge bg-danger">Ditolak</span>
@endif
</td>
<td>
@if($complaint->image)
<a href="{{ asset('storage/' . $complaint->image) }}" target="_blank" class="btn btn-outline-secondary btn-sm">Lihat Foto</a>
@else
<span class="text-muted">-</span>
@endif
</td>
<!-- Menampilkan Tanggapan Admin -->
<td>
@if($complaint->admin_response)
<span class="text-dark">{{ $complaint->admin_response }}</span>
@else
<span class="text-muted fs-7"><em>Belum ada tanggapan</em></span>
@endif
</td>
<td>{{ $complaint->created_at->format('d/m/Y') }}</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center py-4 text-muted">Belum ada laporan pengaduan yang dibuat.</td>
</tr>
@endforelse
</tbody>
🏆 Kesimpulan Seri Tutorial
Selamat! Seluruh Sistem Pengaduan Masyarakat berbasis Framework Laravel kini telah selesai dibuat dari nol hingga siap digunakan.
Ringkasan Fitur yang Berhasil Kita Bangun:
- Tahap 1: Setup Project Laravel & Koneksi Database MySQL.
- Tahap 2: Perancangan Schema Database Multi-Role (
users&complaints). - Tahap 3: Otomatisasi Seeding Data Uji Coba (Admin & User).
- Tahap 4: Sistem Otentikasi (Login/Logout) & Proteksi Middleware Multi-Role.
- Tahap 5: Antarmuka Dashboard Khusus Admin dan User.
- Tahap 6: Form Pengajuan Pengaduan Masyarakat & Upload Foto Bukti.
- Tahap 7: Pengelolaan Status Laporan Pengaduan & Balasan Tanggapan Admin.
