diff --git a/app/Http/Controllers/ImportController.php b/app/Http/Controllers/ImportController.php new file mode 100644 index 0000000..539ebbc --- /dev/null +++ b/app/Http/Controllers/ImportController.php @@ -0,0 +1,101 @@ +validate(['file' => 'required|file|mimes:csv,txt|max:5120']); + + $rows = $this->parseCsv($request->file('file')->getRealPath()); + $imported = 0; $failed = 0; + + DB::beginTransaction(); + try { + foreach ($rows as $r) { + // [0]=ID(diabaikan) [1]=Nama [2]=Jenis [3]=Alamat [4]=Radius [5]=Jemaah [6]=Pengurus [7]=Telepon [8]=Lat [9]=Lon + if (count($r) < 10 || trim($r[1]) === '' || !is_numeric($r[8]) || !is_numeric($r[9])) { $failed++; continue; } + RumahIbadah::create([ + 'user_id' => auth()->id(), + 'nama' => $r[1], + 'jenis' => $r[2] ?: null, + 'alamat' => $r[3] ?: null, + 'radius' => is_numeric($r[4]) ? (int) $r[4] : 500, + 'jumlah_jemaah' => is_numeric($r[5]) ? (int) $r[5] : null, + 'pengurus' => $r[6] ?: null, + 'telepon' => $r[7] ?: null, + 'latitude' => (float) $r[8], + 'longitude' => (float) $r[9], + ]); + $imported++; + } + DB::commit(); + } catch (\Throwable $e) { + DB::rollBack(); + return response()->json(['message' => 'Import gagal: ' . $e->getMessage()], 422); + } + + ActivityLog::record('import', "Import CSV rumah ibadah: {$imported} berhasil, {$failed} gagal", 'ibadah'); + return response()->json(['success' => true, 'imported' => $imported, 'failed' => $failed]); + } + + public function miskin(Request $request) + { + $request->validate(['file' => 'required|file|mimes:csv,txt|max:5120']); + + $rows = $this->parseCsv($request->file('file')->getRealPath()); + $imported = 0; $failed = 0; + + DB::beginTransaction(); + try { + foreach ($rows as $r) { + // [0]=ID Rumah [1]=Nama KK [2]=Alamat [3]=KK [4]=Jiwa [5]=Pekerjaan [6]=Pendapatan [7]=Kondisi [8]=Lat [9]=Lon + if (count($r) < 10 || trim($r[0]) === '' || !is_numeric($r[8]) || !is_numeric($r[9])) { $failed++; continue; } + RumahMiskin::updateOrCreate( + ['id_rumah' => $r[0]], + [ + 'nama_kk' => $r[1] ?: null, + 'alamat' => $r[2] ?: null, + 'jumlah_kk' => is_numeric($r[3]) ? (int) $r[3] : 1, + 'jumlah_orang' => is_numeric($r[4]) ? (int) $r[4] : 1, + 'pekerjaan' => $r[5] ?: null, + 'pendapatan' => is_numeric($r[6]) ? (int) $r[6] : null, + 'kondisi_rumah' => $r[7] ?: null, + 'latitude' => (float) $r[8], + 'longitude' => (float) $r[9], + ] + ); + $imported++; + } + DB::commit(); + } catch (\Throwable $e) { + DB::rollBack(); + return response()->json(['message' => 'Import gagal: ' . $e->getMessage()], 422); + } + + ActivityLog::record('import', "Import CSV keluarga miskin: {$imported} berhasil, {$failed} gagal", 'miskin'); + return response()->json(['success' => true, 'imported' => $imported, 'failed' => $failed]); + } + + /** Baca CSV, lewati baris header, kembalikan array baris data. */ + private function parseCsv(string $path): array + { + $rows = []; + $handle = fopen($path, 'r'); + $first = true; + while (($data = fgetcsv($handle)) !== false) { + if ($first) { $first = false; continue; } // skip header + if (count($data) === 1 && trim((string) $data[0]) === '') continue; // skip blank + $rows[] = $data; + } + fclose($handle); + return $rows; + } +} diff --git a/resources/views/data.blade.php b/resources/views/data.blade.php index bc9741e..62355eb 100644 --- a/resources/views/data.blade.php +++ b/resources/views/data.blade.php @@ -103,6 +103,11 @@ Ekspor PDF @endif + @if(auth()->user()->hasRole('administrator')) + + @endif Tambah Data @@ -317,6 +322,33 @@ + + +
@@ -555,6 +587,43 @@ } } + // ── CSV IMPORT ────────────────────────────────────────────── + const importHints = { + miskin: 'ID Rumah, Nama KK, Alamat, KK, Jiwa, Pekerjaan, Pendapatan, Kondisi, Lat, Lon', + ibadah: 'ID (diabaikan), Nama, Jenis, Alamat, Radius, Jemaah, Pengurus, Telepon, Lat, Lon', + }; + document.getElementById('import_type')?.addEventListener('change', e => { + document.getElementById('import_format_hint').textContent = importHints[e.target.value]; + }); + document.addEventListener('DOMContentLoaded', () => { + const h = document.getElementById('import_format_hint'); + if (h) h.textContent = importHints.miskin; + }); + + async function submitImport() { + const type = document.getElementById('import_type').value; + const fileInput = document.getElementById('import_file'); + if (!fileInput.files.length) { showToast('Pilih file CSV dulu', 'error'); return; } + + const fd = new FormData(); + fd.append('file', fileInput.files[0]); + + const res = await fetch(`{{ url('/api/admin/import') }}/${type}`, { + method: 'POST', + headers: { 'X-CSRF-TOKEN': csrfToken }, + body: fd, + }); + const json = await res.json(); + if (json.success) { + document.getElementById('modal_import').close(); + fileInput.value = ''; + showToast(`Import selesai: ${json.imported} berhasil, ${json.failed} gagal`, 'success'); + loadAllData(); + } else { + showToast(json.message || 'Import gagal', 'error'); + } + } + // ── BULK DELETE ───────────────────────────────────────────── function toggleSelect(type, id, checked) { const set = type === 'ibadah' ? selectedIbadah : selectedMiskin; diff --git a/routes/web.php b/routes/web.php index 0d7997d..3270bb2 100644 --- a/routes/web.php +++ b/routes/web.php @@ -4,6 +4,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\ImportController; use App\Http\Controllers\MiskinController; use Illuminate\Support\Facades\Route; @@ -60,6 +61,9 @@ Route::middleware('auth')->group(function () { Route::post('/ibadah/bulk-delete', [IbadahController::class, 'bulkDestroy']); Route::post('/miskin/bulk-delete', [MiskinController::class, 'bulkDestroy']); + + Route::post('/import/ibadah', [ImportController::class, 'ibadah']); + Route::post('/import/miskin', [ImportController::class, 'miskin']); }); }); });