mirror of
https://github.com/rekywhyd/WebGIS_PengentasanKemiskinan.git
synced 2026-07-07 01:43:07 +00:00
Tampilan awal
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class MapController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$ibadahs = \App\Models\TempatIbadah::all();
|
||||
$penerimas = \App\Models\PenerimaBantuan::with('tempat_ibadah')->get();
|
||||
return view('dashboard', compact('ibadahs', 'penerimas'));
|
||||
}
|
||||
|
||||
public function daftarPenerimaBantuan()
|
||||
{
|
||||
$penerimas = \App\Models\PenerimaBantuan::with('tempat_ibadah')->get();
|
||||
$ibadahs = \App\Models\TempatIbadah::all();
|
||||
return view('daftar-penerima-bantuan', compact('penerimas', 'ibadahs'));
|
||||
}
|
||||
|
||||
public function daftarTempatIbadah()
|
||||
{
|
||||
$ibadahs = \App\Models\TempatIbadah::all();
|
||||
return view('daftar-tempat-ibadah', compact('ibadahs'));
|
||||
}
|
||||
|
||||
public function save(Request $request)
|
||||
{
|
||||
$type = $request->type;
|
||||
$nama = $request->nama;
|
||||
$lat = $request->lat;
|
||||
$lng = $request->lng;
|
||||
$alamat = $request->alamat;
|
||||
|
||||
if ($type == 'ibadah') {
|
||||
\App\Models\TempatIbadah::create([
|
||||
'nama_tempat' => $nama,
|
||||
'jenis_tempat_ibadah' => $request->jenis_tempat_ibadah,
|
||||
'kontak_person' => $request->kontak_person,
|
||||
'radius_meter' => 500,
|
||||
'lat' => $lat,
|
||||
'lng' => $lng,
|
||||
'alamat' => $alamat
|
||||
]);
|
||||
} else {
|
||||
$fotoPath = null;
|
||||
if ($request->hasFile('foto_kondisi_rumah')) {
|
||||
$fotoPath = $request->file('foto_kondisi_rumah')->store('fotos', 'public');
|
||||
}
|
||||
|
||||
\App\Models\PenerimaBantuan::create([
|
||||
'nama_kepala_keluarga' => $nama,
|
||||
'id_tempat_ibadah' => $request->id_tempat_ibadah,
|
||||
'nik_kepala_keluarga' => $request->nik_kepala_keluarga,
|
||||
'nomor_kk' => $request->nomor_kk,
|
||||
'jumlah_tanggungan' => $request->jumlah_tanggungan,
|
||||
'foto_kondisi_rumah' => $fotoPath,
|
||||
'lat' => $lat,
|
||||
'lng' => $lng,
|
||||
'alamat' => $alamat
|
||||
]);
|
||||
}
|
||||
return response()->json(['status' => 'success']);
|
||||
}
|
||||
|
||||
public function updateRadius(Request $request)
|
||||
{
|
||||
$id = $request->id;
|
||||
$radius = $request->radius;
|
||||
\App\Models\TempatIbadah::where('id', $id)->update(['radius_meter' => $radius]);
|
||||
$this->checkAndUnlinkPenerima($id);
|
||||
return response()->json(['status' => 'success']);
|
||||
}
|
||||
|
||||
public function edit(Request $request)
|
||||
{
|
||||
$id = $request->id;
|
||||
$nama = $request->nama;
|
||||
$alamat = $request->alamat;
|
||||
$type = $request->type;
|
||||
|
||||
if ($type == 'ibadah') {
|
||||
\App\Models\TempatIbadah::where('id', $id)->update([
|
||||
'nama_tempat' => $nama,
|
||||
'jenis_tempat_ibadah' => $request->jenis_tempat_ibadah,
|
||||
'kontak_person' => $request->kontak_person,
|
||||
'alamat' => $alamat,
|
||||
'radius_meter' => $request->radius_meter
|
||||
]);
|
||||
$this->checkAndUnlinkPenerima($id);
|
||||
} else {
|
||||
$updateData = [
|
||||
'nama_kepala_keluarga' => $nama,
|
||||
'id_tempat_ibadah' => $request->id_tempat_ibadah,
|
||||
'nik_kepala_keluarga' => $request->nik_kepala_keluarga,
|
||||
'nomor_kk' => $request->nomor_kk,
|
||||
'jumlah_tanggungan' => $request->jumlah_tanggungan,
|
||||
'alamat' => $alamat
|
||||
];
|
||||
|
||||
if ($request->hasFile('foto_kondisi_rumah')) {
|
||||
$updateData['foto_kondisi_rumah'] = $request->file('foto_kondisi_rumah')->store('fotos', 'public');
|
||||
}
|
||||
|
||||
\App\Models\PenerimaBantuan::where('id', $id)->update($updateData);
|
||||
}
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function delete(Request $request)
|
||||
{
|
||||
$id = $request->id;
|
||||
$type = $request->type;
|
||||
|
||||
if ($type == 'ibadah') {
|
||||
\App\Models\TempatIbadah::where('id', $id)->delete();
|
||||
} else {
|
||||
\App\Models\PenerimaBantuan::where('id', $id)->delete();
|
||||
}
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
private function calculateDistance($lat1, $lon1, $lat2, $lon2) {
|
||||
$earthRadius = 6371000; // in meters
|
||||
$dLat = deg2rad($lat2 - $lat1);
|
||||
$dLon = deg2rad($lon2 - $lon1);
|
||||
$a = sin($dLat/2) * sin($dLat/2) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon/2) * sin($dLon/2);
|
||||
$c = 2 * asin(sqrt($a));
|
||||
return $earthRadius * $c;
|
||||
}
|
||||
|
||||
private function checkAndUnlinkPenerima($ibadahId) {
|
||||
$ibadah = \App\Models\TempatIbadah::find($ibadahId);
|
||||
if (!$ibadah) return;
|
||||
|
||||
$penerimas = \App\Models\PenerimaBantuan::where('id_tempat_ibadah', $ibadahId)->get();
|
||||
foreach ($penerimas as $penerima) {
|
||||
$distance = $this->calculateDistance($ibadah->lat, $ibadah->lng, $penerima->lat, $penerima->lng);
|
||||
if ($distance > $ibadah->radius_meter) {
|
||||
$penerima->update(['id_tempat_ibadah' => null]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class PenerimaBantuan extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'penerima_bantuan';
|
||||
|
||||
protected $fillable = [
|
||||
'id_tempat_ibadah',
|
||||
'nik_kepala_keluarga',
|
||||
'nomor_kk',
|
||||
'nama_kepala_keluarga',
|
||||
'jumlah_tanggungan',
|
||||
'foto_kondisi_rumah',
|
||||
'lat',
|
||||
'lng',
|
||||
'alamat'
|
||||
];
|
||||
|
||||
public function tempat_ibadah()
|
||||
{
|
||||
return $this->belongsTo(TempatIbadah::class, 'id_tempat_ibadah');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class TempatIbadah extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'tempat_ibadah';
|
||||
|
||||
protected $fillable = [
|
||||
'nama_tempat',
|
||||
'jenis_tempat_ibadah',
|
||||
'kontak_person',
|
||||
'radius_meter',
|
||||
'lat',
|
||||
'lng',
|
||||
'alamat'
|
||||
];
|
||||
|
||||
public function penerima_bantuan()
|
||||
{
|
||||
return $this->hasMany(PenerimaBantuan::class, 'id_tempat_ibadah');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('tempat_ibadah', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('nama_tempat');
|
||||
$table->integer('radius_meter')->default(500);
|
||||
$table->decimal('lat', 10, 7);
|
||||
$table->decimal('lng', 10, 7);
|
||||
$table->string('alamat')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('tempat_ibadah');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('rumah_miskin', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('nama_kepala_keluarga');
|
||||
$table->decimal('lat', 10, 7);
|
||||
$table->decimal('lng', 10, 7);
|
||||
$table->string('alamat')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('rumah_miskin');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::rename('rumah_miskin', 'penerima_bantuan');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::rename('penerima_bantuan', 'rumah_miskin');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('tempat_ibadah', function (Blueprint $table) {
|
||||
$table->enum('jenis_tempat_ibadah', ['Masjid', 'Gereja Katolik', 'Gereja Protestan', 'Vihara', 'Pura', 'Klenteng'])->nullable()->after('nama_tempat');
|
||||
$table->string('kontak_person')->nullable()->after('alamat');
|
||||
});
|
||||
|
||||
Schema::table('penerima_bantuan', function (Blueprint $table) {
|
||||
$table->foreignId('id_tempat_ibadah')->nullable()->after('id')->constrained('tempat_ibadah')->nullOnDelete();
|
||||
$table->string('nik_kepala_keluarga')->nullable()->unique()->after('nama_kepala_keluarga');
|
||||
$table->string('nomor_kk')->nullable()->after('nik_kepala_keluarga');
|
||||
$table->integer('jumlah_tanggungan')->nullable()->after('alamat');
|
||||
$table->string('foto_kondisi_rumah')->nullable()->after('jumlah_tanggungan');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('penerima_bantuan', function (Blueprint $table) {
|
||||
$table->dropForeign(['id_tempat_ibadah']);
|
||||
$table->dropColumn(['id_tempat_ibadah', 'nik_kepala_keluarga', 'nomor_kk', 'jumlah_tanggungan', 'foto_kondisi_rumah']);
|
||||
});
|
||||
|
||||
Schema::table('tempat_ibadah', function (Blueprint $table) {
|
||||
$table->dropColumn(['jenis_tempat_ibadah', 'kontak_person']);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,114 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Daftar Penerima Bantuan') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<style>
|
||||
.map-table { width: 100%; border-collapse: collapse; background: white; color: black; margin-top: 20px;}
|
||||
.map-table th, .map-table td { padding: 12px; border: 1px solid #ddd; text-align: left; }
|
||||
.map-table th { background: #007bff; color: white; }
|
||||
|
||||
.btn-edit { background: #ffc107; color: #000; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; text-decoration: none; font-size: 13px; }
|
||||
.btn-delete { background: #dc3545; color: #fff; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; text-decoration: none; font-size: 13px; }
|
||||
.edit-form { background: #fff3cd; padding: 15px; border-radius: 12px; display: none; margin-bottom: 15px; border: 1px solid #ffeeba; color: black; }
|
||||
</style>
|
||||
|
||||
<div id="panel-edit" class="edit-form">
|
||||
<form action="{{ route('map.edit') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<strong>Edit Data Penerima Bantuan:</strong>
|
||||
<div style="display: flex; gap: 10px; margin-top: 10px; flex-wrap: wrap; align-items: center;">
|
||||
<input type="hidden" name="id" id="edit-id">
|
||||
<input type="hidden" name="type" id="edit-type" value="penerima">
|
||||
|
||||
<input type="text" name="nama" id="edit-nama" placeholder="Nama KK" style="padding: 8px; border-radius: 4px; border: 1px solid #ccc; color: black;" required>
|
||||
<input type="text" name="nik_kepala_keluarga" id="edit-nik" placeholder="NIK" style="padding: 8px; border-radius: 4px; border: 1px solid #ccc; color: black;">
|
||||
<input type="text" name="nomor_kk" id="edit-kk" placeholder="No. KK" style="padding: 8px; border-radius: 4px; border: 1px solid #ccc; color: black;">
|
||||
<input type="number" name="jumlah_tanggungan" id="edit-tanggungan" placeholder="Jml Tanggungan" style="padding: 8px; width: 150px; border-radius: 4px; border: 1px solid #ccc; color: black;" min="0">
|
||||
|
||||
<select name="id_tempat_ibadah" id="edit-ibadah" style="padding: 8px; border-radius: 4px; border: 1px solid #ccc; color: black;">
|
||||
<option value="">- Pilih Tempat Ibadah -</option>
|
||||
@foreach($ibadahs as $ib)
|
||||
<option value="{{ $ib->id }}">{{ $ib->nama_tempat }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
<input type="text" name="alamat" id="edit-alamat" placeholder="Alamat" style="padding: 8px; width: 250px; border-radius: 4px; border: 1px solid #ccc; color: black;" required>
|
||||
|
||||
<label style="display:flex; align-items:center; gap:5px; color:#333; font-weight:bold; font-size:13px;">
|
||||
Ganti Foto: <input type="file" name="foto_kondisi_rumah" id="edit-foto" accept="image/*" style="color: black; font-size:12px;">
|
||||
</label>
|
||||
|
||||
<button type="submit" style="padding: 8px 15px; background: #28a745; color: white; border: none; border-radius: 4px;">Simpan</button>
|
||||
<button type="button" onclick="closeEdit()" style="padding: 8px 15px; background: #6c757d; color: white; border: none; border-radius: 4px;">Batal</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<table class="map-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Nama Kepala Keluarga</th>
|
||||
<th>NIK / KK</th>
|
||||
<th>Tanggungan</th>
|
||||
<th>Tempat Ibadah</th>
|
||||
<th>Alamat Detail</th>
|
||||
<th>Koordinat (Lat, Lng)</th>
|
||||
<th>Foto</th>
|
||||
<th>Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($penerimas as $r)
|
||||
<tr>
|
||||
<td>{{ $r->id }}</td>
|
||||
<td>{{ $r->nama_kepala_keluarga }}</td>
|
||||
<td>{{ $r->nik_kepala_keluarga ?? '-' }} / {{ $r->nomor_kk ?? '-' }}</td>
|
||||
<td>{{ $r->jumlah_tanggungan ?? '0' }}</td>
|
||||
<td>{{ $r->tempat_ibadah->nama_tempat ?? '-' }}</td>
|
||||
<td>{{ $r->alamat }}</td>
|
||||
<td>{{ $r->lat }}, {{ $r->lng }}</td>
|
||||
<td>
|
||||
@if($r->foto_kondisi_rumah)
|
||||
<a href="{{ asset('storage/' . $r->foto_kondisi_rumah) }}" target="_blank" style="color: blue; text-decoration: underline;">Lihat Foto</a>
|
||||
@else
|
||||
-
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<button class='btn-edit' onclick='openEdit({{ $r->id }}, "{{ addslashes($r->nama_kepala_keluarga) }}", "{{ addslashes($r->alamat) }}", "{{ addslashes($r->nik_kepala_keluarga) }}", "{{ addslashes($r->nomor_kk) }}", "{{ $r->jumlah_tanggungan }}", "{{ $r->id_tempat_ibadah }}")'>Edit</button>
|
||||
<a href='{{ route("map.delete", ["type" => "penerima", "id" => $r->id]) }}' class='btn-delete' onclick='return confirm("Hapus data penerima ini?")'>Hapus</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
function openEdit(id, nama, alamat, nik, kk, tanggungan, ibadah_id) {
|
||||
document.getElementById('panel-edit').style.display = 'block';
|
||||
document.getElementById('edit-id').value = id;
|
||||
document.getElementById('edit-nama').value = nama;
|
||||
document.getElementById('edit-alamat').value = alamat;
|
||||
document.getElementById('edit-nik').value = nik || '';
|
||||
document.getElementById('edit-kk').value = kk || '';
|
||||
document.getElementById('edit-tanggungan').value = tanggungan || '';
|
||||
document.getElementById('edit-ibadah').value = ibadah_id || '';
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
function closeEdit() { document.getElementById('panel-edit').style.display = 'none'; }
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,101 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Daftar Tempat Ibadah') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
|
||||
<style>
|
||||
.map-table { width: 100%; border-collapse: collapse; background: white; color: black; margin-top: 20px;}
|
||||
.map-table th, .map-table td { padding: 12px; border: 1px solid #ddd; text-align: left; }
|
||||
.map-table th { background: #007bff; color: white; }
|
||||
|
||||
.btn-edit { background: #ffc107; color: #000; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; text-decoration: none; font-size: 13px; }
|
||||
.btn-delete { background: #dc3545; color: #fff; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; text-decoration: none; font-size: 13px; }
|
||||
.edit-form { background: #fff3cd; padding: 15px; border-radius: 12px; display: none; margin-bottom: 15px; border: 1px solid #ffeeba; color: black; }
|
||||
</style>
|
||||
|
||||
<div id="panel-edit" class="edit-form">
|
||||
<form action="{{ route('map.edit') }}" method="POST">
|
||||
@csrf
|
||||
<strong>Edit Data Tempat Ibadah:</strong>
|
||||
<div style="display: flex; gap: 10px; margin-top: 10px; flex-wrap: wrap; align-items: center;">
|
||||
<input type="hidden" name="id" id="edit-id">
|
||||
<input type="hidden" name="type" id="edit-type" value="ibadah">
|
||||
<input type="text" name="nama" id="edit-nama" placeholder="Nama Tempat" style="padding: 8px; border-radius: 4px; border: 1px solid #ccc; color: black;" required>
|
||||
|
||||
<select name="jenis_tempat_ibadah" id="edit-jenis" style="padding: 8px; border-radius: 4px; border: 1px solid #ccc; color: black;">
|
||||
<option value="">- Pilih Jenis -</option>
|
||||
<option value="Masjid">Masjid</option>
|
||||
<option value="Gereja Katolik">Gereja Katolik</option>
|
||||
<option value="Gereja Protestan">Gereja Protestan</option>
|
||||
<option value="Vihara">Vihara</option>
|
||||
<option value="Pura">Pura</option>
|
||||
<option value="Klenteng">Klenteng</option>
|
||||
</select>
|
||||
|
||||
<input type="text" name="kontak_person" id="edit-kontak" placeholder="Kontak Person" style="padding: 8px; border-radius: 4px; border: 1px solid #ccc; color: black;">
|
||||
<input type="text" name="alamat" id="edit-alamat" placeholder="Alamat" style="padding: 8px; width: 350px; border-radius: 4px; border: 1px solid #ccc; color: black;" required>
|
||||
<input type="number" name="radius_meter" id="edit-radius" placeholder="Radius (Meter)" style="padding: 8px; width: 130px; border-radius: 4px; border: 1px solid #ccc; color: black;" min="0" required>
|
||||
<button type="submit" style="padding: 8px 15px; background: #28a745; color: white; border: none; border-radius: 4px;">Simpan</button>
|
||||
<button type="button" onclick="closeEdit()" style="padding: 8px 15px; background: #6c757d; color: white; border: none; border-radius: 4px;">Batal</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<table class="map-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Nama Tempat Ibadah</th>
|
||||
<th>Jenis</th>
|
||||
<th>Kontak Person</th>
|
||||
<th>Alamat Detail</th>
|
||||
<th>Radius Layan (Meter)</th>
|
||||
<th>Koordinat (Lat, Lng)</th>
|
||||
<th>Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($ibadahs as $ib)
|
||||
<tr>
|
||||
<td>{{ $ib->id }}</td>
|
||||
<td>{{ $ib->nama_tempat }}</td>
|
||||
<td>{{ $ib->jenis_tempat_ibadah ?? '-' }}</td>
|
||||
<td>{{ $ib->kontak_person ?? '-' }}</td>
|
||||
<td>{{ $ib->alamat }}</td>
|
||||
<td>{{ $ib->radius_meter }}</td>
|
||||
<td>{{ $ib->lat }}, {{ $ib->lng }}</td>
|
||||
<td>
|
||||
<button class='btn-edit' onclick='openEdit({{ $ib->id }}, "{{ addslashes($ib->nama_tempat) }}", "{{ addslashes($ib->alamat) }}", {{ $ib->radius_meter }}, "{{ addslashes($ib->jenis_tempat_ibadah) }}", "{{ addslashes($ib->kontak_person) }}")'>Edit</button>
|
||||
<a href='{{ route("map.delete", ["type" => "ibadah", "id" => $ib->id]) }}' class='btn-delete' onclick='return confirm("Hapus data tempat ibadah ini?")'>Hapus</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
function openEdit(id, nama, alamat, radius, jenis, kontak) {
|
||||
document.getElementById('panel-edit').style.display = 'block';
|
||||
document.getElementById('edit-id').value = id;
|
||||
document.getElementById('edit-nama').value = nama;
|
||||
document.getElementById('edit-alamat').value = alamat;
|
||||
document.getElementById('edit-radius').value = radius;
|
||||
document.getElementById('edit-jenis').value = jenis;
|
||||
document.getElementById('edit-kontak').value = kontak;
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
function closeEdit() { document.getElementById('panel-edit').style.display = 'none'; }
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-app-layout>
|
||||
@@ -1,7 +1,7 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Dashboard') }}
|
||||
{{ __('Dashboard Pemetaan') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
@@ -9,7 +9,258 @@
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
{{ __("You're logged in!") }}
|
||||
|
||||
<!-- Map Interface Start -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||
<style>
|
||||
#map { height: 450px; border-radius: 12px; margin-bottom: 20px; border: 2px solid #333; z-index: 1; }
|
||||
.panel { background: #f4f7f6; padding: 20px; border-radius: 12px; margin-bottom: 15px; color: black;}
|
||||
.edit-form { background: #fff3cd; padding: 15px; border-radius: 12px; display: none; margin-bottom: 15px; border: 1px solid #ffeeba; color: black; }
|
||||
|
||||
.map-table { width: 100%; border-collapse: collapse; background: white; color: black; }
|
||||
.map-table th, .map-table td { padding: 12px; border: 1px solid #ddd; text-align: left; }
|
||||
.map-table th { background: #007bff; color: white; }
|
||||
|
||||
.btn-edit { background: #ffc107; color: #000; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; text-decoration: none; font-size: 13px; }
|
||||
.btn-delete { background: #dc3545; color: #fff; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; text-decoration: none; font-size: 13px; }
|
||||
.leaflet-popup-content { text-align: center; color: black; }
|
||||
.popup-slider { width: 100%; }
|
||||
|
||||
/* Ubah kursor peta menjadi crosshair untuk menandakan mode tambah titik */
|
||||
.mode-tambah .leaflet-container { cursor: crosshair !important; }
|
||||
</style>
|
||||
|
||||
<div class="panel">
|
||||
<strong style="display: block; margin-bottom: 10px;">Tambah Titik Baru:</strong>
|
||||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||||
<input type="text" id="nama" placeholder="Masukkan Nama..." style="padding: 10px; border-radius: 6px; border: 1px solid #ccc; color: black; flex-grow: 1; max-width: 300px;">
|
||||
<input type="hidden" id="mode" value="">
|
||||
|
||||
<button type="button" id="btn-mode-ibadah" onclick="setMode('ibadah')" style="padding: 10px 15px; background: #e2e8f0; color: #333; border: none; border-radius: 6px; font-weight: bold; cursor: pointer; transition: 0.3s;">
|
||||
🏛️ Tempat Ibadah
|
||||
</button>
|
||||
<button type="button" id="btn-mode-penerima" onclick="setMode('penerima')" style="padding: 10px 15px; background: #e2e8f0; color: #333; border: none; border-radius: 6px; font-weight: bold; cursor: pointer; transition: 0.3s;">
|
||||
🏠 Penerima Bantuan
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Extra Fields for Ibadah -->
|
||||
<div id="fields-ibadah" style="display:none; margin-top:10px; gap: 10px; flex-wrap: wrap;">
|
||||
<select id="jenis_tempat_ibadah" style="padding: 10px; border-radius: 6px; border: 1px solid #ccc; color: black;">
|
||||
<option value="">- Pilih Jenis Tempat Ibadah -</option>
|
||||
<option value="Masjid">Masjid</option>
|
||||
<option value="Gereja Katolik">Gereja Katolik</option>
|
||||
<option value="Gereja Protestan">Gereja Protestan</option>
|
||||
<option value="Vihara">Vihara</option>
|
||||
<option value="Pura">Pura</option>
|
||||
<option value="Klenteng">Klenteng</option>
|
||||
</select>
|
||||
<input type="text" id="kontak_person" placeholder="Kontak Person..." style="padding: 10px; border-radius: 6px; border: 1px solid #ccc; color: black;">
|
||||
</div>
|
||||
|
||||
<!-- Extra Fields for Penerima -->
|
||||
<div id="fields-penerima" style="display:none; margin-top:10px; gap: 10px; flex-wrap: wrap;">
|
||||
<input type="text" id="nik_kepala_keluarga" placeholder="NIK Kepala Keluarga..." style="padding: 10px; border-radius: 6px; border: 1px solid #ccc; color: black;">
|
||||
<input type="text" id="nomor_kk" placeholder="Nomor KK..." style="padding: 10px; border-radius: 6px; border: 1px solid #ccc; color: black;">
|
||||
<input type="number" id="jumlah_tanggungan" placeholder="Jml Tanggungan..." style="padding: 10px; border-radius: 6px; border: 1px solid #ccc; color: black; width: 150px;" min="0">
|
||||
<select id="id_tempat_ibadah" style="padding: 10px; border-radius: 6px; border: 1px solid #ccc; color: black;">
|
||||
<option value="">- Pilih Tempat Ibadah -</option>
|
||||
@foreach($ibadahs as $ib)
|
||||
<option value="{{ $ib->id }}">{{ $ib->nama_tempat }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<label style="display:flex; align-items:center; gap:5px; color:#333; font-weight:bold;">
|
||||
Foto Rumah: <input type="file" id="foto_kondisi_rumah" accept="image/*" style="color: black;">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<small style="color: #666; display: block; margin-top: 10px; font-style: italic;">👉 Pilih jenis titik, isi kolom yang tersedia, lalu <b>klik pada peta</b> untuk menyimpan lokasinya.</small>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function setMode(mode) {
|
||||
var currentMode = document.getElementById('mode').value;
|
||||
var mapEl = document.getElementById('map');
|
||||
|
||||
// Toggle off if clicking the same mode
|
||||
if (currentMode === mode) {
|
||||
document.getElementById('mode').value = '';
|
||||
document.getElementById('btn-mode-ibadah').style.background = '#e2e8f0';
|
||||
document.getElementById('btn-mode-ibadah').style.color = '#333';
|
||||
document.getElementById('btn-mode-penerima').style.background = '#e2e8f0';
|
||||
document.getElementById('btn-mode-penerima').style.color = '#333';
|
||||
document.getElementById('fields-ibadah').style.display = 'none';
|
||||
document.getElementById('fields-penerima').style.display = 'none';
|
||||
mapEl.classList.remove('mode-tambah');
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('mode').value = mode;
|
||||
mapEl.classList.add('mode-tambah');
|
||||
|
||||
if(mode === 'ibadah') {
|
||||
document.getElementById('btn-mode-ibadah').style.background = '#007bff';
|
||||
document.getElementById('btn-mode-ibadah').style.color = 'white';
|
||||
document.getElementById('btn-mode-penerima').style.background = '#e2e8f0';
|
||||
document.getElementById('btn-mode-penerima').style.color = '#333';
|
||||
document.getElementById('fields-ibadah').style.display = 'flex';
|
||||
document.getElementById('fields-penerima').style.display = 'none';
|
||||
} else {
|
||||
document.getElementById('btn-mode-penerima').style.background = '#007bff';
|
||||
document.getElementById('btn-mode-penerima').style.color = 'white';
|
||||
document.getElementById('btn-mode-ibadah').style.background = '#e2e8f0';
|
||||
document.getElementById('btn-mode-ibadah').style.color = '#333';
|
||||
document.getElementById('fields-ibadah').style.display = 'none';
|
||||
document.getElementById('fields-penerima').style.display = 'flex';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="panel-edit" class="edit-form">
|
||||
<form action="{{ route('map.edit') }}" method="POST">
|
||||
@csrf
|
||||
<strong>Edit Data:</strong>
|
||||
<input type="hidden" name="id" id="edit-id">
|
||||
<input type="hidden" name="type" id="edit-type">
|
||||
<input type="text" name="nama" id="edit-nama" placeholder="Nama" style="padding: 8px; border-radius: 4px; border: 1px solid #ccc; color: black;">
|
||||
<input type="text" name="alamat" id="edit-alamat" placeholder="Alamat" style="padding: 8px; width: 400px; border-radius: 4px; border: 1px solid #ccc; color: black;">
|
||||
<button type="submit" style="padding: 8px 15px; background: #28a745; color: white; border: none; border-radius: 4px;">Simpan</button>
|
||||
<button type="button" onclick="closeEdit()" style="padding: 8px 15px; background: #6c757d; color: white; border: none; border-radius: 4px;">Batal</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="map"></div>
|
||||
|
||||
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script>
|
||||
var map = L.map('map').setView([-0.02, 109.34], 14);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
|
||||
|
||||
var listIbadah = [];
|
||||
var listPenerima = [];
|
||||
|
||||
var iconIbadah = L.divIcon({
|
||||
className: '',
|
||||
html: `<div style="background:#007bff; color:#fff; border-radius:50%; width:30px; height:30px; display:flex; align-items:center; justify-content:center; border:2px solid #fff;">🏛️</div>`,
|
||||
iconSize: [30, 30]
|
||||
});
|
||||
|
||||
// 1. Fungsi Edit UI
|
||||
function openEdit(id, type, nama, alamat) {
|
||||
document.getElementById('panel-edit').style.display = 'block';
|
||||
document.getElementById('edit-id').value = id;
|
||||
document.getElementById('edit-type').value = type;
|
||||
document.getElementById('edit-nama').value = nama;
|
||||
document.getElementById('edit-alamat').value = alamat;
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
function closeEdit() { document.getElementById('panel-edit').style.display = 'none'; }
|
||||
|
||||
// 2. Reverse Geocoding & Save
|
||||
map.on('click', async function(e) {
|
||||
const mode = document.getElementById('mode').value;
|
||||
if(!mode) return; // Mode biasa, jangan lakukan apa-apa
|
||||
|
||||
const nama = document.getElementById('nama').value;
|
||||
if(!nama) return alert("Isi nama dulu!");
|
||||
|
||||
const res = await fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${e.latlng.lat}&lon=${e.latlng.lng}`);
|
||||
const data = await res.json();
|
||||
const addr = data.address || {};
|
||||
const alamatDetail = [addr.road, addr.village || addr.suburb, addr.city].filter(Boolean).join(", ");
|
||||
|
||||
let fd = new FormData();
|
||||
fd.append('_token', '{{ csrf_token() }}');
|
||||
fd.append('type', mode);
|
||||
fd.append('nama', nama);
|
||||
fd.append('lat', e.latlng.lat);
|
||||
fd.append('lng', e.latlng.lng);
|
||||
fd.append('alamat', alamatDetail || data.display_name);
|
||||
|
||||
if (mode === 'ibadah') {
|
||||
fd.append('jenis_tempat_ibadah', document.getElementById('jenis_tempat_ibadah').value);
|
||||
fd.append('kontak_person', document.getElementById('kontak_person').value);
|
||||
} else {
|
||||
fd.append('nik_kepala_keluarga', document.getElementById('nik_kepala_keluarga').value);
|
||||
fd.append('nomor_kk', document.getElementById('nomor_kk').value);
|
||||
fd.append('jumlah_tanggungan', document.getElementById('jumlah_tanggungan').value);
|
||||
fd.append('id_tempat_ibadah', document.getElementById('id_tempat_ibadah').value);
|
||||
|
||||
const fotoInput = document.getElementById('foto_kondisi_rumah');
|
||||
if (fotoInput.files.length > 0) {
|
||||
fd.append('foto_kondisi_rumah', fotoInput.files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
fetch('{{ route("map.save") }}', { method: 'POST', body: fd }).then(() => location.reload());
|
||||
});
|
||||
|
||||
// 3. Load Data Ibadah
|
||||
@foreach($ibadahs as $ib)
|
||||
(function(){
|
||||
var rAwal = {{ $ib->radius_meter }};
|
||||
var circle = L.circle([{{ $ib->lat }}, {{ $ib->lng }}], {
|
||||
radius: rAwal, color: 'blue', fillOpacity: 0.1
|
||||
}).addTo(map);
|
||||
|
||||
var marker = L.marker([{{ $ib->lat }}, {{ $ib->lng }}], {icon: iconIbadah}).addTo(map);
|
||||
listIbadah.push({id: {{ $ib->id }}, nama: "{{ addslashes($ib->nama_tempat) }}", circle: circle, latlng: L.latLng({{ $ib->lat }}, {{ $ib->lng }})});
|
||||
|
||||
marker.bindPopup(`
|
||||
<b>{{ addslashes($ib->nama_tempat) }}</b><br>
|
||||
Radius: <span id="val_{{ $ib->id }}">${rAwal}</span> m<br>
|
||||
<input type="range" class="popup-slider" min="100" max="3000" value="${rAwal}" oninput="updateRadius(${rAwal}, this.value, {{ $ib->id }})" onchange="saveRadius(this.value, {{ $ib->id }})">
|
||||
<button onclick="window.location.href='{{ route('map.delete', ['type' => 'ibadah', 'id' => $ib->id]) }}'" style="background:red; color:white; border:none; padding:5px; width:100%; cursor:pointer; margin-top:5px;">Hapus</button>
|
||||
`);
|
||||
})();
|
||||
@endforeach
|
||||
|
||||
// 4. Load Data Penerima Bantuan
|
||||
@foreach($penerimas as $r)
|
||||
var rm = L.circleMarker([{{ $r->lat }}, {{ $r->lng }}], {radius: 8, color: '#fff', fillOpacity: 1, weight: 2}).addTo(map);
|
||||
listPenerima.push({marker: rm, latlng: L.latLng({{ $r->lat }}, {{ $r->lng }})});
|
||||
@endforeach
|
||||
|
||||
// 5. Real-time Update Logic
|
||||
function updateRadius(oldVal, newVal, id) {
|
||||
document.getElementById('val_' + id).innerText = newVal;
|
||||
listIbadah.forEach(item => { if(item.id === id) item.circle.setRadius(newVal); });
|
||||
syncUI();
|
||||
}
|
||||
|
||||
function saveRadius(val, id) {
|
||||
let fd = new FormData();
|
||||
fd.append('_token', '{{ csrf_token() }}');
|
||||
fd.append('id', id);
|
||||
fd.append('radius', val);
|
||||
fetch('{{ route("map.updateRadius") }}', { method: 'POST', body: fd });
|
||||
}
|
||||
|
||||
function syncUI() {
|
||||
var baris = document.querySelectorAll('.baris-penerima');
|
||||
baris.forEach(row => {
|
||||
var rLoc = L.latLng(row.getAttribute('data-lat'), row.getAttribute('data-lng'));
|
||||
var covered = [];
|
||||
listIbadah.forEach(ib => { if(rLoc.distanceTo(ib.latlng) <= ib.circle.getRadius()) covered.push(ib.nama); });
|
||||
|
||||
row.querySelector('.status-cell').innerHTML = covered.length > 0 ?
|
||||
`<span style='color:green'>✔ ${covered.join(", ")}</span>` :
|
||||
`<span style='color:red'>✘ Luar Jangkauan</span>`;
|
||||
});
|
||||
|
||||
listPenerima.forEach(penerima => {
|
||||
let check = false;
|
||||
listIbadah.forEach(ib => { if(penerima.latlng.distanceTo(ib.latlng) <= ib.circle.getRadius()) check = true; });
|
||||
penerima.marker.setStyle({fillColor: check ? 'green' : 'red'});
|
||||
});
|
||||
}
|
||||
|
||||
syncUI();
|
||||
</script>
|
||||
<!-- Map Interface End -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,12 @@
|
||||
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('ibadah.index')" :active="request()->routeIs('ibadah.index')">
|
||||
{{ __('Daftar Tempat Ibadah') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('penerima.index')" :active="request()->routeIs('penerima.index')">
|
||||
{{ __('Daftar Penerima Bantuan') }}
|
||||
</x-nav-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -70,6 +76,12 @@
|
||||
<x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('penerima.index')" :active="request()->routeIs('penerima.index')">
|
||||
{{ __('Daftar Penerima Bantuan') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('ibadah.index')" :active="request()->routeIs('ibadah.index')">
|
||||
{{ __('Daftar Tempat Ibadah') }}
|
||||
</x-responsive-nav-link>
|
||||
</div>
|
||||
|
||||
<!-- Responsive Settings Options -->
|
||||
|
||||
+11
-3
@@ -3,13 +3,21 @@
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
use App\Http\Controllers\MapController;
|
||||
|
||||
Route::get('/', function () {
|
||||
return view('welcome');
|
||||
});
|
||||
|
||||
Route::get('/dashboard', function () {
|
||||
return view('dashboard');
|
||||
})->middleware(['auth', 'verified'])->name('dashboard');
|
||||
Route::middleware(['auth', 'verified'])->group(function () {
|
||||
Route::get('/dashboard', [MapController::class, 'index'])->name('dashboard');
|
||||
Route::get('/daftar-penerima-bantuan', [MapController::class, 'daftarPenerimaBantuan'])->name('penerima.index');
|
||||
Route::get('/daftar-tempat-ibadah', [MapController::class, 'daftarTempatIbadah'])->name('ibadah.index');
|
||||
Route::post('/map/save', [MapController::class, 'save'])->name('map.save');
|
||||
Route::post('/map/update-radius', [MapController::class, 'updateRadius'])->name('map.updateRadius');
|
||||
Route::post('/map/edit', [MapController::class, 'edit'])->name('map.edit');
|
||||
Route::get('/map/delete', [MapController::class, 'delete'])->name('map.delete');
|
||||
});
|
||||
|
||||
Route::middleware('auth')->group(function () {
|
||||
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
|
||||
|
||||
Reference in New Issue
Block a user