From 0d995aee1e51481cce06a5f7177c2d619e9aed03 Mon Sep 17 00:00:00 2001 From: HeroKyou Date: Fri, 12 Jun 2026 10:14:09 +0700 Subject: [PATCH] fix: real-time reverse geocoding on drag, copy missing backend scripts to publik, update right sidebar card UI --- kemiskinan/assets/css/style.css | 150 ++++++++++--------- kemiskinan/assets/js/app.js | 36 ++++- kemiskinan/index.php | 6 + kemiskinan/koneksi.php | 3 + kemiskinan/penduduk_miskin/simpan.php | 7 +- kemiskinan/penduduk_miskin/update.php | 6 +- kemiskinan/penduduk_miskin/update_lokasi.php | 21 ++- publik/assets/css/style.css | 150 ++++++++++--------- publik/assets/js/app.js | 36 ++++- publik/fasilitas_publik/hapus.php | 22 +++ publik/fasilitas_publik/simpan.php | 48 ++++++ publik/fasilitas_publik/update.php | 41 +++++ publik/index.php | 6 + publik/koneksi.php | 15 ++ publik/penduduk_miskin/hapus.php | 22 +++ publik/penduduk_miskin/simpan.php | 80 ++++++++++ publik/penduduk_miskin/update.php | 87 +++++++++++ publik/penduduk_miskin/update_lokasi.php | 48 ++++++ 18 files changed, 624 insertions(+), 160 deletions(-) create mode 100644 publik/fasilitas_publik/hapus.php create mode 100644 publik/fasilitas_publik/simpan.php create mode 100644 publik/fasilitas_publik/update.php create mode 100644 publik/penduduk_miskin/hapus.php create mode 100644 publik/penduduk_miskin/simpan.php create mode 100644 publik/penduduk_miskin/update.php create mode 100644 publik/penduduk_miskin/update_lokasi.php diff --git a/kemiskinan/assets/css/style.css b/kemiskinan/assets/css/style.css index 55a3bf4..0264dd7 100644 --- a/kemiskinan/assets/css/style.css +++ b/kemiskinan/assets/css/style.css @@ -1211,23 +1211,22 @@ body { background: rgba(225,29,72,0.45); } -/* ─── DATA CARD (Ultra Compact) ─── */ +/* ─── DATA CARD (Premium Modern) ─── */ .data-card { - background: #ffffff; - border: 1px solid rgba(0,0,0,0.05); - border-radius: 8px; - padding: 6px 8px; + background: rgba(255, 255, 255, 0.7); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.8); + border-radius: 16px; + padding: 14px 16px; cursor: pointer; - transition: - transform 0.3s cubic-bezier(0.25, 1, 0.5, 1), - border-color 0.2s ease, - box-shadow 0.3s cubic-bezier(0.25, 1, 0.5, 1), - background-color 0.2s ease; + transition: all 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275); display: flex; flex-direction: column; - gap: 0px; + gap: 8px; position: relative; overflow: hidden; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255,255,255,0.6); flex-shrink: 0; } @@ -1235,53 +1234,61 @@ body { content: ''; position: absolute; left: 0; top: 0; bottom: 0; - width: 3px; + width: 5px; background: linear-gradient(180deg, var(--accent), #fb7185); - border-radius: 3px 0 0 3px; + border-radius: 16px 0 0 16px; opacity: 0; - transition: opacity 0.2s ease; + transform: scaleY(0); + transform-origin: center; + transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.4s ease; } .data-card:hover { - transform: translateY(-2px) scale(1.005); - border-color: rgba(225, 29, 72, 0.25); - box-shadow: 0 8px 24px rgba(225, 29, 72, 0.08), 0 2px 6px rgba(0,0,0,0.03); - background: #fafaf9; + transform: translateY(-4px); + border-color: rgba(225, 29, 72, 0.4); + background: rgba(255, 255, 255, 0.95); + box-shadow: 0 12px 30px rgba(225, 29, 72, 0.12), 0 4px 8px rgba(0,0,0,0.06); } .data-card:hover::before { opacity: 1; + transform: scaleY(1); } .data-card:active { - transform: translateY(0px) scale(0.98); - box-shadow: 0 2px 8px rgba(225, 29, 72, 0.05); + transform: translateY(1px) scale(0.98); + box-shadow: 0 4px 12px rgba(225, 29, 72, 0.08); } .data-card-header { display: flex; - align-items: flex-start; - gap: 6px; + align-items: center; + gap: 12px; } .data-card-icon-wrap { - width: 24px; - height: 24px; - border-radius: 6px; + width: 40px; + height: 40px; + border-radius: 12px; display: flex; align-items: center; justify-content: center; - font-size: 13px; + font-size: 18px; flex-shrink: 0; - box-shadow: 0 2px 4px rgba(0,0,0,0.04); + box-shadow: 0 4px 10px rgba(0,0,0,0.06); + transition: transform 0.3s ease; } -.dc-icon-spbu { background: linear-gradient(135deg, #dcfce7, #bbf7d0); } -.dc-icon-pm { background: linear-gradient(135deg, #ffe4e6, #fecdd3); } -.dc-icon-fp { background: linear-gradient(135deg, #dbeafe, #bfdbfe); } -.dc-icon-jalan { background: linear-gradient(135deg, #fef9c3, #fef08a); } -.dc-icon-poly { background: linear-gradient(135deg, #f3e8ff, #e9d5ff); } -.dc-icon-geo { background: linear-gradient(135deg, #e0f2fe, #bae6fd); } +.data-card:hover .data-card-icon-wrap { + transform: scale(1.1) rotate(5deg); +} + +.dc-icon-spbu { background: linear-gradient(135deg, #dcfce7, #bbf7d0); color: #166534; } +.dc-icon-pm { background: linear-gradient(135deg, #ffe4e6, #fecdd3); color: #991b1b; } +.dc-icon-fp { background: linear-gradient(135deg, #dbeafe, #bfdbfe); color: #1e40af; } +.dc-icon-jalan { background: linear-gradient(135deg, #fef9c3, #fef08a); color: #854d0e; } +.dc-icon-poly { background: linear-gradient(135deg, #f3e8ff, #e9d5ff); color: #6b21a8; } +.dc-icon-geo { background: linear-gradient(135deg, #e0f2fe, #bae6fd); color: #075985; } .data-card-body { flex: 1; @@ -1292,69 +1299,78 @@ body { } .data-card-name { - font-size: 12.5px; - font-weight: 700; - color: #1e293b; - line-height: 1.2; + font-size: 14.5px; + font-weight: 800; + color: #0f172a; + line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - margin-top: 2px; + font-family: var(--font); } .data-card-sub { - font-size: 10.5px; - color: var(--sidebar-text-muted); + font-size: 11.5px; + color: #64748b; font-weight: 500; - margin-top: 1px; + margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .data-card-badge { - font-size: 9px; + font-size: 10px; font-weight: 800; - padding: 2px 6px; - border-radius: 99px; - letter-spacing: 0.3px; + padding: 4px 10px; + border-radius: 8px; + letter-spacing: 0.5px; text-transform: uppercase; flex-shrink: 0; align-self: flex-start; - margin-top: 2px; + box-shadow: 0 2px 6px rgba(0,0,0,0.05); } -.badge-24jam { background: #dcfce7; color: #166534; } -.badge-tidak24 { background: #fee2e2; color: #991b1b; } -.badge-dalam-jk { background: #dcfce7; color: #166534; } -.badge-luar-jk { background: #fee2e2; color: #991b1b; } -.badge-shm { background: #dcfce7; color: #166534; } -.badge-hgb { background: #dbeafe; color: #1e40af; } -.badge-hgu { background: #fef9c3; color: #854d0e; } -.badge-hp { background: #f3e8ff; color: #6b21a8; } -.badge-geo { background: #f1f5f9; color: #475569; } +.badge-24jam { background: #dcfce7; color: #166534; border: 1px solid #bbf7d0;} +.badge-tidak24 { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca;} +.badge-dalam-jk { background: #dcfce7; color: #166534; border: 1px solid #bbf7d0;} +.badge-luar-jk { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca;} +.badge-shm { background: #dcfce7; color: #166534; border: 1px solid #bbf7d0;} +.badge-hgb { background: #dbeafe; color: #1e40af; border: 1px solid #bfdbfe;} +.badge-hgu { background: #fef9c3; color: #854d0e; border: 1px solid #fef08a;} +.badge-hp { background: #f3e8ff; color: #6b21a8; border: 1px solid #e9d5ff;} +.badge-geo { background: #f1f5f9; color: #475569; border: 1px solid #e2e8f0;} .data-card-coords { - font-size: 9.5px; - color: #94a3b8; + font-size: 11px; + color: #64748b; font-family: var(--font-mono); - font-weight: 500; - display: flex; + font-weight: 600; + display: inline-flex; align-items: center; - gap: 4px; - opacity: 0.8; - padding-left: 0; - margin-top: 2px; + gap: 6px; + background: #f8fafc; + padding: 6px 10px; + border-radius: 8px; + border: 1px solid #e2e8f0; + margin-top: 6px; + align-self: flex-start; + transition: background 0.3s ease; +} + +.data-card:hover .data-card-coords { + background: #f1f5f9; + color: #334155; } .data-card-footer { - margin-top: 4px; - padding-top: 4px; - border-top: 1px dashed rgba(0,0,0,0.06); + margin-top: 8px; + padding-top: 10px; + border-top: 1px solid rgba(0,0,0,0.04); width: 100%; } .data-card-footer span, .data-card-footer span[class^="badge-"] { - font-size: 9.5px !important; + font-size: 10px !important; } /* Tab System for Modul Utuh */ diff --git a/kemiskinan/assets/js/app.js b/kemiskinan/assets/js/app.js index 81d82d9..457714d 100644 --- a/kemiskinan/assets/js/app.js +++ b/kemiskinan/assets/js/app.js @@ -763,19 +763,27 @@ Promise.all([ ------------------------------------------------------- */ function reverseGeocode(lat, lng) { - el('fp_alamat').value = ''; - el('geocodeStatus').textContent = '\u23F3 Mencari alamat...'; + if (el('fp_alamat')) el('fp_alamat').value = ''; + if (el('pm_alamat')) el('pm_alamat').value = ''; + + const statusEl = el('geocodeStatus'); + if (statusEl) statusEl.textContent = '\u23F3 Mencari alamat...'; fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&accept-language=id`) .then(r => r.json()) .then(data => { - el('fp_alamat').value = data.display_name || 'Alamat tidak ditemukan'; - el('geocodeStatus').textContent = '\u2713'; - setTimeout(() => { el('geocodeStatus').textContent = ''; }, 2000); + const alamat = data.display_name || 'Alamat tidak ditemukan'; + if (el('fp_alamat')) el('fp_alamat').value = alamat; + if (el('pm_alamat')) el('pm_alamat').value = alamat; + if (statusEl) { + statusEl.textContent = '\u2713'; + setTimeout(() => { statusEl.textContent = ''; }, 2000); + } }) .catch(() => { - el('fp_alamat').value = 'Gagal mengambil alamat'; - el('geocodeStatus').textContent = '\u2715'; + if (el('fp_alamat')) el('fp_alamat').value = 'Gagal mengambil alamat'; + if (el('pm_alamat')) el('pm_alamat').value = 'Gagal mengambil alamat'; + if (statusEl) statusEl.textContent = '\u2715'; }); } @@ -840,6 +848,7 @@ map.on('click', e => { previewMarker = L.marker(e.latlng, { icon: createPendudukIcon(cek.status === 'Dalam Jangkauan') }).addTo(map); + reverseGeocode(lat, lng); showModal('pendudukModal'); return; } @@ -1317,7 +1326,7 @@ window.hapusData = function (tipe, id, pesan) { /* ------------------------------------------------------- GLOBAL: UPDATE LOKASI DARI DRAG MARKER ------------------------------------------------------- */ -window.updateLocation = function(tipe, id, lat, lng, nama) { +window.updateLocation = async function(tipe, id, lat, lng, nama) { const urlMap = { point: 'point/update_lokasi.php', penduduk_miskin: 'penduduk_miskin/update_lokasi.php' @@ -1332,6 +1341,17 @@ window.updateLocation = function(tipe, id, lat, lng, nama) { const cek = cekJangkauan(lat, lng); fd.append('status_jangkauan', cek.status); fd.append('fasilitas_publik_id', cek.id || ''); + + // Ambil alamat baru + try { + const r = await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&accept-language=id`); + const data = await r.json(); + if (data && data.display_name) { + fd.append('alamat', data.display_name); + } + } catch(e) { + console.warn("Gagal mendapatkan alamat saat drag"); + } } postData(urlMap[tipe], fd).then(res => { diff --git a/kemiskinan/index.php b/kemiskinan/index.php index fac3540..117fd1c 100644 --- a/kemiskinan/index.php +++ b/kemiskinan/index.php @@ -879,6 +879,12 @@ $modulTitle = $modulNames[$modul] ?? 'Analisis Spasial';
+
+ +
+ +
+
diff --git a/kemiskinan/koneksi.php b/kemiskinan/koneksi.php index a040555..51ce72d 100644 --- a/kemiskinan/koneksi.php +++ b/kemiskinan/koneksi.php @@ -61,6 +61,9 @@ try { // 5. Migrasi Tabel Penduduk Miskin (Tambah foto_rumah, foto_kk) $pm_cols = $pdo->query("SHOW COLUMNS FROM penduduk_miskin")->fetchAll(PDO::FETCH_COLUMN); + if (!in_array('alamat', $pm_cols)) { + $pdo->exec("ALTER TABLE penduduk_miskin ADD COLUMN alamat TEXT NULL AFTER fasilitas_publik_id"); + } if (!in_array('foto_rumah', $pm_cols)) { $pdo->exec("ALTER TABLE penduduk_miskin ADD COLUMN foto_rumah VARCHAR(255) NULL AFTER fasilitas_publik_id"); } diff --git a/kemiskinan/penduduk_miskin/simpan.php b/kemiskinan/penduduk_miskin/simpan.php index f728549..bb6723c 100644 --- a/kemiskinan/penduduk_miskin/simpan.php +++ b/kemiskinan/penduduk_miskin/simpan.php @@ -13,6 +13,7 @@ header('Content-Type: application/json'); $nama = trim($_POST['nama'] ?? ''); $nik = trim($_POST['nik'] ?? ''); $keterangan = trim($_POST['keterangan'] ?? ''); +$alamat = trim($_POST['alamat'] ?? ''); $latitude = trim($_POST['latitude'] ?? ''); $longitude = trim($_POST['longitude'] ?? ''); $status_jangkauan= trim($_POST['status_jangkauan']?? 'Luar Jangkauan'); @@ -62,10 +63,10 @@ $foto_kk = uploadFoto('foto_kk'); try { $stmt = $pdo->prepare( - "INSERT INTO penduduk_miskin (nama, nik, keterangan, latitude, longitude, status_jangkauan, fasilitas_publik_id, foto_rumah, foto_kk, status_bantuan) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + "INSERT INTO penduduk_miskin (nama, nik, keterangan, alamat, latitude, longitude, status_jangkauan, fasilitas_publik_id, foto_rumah, foto_kk, status_bantuan) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); - $stmt->execute([$nama, $nik, $keterangan, $latitude, $longitude, $status_jangkauan, $fp_id, $foto_rumah, $foto_kk, $status_bantuan]); + $stmt->execute([$nama, $nik, $keterangan, $alamat, $latitude, $longitude, $status_jangkauan, $fp_id, $foto_rumah, $foto_kk, $status_bantuan]); $id = $pdo->lastInsertId(); $stmtSelect = $pdo->prepare("SELECT pm.*, fp.nama AS fp_nama FROM penduduk_miskin pm LEFT JOIN fasilitas_publik fp ON pm.fasilitas_publik_id = fp.id WHERE pm.id = ?"); diff --git a/kemiskinan/penduduk_miskin/update.php b/kemiskinan/penduduk_miskin/update.php index 574bc24..80f3ac0 100644 --- a/kemiskinan/penduduk_miskin/update.php +++ b/kemiskinan/penduduk_miskin/update.php @@ -55,9 +55,11 @@ function uploadFoto($file_input_name) { $foto_rumah = uploadFoto('foto_rumah'); $foto_kk = uploadFoto('foto_kk'); +$alamat = trim($_POST['alamat'] ?? ''); + try { - $query = "UPDATE penduduk_miskin SET nama=?, nik=?, keterangan=?, latitude=?, longitude=?, status_jangkauan=?, fasilitas_publik_id=?, status_bantuan=?"; - $params = [$nama, $nik, $keterangan, $latitude, $longitude, $status_jangkauan, $fp_id, $status_bantuan]; + $query = "UPDATE penduduk_miskin SET nama=?, nik=?, keterangan=?, alamat=?, latitude=?, longitude=?, status_jangkauan=?, fasilitas_publik_id=?, status_bantuan=?"; + $params = [$nama, $nik, $keterangan, $alamat, $latitude, $longitude, $status_jangkauan, $fp_id, $status_bantuan]; if ($foto_rumah) { $query .= ", foto_rumah=?"; diff --git a/kemiskinan/penduduk_miskin/update_lokasi.php b/kemiskinan/penduduk_miskin/update_lokasi.php index 47d5fcb..ccef55e 100644 --- a/kemiskinan/penduduk_miskin/update_lokasi.php +++ b/kemiskinan/penduduk_miskin/update_lokasi.php @@ -2,7 +2,7 @@ session_start(); include '../koneksi.php'; -if (!isset($_SESSION['user_id'])) { +if (!isset($_SESSION['role']) || !in_array($_SESSION['role'], ['admin', 'petugas'])) { echo json_encode(['status' => 'error', 'message' => 'Unauthorized']); exit; } @@ -12,6 +12,7 @@ $latitude = $_POST['latitude'] ?? ''; $longitude = $_POST['longitude'] ?? ''; $status_jangkauan = $_POST['status_jangkauan'] ?? ''; $fasilitas_publik_id = $_POST['fasilitas_publik_id'] ?? null; +$alamat = $_POST['alamat'] ?? null; if (!$id || !$latitude || !$longitude) { echo json_encode(['status' => 'error', 'message' => 'Data tidak lengkap']); @@ -24,11 +25,21 @@ if ($fasilitas_publik_id === '') { try { if ($status_jangkauan !== '') { - $stmt = $pdo->prepare("UPDATE penduduk_miskin SET latitude = ?, longitude = ?, status_jangkauan = ?, fasilitas_publik_id = ? WHERE id = ?"); - $stmt->execute([$latitude, $longitude, $status_jangkauan, $fasilitas_publik_id, $id]); + if ($alamat !== null) { + $stmt = $pdo->prepare("UPDATE penduduk_miskin SET latitude = ?, longitude = ?, status_jangkauan = ?, fasilitas_publik_id = ?, alamat = ? WHERE id = ?"); + $stmt->execute([$latitude, $longitude, $status_jangkauan, $fasilitas_publik_id, $alamat, $id]); + } else { + $stmt = $pdo->prepare("UPDATE penduduk_miskin SET latitude = ?, longitude = ?, status_jangkauan = ?, fasilitas_publik_id = ? WHERE id = ?"); + $stmt->execute([$latitude, $longitude, $status_jangkauan, $fasilitas_publik_id, $id]); + } } else { - $stmt = $pdo->prepare("UPDATE penduduk_miskin SET latitude = ?, longitude = ? WHERE id = ?"); - $stmt->execute([$latitude, $longitude, $id]); + if ($alamat !== null) { + $stmt = $pdo->prepare("UPDATE penduduk_miskin SET latitude = ?, longitude = ?, alamat = ? WHERE id = ?"); + $stmt->execute([$latitude, $longitude, $alamat, $id]); + } else { + $stmt = $pdo->prepare("UPDATE penduduk_miskin SET latitude = ?, longitude = ? WHERE id = ?"); + $stmt->execute([$latitude, $longitude, $id]); + } } echo json_encode(['status' => 'success']); diff --git a/publik/assets/css/style.css b/publik/assets/css/style.css index 55a3bf4..0264dd7 100644 --- a/publik/assets/css/style.css +++ b/publik/assets/css/style.css @@ -1211,23 +1211,22 @@ body { background: rgba(225,29,72,0.45); } -/* ─── DATA CARD (Ultra Compact) ─── */ +/* ─── DATA CARD (Premium Modern) ─── */ .data-card { - background: #ffffff; - border: 1px solid rgba(0,0,0,0.05); - border-radius: 8px; - padding: 6px 8px; + background: rgba(255, 255, 255, 0.7); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.8); + border-radius: 16px; + padding: 14px 16px; cursor: pointer; - transition: - transform 0.3s cubic-bezier(0.25, 1, 0.5, 1), - border-color 0.2s ease, - box-shadow 0.3s cubic-bezier(0.25, 1, 0.5, 1), - background-color 0.2s ease; + transition: all 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.275); display: flex; flex-direction: column; - gap: 0px; + gap: 8px; position: relative; overflow: hidden; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255,255,255,0.6); flex-shrink: 0; } @@ -1235,53 +1234,61 @@ body { content: ''; position: absolute; left: 0; top: 0; bottom: 0; - width: 3px; + width: 5px; background: linear-gradient(180deg, var(--accent), #fb7185); - border-radius: 3px 0 0 3px; + border-radius: 16px 0 0 16px; opacity: 0; - transition: opacity 0.2s ease; + transform: scaleY(0); + transform-origin: center; + transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.4s ease; } .data-card:hover { - transform: translateY(-2px) scale(1.005); - border-color: rgba(225, 29, 72, 0.25); - box-shadow: 0 8px 24px rgba(225, 29, 72, 0.08), 0 2px 6px rgba(0,0,0,0.03); - background: #fafaf9; + transform: translateY(-4px); + border-color: rgba(225, 29, 72, 0.4); + background: rgba(255, 255, 255, 0.95); + box-shadow: 0 12px 30px rgba(225, 29, 72, 0.12), 0 4px 8px rgba(0,0,0,0.06); } .data-card:hover::before { opacity: 1; + transform: scaleY(1); } .data-card:active { - transform: translateY(0px) scale(0.98); - box-shadow: 0 2px 8px rgba(225, 29, 72, 0.05); + transform: translateY(1px) scale(0.98); + box-shadow: 0 4px 12px rgba(225, 29, 72, 0.08); } .data-card-header { display: flex; - align-items: flex-start; - gap: 6px; + align-items: center; + gap: 12px; } .data-card-icon-wrap { - width: 24px; - height: 24px; - border-radius: 6px; + width: 40px; + height: 40px; + border-radius: 12px; display: flex; align-items: center; justify-content: center; - font-size: 13px; + font-size: 18px; flex-shrink: 0; - box-shadow: 0 2px 4px rgba(0,0,0,0.04); + box-shadow: 0 4px 10px rgba(0,0,0,0.06); + transition: transform 0.3s ease; } -.dc-icon-spbu { background: linear-gradient(135deg, #dcfce7, #bbf7d0); } -.dc-icon-pm { background: linear-gradient(135deg, #ffe4e6, #fecdd3); } -.dc-icon-fp { background: linear-gradient(135deg, #dbeafe, #bfdbfe); } -.dc-icon-jalan { background: linear-gradient(135deg, #fef9c3, #fef08a); } -.dc-icon-poly { background: linear-gradient(135deg, #f3e8ff, #e9d5ff); } -.dc-icon-geo { background: linear-gradient(135deg, #e0f2fe, #bae6fd); } +.data-card:hover .data-card-icon-wrap { + transform: scale(1.1) rotate(5deg); +} + +.dc-icon-spbu { background: linear-gradient(135deg, #dcfce7, #bbf7d0); color: #166534; } +.dc-icon-pm { background: linear-gradient(135deg, #ffe4e6, #fecdd3); color: #991b1b; } +.dc-icon-fp { background: linear-gradient(135deg, #dbeafe, #bfdbfe); color: #1e40af; } +.dc-icon-jalan { background: linear-gradient(135deg, #fef9c3, #fef08a); color: #854d0e; } +.dc-icon-poly { background: linear-gradient(135deg, #f3e8ff, #e9d5ff); color: #6b21a8; } +.dc-icon-geo { background: linear-gradient(135deg, #e0f2fe, #bae6fd); color: #075985; } .data-card-body { flex: 1; @@ -1292,69 +1299,78 @@ body { } .data-card-name { - font-size: 12.5px; - font-weight: 700; - color: #1e293b; - line-height: 1.2; + font-size: 14.5px; + font-weight: 800; + color: #0f172a; + line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - margin-top: 2px; + font-family: var(--font); } .data-card-sub { - font-size: 10.5px; - color: var(--sidebar-text-muted); + font-size: 11.5px; + color: #64748b; font-weight: 500; - margin-top: 1px; + margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .data-card-badge { - font-size: 9px; + font-size: 10px; font-weight: 800; - padding: 2px 6px; - border-radius: 99px; - letter-spacing: 0.3px; + padding: 4px 10px; + border-radius: 8px; + letter-spacing: 0.5px; text-transform: uppercase; flex-shrink: 0; align-self: flex-start; - margin-top: 2px; + box-shadow: 0 2px 6px rgba(0,0,0,0.05); } -.badge-24jam { background: #dcfce7; color: #166534; } -.badge-tidak24 { background: #fee2e2; color: #991b1b; } -.badge-dalam-jk { background: #dcfce7; color: #166534; } -.badge-luar-jk { background: #fee2e2; color: #991b1b; } -.badge-shm { background: #dcfce7; color: #166534; } -.badge-hgb { background: #dbeafe; color: #1e40af; } -.badge-hgu { background: #fef9c3; color: #854d0e; } -.badge-hp { background: #f3e8ff; color: #6b21a8; } -.badge-geo { background: #f1f5f9; color: #475569; } +.badge-24jam { background: #dcfce7; color: #166534; border: 1px solid #bbf7d0;} +.badge-tidak24 { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca;} +.badge-dalam-jk { background: #dcfce7; color: #166534; border: 1px solid #bbf7d0;} +.badge-luar-jk { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca;} +.badge-shm { background: #dcfce7; color: #166534; border: 1px solid #bbf7d0;} +.badge-hgb { background: #dbeafe; color: #1e40af; border: 1px solid #bfdbfe;} +.badge-hgu { background: #fef9c3; color: #854d0e; border: 1px solid #fef08a;} +.badge-hp { background: #f3e8ff; color: #6b21a8; border: 1px solid #e9d5ff;} +.badge-geo { background: #f1f5f9; color: #475569; border: 1px solid #e2e8f0;} .data-card-coords { - font-size: 9.5px; - color: #94a3b8; + font-size: 11px; + color: #64748b; font-family: var(--font-mono); - font-weight: 500; - display: flex; + font-weight: 600; + display: inline-flex; align-items: center; - gap: 4px; - opacity: 0.8; - padding-left: 0; - margin-top: 2px; + gap: 6px; + background: #f8fafc; + padding: 6px 10px; + border-radius: 8px; + border: 1px solid #e2e8f0; + margin-top: 6px; + align-self: flex-start; + transition: background 0.3s ease; +} + +.data-card:hover .data-card-coords { + background: #f1f5f9; + color: #334155; } .data-card-footer { - margin-top: 4px; - padding-top: 4px; - border-top: 1px dashed rgba(0,0,0,0.06); + margin-top: 8px; + padding-top: 10px; + border-top: 1px solid rgba(0,0,0,0.04); width: 100%; } .data-card-footer span, .data-card-footer span[class^="badge-"] { - font-size: 9.5px !important; + font-size: 10px !important; } /* Tab System for Modul Utuh */ diff --git a/publik/assets/js/app.js b/publik/assets/js/app.js index 81d82d9..457714d 100644 --- a/publik/assets/js/app.js +++ b/publik/assets/js/app.js @@ -763,19 +763,27 @@ Promise.all([ ------------------------------------------------------- */ function reverseGeocode(lat, lng) { - el('fp_alamat').value = ''; - el('geocodeStatus').textContent = '\u23F3 Mencari alamat...'; + if (el('fp_alamat')) el('fp_alamat').value = ''; + if (el('pm_alamat')) el('pm_alamat').value = ''; + + const statusEl = el('geocodeStatus'); + if (statusEl) statusEl.textContent = '\u23F3 Mencari alamat...'; fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&accept-language=id`) .then(r => r.json()) .then(data => { - el('fp_alamat').value = data.display_name || 'Alamat tidak ditemukan'; - el('geocodeStatus').textContent = '\u2713'; - setTimeout(() => { el('geocodeStatus').textContent = ''; }, 2000); + const alamat = data.display_name || 'Alamat tidak ditemukan'; + if (el('fp_alamat')) el('fp_alamat').value = alamat; + if (el('pm_alamat')) el('pm_alamat').value = alamat; + if (statusEl) { + statusEl.textContent = '\u2713'; + setTimeout(() => { statusEl.textContent = ''; }, 2000); + } }) .catch(() => { - el('fp_alamat').value = 'Gagal mengambil alamat'; - el('geocodeStatus').textContent = '\u2715'; + if (el('fp_alamat')) el('fp_alamat').value = 'Gagal mengambil alamat'; + if (el('pm_alamat')) el('pm_alamat').value = 'Gagal mengambil alamat'; + if (statusEl) statusEl.textContent = '\u2715'; }); } @@ -840,6 +848,7 @@ map.on('click', e => { previewMarker = L.marker(e.latlng, { icon: createPendudukIcon(cek.status === 'Dalam Jangkauan') }).addTo(map); + reverseGeocode(lat, lng); showModal('pendudukModal'); return; } @@ -1317,7 +1326,7 @@ window.hapusData = function (tipe, id, pesan) { /* ------------------------------------------------------- GLOBAL: UPDATE LOKASI DARI DRAG MARKER ------------------------------------------------------- */ -window.updateLocation = function(tipe, id, lat, lng, nama) { +window.updateLocation = async function(tipe, id, lat, lng, nama) { const urlMap = { point: 'point/update_lokasi.php', penduduk_miskin: 'penduduk_miskin/update_lokasi.php' @@ -1332,6 +1341,17 @@ window.updateLocation = function(tipe, id, lat, lng, nama) { const cek = cekJangkauan(lat, lng); fd.append('status_jangkauan', cek.status); fd.append('fasilitas_publik_id', cek.id || ''); + + // Ambil alamat baru + try { + const r = await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&accept-language=id`); + const data = await r.json(); + if (data && data.display_name) { + fd.append('alamat', data.display_name); + } + } catch(e) { + console.warn("Gagal mendapatkan alamat saat drag"); + } } postData(urlMap[tipe], fd).then(res => { diff --git a/publik/fasilitas_publik/hapus.php b/publik/fasilitas_publik/hapus.php new file mode 100644 index 0000000..15d5b72 --- /dev/null +++ b/publik/fasilitas_publik/hapus.php @@ -0,0 +1,22 @@ +'error','message'=>'Akses ditolak. Hanya admin yang dapat menghapus data.']); + exit; +} + +include '../koneksi.php'; +header('Content-Type: application/json'); + +$id = (int)($_POST['id'] ?? $_GET['id'] ?? 0); +if (!$id) { echo json_encode(['status'=>'error','message'=>'ID tidak valid.']); exit; } + +try { + $pdo->prepare("DELETE FROM fasilitas_publik WHERE id=?")->execute([$id]); + echo json_encode(['status'=>'success']); +} catch (PDOException $e) { + echo json_encode(['status'=>'error','message'=>$e->getMessage()]); +} +?> diff --git a/publik/fasilitas_publik/simpan.php b/publik/fasilitas_publik/simpan.php new file mode 100644 index 0000000..ca50112 --- /dev/null +++ b/publik/fasilitas_publik/simpan.php @@ -0,0 +1,48 @@ +'error','message'=>'Akses ditolak.']); + exit; +} + +// fasilitas_publik/simpan.php +include '../koneksi.php'; +header('Content-Type: application/json'); + +$nama = trim($_POST['nama'] ?? ''); +$jenis = trim($_POST['jenis'] ?? ''); +$alamat = trim($_POST['alamat'] ?? ''); +$radius = (int)($_POST['radius'] ?? 300); +$kontak = trim($_POST['kontak'] ?? ''); +$latitude = trim($_POST['latitude'] ?? ''); +$longitude = trim($_POST['longitude'] ?? ''); + +if (!$nama || !$jenis || !$latitude || !$longitude) { + echo json_encode(['status'=>'error','message'=>'Nama, Jenis, Latitude, dan Longitude wajib diisi.']); + exit; +} + +if (!is_numeric($latitude) || !is_numeric($longitude) || !is_numeric($radius)) { + echo json_encode(['status'=>'error','message'=>'Koordinat dan radius harus berupa angka.']); + exit; +} + +try { + $stmt = $pdo->prepare( + "INSERT INTO fasilitas_publik (nama, jenis, alamat, radius, kontak, latitude, longitude) + VALUES (?, ?, ?, ?, ?, ?, ?)" + ); + $stmt->execute([$nama, $jenis, $alamat, $radius, $kontak, $latitude, $longitude]); + $id = $pdo->lastInsertId(); + + $stmtSelect = $pdo->prepare("SELECT * FROM fasilitas_publik WHERE id = ?"); + $stmtSelect->execute([$id]); + $data = $stmtSelect->fetch(PDO::FETCH_ASSOC); + + echo json_encode(['status'=>'success','data'=>$data]); +} catch (PDOException $e) { + echo json_encode(['status'=>'error','message'=>'Gagal menyimpan: '.$e->getMessage()]); +} +?> diff --git a/publik/fasilitas_publik/update.php b/publik/fasilitas_publik/update.php new file mode 100644 index 0000000..7cb577b --- /dev/null +++ b/publik/fasilitas_publik/update.php @@ -0,0 +1,41 @@ +'error','message'=>'Akses ditolak.']); + exit; +} + +include '../koneksi.php'; +header('Content-Type: application/json'); + +$id = (int)($_POST['id'] ?? 0); +$nama = trim($_POST['nama'] ?? ''); +$jenis = trim($_POST['jenis'] ?? ''); +$alamat = trim($_POST['alamat'] ?? ''); +$radius = (int)($_POST['radius'] ?? 300); +$kontak = trim($_POST['kontak'] ?? ''); +$latitude = trim($_POST['latitude'] ?? ''); +$longitude = trim($_POST['longitude'] ?? ''); + +if (!$id || !$nama || !$jenis || !$latitude || !$longitude) { + echo json_encode(['status'=>'error','message'=>'Data tidak lengkap.']); + exit; +} + +try { + $stmt = $pdo->prepare( + "UPDATE fasilitas_publik SET nama=?, jenis=?, alamat=?, radius=?, kontak=?, latitude=?, longitude=? WHERE id=?" + ); + $stmt->execute([$nama, $jenis, $alamat, $radius, $kontak, $latitude, $longitude, $id]); + + $stmtSelect = $pdo->prepare("SELECT * FROM fasilitas_publik WHERE id=?"); + $stmtSelect->execute([$id]); + $data = $stmtSelect->fetch(PDO::FETCH_ASSOC); + + echo json_encode(['status'=>'success','data'=>$data]); +} catch (PDOException $e) { + echo json_encode(['status'=>'error','message'=>$e->getMessage()]); +} +?> diff --git a/publik/index.php b/publik/index.php index f3be193..8757185 100644 --- a/publik/index.php +++ b/publik/index.php @@ -942,6 +942,12 @@ $modulTitle = $modulNames[$modul] ?? 'Analisis Spasial';
+
+ +
+ +
+
diff --git a/publik/koneksi.php b/publik/koneksi.php index bdf6300..51ce72d 100644 --- a/publik/koneksi.php +++ b/publik/koneksi.php @@ -59,6 +59,21 @@ try { // 4. Masukkan data default settings jika belum ada $pdo->exec("INSERT IGNORE INTO settings (setting_key, setting_value) VALUES ('show_demo_accounts', '1')"); + // 5. Migrasi Tabel Penduduk Miskin (Tambah foto_rumah, foto_kk) + $pm_cols = $pdo->query("SHOW COLUMNS FROM penduduk_miskin")->fetchAll(PDO::FETCH_COLUMN); + if (!in_array('alamat', $pm_cols)) { + $pdo->exec("ALTER TABLE penduduk_miskin ADD COLUMN alamat TEXT NULL AFTER fasilitas_publik_id"); + } + if (!in_array('foto_rumah', $pm_cols)) { + $pdo->exec("ALTER TABLE penduduk_miskin ADD COLUMN foto_rumah VARCHAR(255) NULL AFTER fasilitas_publik_id"); + } + if (!in_array('foto_kk', $pm_cols)) { + $pdo->exec("ALTER TABLE penduduk_miskin ADD COLUMN foto_kk VARCHAR(255) NULL AFTER foto_rumah"); + } + if (!in_array('status_bantuan', $pm_cols)) { + $pdo->exec("ALTER TABLE penduduk_miskin ADD COLUMN status_bantuan ENUM('Belum Disalurkan', 'Proses', 'Sudah Disalurkan') DEFAULT 'Belum Disalurkan'"); + } + } catch (PDOException $e) { http_response_code(500); die(json_encode([ diff --git a/publik/penduduk_miskin/hapus.php b/publik/penduduk_miskin/hapus.php new file mode 100644 index 0000000..a9e42b1 --- /dev/null +++ b/publik/penduduk_miskin/hapus.php @@ -0,0 +1,22 @@ +'error','message'=>'Akses ditolak. Hanya admin yang dapat menghapus data.']); + exit; +} + +include '../koneksi.php'; +header('Content-Type: application/json'); + +$id = (int)($_POST['id'] ?? $_GET['id'] ?? 0); +if (!$id) { echo json_encode(['status'=>'error','message'=>'ID tidak valid.']); exit; } + +try { + $pdo->prepare("DELETE FROM penduduk_miskin WHERE id=?")->execute([$id]); + echo json_encode(['status'=>'success']); +} catch (PDOException $e) { + echo json_encode(['status'=>'error','message'=>$e->getMessage()]); +} +?> diff --git a/publik/penduduk_miskin/simpan.php b/publik/penduduk_miskin/simpan.php new file mode 100644 index 0000000..bb6723c --- /dev/null +++ b/publik/penduduk_miskin/simpan.php @@ -0,0 +1,80 @@ +'error','message'=>'Akses ditolak.']); + exit; +} + +include '../koneksi.php'; +header('Content-Type: application/json'); + +$nama = trim($_POST['nama'] ?? ''); +$nik = trim($_POST['nik'] ?? ''); +$keterangan = trim($_POST['keterangan'] ?? ''); +$alamat = trim($_POST['alamat'] ?? ''); +$latitude = trim($_POST['latitude'] ?? ''); +$longitude = trim($_POST['longitude'] ?? ''); +$status_jangkauan= trim($_POST['status_jangkauan']?? 'Luar Jangkauan'); +$status_bantuan = trim($_POST['status_bantuan'] ?? 'Belum Disalurkan'); +$fasilitas_publik_id = trim($_POST['fasilitas_publik_id'] ?? ''); + +if (!$nama || !$latitude || !$longitude) { + echo json_encode(['status'=>'error','message'=>'Nama dan lokasi wajib diisi.']); + exit; +} + +$valid_status = ['Dalam Jangkauan','Luar Jangkauan']; +if (!in_array($status_jangkauan, $valid_status)) $status_jangkauan = 'Luar Jangkauan'; + +$fp_id = ($fasilitas_publik_id !== '') ? (int)$fasilitas_publik_id : null; + +// Fungsi upload foto +function uploadFoto($file_input_name) { + if (isset($_FILES[$file_input_name]) && $_FILES[$file_input_name]['error'] === UPLOAD_ERR_OK) { + $file_tmp_path = $_FILES[$file_input_name]['tmp_name']; + $file_name = $_FILES[$file_input_name]['name']; + $file_size = $_FILES[$file_input_name]['size']; + $file_type = mime_content_type($file_tmp_path); + + $allowed_mime_types = ['image/jpeg', 'image/png', 'image/jpg']; + $max_size = 2 * 1024 * 1024; // 2MB + + if (in_array($file_type, $allowed_mime_types) && $file_size <= $max_size) { + $file_extension = pathinfo($file_name, PATHINFO_EXTENSION); + $new_file_name = uniqid($file_input_name . '_', true) . '.' . $file_extension; + $upload_file_dir = '../uploads/'; + if (!is_dir($upload_file_dir)) { + @mkdir($upload_file_dir, 0777, true); + } + $dest_path = $upload_file_dir . $new_file_name; + + if (@move_uploaded_file($file_tmp_path, $dest_path)) { + return $new_file_name; + } + } + } + return null; +} + +$foto_rumah = uploadFoto('foto_rumah'); +$foto_kk = uploadFoto('foto_kk'); + +try { + $stmt = $pdo->prepare( + "INSERT INTO penduduk_miskin (nama, nik, keterangan, alamat, latitude, longitude, status_jangkauan, fasilitas_publik_id, foto_rumah, foto_kk, status_bantuan) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + ); + $stmt->execute([$nama, $nik, $keterangan, $alamat, $latitude, $longitude, $status_jangkauan, $fp_id, $foto_rumah, $foto_kk, $status_bantuan]); + $id = $pdo->lastInsertId(); + + $stmtSelect = $pdo->prepare("SELECT pm.*, fp.nama AS fp_nama FROM penduduk_miskin pm LEFT JOIN fasilitas_publik fp ON pm.fasilitas_publik_id = fp.id WHERE pm.id = ?"); + $stmtSelect->execute([$id]); + $data = $stmtSelect->fetch(PDO::FETCH_ASSOC); + + echo json_encode(['status'=>'success','data'=>$data]); +} catch (PDOException $e) { + echo json_encode(['status'=>'error','message'=>$e->getMessage()]); +} +?> diff --git a/publik/penduduk_miskin/update.php b/publik/penduduk_miskin/update.php new file mode 100644 index 0000000..80f3ac0 --- /dev/null +++ b/publik/penduduk_miskin/update.php @@ -0,0 +1,87 @@ +'error','message'=>'Akses ditolak.']); + exit; +} + +include '../koneksi.php'; +header('Content-Type: application/json'); + +$id = (int)($_POST['id'] ?? 0); +$nama = trim($_POST['nama'] ?? ''); +$nik = trim($_POST['nik'] ?? ''); +$keterangan = trim($_POST['keterangan'] ?? ''); +$latitude = trim($_POST['latitude'] ?? ''); +$longitude = trim($_POST['longitude'] ?? ''); +$status_jangkauan= trim($_POST['status_jangkauan']?? 'Luar Jangkauan'); +$status_bantuan = trim($_POST['status_bantuan'] ?? 'Belum Disalurkan'); +$fasilitas_publik_id = trim($_POST['fasilitas_publik_id'] ?? ''); + +if (!$id || !$nama || !$latitude || !$longitude) { + echo json_encode(['status'=>'error','message'=>'Data tidak lengkap.']); + exit; +} + +$fp_id = ($fasilitas_publik_id !== '') ? (int)$fasilitas_publik_id : null; + +// Fungsi upload foto +function uploadFoto($file_input_name) { + if (isset($_FILES[$file_input_name]) && $_FILES[$file_input_name]['error'] === UPLOAD_ERR_OK) { + $file_tmp_path = $_FILES[$file_input_name]['tmp_name']; + $file_name = $_FILES[$file_input_name]['name']; + $file_size = $_FILES[$file_input_name]['size']; + $file_type = mime_content_type($file_tmp_path); + + $allowed_mime_types = ['image/jpeg', 'image/png', 'image/jpg']; + $max_size = 2 * 1024 * 1024; // 2MB + + if (in_array($file_type, $allowed_mime_types) && $file_size <= $max_size) { + $file_extension = pathinfo($file_name, PATHINFO_EXTENSION); + $new_file_name = uniqid($file_input_name . '_', true) . '.' . $file_extension; + $upload_file_dir = '../uploads/'; + $dest_path = $upload_file_dir . $new_file_name; + + if (move_uploaded_file($file_tmp_path, $dest_path)) { + return $new_file_name; + } + } + } + return null; +} + +$foto_rumah = uploadFoto('foto_rumah'); +$foto_kk = uploadFoto('foto_kk'); + +$alamat = trim($_POST['alamat'] ?? ''); + +try { + $query = "UPDATE penduduk_miskin SET nama=?, nik=?, keterangan=?, alamat=?, latitude=?, longitude=?, status_jangkauan=?, fasilitas_publik_id=?, status_bantuan=?"; + $params = [$nama, $nik, $keterangan, $alamat, $latitude, $longitude, $status_jangkauan, $fp_id, $status_bantuan]; + + if ($foto_rumah) { + $query .= ", foto_rumah=?"; + $params[] = $foto_rumah; + } + if ($foto_kk) { + $query .= ", foto_kk=?"; + $params[] = $foto_kk; + } + + $query .= " WHERE id=?"; + $params[] = $id; + + $stmt = $pdo->prepare($query); + $stmt->execute($params); + + $stmtSelect = $pdo->prepare("SELECT pm.*, fp.nama AS fp_nama FROM penduduk_miskin pm LEFT JOIN fasilitas_publik fp ON pm.fasilitas_publik_id = fp.id WHERE pm.id = ?"); + $stmtSelect->execute([$id]); + $data = $stmtSelect->fetch(PDO::FETCH_ASSOC); + + echo json_encode(['status'=>'success','data'=>$data]); +} catch (PDOException $e) { + echo json_encode(['status'=>'error','message'=>$e->getMessage()]); +} +?> diff --git a/publik/penduduk_miskin/update_lokasi.php b/publik/penduduk_miskin/update_lokasi.php new file mode 100644 index 0000000..ccef55e --- /dev/null +++ b/publik/penduduk_miskin/update_lokasi.php @@ -0,0 +1,48 @@ + 'error', 'message' => 'Unauthorized']); + exit; +} + +$id = $_POST['id'] ?? ''; +$latitude = $_POST['latitude'] ?? ''; +$longitude = $_POST['longitude'] ?? ''; +$status_jangkauan = $_POST['status_jangkauan'] ?? ''; +$fasilitas_publik_id = $_POST['fasilitas_publik_id'] ?? null; +$alamat = $_POST['alamat'] ?? null; + +if (!$id || !$latitude || !$longitude) { + echo json_encode(['status' => 'error', 'message' => 'Data tidak lengkap']); + exit; +} + +if ($fasilitas_publik_id === '') { + $fasilitas_publik_id = null; +} + +try { + if ($status_jangkauan !== '') { + if ($alamat !== null) { + $stmt = $pdo->prepare("UPDATE penduduk_miskin SET latitude = ?, longitude = ?, status_jangkauan = ?, fasilitas_publik_id = ?, alamat = ? WHERE id = ?"); + $stmt->execute([$latitude, $longitude, $status_jangkauan, $fasilitas_publik_id, $alamat, $id]); + } else { + $stmt = $pdo->prepare("UPDATE penduduk_miskin SET latitude = ?, longitude = ?, status_jangkauan = ?, fasilitas_publik_id = ? WHERE id = ?"); + $stmt->execute([$latitude, $longitude, $status_jangkauan, $fasilitas_publik_id, $id]); + } + } else { + if ($alamat !== null) { + $stmt = $pdo->prepare("UPDATE penduduk_miskin SET latitude = ?, longitude = ?, alamat = ? WHERE id = ?"); + $stmt->execute([$latitude, $longitude, $alamat, $id]); + } else { + $stmt = $pdo->prepare("UPDATE penduduk_miskin SET latitude = ?, longitude = ? WHERE id = ?"); + $stmt->execute([$latitude, $longitude, $id]); + } + } + + echo json_encode(['status' => 'success']); +} catch (PDOException $e) { + echo json_encode(['status' => 'error', 'message' => 'Database error: ' . $e->getMessage()]); +}