Skip to content

Commit 123f403

Browse files
committed
filter marker in web profile based on jurusan or komisariat
1 parent 677a367 commit 123f403

File tree

3 files changed

+151
-37
lines changed

3 files changed

+151
-37
lines changed

app/Livewire/MapPage.php

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,57 @@
33
namespace App\Livewire;
44

55
use App\Models\Anggota;
6+
use App\Models\Jurusan;
7+
use App\Models\Komisariat;
68
use Livewire\Component;
79
use Livewire\Attributes\Layout;
810
use Livewire\Attributes\Title;
911

1012
#[Layout('components.layouts.app')]
11-
#[Title('Peta')]
13+
#[Title('Peta Anggota')]
1214
class MapPage extends Component
1315
{
16+
public $selectedKomisariat = null;
17+
public $selectedJurusan = null;
18+
public int $markerCount = 0;
19+
20+
public function getKomisariatOptionsProperty()
21+
{
22+
return Komisariat::orderBy('nama')->pluck('nama', 'id');
23+
}
24+
25+
public function getJurusanOptionsProperty()
26+
{
27+
return Jurusan::orderBy('nama_jurusan')->pluck('nama_jurusan', 'id');
28+
}
29+
public function triggerRender(): void
30+
{
31+
// Tidak perlu melakukan apa-apa di sini.
32+
// Memanggil method ini saja sudah cukup untuk memicu ulang render().
33+
}
1434
public function render()
1535
{
16-
$kecamatanData = Anggota::selectRaw('alamat as kecamatan, AVG(latitude) as latitude, AVG(longitude) as longitude, COUNT(*) as jumlah')
17-
->groupBy('alamat')
18-
->get();
36+
$query = Anggota::query()
37+
->with(['jurusan', 'komisariat'])
38+
->whereNotNull('latitude')
39+
->whereNotNull('longitude');
40+
41+
if ($this->selectedKomisariat) {
42+
$query->where('komisariat_id', $this->selectedKomisariat);
43+
}
44+
45+
if ($this->selectedJurusan) {
46+
$query->where('jurusan_id', $this->selectedJurusan);
47+
}
48+
49+
$anggotas = $query->get();
50+
$this->markerCount = $anggotas->count();
51+
52+
// 3. Kirim event ke browser dengan data baru setiap kali render
53+
$this->dispatch('updateMap', data: $anggotas);
1954

2055
return view('livewire.map-page', [
21-
'kecamatanData' => $kecamatanData
56+
'anggotas' => $anggotas,
2257
]);
2358
}
2459
}

resources/views/components/layouts/app.blade.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ class="block pl-3 pr-4 py-2 border-l-4 {{ request()->routeIs('berita.*') ? 'bg-g
135135
class="block pl-3 pr-4 py-2 border-l-4 {{ request()->routeIs('program-kerja') ? 'bg-green-50 border-green-700 text-green-700' : 'border-transparent text-gray-600 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-800' }} text-base font-medium">
136136
Program Kerja
137137
</a>
138+
<a href="{{ route('map') }}" wire:navigate
139+
class="block pl-3 pr-4 py-2 border-l-4 {{ request()->routeIs('map') ? 'bg-green-50 border-green-700 text-green-700' : 'border-transparent text-gray-600 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-800' }} text-base font-medium">
140+
Peta
141+
</a>
138142
<div class="border-t border-gray-200 pt-3 pb-2">
139143
@auth
140144
<a href="{{ url('/dashboard') }}"

resources/views/livewire/map-page.blade.php

Lines changed: 107 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,54 +6,129 @@
66
<p class="text-blue-100">Marker berdasarkan jumlah anggota per kecamatan.</p>
77
</div>
88
</div>
9+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
10+
<div class="bg-white p-4 rounded-lg shadow-md border border-gray-200">
11+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 items-center">
12+
13+
<div class="md:col-span-1">
14+
<label for="filter-komisariat" class="block text-sm font-medium text-gray-700">Filter Komisariat</label>
15+
<select id="filter-komisariat" wire:model.live="selectedKomisariat" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border border-gray-300 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 sm:text-sm rounded-md">
16+
<option value="">Semua Komisariat</option>
17+
@foreach($this->komisariatOptions as $id => $nama)
18+
<option value="{{ $id }}">{{ $nama }}</option>
19+
@endforeach
20+
</select>
21+
</div>
22+
23+
<div class="md:col-span-1">
24+
<label for="filter-jurusan" class="block text-sm font-medium text-gray-700">Filter Jurusan</label>
25+
<select id="filter-jurusan" wire:model.live="selectedJurusan" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border border-gray-300 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 sm:text-sm rounded-md">
26+
<option value="">Semua Jurusan</option>
27+
@foreach($this->jurusanOptions as $id => $nama)
28+
<option value="{{ $id }}">{{ $nama }}</option>
29+
@endforeach
30+
</select>
31+
</div>
932

33+
<div class="md:col-span-1 text-center md:text-right text-gray-600">
34+
<p class="text-sm">Menampilkan <span class="font-bold text-emerald-700">{{ $markerCount }}</span> marker</p>
35+
</div>
36+
</div>
37+
</div>
38+
</div>
1039
<!-- Map Section -->
1140
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10">
12-
<div id="map" class="w-full h-[500px] rounded-lg shadow-md"></div>
41+
<div wire:ignore id="map" class="w-full h-[500px] rounded-lg shadow-md"></div>
1342
</div>
1443
</div>
1544

1645
<script>
17-
const kecamatanData = @json($kecamatanData);
18-
19-
function initMap() {
20-
// Titik tengah default (Pontianak)
21-
const centerLocation = { lat: -0.026330, lng: 109.342503 };
46+
document.addEventListener('livewire:navigated', () => {
47+
48+
let map = null;
49+
let activeMarkers = [];
50+
const defaultAvatar = "{{ asset('images/default-avatar.png') }}";
51+
52+
const abortController = new AbortController();
2253
23-
const map = new google.maps.Map(document.getElementById("map"), {
24-
zoom: 12,
25-
center: centerLocation,
26-
});
54+
document.addEventListener('livewire:navigating', () => {
55+
56+
abortController.abort();
57+
58+
map = null;
59+
}, { once: true });
2760
28-
if (!kecamatanData.length) {
29-
console.warn("Tidak ada data kecamatan");
30-
return;
61+
function clearMarkers() {
62+
activeMarkers.forEach(marker => marker.setMap(null));
63+
activeMarkers = [];
3164
}
3265
33-
kecamatanData.forEach(item => {
34-
const lat = parseFloat(item.latitude);
35-
const lng = parseFloat(item.longitude);
66+
function drawMarkers(anggotaData) {
67+
if (!map) return;
68+
clearMarkers();
3669
37-
if (isNaN(lat) || isNaN(lng)) return;
70+
const infowindow = new google.maps.InfoWindow();
3871
39-
const marker = new google.maps.Marker({
40-
position: { lat, lng },
41-
map,
42-
title: item.kecamatan
43-
});
72+
anggotaData.forEach(anggota => {
73+
const lat = parseFloat(anggota.latitude);
74+
const lng = parseFloat(anggota.longitude);
75+
if (isNaN(lat) || isNaN(lng)) return;
76+
77+
const marker = new google.maps.Marker({
78+
position: { lat, lng },
79+
map,
80+
title: anggota.nama
81+
});
82+
activeMarkers.push(marker);
4483
45-
const infoWindow = new google.maps.InfoWindow({
46-
content: `
47-
<div>
48-
<strong>Kecamatan:</strong> ${item.kecamatan} <br>
49-
<strong>Jumlah Anggota:</strong> ${item.jumlah}
84+
const contentString = `
85+
<div style="display: flex; align-items: center; gap: 15px; color: black; max-width: 350px;">
86+
<img src="${anggota.foto ? '/storage/' + anggota.foto : defaultAvatar}" alt="${anggota.nama}" style="width: 80px; height: 80px; object-fit: cover; border-radius: 50%;">
87+
<div>
88+
<strong style="font-size: 1.1em;">${anggota.nama}</strong><br>
89+
<strong>Kuliah:</strong> ${anggota.tahun_masuk_kuliah || 'N/A'}<br>
90+
<strong>LK1:</strong> ${anggota.tahun_lk1 || 'N/A'}<br>
91+
<strong>Jurusan:</strong> ${anggota.jurusan ? anggota.jurusan.nama_jurusan : 'N/A'}<br>
92+
<strong>Komisariat:</strong> ${anggota.komisariat ? anggota.komisariat.nama : 'N/A'}
93+
</div>
5094
</div>
51-
`
95+
`;
96+
97+
marker.addListener('click', () => {
98+
infowindow.setContent(contentString);
99+
infowindow.open(map, marker);
100+
});
52101
});
102+
}
53103
54-
marker.addListener('click', () => infoWindow.open(map, marker));
55-
});
56-
}
104+
function initialize() {
105+
const mapElement = document.getElementById("map");
106+
if (!mapElement || map) return;
107+
108+
const centerLocation = { lat: -0.026330, lng: 109.342503 };
109+
map = new google.maps.Map(mapElement, {
110+
zoom: 12,
111+
center: centerLocation,
112+
});
113+
114+
Livewire.on('updateMap', ({ data }) => {
115+
drawMarkers(data);
116+
}, { signal: abortController.signal });
117+
118+
@this.call('triggerRender');
119+
}
120+
121+
if (typeof google === 'object' && typeof google.maps === 'object') {
122+
initialize();
123+
} else {
124+
window.addEventListener('google-maps-loaded', initialize, { once: true });
125+
}
126+
});
57127
</script>
58128

59-
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAgGBjlEnlrlO2KdsQMFL70E_Ppo3GmFPs&callback=initMap&v=weeklyy" async defer></script>
129+
<script>
130+
function initAllMaps() {
131+
window.dispatchEvent(new CustomEvent('google-maps-loaded'));
132+
}
133+
</script>
134+
<script src="https://maps.googleapis.com/maps/api/js?key={{ env('GOOGLE_MAPS_API_KEY') }}&callback=initAllMaps&v=weekly" async defer></script>

0 commit comments

Comments
 (0)