Files
thrillwiki_laravel/resources/views/livewire/location/location-display.blade.php
pacnpal cc33781245 feat: Implement rides management with CRUD functionality
- Added rides index view with search and filter options.
- Created rides show view to display ride details.
- Implemented API routes for rides.
- Developed authentication routes for user registration, login, and email verification.
- Created tests for authentication, email verification, password reset, and user profile management.
- Added feature tests for rides and operators, including creation, updating, deletion, and searching.
- Implemented soft deletes and caching for rides and operators.
- Enhanced manufacturer and operator model tests for various functionalities.
2025-06-19 22:34:10 -04:00

190 lines
6.0 KiB
PHP

@push('styles')
<style>
/* Ensure map container and its elements stay below other UI elements */
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
z-index: 1 !important;
}
.leaflet-control {
z-index: 2 !important;
}
/* Custom marker cluster styling */
.marker-cluster {
background-color: rgba(255, 255, 255, 0.8);
border-radius: 50%;
text-align: center;
color: #333;
border: 2px solid rgba(0, 120, 255, 0.5);
font-weight: 600;
}
.marker-cluster div {
width: 30px;
height: 30px;
margin-left: 5px;
margin-top: 5px;
padding-top: 5px;
background-color: rgba(0, 120, 255, 0.6);
border-radius: 50%;
}
/* Custom info window styling */
.location-info-window {
padding: 10px;
background: white;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
max-width: 300px;
}
.location-info-window h3 {
margin-top: 0;
margin-bottom: 8px;
font-weight: 600;
}
</style>
@endpush
<div class="location-display-component">
<div wire:ignore class="relative mb-4" style="z-index: 1;">
<div id="locationMap" class="h-[400px] w-full rounded-lg border border-gray-300"></div>
@if($showInfoWindow && $activeMarker)
<div class="location-info-window absolute top-4 right-4 z-10">
<div class="flex justify-between items-start">
<h3 class="text-lg font-semibold">{{ $activeMarker['name'] ?? 'Location' }}</h3>
<button wire:click="closeInfoWindow" class="text-gray-500 hover:text-gray-700">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<div class="mt-2">
@if(isset($activeMarker['address']))
<p class="text-sm text-gray-600">
{{ $activeMarker['address']['street'] ?? '' }}
{{ $activeMarker['address']['city'] ?? '' }}
{{ $activeMarker['address']['state'] ?? '' }}
{{ $activeMarker['address']['country'] ?? '' }}
</p>
@endif
@if(isset($activeMarker['description']))
<p class="mt-2 text-sm">{{ $activeMarker['description'] }}</p>
@endif
</div>
</div>
@endif
</div>
</div>
@push('scripts')
<script>
document.addEventListener('livewire:initialized', function () {
const map = L.map('locationMap');
let markerClusterGroup;
let markers = {};
// Initialize map with OSM tiles
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
// Initialize marker cluster group
markerClusterGroup = L.markerClusterGroup({
maxClusterRadius: @entangle('clusterRadius'),
spiderfyOnMaxZoom: true,
showCoverageOnHover: false,
zoomToBoundsOnClick: true,
removeOutsideVisibleBounds: true,
iconCreateFunction: function(cluster) {
const count = cluster.getChildCount();
return L.divIcon({
html: `<div><span>${count}</span></div>`,
className: 'marker-cluster',
iconSize: L.point(40, 40)
});
}
});
map.addLayer(markerClusterGroup);
// Listen for marker updates from Livewire
@this.on('updateMarkers', (markerData) => {
// Clear existing markers
markerClusterGroup.clearLayers();
markers = {};
// Add new markers
markerData.forEach(marker => {
const leafletMarker = L.marker([marker.lat, marker.lng], {
icon: getMarkerIcon(marker.category)
});
leafletMarker.on('click', () => {
@this.markerClicked(marker.id);
});
markers[marker.id] = leafletMarker;
markerClusterGroup.addLayer(leafletMarker);
});
// Fit bounds if available
if (@this.bounds) {
map.fitBounds([
[@this.bounds.south, @this.bounds.west],
[@this.bounds.north, @this.bounds.east]
]);
}
});
// Handle cluster clicks
markerClusterGroup.on('clusterclick', (e) => {
const clusterMarkers = e.layer.getAllChildMarkers().map(marker => {
const markerId = Object.keys(markers).find(key => markers[key] === marker);
return { id: markerId, lat: marker.getLatLng().lat, lng: marker.getLatLng().lng };
});
@this.clusterClicked(clusterMarkers);
});
// Handle map bounds changes
map.on('moveend', () => {
const bounds = map.getBounds();
@this.boundsChanged({
north: bounds.getNorth(),
south: bounds.getSouth(),
east: bounds.getEast(),
west: bounds.getWest()
});
});
// Initialize markers
@this.getMarkers().then(markerData => {
@this.emit('updateMarkers', markerData);
});
// Helper function to get marker icon based on category
function getMarkerIcon(category) {
// Customize icons based on category
return L.divIcon({
className: `marker-icon marker-${category || 'default'}`,
html: `<div class="w-8 h-8 rounded-full bg-blue-500 border-2 border-white shadow-lg flex items-center justify-center">
<svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/>
</svg>
</div>`,
iconSize: [32, 32],
iconAnchor: [16, 32]
});
}
});
</script>
@endpush