817 lines
31 KiB
PHP
817 lines
31 KiB
PHP
@extends('dashboard.base')
|
|
|
|
@section('title', 'Manajemen Lokasi')
|
|
|
|
@push('css')
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
|
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
|
crossorigin=""/>
|
|
@endpush
|
|
|
|
@section('content')
|
|
<div class="content-wrapper">
|
|
<div class="row">
|
|
<div class="col-md-12 grid-margin">
|
|
<div class="row">
|
|
<div class="col-12 col-xl-8 mb-4 mb-xl-0">
|
|
<h3 class="font-weight-bold">Manajemen Lokasi</h3>
|
|
<h6 class="font-weight-normal mb-0">Kelola data lokasi berbasis radius (titik pusat & radius meter)</h6>
|
|
</div>
|
|
<div class="col-12 col-xl-4">
|
|
<button type="button" class="btn btn-primary btn-sm float-right" data-toggle="modal" data-target="#addLocationModal">
|
|
<i class="mdi mdi-plus"></i> Tambah Lokasi
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-12 grid-margin stretch-card">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label>Cari Lokasi:</label>
|
|
<input type="text" class="form-control" id="searchLocation" placeholder="Cari berdasarkan nama lokasi...">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-striped" id="locationsTable">
|
|
<thead>
|
|
<tr>
|
|
<th>No</th>
|
|
<th>Nama Lokasi</th>
|
|
<th>Latitude</th>
|
|
<th>Longitude</th>
|
|
<th>Radius (m)</th>
|
|
<th>Tanggal Upload</th>
|
|
<th>Aksi</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="addLocationModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog modal-lg" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Tambah Lokasi Radius</h5>
|
|
<button type="button" class="close" data-dismiss="modal">
|
|
<span>×</span>
|
|
</button>
|
|
</div>
|
|
<form id="addLocationForm">
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label>Nama Lokasi <span class="text-danger">*</span></label>
|
|
<input type="text" class="form-control" name="name" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Latitude <span class="text-danger">*</span></label>
|
|
<input type="number" step="any" class="form-control" name="center_lat" required placeholder="Contoh: -8.123456">
|
|
<small class="form-text text-muted">Gunakan titik, minimal 4 digit di belakang koma. Contoh: -8.1234</small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Longitude <span class="text-danger">*</span></label>
|
|
<input type="number" step="any" class="form-control" name="center_lng" required placeholder="Contoh: 114.123456">
|
|
<small class="form-text text-muted">Gunakan titik, minimal 4 digit di belakang koma. Contoh: 114.1234</small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Radius (meter) <span class="text-danger">*</span></label>
|
|
<input type="number" class="form-control" name="radius" min="1" required>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="mdi mdi-upload"></i> Simpan
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="editLocationModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog modal-lg" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Edit Lokasi Radius</h5>
|
|
<button type="button" class="close" data-dismiss="modal">
|
|
<span>×</span>
|
|
</button>
|
|
</div>
|
|
<form id="editLocationForm">
|
|
<input type="hidden" name="location_name" id="editLocationName">
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label>Latitude <span class="text-danger">*</span></label>
|
|
<input type="number" step="any" class="form-control" name="center_lat" id="editCenterLat" required placeholder="Contoh: -8.123456">
|
|
<small class="form-text text-muted">Gunakan titik, minimal 4 digit di belakang koma. Contoh: -8.1234</small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Longitude <span class="text-danger">*</span></label>
|
|
<input type="number" step="any" class="form-control" name="center_lng" id="editCenterLng" required placeholder="Contoh: 114.123456">
|
|
<small class="form-text text-muted">Gunakan titik, minimal 4 digit di belakang koma. Contoh: 114.1234</small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Radius (meter) <span class="text-danger">*</span></label>
|
|
<input type="number" class="form-control" name="radius" id="editRadius" min="1" required>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Batal</button>
|
|
<button type="submit" class="btn btn-warning">
|
|
<i class="mdi mdi-refresh"></i> Update
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="detailLocationModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog modal-xl" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Detail Lokasi</h5>
|
|
<button type="button" class="close" data-dismiss="modal">
|
|
<span>×</span>
|
|
</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6>Informasi Lokasi</h6>
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td><strong>Nama:</strong></td>
|
|
<td id="detailName"></td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Latitude:</strong></td>
|
|
<td id="detailLat"></td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Longitude:</strong></td>
|
|
<td id="detailLng"></td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Radius:</strong></td>
|
|
<td id="detailRadius"></td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>Aksi</h6>
|
|
<button type="button" class="btn btn-info btn-sm" onclick="exportLocationData()">
|
|
<i class="mdi mdi-download"></i> Export JSON
|
|
</button>
|
|
<button type="button" class="btn btn-success btn-sm" onclick="viewOnMap()">
|
|
<i class="mdi mdi-map"></i> Lihat di Peta
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<hr>
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div id="radiusMapPreview" style="height: 300px;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Tutup</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="formatHelpModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog modal-lg" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Format File yang Didukung</h5>
|
|
<button type="button" class="close" data-dismiss="modal">
|
|
<span>×</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="modal-body">
|
|
<div class="accordion" id="formatAccordion">
|
|
|
|
<div class="card">
|
|
<div class="card-header p-2">
|
|
<h6 class="mb-0">
|
|
<button class="btn btn-link text-left w-100" type="button" data-toggle="collapse" data-target="#geojsonFormat">
|
|
<strong>1. Format GeoJSON dari QGIS</strong>
|
|
</button>
|
|
</h6>
|
|
</div>
|
|
<div id="geojsonFormat" class="collapse show" data-parent="#formatAccordion">
|
|
<div class="card-body bg-light">
|
|
<pre class="font-monospace"><code>{
|
|
"type": "FeatureCollection",
|
|
"name": "Nama Lokasi",
|
|
"features": [
|
|
{
|
|
"type": "Feature",
|
|
"geometry": {
|
|
"type": "Polygon",
|
|
"coordinates": [
|
|
[
|
|
[106.8456, -6.2144],
|
|
[106.8556, -6.2144],
|
|
[106.8556, -6.2044],
|
|
[106.8456, -6.2044],
|
|
[106.8456, -6.2144]
|
|
]
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header p-2">
|
|
<h6 class="mb-0">
|
|
<button class="btn btn-link text-left w-100 collapsed" type="button" data-toggle="collapse" data-target="#jsonFormat">
|
|
<strong>2. Format JSON Biasa</strong>
|
|
</button>
|
|
</h6>
|
|
</div>
|
|
<div id="jsonFormat" class="collapse" data-parent="#formatAccordion">
|
|
<div class="card-body bg-light">
|
|
<pre class="font-monospace"><code>{
|
|
"name": "Nama Lokasi",
|
|
"coordinates": [
|
|
{ "lat": -6.2144, "lng": 106.8456 },
|
|
{ "lat": -6.2144, "lng": 106.8556 },
|
|
{ "lat": -6.2044, "lng": 106.8556 },
|
|
{ "lat": -6.2044, "lng": 106.8456 },
|
|
{ "lat": -6.2144, "lng": 106.8456 }
|
|
]
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Tutup</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="mapDetailModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog modal-xl" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">
|
|
<i class="mdi mdi-map-marker"></i> Peta Lokasi:
|
|
<span id="mapLocationName" class="text-primary"></span>
|
|
</h5>
|
|
<button type="button" class="close" data-dismiss="modal">
|
|
<span>×</span>
|
|
</button>
|
|
</div>
|
|
<div class="modal-body p-0">
|
|
<div class="row no-gutters">
|
|
<div class="col-md-8" style="position: relative;">
|
|
<div id="locationMap"
|
|
style="
|
|
height: 400px;
|
|
border: 1px solid rgb(0, 0, 0);
|
|
position: absolute;
|
|
width: 100%;
|
|
top: 50%;
|
|
transform: translate(-50%,-50%);
|
|
left: 50%;
|
|
">
|
|
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="p-3">
|
|
<h6 class="mb-3">Informasi Lokasi</h6>
|
|
<div class="mb-3">
|
|
<small class="text-muted d-block">Nama Lokasi</small>
|
|
<strong id="mapInfoName">-</strong>
|
|
</div>
|
|
<div class="mb-3">
|
|
<small class="text-muted d-block">Total Polygon</small>
|
|
<span id="mapInfoPolygons" class="badge badge-info">0</span>
|
|
</div>
|
|
<div class="mb-3">
|
|
<small class="text-muted d-block">Total Koordinat</small>
|
|
<span id="mapInfoCoordinates" class="badge badge-success">0</span>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<h6 class="mb-3">Kontrol Peta</h6>
|
|
<div class="btn-group-vertical btn-block mb-3">
|
|
<button type="button" class="btn btn-sm btn-outline-primary" onclick="fitMapToBounds()">
|
|
<i class="mdi mdi-fit-to-page-outline"></i> Fit ke Polygon
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="togglePolygonLabels()">
|
|
<i class="mdi mdi-label"></i> Toggle Label
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-info" onclick="changeMapLayer()">
|
|
<i class="mdi mdi-layers"></i> Ganti Layer
|
|
</button>
|
|
</div>
|
|
|
|
<div class="alert alert-light p-2">
|
|
<small class="text-muted">
|
|
<i class="mdi mdi-information-outline"></i>
|
|
Klik pada polygon untuk melihat detail koordinat
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-info btn-sm" onclick="exportLocationData()">
|
|
<i class="mdi mdi-download"></i> Export Data
|
|
</button>
|
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Tutup</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@push('script')
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
|
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
|
|
crossorigin=""></script>
|
|
<script>
|
|
let locationsTable;
|
|
let currentLocationData = null;
|
|
|
|
$(document).ready(function() {
|
|
initDataTable();
|
|
});
|
|
|
|
function initDataTable() {
|
|
locationsTable = $('#locationsTable').DataTable({
|
|
processing: true,
|
|
serverSide: true,
|
|
responsive: true,
|
|
dom: '<"top"l>rt<"bottom"ip><"clear">',
|
|
ajax: {
|
|
url: '/api/admin/locations',
|
|
headers: {
|
|
'Authorization': getAuthorizationHeader(),
|
|
'Accept': 'application/json'
|
|
},
|
|
data: function(d) {
|
|
d.search.value = $('#searchLocation').val();
|
|
}
|
|
},
|
|
columns: [
|
|
{
|
|
data: null,
|
|
orderable: false,
|
|
searchable: false,
|
|
render: function(data, type, row, meta) {
|
|
return meta.row + meta.settings._iDisplayStart + 1;
|
|
}
|
|
},
|
|
{
|
|
data: 'name',
|
|
name: 'name',
|
|
render: function(data) {
|
|
return `<span class="badge badge-primary">${data}</span>`;
|
|
}
|
|
},
|
|
{
|
|
data: 'center_lat',
|
|
name: 'center_lat',
|
|
render: function(data) {
|
|
return `<span class="text-info">${data}</span>`;
|
|
}
|
|
},
|
|
{
|
|
data: 'center_lng',
|
|
name: 'center_lng',
|
|
render: function(data) {
|
|
return `<span class="text-info">${data}</span>`;
|
|
}
|
|
},
|
|
{
|
|
data: 'radius',
|
|
name: 'radius',
|
|
render: function(data) {
|
|
return `<span class="text-info">${data} meter</span>`;
|
|
}
|
|
},
|
|
{
|
|
data: 'created_at',
|
|
name: 'created_at',
|
|
render: function(data) {
|
|
return new Date(data).toLocaleDateString('id-ID', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
});
|
|
}
|
|
},
|
|
{
|
|
data: 'name',
|
|
orderable: false,
|
|
searchable: false,
|
|
render: function(data, type, row) {
|
|
return `
|
|
<div class="btn-group" role="group">
|
|
<button class="btn btn-sm btn-success" onclick="showLocationMap('${data}')" title="Lihat di Peta">
|
|
<i class="ti ti-map"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-info" onclick="viewLocation('${data}')" title="Detail">
|
|
<i class="ti ti-eye"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-warning" onclick="editLocation('${data}')" title="Edit">
|
|
<i class="ti ti-pencil"></i>
|
|
</button>
|
|
<button class="btn btn-sm btn-danger" onclick="deleteLocation('${data}')" title="Hapus">
|
|
<i class="ti ti-trash"></i>
|
|
</button>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
]
|
|
});
|
|
|
|
$('#searchLocation').on('keyup', function() {
|
|
locationsTable.ajax.reload();
|
|
});
|
|
}
|
|
|
|
$('#addLocationForm').submit(function(e) {
|
|
e.preventDefault();
|
|
const formData = $(this).serialize();
|
|
const submitBtn = $(this).find('button[type="submit"]');
|
|
const originalText = submitBtn.html();
|
|
submitBtn.html('<i class="mdi mdi-loading mdi-spin"></i> Menyimpan...').prop('disabled', true);
|
|
$.ajax({
|
|
url: '/api/admin/locations',
|
|
type: 'POST',
|
|
data: formData,
|
|
headers: {
|
|
'Authorization': getAuthorizationHeader(),
|
|
'Accept': 'application/json'
|
|
},
|
|
success: function(response) {
|
|
$('#addLocationModal').modal('hide');
|
|
$('#addLocationForm')[0].reset();
|
|
locationsTable.ajax.reload();
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Berhasil!',
|
|
text: response.message,
|
|
html: `
|
|
<p>${response.message}</p>
|
|
<p><strong>Nama:</strong> ${response.data.name}</p>
|
|
<p><strong>Latitude:</strong> ${response.data.center_lat}</p>
|
|
<p><strong>Longitude:</strong> ${response.data.center_lng}</p>
|
|
<p><strong>Radius:</strong> ${response.data.radius} meter</p>
|
|
`
|
|
});
|
|
},
|
|
error: function(xhr) {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Gagal!',
|
|
text: xhr.responseJSON.message || 'Terjadi kesalahan saat menyimpan lokasi'
|
|
});
|
|
},
|
|
complete: function() {
|
|
submitBtn.html(originalText).prop('disabled', false);
|
|
}
|
|
});
|
|
});
|
|
|
|
function viewLocation(name) {
|
|
$.ajax({
|
|
url: `/api/admin/locations/${name}`,
|
|
type: 'GET',
|
|
headers: {
|
|
'Authorization': getAuthorizationHeader(),
|
|
'Accept': 'application/json'
|
|
},
|
|
success: function(response) {
|
|
currentLocationData = response.data;
|
|
$('#detailName').text(response.data.name);
|
|
$('#detailLat').text(response.data.center_lat);
|
|
$('#detailLng').text(response.data.center_lng);
|
|
$('#detailRadius').text(response.data.radius + ' meter');
|
|
$('#detailLocationModal').modal('show');
|
|
renderRadiusMapPreview(response.data);
|
|
},
|
|
error: function(xhr) {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Gagal!',
|
|
text: 'Gagal mengambil detail lokasi'
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function renderRadiusMapPreview(data) {
|
|
setTimeout(function() {
|
|
let mapPreview = L.map('radiusMapPreview').setView([data.center_lat, data.center_lng], 16);
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© OpenStreetMap contributors',
|
|
maxZoom: 18
|
|
}).addTo(mapPreview);
|
|
L.circle([data.center_lat, data.center_lng], {
|
|
color: '#007bff',
|
|
fillColor: '#007bff',
|
|
fillOpacity: 0.2,
|
|
radius: data.radius
|
|
}).addTo(mapPreview);
|
|
mapPreview.invalidateSize();
|
|
}, 200);
|
|
}
|
|
|
|
function editLocation(name) {
|
|
$.ajax({
|
|
url: `/api/admin/locations/${name}`,
|
|
type: 'GET',
|
|
headers: {
|
|
'Authorization': getAuthorizationHeader(),
|
|
'Accept': 'application/json'
|
|
},
|
|
success: function(response) {
|
|
currentLocationData = response.data;
|
|
$('#editLocationName').val(name);
|
|
$('#editCenterLat').val(response.data.center_lat);
|
|
$('#editCenterLng').val(response.data.center_lng);
|
|
$('#editRadius').val(response.data.radius);
|
|
$('#editLocationModal').modal('show');
|
|
},
|
|
error: function(xhr) {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Gagal!',
|
|
text: 'Gagal mengambil data lokasi untuk edit'
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
$('#editLocationForm').submit(function(e) {
|
|
e.preventDefault();
|
|
const locationName = $('#editLocationName').val();
|
|
const formData = $(this).serialize();
|
|
const submitBtn = $(this).find('button[type="submit"]');
|
|
const originalText = submitBtn.html();
|
|
submitBtn.html('<i class="mdi mdi-loading mdi-spin"></i> Updating...').prop('disabled', true);
|
|
$.ajax({
|
|
url: `/api/admin/locations/${locationName}`,
|
|
type: 'POST',
|
|
data: formData,
|
|
headers: {
|
|
'Authorization': getAuthorizationHeader(),
|
|
'Accept': 'application/json',
|
|
'X-HTTP-Method-Override': 'PUT'
|
|
},
|
|
success: function(response) {
|
|
$('#editLocationModal').modal('hide');
|
|
$('#editLocationForm')[0].reset();
|
|
locationsTable.ajax.reload();
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Berhasil!',
|
|
text: response.message,
|
|
html: `
|
|
<p>${response.message}</p>
|
|
<p><strong>Nama:</strong> ${response.data.name}</p>
|
|
<p><strong>Latitude:</strong> ${response.data.center_lat}</p>
|
|
<p><strong>Longitude:</strong> ${response.data.center_lng}</p>
|
|
<p><strong>Radius:</strong> ${response.data.radius} meter</p>
|
|
`
|
|
});
|
|
},
|
|
error: function(xhr) {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Gagal!',
|
|
text: xhr.responseJSON.message || 'Terjadi kesalahan saat update lokasi'
|
|
});
|
|
},
|
|
complete: function() {
|
|
submitBtn.html(originalText).prop('disabled', false);
|
|
}
|
|
});
|
|
});
|
|
|
|
function deleteLocation(name) {
|
|
Swal.fire({
|
|
title: 'Konfirmasi Hapus',
|
|
text: `Apakah Anda yakin ingin menghapus lokasi "${name}"?`,
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#d33',
|
|
cancelButtonColor: '#3085d6',
|
|
confirmButtonText: 'Ya, Hapus!',
|
|
cancelButtonText: 'Batal'
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
$.ajax({
|
|
url: `/api/admin/locations/${name}`,
|
|
type: 'DELETE',
|
|
headers: {
|
|
'Authorization': getAuthorizationHeader(),
|
|
'Accept': 'application/json'
|
|
},
|
|
success: function(response) {
|
|
locationsTable.ajax.reload();
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: 'Berhasil!',
|
|
text: response.message
|
|
});
|
|
},
|
|
error: function(xhr) {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Gagal!',
|
|
text: xhr.responseJSON.message || 'Gagal menghapus lokasi'
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function exportLocationData() {
|
|
if (currentLocationData) {
|
|
const dataStr = JSON.stringify(currentLocationData, null, 2);
|
|
const dataBlob = new Blob([dataStr], {type: 'application/json'});
|
|
const url = URL.createObjectURL(dataBlob);
|
|
const link = document.createElement('a');
|
|
link.href = url;
|
|
link.download = `location_${currentLocationData.name}.json`;
|
|
link.click();
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
}
|
|
|
|
function viewOnMap() {
|
|
if (currentLocationData) {
|
|
$('#detailLocationModal').modal('hide');
|
|
showLocationMap(currentLocationData.name);
|
|
}
|
|
}
|
|
|
|
// MAP
|
|
var map = null;
|
|
let radiusLayer = null;
|
|
let currentMapLayer = 0;
|
|
const mapLayers = [
|
|
{
|
|
name: 'OpenStreetMap',
|
|
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
|
attribution: '© OpenStreetMap contributors'
|
|
},
|
|
{
|
|
name: 'Satellite',
|
|
url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
|
attribution: 'Tiles © Esri'
|
|
},
|
|
{
|
|
name: 'Terrain',
|
|
url: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
|
|
attribution: '© OpenTopoMap contributors'
|
|
}
|
|
];
|
|
|
|
function initializeMap() {
|
|
map = L.map('locationMap').setView([0, 0], 2);
|
|
L.tileLayer(mapLayers[currentMapLayer].url, {
|
|
attribution: mapLayers[currentMapLayer].attribution,
|
|
maxZoom: 18
|
|
}).addTo(map);
|
|
setTimeout(() => {
|
|
map.invalidateSize();
|
|
}, 100);
|
|
}
|
|
initializeMap();
|
|
|
|
function showLocationMap(name) {
|
|
$.ajax({
|
|
url: `/api/admin/locations/${name}`,
|
|
type: 'GET',
|
|
headers: {
|
|
'Authorization': getAuthorizationHeader(),
|
|
'Accept': 'application/json'
|
|
},
|
|
success: function(response) {
|
|
currentLocationData = response.data;
|
|
$('#mapLocationName').text(response.data.name);
|
|
$('#mapDetailModal').modal('show');
|
|
},
|
|
error: function(xhr) {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Gagal!',
|
|
text: 'Gagal mengambil data lokasi untuk peta'
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
$('#mapDetailModal').on('shown.bs.modal', function () {
|
|
if (map) {
|
|
setTimeout(() => {
|
|
map.invalidateSize();
|
|
if (currentLocationData) {
|
|
displayLocationOnMap(currentLocationData);
|
|
}
|
|
}, 100);
|
|
} else {
|
|
initializeMap();
|
|
}
|
|
});
|
|
|
|
function displayLocationOnMap(locationData) {
|
|
clearMapLayers();
|
|
if (!locationData) return;
|
|
$('#mapInfoName').text(locationData.name);
|
|
$('#mapInfoCoordinates').text('-');
|
|
$('#mapInfoPolygons').text('-');
|
|
if (locationData.center_lat && locationData.center_lng && locationData.radius) {
|
|
map.setView([locationData.center_lat, locationData.center_lng], 16);
|
|
radiusLayer = L.circle([locationData.center_lat, locationData.center_lng], {
|
|
color: '#007bff',
|
|
fillColor: '#007bff',
|
|
fillOpacity: 0.2,
|
|
radius: locationData.radius
|
|
}).addTo(map);
|
|
}
|
|
}
|
|
|
|
function clearMapLayers() {
|
|
if (radiusLayer && map) {
|
|
map.removeLayer(radiusLayer);
|
|
radiusLayer = null;
|
|
}
|
|
}
|
|
|
|
function changeMapLayer() {
|
|
currentMapLayer = (currentMapLayer + 1) % mapLayers.length;
|
|
const layer = mapLayers[currentMapLayer];
|
|
map.eachLayer(function(layerObj) {
|
|
if (layerObj instanceof L.TileLayer) {
|
|
map.removeLayer(layerObj);
|
|
}
|
|
});
|
|
L.tileLayer(layer.url, {
|
|
attribution: layer.attribution,
|
|
maxZoom: 18
|
|
}).addTo(map);
|
|
const toast = $(`
|
|
<div class="toast" style="position: fixed; top: 20px; right: 20px; z-index: 9999;">
|
|
<div class="toast-body bg-info text-white">
|
|
Layer: ${layer.name}
|
|
</div>
|
|
</div>
|
|
`);
|
|
$('body').append(toast);
|
|
toast.fadeIn().delay(2000).fadeOut(function() {
|
|
$(this).remove();
|
|
});
|
|
}
|
|
|
|
$('input[name="center_lat"], input[name="center_lng"]').on('blur', function() {
|
|
let val = $(this).val();
|
|
if (!/^[-+]?[0-9]{1,3}\.[0-9]{4,}$/.test(val)) {
|
|
alert('Masukkan koordinat desimal minimal 4 digit di belakang koma!');
|
|
$(this).val('');
|
|
}
|
|
});
|
|
</script>
|
|
@endpush
|