pembaruan sig tps
This commit is contained in:
parent
d54ab1258e
commit
ccf66298ab
|
|
@ -10,186 +10,140 @@
|
||||||
|
|
||||||
class TpsController extends Controller
|
class TpsController extends Controller
|
||||||
{
|
{
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$title = 'Data TPS';
|
$title = 'Data TPS';
|
||||||
|
$tps = LokasiTps::with('kategori')
|
||||||
// Ambil TPS + kategori + jumlah aduan
|
->withCount('aduan')
|
||||||
$tps = LokasiTps::with('kategori')
|
->get();
|
||||||
->withCount('aduan')
|
return view('admin.tps.index', compact('title', 'tps'));
|
||||||
->get();
|
}
|
||||||
|
|
||||||
return view('admin.tps.index', compact('title', 'tps'));
|
public function create()
|
||||||
}
|
{
|
||||||
|
$title = 'Tambah TPS';
|
||||||
public function create()
|
$kategori = KategoriTps::all();
|
||||||
{
|
return view('admin.tps.create', compact('title', 'kategori'));
|
||||||
$title = 'Tambah TPS';
|
}
|
||||||
$kategori = KategoriTps::all();
|
private function convertToDecimal($coordinate)
|
||||||
|
{
|
||||||
return view('admin.tps.create', compact('title', 'kategori'));
|
if (is_numeric($coordinate)) {
|
||||||
}
|
return (float) $coordinate;
|
||||||
|
}
|
||||||
private function convertToDecimal($coordinate)
|
$coordinate = html_entity_decode($coordinate);
|
||||||
{
|
$coordinate = strtoupper(trim($coordinate));
|
||||||
// decimal langsung
|
$coordinate = str_replace(
|
||||||
if (is_numeric($coordinate)) {
|
['°', "'", '"'],
|
||||||
return (float) $coordinate;
|
[' ', ' ', ' '],
|
||||||
}
|
$coordinate
|
||||||
|
);
|
||||||
$coordinate = html_entity_decode($coordinate);
|
preg_match('/([NSEW])/', $coordinate, $dirMatch);
|
||||||
$coordinate = strtoupper(trim($coordinate));
|
if (!$dirMatch) return null;
|
||||||
|
$direction = $dirMatch[1];
|
||||||
// ganti simbol jadi seragam
|
preg_match_all('/\d+(\.\d+)?/', $coordinate, $numbers);
|
||||||
$coordinate = str_replace(
|
if (count($numbers[0]) < 3) return null;
|
||||||
['°', "'", '"'],
|
[$deg, $min, $sec] = array_map('floatval', $numbers[0]);
|
||||||
[' ', ' ', ' '],
|
$decimal = $deg + ($min / 60) + ($sec / 3600);
|
||||||
$coordinate
|
if (in_array($direction, ['S', 'W'])) {
|
||||||
);
|
$decimal *= -1;
|
||||||
|
}
|
||||||
// ambil arah
|
return $decimal;
|
||||||
preg_match('/([NSEW])/', $coordinate, $dirMatch);
|
}
|
||||||
if (!$dirMatch) return null;
|
public function store(Request $request)
|
||||||
|
{
|
||||||
$direction = $dirMatch[1];
|
$request->validate([
|
||||||
|
'kategori_tps_id' => 'required|exists:kategori_tps,id_kategori_tps',
|
||||||
// ambil angka
|
'nama_tps' => 'required|string|max:255',
|
||||||
preg_match_all('/\d+(\.\d+)?/', $coordinate, $numbers);
|
'alamat_tps' => 'required|string|max:255',
|
||||||
if (count($numbers[0]) < 3) return null;
|
'status_tps' => 'required',
|
||||||
|
'tahun_pembuatan' => 'required|numeric',
|
||||||
[$deg, $min, $sec] = array_map('floatval', $numbers[0]);
|
'kapasitas_tps' => 'required',
|
||||||
|
'latitude' => 'required',
|
||||||
$decimal = $deg + ($min / 60) + ($sec / 3600);
|
'longitude' => 'required',
|
||||||
|
'foto_tps' => 'nullable|image|mimes:jpg,jpeg,png|max:2048',
|
||||||
if (in_array($direction, ['S', 'W'])) {
|
]);
|
||||||
$decimal *= -1;
|
$latitude = $this->convertToDecimal($request->latitude);
|
||||||
}
|
$longitude = $this->convertToDecimal($request->longitude);
|
||||||
|
if ($latitude === null || $longitude === null) {
|
||||||
return $decimal;
|
return back()->withErrors(['Koordinat tidak valid'])->withInput();
|
||||||
}
|
}
|
||||||
|
$foto = $request->hasFile('foto_tps')
|
||||||
public function store(Request $request)
|
? $request->file('foto_tps')->store('foto-tps', 'public')
|
||||||
{
|
: null;
|
||||||
$request->validate([
|
LokasiTps::create([
|
||||||
'kategori_tps_id' => 'required|exists:kategori_tps,id_kategori_tps',
|
'kategori_tps_id' => $request->kategori_tps_id,
|
||||||
'nama_tps' => 'required|string|max:255',
|
'nama_tps' => $request->nama_tps,
|
||||||
'alamat_tps' => 'required|string|max:255',
|
'alamat_tps' => $request->alamat_tps,
|
||||||
'status_tps' => 'required',
|
'status_tps' => $request->status_tps,
|
||||||
'tahun_pembuatan' => 'required|numeric',
|
'tahun_pembuatan' => $request->tahun_pembuatan,
|
||||||
'kapasitas_tps' => 'required',
|
'kapasitas_tps' => $request->kapasitas_tps,
|
||||||
'latitude' => 'required',
|
'latitude' => $latitude,
|
||||||
'longitude' => 'required',
|
'longitude' => $longitude,
|
||||||
'foto_tps' => 'nullable|image|mimes:jpg,jpeg,png|max:2048',
|
'foto_tps' => $foto,
|
||||||
]);
|
]);
|
||||||
|
return redirect()->route('admin.tps.index')
|
||||||
// 🔥 KONVERSI KOORDINAT
|
->with('success', 'Data TPS berhasil ditambahkan');
|
||||||
$latitude = $this->convertToDecimal($request->latitude);
|
}
|
||||||
$longitude = $this->convertToDecimal($request->longitude);
|
|
||||||
|
public function edit($id)
|
||||||
if ($latitude === null || $longitude === null) {
|
{
|
||||||
return back()->withErrors(['Koordinat tidak valid'])->withInput();
|
$title = 'Edit TPS';
|
||||||
}
|
$tps = LokasiTps::findOrFail($id);
|
||||||
|
$kategori = KategoriTps::all();
|
||||||
// Upload foto
|
return view('admin.tps.edit', compact('title', 'tps', 'kategori'));
|
||||||
$foto = $request->hasFile('foto_tps')
|
}
|
||||||
? $request->file('foto_tps')->store('foto-tps', 'public')
|
public function update(Request $request, $id)
|
||||||
: null;
|
{
|
||||||
|
$tps = LokasiTps::findOrFail($id);
|
||||||
LokasiTps::create([
|
$request->validate([
|
||||||
'kategori_tps_id' => $request->kategori_tps_id,
|
'kategori_tps_id' => 'required|exists:kategori_tps,id_kategori_tps',
|
||||||
'nama_tps' => $request->nama_tps,
|
'nama_tps' => 'required|string|max:255',
|
||||||
'alamat_tps' => $request->alamat_tps,
|
'alamat_tps' => 'required|string|max:255',
|
||||||
'status_tps' => $request->status_tps,
|
'status_tps' => 'required',
|
||||||
'tahun_pembuatan' => $request->tahun_pembuatan,
|
'tahun_pembuatan' => 'required|numeric',
|
||||||
'kapasitas_tps' => $request->kapasitas_tps,
|
'kapasitas_tps' => 'required',
|
||||||
'latitude' => $latitude,
|
'latitude' => 'required',
|
||||||
'longitude' => $longitude,
|
'longitude' => 'required',
|
||||||
'foto_tps' => $foto,
|
'foto_tps' => 'nullable|image|mimes:jpg,jpeg,png|max:4096',
|
||||||
]);
|
]);
|
||||||
|
$latitude = $this->convertToDecimal($request->latitude);
|
||||||
return redirect()->route('admin.tps.index')
|
$longitude = $this->convertToDecimal($request->longitude);
|
||||||
->with('success', 'Data TPS berhasil ditambahkan');
|
if ($latitude === null || $longitude === null) {
|
||||||
}
|
return back()
|
||||||
|
->withErrors(['Koordinat tidak valid. Gunakan format Decimal atau DMS.'])
|
||||||
|
->withInput();
|
||||||
public function edit($id)
|
}
|
||||||
{
|
if ($request->hasFile('foto_tps')) {
|
||||||
$title = 'Edit TPS';
|
if ($tps->foto_tps) {
|
||||||
$tps = LokasiTps::findOrFail($id);
|
Storage::disk('public')->delete($tps->foto_tps);
|
||||||
$kategori = KategoriTps::all();
|
}
|
||||||
|
$foto = $request->file('foto_tps')->store('foto-tps', 'public');
|
||||||
return view('admin.tps.edit', compact('title', 'tps', 'kategori'));
|
} else {
|
||||||
}
|
$foto = $tps->foto_tps;
|
||||||
|
}
|
||||||
public function update(Request $request, $id)
|
$tps->update([
|
||||||
{
|
'kategori_tps_id' => $request->kategori_tps_id,
|
||||||
$tps = LokasiTps::findOrFail($id);
|
'nama_tps' => $request->nama_tps,
|
||||||
|
'alamat_tps' => $request->alamat_tps,
|
||||||
// VALIDASI
|
'status_tps' => $request->status_tps,
|
||||||
$request->validate([
|
'tahun_pembuatan' => $request->tahun_pembuatan,
|
||||||
'kategori_tps_id' => 'required|exists:kategori_tps,id_kategori_tps',
|
'kapasitas_tps' => $request->kapasitas_tps,
|
||||||
'nama_tps' => 'required|string|max:255',
|
'latitude' => $latitude,
|
||||||
'alamat_tps' => 'required|string|max:255',
|
'longitude' => $longitude,
|
||||||
'status_tps' => 'required',
|
'foto_tps' => $foto,
|
||||||
'tahun_pembuatan' => 'required|numeric',
|
]);
|
||||||
'kapasitas_tps' => 'required',
|
return redirect()->route('admin.tps.index')
|
||||||
'latitude' => 'required',
|
->with('success', 'Data TPS berhasil diperbarui');
|
||||||
'longitude' => 'required',
|
}
|
||||||
'foto_tps' => 'nullable|image|mimes:jpg,jpeg,png|max:4096',
|
|
||||||
]);
|
public function destroy($id)
|
||||||
|
{
|
||||||
// 🔥 KONVERSI KOORDINAT (DMS / DECIMAL)
|
$tps = LokasiTps::findOrFail($id);
|
||||||
$latitude = $this->convertToDecimal($request->latitude);
|
if ($tps->foto_tps) {
|
||||||
$longitude = $this->convertToDecimal($request->longitude);
|
Storage::disk('public')->delete($tps->foto_tps);
|
||||||
|
}
|
||||||
// Jika koordinat tidak valid
|
$tps->delete();
|
||||||
if ($latitude === null || $longitude === null) {
|
return redirect()->route('admin.tps.index')
|
||||||
return back()
|
->with('success', 'Data TPS berhasil dihapus');
|
||||||
->withErrors(['Koordinat tidak valid. Gunakan format Decimal atau DMS.'])
|
}
|
||||||
->withInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 📸 UPLOAD FOTO JIKA ADA
|
|
||||||
if ($request->hasFile('foto_tps')) {
|
|
||||||
|
|
||||||
// Hapus foto lama
|
|
||||||
if ($tps->foto_tps) {
|
|
||||||
Storage::disk('public')->delete($tps->foto_tps);
|
|
||||||
}
|
|
||||||
|
|
||||||
$foto = $request->file('foto_tps')->store('foto-tps', 'public');
|
|
||||||
} else {
|
|
||||||
$foto = $tps->foto_tps;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 💾 UPDATE DATA
|
|
||||||
$tps->update([
|
|
||||||
'kategori_tps_id' => $request->kategori_tps_id,
|
|
||||||
'nama_tps' => $request->nama_tps,
|
|
||||||
'alamat_tps' => $request->alamat_tps,
|
|
||||||
'status_tps' => $request->status_tps,
|
|
||||||
'tahun_pembuatan' => $request->tahun_pembuatan,
|
|
||||||
'kapasitas_tps' => $request->kapasitas_tps,
|
|
||||||
'latitude' => $latitude,
|
|
||||||
'longitude' => $longitude,
|
|
||||||
'foto_tps' => $foto,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return redirect()->route('admin.tps.index')
|
|
||||||
->with('success', 'Data TPS berhasil diperbarui');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function destroy($id)
|
|
||||||
{
|
|
||||||
$tps = LokasiTps::findOrFail($id);
|
|
||||||
|
|
||||||
if ($tps->foto_tps) {
|
|
||||||
Storage::disk('public')->delete($tps->foto_tps);
|
|
||||||
}
|
|
||||||
|
|
||||||
$tps->delete();
|
|
||||||
|
|
||||||
return redirect()->route('admin.tps.index')
|
|
||||||
->with('success', 'Data TPS berhasil dihapus');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,22 +8,15 @@
|
||||||
use Illuminate\Support\Facades\RateLimiter;
|
use Illuminate\Support\Facades\RateLimiter;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
class LoginRequest extends FormRequest
|
class LoginRequest extends FormRequest
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*/
|
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|
@ -32,31 +25,25 @@ public function rules(): array
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function authenticate(): void
|
||||||
* Attempt to authenticate the request's credentials.
|
{
|
||||||
*
|
$user = User::where('username', $this->username)->first();
|
||||||
* @throws \Illuminate\Validation\ValidationException
|
if (!$user) {
|
||||||
*/
|
throw ValidationException::withMessages([
|
||||||
public function authenticate(): void
|
'username' => 'Username tidak terdaftar.',
|
||||||
{
|
]);
|
||||||
$this->ensureIsNotRateLimited();
|
|
||||||
|
|
||||||
if (! Auth::attempt($this->only('username', 'password'), $this->boolean('remember'))) {
|
|
||||||
RateLimiter::hit($this->throttleKey());
|
|
||||||
|
|
||||||
throw ValidationException::withMessages([
|
|
||||||
'email' => trans('auth.failed'),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
RateLimiter::clear($this->throttleKey());
|
|
||||||
}
|
}
|
||||||
|
if (!Auth::attempt([
|
||||||
|
'username' => $this->username,
|
||||||
|
'password' => $this->password,
|
||||||
|
])) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'password' => 'Password yang dimasukkan salah.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure the login request is not rate limited.
|
|
||||||
*
|
|
||||||
* @throws \Illuminate\Validation\ValidationException
|
|
||||||
*/
|
|
||||||
public function ensureIsNotRateLimited(): void
|
public function ensureIsNotRateLimited(): void
|
||||||
{
|
{
|
||||||
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
|
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
|
||||||
|
|
@ -68,18 +55,16 @@ public function ensureIsNotRateLimited(): void
|
||||||
$seconds = RateLimiter::availableIn($this->throttleKey());
|
$seconds = RateLimiter::availableIn($this->throttleKey());
|
||||||
|
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
'email' => trans('auth.throttle', [
|
'username' => trans('auth.throttle', [
|
||||||
'seconds' => $seconds,
|
'seconds' => $seconds,
|
||||||
'minutes' => ceil($seconds / 60),
|
'minutes' => ceil($seconds / 60),
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the rate limiting throttle key for the request.
|
|
||||||
*/
|
|
||||||
public function throttleKey(): string
|
public function throttleKey(): string
|
||||||
{
|
{
|
||||||
return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip());
|
return Str::transliterate(Str::lower($this->string('username')) . '|' . $this->ip());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3027,3 +3027,44 @@ .btn-aduan-tps:hover {
|
||||||
background: #bb2d3b; /* merah lebih gelap */
|
background: #bb2d3b; /* merah lebih gelap */
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.map-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-action {
|
||||||
|
position: absolute;
|
||||||
|
top: 16px;
|
||||||
|
right: 16px;
|
||||||
|
z-index: 1000;
|
||||||
|
background: #fff;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 6px 20px rgba(0,0,0,.15);
|
||||||
|
max-width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-action-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-lokasi {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
background: #00A86B;
|
||||||
|
color: #fff;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-lokasi:hover,
|
||||||
|
.btn-lokasi:focus:hover {
|
||||||
|
color: var(--contrast-color);
|
||||||
|
background: color-mix(in srgb, var(--accent-color), transparent 15%);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
<div class="mb-3 d-flex justify-content-between align-items-center">
|
||||||
<div>
|
<div>
|
||||||
<h4 class="card-title mb-0">Data TPS</h4>
|
<h4 class="mb-0 card-title">Data TPS</h4>
|
||||||
<p class="card-description mb-0">
|
<p class="mb-0 card-description">
|
||||||
Daftar Tempat Pembuangan Sampah (TPS)
|
Daftar Tempat Pembuangan Sampah (TPS)
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -20,82 +20,68 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Nama TPS</th>
|
<th>Nama TPS</th>
|
||||||
<th>Kategori</th>
|
<th>Kategori</th>
|
||||||
<th>Foto</th>
|
<th>Foto</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>Aksi</th>
|
<th>Aksi</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@forelse ($tps as $item)
|
||||||
@forelse ($tps as $item)
|
<tr>
|
||||||
<tr>
|
<td>{{ $item->nama_tps }}</td>
|
||||||
<!-- NAMA -->
|
<td>
|
||||||
<td>{{ $item->nama_tps }}</td>
|
{{ $item->kategori->nama_kategori ?? '-' }}
|
||||||
|
</td>
|
||||||
<!-- KATEGORI -->
|
<td>
|
||||||
<td>
|
@if ($item->foto_tps)
|
||||||
{{ $item->kategori->nama_kategori ?? '-' }}
|
<img src="{{ asset('storage/' . $item->foto_tps) }}" alt="Foto TPS"
|
||||||
</td>
|
style="
|
||||||
|
width:200px;
|
||||||
<!-- FOTO -->
|
height:auto;
|
||||||
<td>
|
border-radius:2px;
|
||||||
@if ($item->foto_tps)
|
">
|
||||||
<img src="{{ asset('storage/' . $item->foto_tps) }}" alt="Foto TPS"
|
@else
|
||||||
style="
|
<span class="text-muted">-</span>
|
||||||
width:200px;
|
@endif
|
||||||
height:auto;
|
</td>
|
||||||
border-radius:2px;
|
<td>
|
||||||
">
|
@if ($item->status_tps == 'Aktif')
|
||||||
@else
|
<label class="badge badge-success">Aktif</label>
|
||||||
<span class="text-muted">-</span>
|
@elseif ($item->status_tps == 'Tidak Aktif')
|
||||||
@endif
|
<label class="badge badge-secondary">Tidak Aktif</label>
|
||||||
</td>
|
@else
|
||||||
|
<label class="badge badge-warning">Pembangunan</label>
|
||||||
|
@endif
|
||||||
<!-- STATUS -->
|
</td>
|
||||||
<td>
|
<td class="text-center">
|
||||||
@if ($item->status_tps == 'Aktif')
|
<a href="{{ route('admin.tps.edit', $item->id_tps) }}"
|
||||||
<label class="badge badge-success">Aktif</label>
|
class="btn btn-warning btn-sm me-1">
|
||||||
@elseif ($item->status_tps == 'Tidak Aktif')
|
<i class="bi bi-pencil-square"></i>
|
||||||
<label class="badge badge-secondary">Tidak Aktif</label>
|
</a>
|
||||||
@else
|
<form action="{{ route('admin.tps.destroy', $item->id_tps) }}" method="POST"
|
||||||
<label class="badge badge-warning">Pembangunan</label>
|
class="form-hapus" style="display:inline;">
|
||||||
@endif
|
@csrf
|
||||||
</td>
|
@method('DELETE')
|
||||||
|
<button type="submit" class="btn btn-danger btn-sm">
|
||||||
<!-- AKSI -->
|
<i class="bi bi-trash"></i>
|
||||||
<td class="text-center">
|
</button>
|
||||||
<a href="{{ route('admin.tps.edit', $item->id_tps) }}"
|
</form>
|
||||||
class="btn btn-warning btn-sm me-1">
|
</td>
|
||||||
<i class="bi bi-pencil-square"></i>
|
</tr>
|
||||||
</a>
|
@empty
|
||||||
|
<tr>
|
||||||
<form action="{{ route('admin.tps.destroy', $item->id_tps) }}" method="POST"
|
<td colspan="5" class="text-center">
|
||||||
class="form-hapus" style="display:inline;">
|
Data TPS belum tersedia
|
||||||
@csrf
|
</td>
|
||||||
@method('DELETE')
|
</tr>
|
||||||
<button type="submit" class="btn btn-danger btn-sm">
|
@endforelse
|
||||||
<i class="bi bi-trash"></i>
|
</tbody>
|
||||||
</button>
|
</table>
|
||||||
</form>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
@empty
|
|
||||||
<tr>
|
|
||||||
<td colspan="5" class="text-center">
|
|
||||||
Data TPS belum tersedia
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
@endforelse
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?php
|
||||||
|
public function create()
|
||||||
|
{
|
||||||
|
$title = 'Tambah Data Sampah';
|
||||||
|
$users = User::all();
|
||||||
|
return view('admin.sampah.create', compact('title', 'users'));
|
||||||
|
}
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'tahun' => 'required|numeric',
|
||||||
|
'total_sampah' => 'required|numeric',
|
||||||
|
'total_kelola' => 'required|numeric',
|
||||||
|
'total_daur_ulang' => 'required|numeric',
|
||||||
|
]);
|
||||||
|
$sisa_sampah = $request->total_sampah
|
||||||
|
- ($request->total_kelola + $request->total_daur_ulang);
|
||||||
|
Sampah::create([
|
||||||
|
'user_id' => Auth::id(),
|
||||||
|
'tahun' => $request->tahun,
|
||||||
|
'total_sampah' => $request->total_sampah,
|
||||||
|
'total_kelola' => $request->total_kelola,
|
||||||
|
'total_daur_ulang' => $request->total_daur_ulang,
|
||||||
|
'sisa_sampah' => $sisa_sampah,
|
||||||
|
]);
|
||||||
|
return redirect()->route('admin.sampah.index')
|
||||||
|
->with('success', 'Data sampah berhasil ditambahkan');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit($id)
|
||||||
|
{
|
||||||
|
$title = 'Edit Data Sampah';
|
||||||
|
$sampah = Sampah::findOrFail($id);
|
||||||
|
$users = User::all();
|
||||||
|
return view('admin.sampah.edit', compact('title', 'sampah', 'users'));
|
||||||
|
}
|
||||||
|
public function update(Request $request, $id)
|
||||||
|
{
|
||||||
|
$sampah = Sampah::findOrFail($id);
|
||||||
|
$request->validate([
|
||||||
|
'user_id' => 'required|exists:users,id',
|
||||||
|
'tahun' => 'required|numeric',
|
||||||
|
'total_sampah' => 'required|numeric',
|
||||||
|
'total_kelola' => 'required|numeric',
|
||||||
|
'total_daur_ulang' => 'required|numeric',
|
||||||
|
]);
|
||||||
|
$sisa_sampah = $request->total_sampah
|
||||||
|
- ($request->total_kelola + $request->total_daur_ulang);
|
||||||
|
$sampah->update([
|
||||||
|
'user_id' => $request->user_id,
|
||||||
|
'tahun' => $request->tahun,
|
||||||
|
'total_sampah' => $request->total_sampah,
|
||||||
|
'total_kelola' => $request->total_kelola,
|
||||||
|
'total_daur_ulang' => $request->total_daur_ulang,
|
||||||
|
'sisa_sampah' => $sisa_sampah,
|
||||||
|
]);
|
||||||
|
return redirect()->route('admin.sampah.index')
|
||||||
|
->with('success', 'Data sampah berhasil diperbarui');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy($id)
|
||||||
|
{
|
||||||
|
$sampah = Sampah::findOrFail($id);
|
||||||
|
$sampah->delete();
|
||||||
|
return redirect()->route('admin.sampah.index')
|
||||||
|
->with('success', 'Data sampah berhasil dihapus');
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,9 @@
|
||||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
header, .navbar, #header {
|
header,
|
||||||
|
.navbar,
|
||||||
|
#header {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 2000 !important;
|
z-index: 2000 !important;
|
||||||
|
|
@ -22,20 +24,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 450px;
|
height: 450px;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 6px 20px rgba(0,0,0,.12);
|
box-shadow: 0 6px 20px rgba(0, 0, 0, .12);
|
||||||
}
|
|
||||||
|
|
||||||
.map-filter {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.map-info {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #555;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.legend {
|
.legend {
|
||||||
|
|
@ -43,7 +32,7 @@
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
box-shadow: 0 2px 12px rgba(0,0,0,.15);
|
box-shadow: 0 2px 12px rgba(0, 0, 0, .15);
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,6 +42,11 @@
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user-location {
|
||||||
|
filter: drop-shadow(0 0 8px rgba(13,110,253,0.8))
|
||||||
|
drop-shadow(0 0 16px rgba(13,110,253,0.6));
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="page-title">
|
<div class="page-title">
|
||||||
|
|
@ -69,156 +63,152 @@
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<div class="map-wrapper">
|
||||||
<!-- INFO & BUTTON -->
|
<div class="map-action">
|
||||||
<div class="map-filter">
|
<div class="map-action-text">
|
||||||
<div class="map-info">
|
Temukan <b>TPS terdekat</b> dari lokasi Anda saat ini
|
||||||
Klik tombol di samping untuk menemukan <b>TPS terdekat</b> dari lokasi Anda saat ini.
|
</div>
|
||||||
|
<button type="button" id="btnLokasi" class="btn-lokasi">
|
||||||
|
Cari TPS Terdekat
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="btnLokasi" class="btn btn-primary btn-sm">
|
<div id="mapTPS"></div>
|
||||||
Cari TPS Terdekat
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="mapTPS"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* =============================
|
var map = L.map('mapTPS').setView([-7.6078, 111.903], 12);
|
||||||
INIT MAP
|
|
||||||
============================= */
|
|
||||||
var map = L.map('mapTPS').setView([-7.6078, 111.903], 12);
|
|
||||||
|
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
maxZoom: 19,
|
maxZoom: 19,
|
||||||
attribution: '© OpenStreetMap'
|
attribution: '© OpenStreetMap'
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
|
|
||||||
/* =============================
|
function icon(color) {
|
||||||
ICON TPS
|
return new L.Icon({
|
||||||
============================= */
|
iconUrl: `https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-${color}.png`,
|
||||||
function icon(color) {
|
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
|
||||||
return new L.Icon({
|
iconSize: [25, 41],
|
||||||
iconUrl: `https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-${color}.png`,
|
iconAnchor: [12, 41],
|
||||||
|
popupAnchor: [1, -34]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var iconTPS = icon('green');
|
||||||
|
var iconTPS3R = icon('blue');
|
||||||
|
var iconTPA = icon('red');
|
||||||
|
|
||||||
|
var iconNear = new L.Icon({
|
||||||
|
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-gold.png',
|
||||||
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
|
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
|
||||||
iconSize: [25, 41],
|
iconSize: [25, 41],
|
||||||
iconAnchor: [12, 41],
|
iconAnchor: [12, 41],
|
||||||
popupAnchor: [1, -34]
|
popupAnchor: [1, -34]
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
var iconTPS = icon('green');
|
var tpsData = @json($tps);
|
||||||
var iconTPS3R = icon('blue');
|
var markers = [];
|
||||||
var iconTPA = icon('red');
|
|
||||||
var iconNear = icon('yellow');
|
|
||||||
|
|
||||||
/* =============================
|
tpsData.forEach(tps => {
|
||||||
DATA TPS
|
if (!tps.latitude || !tps.longitude) return;
|
||||||
============================= */
|
|
||||||
var tpsData = @json($tps);
|
|
||||||
var markers = [];
|
|
||||||
|
|
||||||
/* =============================
|
let iconUse = iconTPS;
|
||||||
MARKER TPS
|
if (tps.kategori_tps_id == 5) iconUse = iconTPS3R;
|
||||||
============================= */
|
if (tps.kategori_tps_id == 6) iconUse = iconTPA;
|
||||||
tpsData.forEach(tps => {
|
|
||||||
if (!tps.latitude || !tps.longitude) return;
|
|
||||||
|
|
||||||
let iconUse = iconTPS;
|
let marker = L.marker([tps.latitude, tps.longitude], { icon: iconUse })
|
||||||
if (tps.kategori_tps_id == 5) iconUse = iconTPS3R;
|
.bindPopup(`
|
||||||
if (tps.kategori_tps_id == 6) iconUse = iconTPA;
|
<strong>${tps.nama_tps}</strong><br>
|
||||||
|
<small>${tps.alamat_tps ?? '-'}</small><br>
|
||||||
|
<span class="badge bg-success">${tps.status_tps ?? '-'}</span>
|
||||||
|
<hr style="margin:6px 0">
|
||||||
|
<a href="/tps/${tps.id_tps}" style="font-size:13px">Detail TPS</a>
|
||||||
|
`)
|
||||||
|
.addTo(map);
|
||||||
|
|
||||||
let marker = L.marker([tps.latitude, tps.longitude], { icon: iconUse })
|
marker.tpsData = tps;
|
||||||
.bindPopup(`
|
markers.push(marker);
|
||||||
<strong>${tps.nama_tps}</strong><br>
|
});
|
||||||
<small>${tps.alamat_tps ?? '-'}</small><br>
|
|
||||||
<span class="mt-1 badge bg-success">${tps.status_tps ?? '-'}</span>
|
|
||||||
<hr style="margin:6px 0">
|
|
||||||
<a href="/tps/${tps.id_tps}" style="font-size:13px">Detail TPS</a>
|
|
||||||
`)
|
|
||||||
.addTo(map);
|
|
||||||
|
|
||||||
marker.tpsData = tps;
|
/* ===== LEGEND KATEGORI (BALIK 😤) ===== */
|
||||||
markers.push(marker);
|
var legend = L.control({ position: 'bottomleft' });
|
||||||
});
|
legend.onAdd = function () {
|
||||||
|
var div = L.DomUtil.create('div', 'legend');
|
||||||
|
div.innerHTML = `
|
||||||
|
<strong>Kategori TPS</strong><br>
|
||||||
|
<i style="background:#198754"></i> TPS<br>
|
||||||
|
<i style="background:#0d6efd"></i> TPS 3R<br>
|
||||||
|
<i style="background:#dc3545"></i> TPA
|
||||||
|
`;
|
||||||
|
return div;
|
||||||
|
};
|
||||||
|
legend.addTo(map);
|
||||||
|
|
||||||
/* =============================
|
function getDistance(lat1, lon1, lat2, lon2) {
|
||||||
LEGEND
|
const R = 6371;
|
||||||
============================= */
|
const dLat = (lat2 - lat1) * Math.PI / 180;
|
||||||
var legend = L.control({ position: 'bottomleft' });
|
const dLon = (lon2 - lon1) * Math.PI / 180;
|
||||||
legend.onAdd = function () {
|
const a =
|
||||||
var div = L.DomUtil.create('div', 'legend');
|
Math.sin(dLat / 2) ** 2 +
|
||||||
div.innerHTML = `
|
Math.cos(lat1 * Math.PI / 180) *
|
||||||
<strong>Kategori TPS</strong><br>
|
Math.cos(lat2 * Math.PI / 180) *
|
||||||
<i style="background:#198754"></i> TPS<br>
|
Math.sin(dLon / 2) ** 2;
|
||||||
<i style="background:#0d6efd"></i> TPS 3R<br>
|
return R * (2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)));
|
||||||
<i style="background:#dc3545"></i> TPA
|
|
||||||
`;
|
|
||||||
return div;
|
|
||||||
};
|
|
||||||
legend.addTo(map);
|
|
||||||
|
|
||||||
/* =============================
|
|
||||||
HITUNG JARAK
|
|
||||||
============================= */
|
|
||||||
function getDistance(lat1, lon1, lat2, lon2) {
|
|
||||||
const R = 6371;
|
|
||||||
const dLat = (lat2-lat1) * Math.PI/180;
|
|
||||||
const dLon = (lon2-lon1) * Math.PI/180;
|
|
||||||
const a =
|
|
||||||
Math.sin(dLat/2)**2 +
|
|
||||||
Math.cos(lat1*Math.PI/180) *
|
|
||||||
Math.cos(lat2*Math.PI/180) *
|
|
||||||
Math.sin(dLon/2)**2;
|
|
||||||
return R * (2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* =============================
|
|
||||||
TPS TERDEKAT
|
|
||||||
============================= */
|
|
||||||
document.getElementById('btnLokasi').addEventListener('click', function () {
|
|
||||||
|
|
||||||
if (!navigator.geolocation) {
|
|
||||||
alert('Browser tidak mendukung GPS');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.geolocation.getCurrentPosition(pos => {
|
document.getElementById('btnLokasi').addEventListener('click', function () {
|
||||||
let userLat = pos.coords.latitude;
|
|
||||||
let userLng = pos.coords.longitude;
|
|
||||||
|
|
||||||
// LINGKARAN BIRU (USER)
|
if (!navigator.geolocation) {
|
||||||
L.circleMarker([userLat, userLng], {
|
alert('Browser tidak mendukung GPS');
|
||||||
radius: 8,
|
return;
|
||||||
color: '#0d6efd',
|
}
|
||||||
fillColor: '#0d6efd',
|
|
||||||
fillOpacity: 0.8
|
|
||||||
}).addTo(map)
|
|
||||||
.bindPopup('Lokasi Anda')
|
|
||||||
.openPopup();
|
|
||||||
|
|
||||||
let nearest = null;
|
navigator.geolocation.getCurrentPosition(pos => {
|
||||||
let minDist = Infinity;
|
let userLat = pos.coords.latitude;
|
||||||
|
let userLng = pos.coords.longitude;
|
||||||
|
|
||||||
markers.forEach(m => {
|
L.circleMarker([userLat, userLng], {
|
||||||
let tps = m.tpsData;
|
radius: 6,
|
||||||
let dist = getDistance(userLat, userLng, tps.latitude, tps.longitude);
|
color: '#0d6efd',
|
||||||
|
fillColor: '#0d6efd',
|
||||||
|
fillOpacity: 1,
|
||||||
|
className: 'user-location'
|
||||||
|
}).addTo(map)
|
||||||
|
.bindPopup('Lokasi Anda')
|
||||||
|
.openPopup();
|
||||||
|
|
||||||
if (dist < minDist) {
|
setInterval(() => {
|
||||||
minDist = dist;
|
let pulse = L.circle([userLat, userLng], {
|
||||||
nearest = m;
|
radius: 40,
|
||||||
nearest.distance = dist;
|
color: '#0d6efd',
|
||||||
|
fillColor: '#0d6efd',
|
||||||
|
fillOpacity: 0.35,
|
||||||
|
weight: 0
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
setTimeout(() => map.removeLayer(pulse), 1000);
|
||||||
|
}, 1200);
|
||||||
|
|
||||||
|
let nearest = null;
|
||||||
|
let minDist = Infinity;
|
||||||
|
|
||||||
|
markers.forEach(m => {
|
||||||
|
let tps = m.tpsData;
|
||||||
|
let dist = getDistance(userLat, userLng, tps.latitude, tps.longitude);
|
||||||
|
if (dist < minDist) {
|
||||||
|
minDist = dist;
|
||||||
|
nearest = m;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (nearest) {
|
||||||
|
nearest.setIcon(iconNear);
|
||||||
|
nearest.openPopup();
|
||||||
|
map.setView(nearest.getLatLng(), 15);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (nearest) {
|
|
||||||
nearest.setIcon(iconNear);
|
|
||||||
nearest.openPopup();
|
|
||||||
map.setView(nearest.getLatLng(), 15);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue