feat: Plan 5 — Ekspor PDF + Ekspor per Radius + Cetak Laporan
- Add ExportController: allMiskinPDF (all data) + radiusMiskinPDF
(server-side Haversine filtering per ibadah radius)
- Add two table-based PDF Blade views (dompdf-compatible, no flexbox/grid)
- Add export routes (/export/pdf, /export/pdf/radius/{id}/{radius})
with numeric route constraints
- Add "Ekspor PDF" button to data tabular view
- Add CSV + PDF radius export buttons to map analysis panel;
exportRadiusCSV() reuses existing isInside(); PDF href synced on
ibadah click and radius slider change
- 5 new tests: access control, PDF content-type, 404 + param validation
- Mark K-04 and Section 5 (Cetak & Ekspor) complete in missing_features.md
- Update README with export features
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,8 @@ Sistem mendukung pengelolaan data secara penuh:
|
||||
- **Update Lokasi (Drag & Drop)**: Marker dapat digeser (drag) untuk memperbarui koordinat lokasi di database secara real-time.
|
||||
- **Hapus Data**: Menghapus data melalui popup pada marker atau melalui tabel data.
|
||||
- **Ownership Rumah Ibadah**: Pengurus Rumah Ibadah hanya dapat mengubah atau menghapus data rumah ibadah yang mereka tambahkan sendiri. Administrator dapat mengubah semua data.
|
||||
- **Ekspor PDF**: Unduh laporan seluruh data keluarga miskin dalam format PDF dari halaman Data Tabular.
|
||||
- **Ekspor CSV/PDF per Radius**: Dari panel analisis peta, ekspor hanya data keluarga miskin yang berada dalam radius terpilih — tersedia dalam format CSV (langsung di browser) dan PDF (via server, filtering menggunakan Haversine).
|
||||
- **Data Tabular**: Halaman khusus yang menampilkan seluruh data dalam bentuk tabel dengan statistik ringkasan.
|
||||
|
||||
### 3. Dashboard Statistik & Gap Analysis
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\RumahIbadah;
|
||||
use App\Models\RumahMiskin;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ExportController extends Controller
|
||||
{
|
||||
public function allMiskinPDF()
|
||||
{
|
||||
$miskin = RumahMiskin::orderBy('kondisi_rumah')->get();
|
||||
$ibadahCount = RumahIbadah::count();
|
||||
$totalJiwa = $miskin->sum('jumlah_orang');
|
||||
$totalKK = $miskin->sum('jumlah_kk');
|
||||
|
||||
$pdf = Pdf::loadView('exports.laporan-miskin', compact('miskin', 'ibadahCount', 'totalJiwa', 'totalKK'));
|
||||
$pdf->setPaper('a4', 'landscape');
|
||||
return $pdf->download('laporan-keluarga-miskin.pdf');
|
||||
}
|
||||
|
||||
public function radiusMiskinPDF(int $ibadahId, int $radius)
|
||||
{
|
||||
$ibadah = RumahIbadah::findOrFail($ibadahId);
|
||||
$allMiskin = RumahMiskin::all();
|
||||
|
||||
$miskin = $allMiskin->filter(fn($m) => $this->haversineDistance(
|
||||
$ibadah->latitude, $ibadah->longitude,
|
||||
$m->latitude, $m->longitude
|
||||
) <= $radius);
|
||||
|
||||
$totalJiwa = $miskin->sum('jumlah_orang');
|
||||
$totalKK = $miskin->sum('jumlah_kk');
|
||||
|
||||
$pdf = Pdf::loadView('exports.laporan-radius', compact('ibadah', 'miskin', 'radius', 'totalJiwa', 'totalKK'));
|
||||
$pdf->setPaper('a4', 'portrait');
|
||||
$filename = 'laporan-radius-' . Str::slug($ibadah->nama) . '-' . $radius . 'm.pdf';
|
||||
return $pdf->download($filename);
|
||||
}
|
||||
|
||||
private function haversineDistance(float $lat1, float $lon1, float $lat2, float $lon2): float
|
||||
{
|
||||
$R = 6371000;
|
||||
$dLat = deg2rad($lat2 - $lat1);
|
||||
$dLon = deg2rad($lon2 - $lon1);
|
||||
$a = sin($dLat / 2) ** 2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon / 2) ** 2;
|
||||
return 2 * $R * asin(sqrt($a));
|
||||
}
|
||||
}
|
||||
+9
-9
@@ -2,7 +2,7 @@
|
||||
|
||||
> Berdasarkan perbandingan dokumen SKPL (`D1041231009_RadityaIndraPutranto_SKPL_SIG.docx`) dengan implementasi aktual project.
|
||||
>
|
||||
> **Terakhir diperbarui:** 2026-06-03 — setelah implementasi Dashboard Statistik + Gap Analysis (Plan 4).
|
||||
> **Terakhir diperbarui:** 2026-06-03 — setelah implementasi Ekspor PDF + per Radius (Plan 5).
|
||||
|
||||
---
|
||||
|
||||
@@ -64,7 +64,7 @@ Data bisa ditambah dan dihapus. Untuk lokasi: drag marker (ibadah & miskin) dan
|
||||
|---|---|---|
|
||||
| K-02 | **Dashboard Statistik Lengkap** — Perbandingan antar wilayah, tren waktu | ✅ Selesai |
|
||||
| K-03 | **Analisis Kesenjangan (Gap Analysis)** — Identifikasi wilayah miskin yang kurang terjangkau rumah ibadah | ✅ Selesai |
|
||||
| K-04 | **Ekspor Laporan Terfilter** — Ekspor khusus per radius/wilayah untuk laporan resmi | ❌ Belum ada (hanya CSV semua data) |
|
||||
| K-04 | **Ekspor Laporan Terfilter** — Ekspor khusus per radius/wilayah untuk laporan resmi | ✅ Selesai |
|
||||
|
||||
---
|
||||
|
||||
@@ -72,9 +72,9 @@ Data bisa ditambah dan dihapus. Untuk lokasi: drag marker (ibadah & miskin) dan
|
||||
|
||||
| Fitur | Role terkait | Status |
|
||||
|---|---|---|
|
||||
| **Cetak Laporan Wilayah** — Print daftar keluarga miskin dalam radius rumah ibadah | I-04 | ❌ Belum ada |
|
||||
| **Ekspor PDF** — Format laporan resmi | K-04 | ❌ Belum ada |
|
||||
| **Ekspor per Radius** — Data yang terfilter per analisis radius | I-04, K-04 | ❌ Belum ada |
|
||||
| **Cetak Laporan Wilayah** — Print daftar keluarga miskin dalam radius rumah ibadah | I-04 | ✅ Selesai |
|
||||
| **Ekspor PDF** — Format laporan resmi | K-04 | ✅ Selesai |
|
||||
| **Ekspor per Radius** — Data yang terfilter per analisis radius | I-04, K-04 | ✅ Selesai |
|
||||
|
||||
---
|
||||
|
||||
@@ -87,9 +87,9 @@ Data bisa ditambah dan dihapus. Untuk lokasi: drag marker (ibadah & miskin) dan
|
||||
| Ownership pengurus_ibadah | ✅ 1/1 | — |
|
||||
| Fitur Admin | ✅ 1/8 | ❌ 7 fitur |
|
||||
| Fitur Edit Data | ✅ 2/2 | — |
|
||||
| Fitur Analisis/Dashboard | ✅ 2/3 | ❌ 1 fitur (K-04 ekspor) |
|
||||
| Fitur Cetak/Ekspor | — | ❌ 3 fitur |
|
||||
| **Total sisa** | | **~11 item** |
|
||||
| Fitur Analisis/Dashboard | ✅ 3/3 | — |
|
||||
| Fitur Cetak/Ekspor | ✅ 3/3 | — |
|
||||
| **Total sisa** | | **~7 item** |
|
||||
|
||||
---
|
||||
|
||||
@@ -101,5 +101,5 @@ Data bisa ditambah dan dihapus. Untuk lokasi: drag marker (ibadah & miskin) dan
|
||||
| ~~Plan 2~~ | ~~Form Edit Rumah Ibadah & Keluarga Miskin + Ownership~~ | ✅ Selesai |
|
||||
| ~~Plan 3~~ | ~~Manajemen Pengguna (Admin: CRUD user, assign role)~~ | ✅ Selesai |
|
||||
| ~~Plan 4~~ | ~~Dashboard Statistik + Gap Analysis (Pemangku Kebijakan)~~ | ✅ Selesai |
|
||||
| Plan 5 | Ekspor PDF + Ekspor per Radius + Cetak Laporan | Sedang |
|
||||
| ~~Plan 5~~ | ~~Ekspor PDF + Ekspor per Radius + Cetak Laporan~~ | ✅ Selesai |
|
||||
| Plan 6 | Fitur Admin lanjutan (Bulk delete, CSV import, Log, Backup) | Rendah |
|
||||
|
||||
@@ -99,6 +99,9 @@
|
||||
<button onclick="exportCSV()" class="btn btn-md btn-outline border-base-300 gap-2">
|
||||
<i class="fa-solid fa-file-csv"></i> Ekspor CSV
|
||||
</button>
|
||||
<a href="{{ route('export.pdf.all') }}" target="_blank" class="btn btn-md btn-outline border-base-300 gap-2">
|
||||
<i class="fa-solid fa-file-pdf text-error"></i> Ekspor PDF
|
||||
</a>
|
||||
@endif
|
||||
<a href="{{ url('/map') }}" class="btn btn-md btn-primary shadow-lg gap-2">
|
||||
<i class="fa-solid fa-plus"></i> Tambah Data
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Laporan Keluarga Miskin</title>
|
||||
<style>
|
||||
body { font-family: DejaVu Sans, sans-serif; font-size: 9px; color: #1f2937; margin: 20px; }
|
||||
h1 { font-size: 15px; text-align: center; margin: 0 0 2px 0; }
|
||||
.subtitle { text-align: center; font-size: 9px; color: #6b7280; margin: 0 0 12px 0; }
|
||||
.summary { background: #f3f4f6; padding: 6px 10px; margin-bottom: 10px; font-size: 9px; }
|
||||
table { width: 100%; border-collapse: collapse; margin-bottom: 10px; }
|
||||
th { background: #1e40af; color: #fff; padding: 5px 6px; text-align: left; font-size: 8px; text-transform: uppercase; }
|
||||
td { padding: 4px 6px; border-bottom: 1px solid #e5e7eb; font-size: 8px; vertical-align: top; }
|
||||
tr:nth-child(even) td { background: #f9fafb; }
|
||||
.footer { font-size: 7px; color: #9ca3af; text-align: right; border-top: 1px solid #e5e7eb; padding-top: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Laporan Data Keluarga Miskin</h1>
|
||||
<p class="subtitle">WebGIS Pemetaan Kemiskinan — Dicetak: {{ now()->format('d/m/Y H:i') }} WIB</p>
|
||||
|
||||
<div class="summary">
|
||||
<strong>Total Keluarga:</strong> {{ $miskin->count() }}
|
||||
<strong>Total KK:</strong> {{ $totalKK }}
|
||||
<strong>Total Jiwa:</strong> {{ $totalJiwa }}
|
||||
<strong>Total Rumah Ibadah:</strong> {{ $ibadahCount }}
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:20px">#</th>
|
||||
<th style="width:60px">ID Rumah</th>
|
||||
<th style="width:80px">Nama KK</th>
|
||||
<th>Alamat</th>
|
||||
<th style="width:20px">KK</th>
|
||||
<th style="width:20px">Jiwa</th>
|
||||
<th style="width:60px">Pekerjaan</th>
|
||||
<th style="width:50px">Kondisi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($miskin as $i => $m)
|
||||
<tr>
|
||||
<td>{{ $i + 1 }}</td>
|
||||
<td>{{ $m->id_rumah }}</td>
|
||||
<td>{{ $m->nama_kk ?: '-' }}</td>
|
||||
<td>{{ Str::limit($m->alamat, 45) }}</td>
|
||||
<td style="text-align:center">{{ $m->jumlah_kk }}</td>
|
||||
<td style="text-align:center">{{ $m->jumlah_orang }}</td>
|
||||
<td>{{ $m->pekerjaan ?: '-' }}</td>
|
||||
<td>{{ str_replace('_', ' ', $m->kondisi_rumah ?: '-') }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="8" style="text-align:center;padding:12px;">Tidak ada data</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="footer">Dokumen ini dibuat otomatis oleh sistem WebGIS Pemetaan Kemiskinan.</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,81 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Laporan Radius {{ $ibadah->nama }}</title>
|
||||
<style>
|
||||
body { font-family: DejaVu Sans, sans-serif; font-size: 9px; color: #1f2937; margin: 20px; }
|
||||
h1 { font-size: 15px; margin: 0 0 2px 0; }
|
||||
.subtitle { font-size: 9px; color: #6b7280; margin: 0 0 12px 0; }
|
||||
.info-box { border: 1px solid #d1d5db; padding: 7px 10px; margin-bottom: 10px; font-size: 9px; }
|
||||
.info-row { margin-bottom: 2px; }
|
||||
.info-label { display: inline-block; width: 100px; font-weight: bold; }
|
||||
table { width: 100%; border-collapse: collapse; }
|
||||
th { background: #1e40af; color: #fff; padding: 5px 6px; text-align: left; font-size: 8px; text-transform: uppercase; }
|
||||
td { padding: 4px 6px; border-bottom: 1px solid #e5e7eb; font-size: 8px; vertical-align: top; }
|
||||
tr:nth-child(even) td { background: #f9fafb; }
|
||||
.footer { font-size: 7px; color: #9ca3af; text-align: right; border-top: 1px solid #e5e7eb; padding-top: 4px; margin-top: 10px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Laporan Radius: {{ $ibadah->nama }}</h1>
|
||||
<p class="subtitle">WebGIS Pemetaan Kemiskinan — Dicetak: {{ now()->format('d/m/Y H:i') }} WIB</p>
|
||||
|
||||
<div class="info-box">
|
||||
<div class="info-row"><span class="info-label">Rumah Ibadah:</span> {{ $ibadah->nama }} ({{ ucfirst($ibadah->jenis ?: '-') }})</div>
|
||||
<div class="info-row"><span class="info-label">Alamat:</span> {{ $ibadah->alamat ?: '-' }}</div>
|
||||
<div class="info-row"><span class="info-label">Pengurus:</span> {{ $ibadah->pengurus ?: '-' }}</div>
|
||||
<div class="info-row"><span class="info-label">Radius Analisis:</span> {{ number_format($radius) }} meter</div>
|
||||
</div>
|
||||
|
||||
<table style="width:auto; border-collapse:collapse; margin-bottom:12px;">
|
||||
<tr>
|
||||
<td style="border:1px solid #d1d5db; padding:6px 20px; text-align:center;">
|
||||
<span style="font-size:18px; font-weight:bold; color:#1e40af; display:block;">{{ $miskin->count() }}</span>
|
||||
<span style="font-size:7px; text-transform:uppercase; color:#6b7280;">Rumah</span>
|
||||
</td>
|
||||
<td style="border:1px solid #d1d5db; padding:6px 20px; text-align:center;">
|
||||
<span style="font-size:18px; font-weight:bold; color:#dc2626; display:block;">{{ $totalKK }}</span>
|
||||
<span style="font-size:7px; text-transform:uppercase; color:#6b7280;">KK</span>
|
||||
</td>
|
||||
<td style="border:1px solid #d1d5db; padding:6px 20px; text-align:center;">
|
||||
<span style="font-size:18px; font-weight:bold; color:#d97706; display:block;">{{ $totalJiwa }}</span>
|
||||
<span style="font-size:7px; text-transform:uppercase; color:#6b7280;">Jiwa</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:20px">#</th>
|
||||
<th style="width:70px">ID Rumah</th>
|
||||
<th style="width:90px">Nama KK</th>
|
||||
<th>Alamat</th>
|
||||
<th style="width:20px">KK</th>
|
||||
<th style="width:20px">Jiwa</th>
|
||||
<th style="width:70px">Pekerjaan</th>
|
||||
<th style="width:55px">Kondisi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($miskin as $i => $m)
|
||||
<tr>
|
||||
<td>{{ $i + 1 }}</td>
|
||||
<td>{{ $m->id_rumah }}</td>
|
||||
<td>{{ $m->nama_kk ?: '-' }}</td>
|
||||
<td>{{ Str::limit($m->alamat, 40) }}</td>
|
||||
<td style="text-align:center">{{ $m->jumlah_kk }}</td>
|
||||
<td style="text-align:center">{{ $m->jumlah_orang }}</td>
|
||||
<td>{{ $m->pekerjaan ?: '-' }}</td>
|
||||
<td>{{ str_replace('_', ' ', $m->kondisi_rumah ?: '-') }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="8" style="text-align:center;padding:12px;">Tidak ada keluarga miskin dalam radius ini.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="footer">Dokumen ini dibuat otomatis oleh sistem WebGIS Pemetaan Kemiskinan.</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -191,6 +191,14 @@
|
||||
<div class="text-[8px] uppercase tracking-wider font-bold opacity-50">Jiwa</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-t border-base-200 pt-3 mt-3 flex flex-col gap-2">
|
||||
<button onclick="exportRadiusCSV()" class="btn btn-xs btn-outline gap-1 w-full">
|
||||
<i class="fa-solid fa-file-csv text-success"></i> Export CSV Radius
|
||||
</button>
|
||||
<a id="btnExportRadiusPDF" href="#" target="_blank" class="btn btn-xs btn-error btn-outline gap-1 w-full">
|
||||
<i class="fa-solid fa-file-pdf"></i> Export PDF Radius
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -666,6 +674,10 @@
|
||||
|
||||
const radius = document.getElementById('radiusSlider').value = item.radius || 500;
|
||||
updateRadius(item.latitude, item.longitude, radius);
|
||||
|
||||
// Update PDF export link
|
||||
const pdfBtn = document.getElementById('btnExportRadiusPDF');
|
||||
if (pdfBtn) pdfBtn.href = `{{ url('/export/pdf/radius') }}/${item.id}/${item.radius || 500}`;
|
||||
}
|
||||
|
||||
// ── RADIUS ANALYSIS ────────────────────────────────────────────────
|
||||
@@ -711,6 +723,26 @@
|
||||
document.getElementById('statJiwa').innerText = jiwa;
|
||||
}
|
||||
|
||||
function exportRadiusCSV() {
|
||||
if (!activeIbadah) return;
|
||||
const radius = parseInt(document.getElementById('radiusSlider').value);
|
||||
const inRadius = miskinData.filter(m =>
|
||||
isInside(m, { lat: activeIbadah.latitude, lng: activeIbadah.longitude }, radius)
|
||||
);
|
||||
|
||||
let csv = "data:text/csv;charset=utf-8,ID Rumah,Nama KK,Alamat,KK,Jiwa,Pekerjaan,Pendapatan,Kondisi\n";
|
||||
inRadius.forEach(m => {
|
||||
csv += `"${m.id_rumah}","${m.nama_kk || ''}","${(m.alamat || '').replace(/"/g, '""')}",${m.jumlah_kk},${m.jumlah_orang},"${m.pekerjaan || ''}",${m.pendapatan || 0},"${m.kondisi_rumah || ''}"\n`;
|
||||
});
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.setAttribute('href', encodeURI(csv));
|
||||
link.setAttribute('download', `radius-${activeIbadah.nama.replace(/\s+/g, '-')}-${radius}m.csv`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
function isInside(point, center, radius) {
|
||||
// center can be [lat, lng] or {lat, lng}
|
||||
const cLat = center.lat !== undefined ? center.lat : center[0];
|
||||
@@ -752,6 +784,11 @@
|
||||
// Update the local data copy so it persists in memory
|
||||
const localItem = ibadahData.find(i => i.id == activeIbadah.id);
|
||||
if (localItem) localItem.radius = newRadius;
|
||||
// Update PDF export link with new radius
|
||||
const pdfBtn = document.getElementById('btnExportRadiusPDF');
|
||||
if (pdfBtn && activeIbadah) {
|
||||
pdfBtn.href = `{{ url('/export/pdf/radius') }}/${activeIbadah.id}/${newRadius}`;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
showToast('Gagal menyimpan radius ke database', 'error');
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use App\Http\Controllers\AdminUserController;
|
||||
use App\Http\Controllers\AuthController;
|
||||
use App\Http\Controllers\ExportController;
|
||||
use App\Http\Controllers\IbadahController;
|
||||
use App\Http\Controllers\MiskinController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
@@ -20,6 +21,12 @@ Route::middleware('auth')->group(function () {
|
||||
Route::get('/data', fn() => view('data'));
|
||||
Route::get('/dashboard', fn() => view('dashboard'))->name('dashboard');
|
||||
|
||||
// Export routes — semua role authenticated bisa ekspor
|
||||
Route::get('/export/pdf', [ExportController::class, 'allMiskinPDF'])->name('export.pdf.all');
|
||||
Route::get('/export/pdf/radius/{ibadahId}/{radius}', [ExportController::class, 'radiusMiskinPDF'])
|
||||
->name('export.pdf.radius')
|
||||
->where(['ibadahId' => '[0-9]+', 'radius' => '[0-9]+']);
|
||||
|
||||
// API routes moved here for session + CSRF middleware
|
||||
Route::prefix('api')->group(function () {
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\RumahIbadah;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ExportTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
foreach (['administrator', 'pengurus_ibadah', 'petugas_pendataan', 'pemangku_kebijakan'] as $role) {
|
||||
\Spatie\Permission\Models\Role::create(['name' => $role, 'guard_name' => 'web']);
|
||||
}
|
||||
}
|
||||
|
||||
private function createUser(string $role): User
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$user->assignRole($role);
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function test_unauthenticated_cannot_export_pdf(): void
|
||||
{
|
||||
$this->get('/export/pdf')->assertRedirect('/login');
|
||||
}
|
||||
|
||||
public function test_authenticated_can_export_all_miskin_pdf(): void
|
||||
{
|
||||
$this->actingAs($this->createUser('pemangku_kebijakan'));
|
||||
|
||||
$response = $this->get('/export/pdf');
|
||||
$response->assertOk();
|
||||
$response->assertHeader('content-type', 'application/pdf');
|
||||
}
|
||||
|
||||
public function test_radius_pdf_returns_404_for_invalid_ibadah(): void
|
||||
{
|
||||
$this->actingAs($this->createUser('administrator'));
|
||||
|
||||
$this->get('/export/pdf/radius/99999/500')->assertNotFound();
|
||||
}
|
||||
|
||||
public function test_radius_pdf_returns_pdf_for_valid_ibadah(): void
|
||||
{
|
||||
$admin = $this->createUser('administrator');
|
||||
$this->actingAs($admin);
|
||||
|
||||
$ibadah = RumahIbadah::create([
|
||||
'user_id' => $admin->id,
|
||||
'nama' => 'Test Masjid',
|
||||
'jenis' => 'masjid',
|
||||
'latitude' => -7.0,
|
||||
'longitude' => 110.0,
|
||||
'radius' => 500,
|
||||
]);
|
||||
|
||||
$response = $this->get("/export/pdf/radius/{$ibadah->id}/500");
|
||||
$response->assertOk();
|
||||
$response->assertHeader('content-type', 'application/pdf');
|
||||
}
|
||||
|
||||
public function test_radius_pdf_rejects_non_numeric_params(): void
|
||||
{
|
||||
$this->actingAs($this->createUser('administrator'));
|
||||
|
||||
$this->get('/export/pdf/radius/abc/def')->assertNotFound();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user