542 lines
20 KiB
PHP

@props(['club' => null, 'mode' => 'create'])
@php
$isEdit = $mode === 'edit' && $club;
@endphp
<div class="container-fluid px-0">
<h5 class="fw-bold mb-3">Location</h5>
<p class="text-muted mb-4">Set your club's geographic location and regional settings</p>
<!-- Country, Timezone, Currency Row -->
<div class="row mb-4">
<div class="col-md-4">
<x-nationality-dropdown
name="country"
id="country"
label="Country"
:value="$club->country ?? old('country', 'Bahrain')"
:required="true"
:error="null" />
</div>
<div class="col-md-4">
<x-timezone-dropdown-bootstrap
name="timezone"
id="timezone"
:value="$club->timezone ?? old('timezone', 'Asia/Bahrain')"
:required="false"
:error="null" />
</div>
<div class="col-md-4">
<x-currency-dropdown-bootstrap
name="currency"
id="currency"
:value="$club->currency ?? old('currency', 'BHD')"
:required="false"
:error="null" />
</div>
</div>
<!-- Address -->
<div class="mb-4">
<label for="address" class="form-label">Street Address</label>
<textarea class="form-control"
id="address"
name="address"
rows="2"
placeholder="Enter the full street address of your club">{{ $club->address ?? old('address') }}</textarea>
<small class="text-muted">Full address including building number, street name, area, etc.</small>
</div>
<!-- Map -->
<div class="mb-4">
<label class="form-label">Location on Map</label>
<div id="modalClubMap" style="height: 400px; border-radius: 0.5rem; border: 1px solid hsl(var(--border));"></div>
<small class="text-muted">Drag the marker to set the exact location of your club</small>
</div>
<!-- GPS Coordinates -->
<div class="row mb-4">
<div class="col-md-6">
<label for="gps_lat" class="form-label">
<i class="bi bi-geo-alt me-1"></i>Latitude
</label>
<input type="number"
class="form-control"
id="gps_lat"
name="gps_lat"
value="{{ $club->gps_lat ?? old('gps_lat') }}"
step="0.0000001"
min="-90"
max="90"
placeholder="e.g., 26.0667">
<small class="text-muted">Decimal degrees (-90 to 90)</small>
</div>
<div class="col-md-6">
<label for="gps_long" class="form-label">
<i class="bi bi-geo-alt me-1"></i>Longitude
</label>
<input type="number"
class="form-control"
id="gps_long"
name="gps_long"
value="{{ $club->gps_long ?? old('gps_long') }}"
step="0.0000001"
min="-180"
max="180"
placeholder="e.g., 50.5577">
<small class="text-muted">Decimal degrees (-180 to 180)</small>
</div>
</div>
<!-- Google Maps Link -->
<div class="mb-4">
<label for="google_maps_link" class="form-label">
<i class="bi bi-google me-1"></i>Google Maps Link (Optional)
</label>
<div class="input-group">
<span class="input-group-text bg-white">
<i class="bi bi-link-45deg"></i>
</span>
<input type="url"
class="form-control"
id="google_maps_link"
name="google_maps_link"
placeholder="Paste Google Maps share link here..."
pattern="https?://.*google\.com/maps.*|https?://goo\.gl/maps/.*">
</div>
<small class="text-muted">Paste a Google Maps share URL to auto-fill coordinates</small>
</div>
<!-- Quick Location Actions -->
<div class="d-flex gap-2 flex-wrap">
<button type="button" class="btn btn-outline-primary btn-sm" onclick="getCurrentLocation()">
<i class="bi bi-crosshair me-2"></i>Use My Current Location
</button>
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="centerOnCountry()">
<i class="bi bi-globe me-2"></i>Center on Selected Country
</button>
</div>
</div>
@push('scripts')
<script>
let clubMap = null;
let clubMarker = null;
let countriesData = null;
let mapInitialized = false;
document.addEventListener('DOMContentLoaded', function() {
// Load countries data
fetch('/data/countries.json')
.then(response => response.json())
.then(countries => {
countriesData = countries;
// PART 1A: Device-based preselection on modal open (create mode only)
const clubForm = document.getElementById('clubForm');
if (clubForm && clubForm.dataset.mode === 'create') {
detectAndPreselectCountries(countries);
}
setupLocationHandlers();
})
.catch(error => console.error('Error loading countries:', error));
// Initialize map when location tab is shown OR when modal opens with location tab active
const locationTab = document.getElementById('location-tab');
const clubModal = document.getElementById('clubModal');
// Function to initialize map if not already done
function tryInitializeMap() {
if (!mapInitialized && locationTab && locationTab.classList.contains('active')) {
setTimeout(() => {
initializeMap();
mapInitialized = true;
}, 100);
} else if (clubMap) {
// ISSUE 4 FIX: Fix gray tiles by invalidating size
setTimeout(() => {
clubMap.invalidateSize();
}, 100);
}
}
// Listen for tab shown event
if (locationTab) {
locationTab.addEventListener('shown.bs.tab', tryInitializeMap);
}
// Listen for modal shown event (for edit mode when location tab is already active)
if (clubModal) {
clubModal.addEventListener('shown.bs.modal', function() {
// Reset map initialization flag when modal opens
mapInitialized = false;
// Try to initialize map after modal is fully shown
setTimeout(tryInitializeMap, 150);
});
// Clean up map when modal closes
clubModal.addEventListener('hidden.bs.modal', function() {
if (clubMap) {
clubMap.remove();
clubMap = null;
clubMarker = null;
mapInitialized = false;
}
});
}
});
// PART 1A: Detect device location and preselect country/timezone/currency
function detectAndPreselectCountries(countries) {
if (!navigator.geolocation) {
// Fallback to default (Bahrain)
preselectCountryData('Bahrain', countries);
return;
}
navigator.geolocation.getCurrentPosition(
function(position) {
const lat = position.coords.latitude;
const lon = position.coords.longitude;
// Use reverse geocoding to get country
fetch(`https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${lat}&longitude=${lon}&localityLanguage=en`)
.then(response => response.json())
.then(data => {
const countryName = data.countryName || 'Bahrain';
preselectCountryData(countryName, countries);
})
.catch(() => {
// Fallback to default
preselectCountryData('Bahrain', countries);
});
},
function() {
// Geolocation denied or failed, use default
preselectCountryData('Bahrain', countries);
}
);
}
// Preselect country, timezone, and currency based on detected/default country
function preselectCountryData(countryName, countries) {
const country = countries.find(c => c.name.toLowerCase() === countryName.toLowerCase());
if (!country) return;
// Preselect country
const countryInput = document.getElementById('country');
if (countryInput && !countryInput.value) {
countryInput.value = country.iso3 || country.name;
countryInput.dispatchEvent(new Event('change'));
}
// Preselect timezone using Bootstrap dropdown
const timezoneInput = document.getElementById('timezone');
if (timezoneInput && country.timezone && !timezoneInput.value && typeof setTimezoneValue === 'function') {
setTimezoneValue('timezone', country.timezone, countries);
}
// Preselect currency using Bootstrap dropdown
const currencyInput = document.getElementById('currency');
if (currencyInput && country.currency && !currencyInput.value && typeof setCurrencyValue === 'function') {
setCurrencyValue('currency', country.currency, countries);
}
// Set map center to country
if (country.latitude && country.longitude) {
const lat = parseFloat(country.latitude);
const lng = parseFloat(country.longitude);
if (!isNaN(lat) && !isNaN(lng)) {
const latInput = document.getElementById('gps_lat');
const lngInput = document.getElementById('gps_long');
if (latInput && !latInput.value) latInput.value = lat.toFixed(7);
if (lngInput && !lngInput.value) lngInput.value = lng.toFixed(7);
}
}
}
// Initialize Leaflet map
function initializeMap() {
const mapElement = document.getElementById('modalClubMap');
if (!mapElement) return;
const lat = parseFloat(document.getElementById('gps_lat')?.value) || 26.0667;
const lng = parseFloat(document.getElementById('gps_long')?.value) || 50.5577;
// ISSUE 4 FIX: Initialize map without attribution control
clubMap = L.map('clubMap', {
attributionControl: false // Disable attribution
}).setView([lat, lng], 13);
// ISSUE 4 FIX: Add tile layer without attribution text
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '', // Empty attribution
maxZoom: 19
}).addTo(clubMap);
// Add draggable marker
clubMarker = L.marker([lat, lng], {
draggable: true
}).addTo(clubMap);
// Update coordinates when marker is dragged
clubMarker.on('dragend', function(e) {
const position = e.target.getLatLng();
updateCoordinates(position.lat, position.lng);
});
// Update marker when coordinates are manually entered
const latInput = document.getElementById('gps_lat');
const lngInput = document.getElementById('gps_long');
if (latInput && lngInput) {
latInput.addEventListener('change', function() {
updateMarkerPosition();
});
lngInput.addEventListener('change', function() {
updateMarkerPosition();
});
}
// ISSUE 4 FIX: Fix initial rendering after a short delay
setTimeout(() => {
if (clubMap) {
clubMap.invalidateSize();
}
}, 100);
}
// Update coordinates in inputs
function updateCoordinates(lat, lng) {
const latInput = document.getElementById('gps_lat');
const lngInput = document.getElementById('gps_long');
if (latInput) latInput.value = lat.toFixed(7);
if (lngInput) lngInput.value = lng.toFixed(7);
}
// Update marker position from inputs
function updateMarkerPosition() {
const lat = parseFloat(document.getElementById('gps_lat')?.value);
const lng = parseFloat(document.getElementById('gps_long')?.value);
if (!isNaN(lat) && !isNaN(lng) && clubMarker && clubMap) {
const newPos = L.latLng(lat, lng);
clubMarker.setLatLng(newPos);
clubMap.setView(newPos, clubMap.getZoom());
}
}
// Setup location-related event handlers
function setupLocationHandlers() {
// Watch for country changes
const countryInput = document.getElementById('country');
if (countryInput) {
// Use MutationObserver to detect value changes
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'attributes' && mutation.attributeName === 'value') {
handleCountryChange(countryInput.value);
}
});
});
observer.observe(countryInput, { attributes: true });
// Also listen for direct changes
countryInput.addEventListener('change', function() {
handleCountryChange(this.value);
});
// Check periodically for value changes (fallback)
let lastCountryValue = countryInput.value;
setInterval(function() {
if (countryInput.value !== lastCountryValue) {
lastCountryValue = countryInput.value;
handleCountryChange(countryInput.value);
}
}, 500);
}
// Parse Google Maps link
const googleMapsInput = document.getElementById('google_maps_link');
if (googleMapsInput) {
googleMapsInput.addEventListener('change', function() {
parseGoogleMapsLink(this.value);
});
}
}
// PART 1B: Handle country change - update timezone, currency, and map
function handleCountryChange(countryName) {
if (!countriesData || !countryName) return;
const country = countriesData.find(c =>
c.name.toLowerCase() === countryName.toLowerCase() ||
c.iso3 === countryName
);
if (!country) return;
// PART 1B: Always update timezone to match country using Bootstrap dropdown
if (country.timezone && typeof setTimezoneValue === 'function') {
setTimezoneValue('timezone', country.timezone, countriesData);
}
// PART 1B: Always update currency to match country using Bootstrap dropdown
if (country.currency && typeof setCurrencyValue === 'function') {
setCurrencyValue('currency', country.currency, countriesData);
}
// Center map on country
if (country.latitude && country.longitude) {
const lat = parseFloat(country.latitude);
const lng = parseFloat(country.longitude);
if (!isNaN(lat) && !isNaN(lng)) {
// Update coordinates
const latInput = document.getElementById('gps_lat');
const lngInput = document.getElementById('gps_long');
// Only update if empty or user wants to recenter
const shouldUpdate = !latInput?.value || !lngInput?.value;
if (shouldUpdate) {
updateCoordinates(lat, lng);
}
// Always recenter map view on country
if (clubMarker && clubMap) {
if (shouldUpdate) {
clubMarker.setLatLng([lat, lng]);
}
clubMap.setView([lat, lng], 10);
}
}
}
}
// Get current location using browser geolocation
function getCurrentLocation() {
if (!navigator.geolocation) {
alert('Geolocation is not supported by your browser');
return;
}
// Show loading state
const btn = event.target.closest('button');
const originalHtml = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Getting location...';
navigator.geolocation.getCurrentPosition(
function(position) {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
updateCoordinates(lat, lng);
if (clubMarker && clubMap) {
clubMarker.setLatLng([lat, lng]);
clubMap.setView([lat, lng], 15);
}
btn.disabled = false;
btn.innerHTML = originalHtml;
if (typeof Toast !== 'undefined') {
Toast.success('Success', 'Location updated successfully');
}
},
function(error) {
console.error('Geolocation error:', error);
alert('Unable to get your location. Please check your browser permissions.');
btn.disabled = false;
btn.innerHTML = originalHtml;
}
);
}
// Center map on selected country
function centerOnCountry() {
const countryInput = document.getElementById('country');
if (!countryInput || !countriesData) return;
const countryName = countryInput.value;
const country = countriesData.find(c =>
c.name.toLowerCase() === countryName.toLowerCase() ||
c.iso3 === countryName
);
if (country && country.latitude && country.longitude) {
const lat = parseFloat(country.latitude);
const lng = parseFloat(country.longitude);
if (!isNaN(lat) && !isNaN(lng) && clubMap) {
clubMap.setView([lat, lng], 10);
if (typeof Toast !== 'undefined') {
Toast.info('Info', `Centered on ${country.name}`);
}
}
}
}
// Parse Google Maps link to extract coordinates
function parseGoogleMapsLink(url) {
if (!url) return;
try {
// Try to extract coordinates from various Google Maps URL formats
let lat, lng;
// Format: @lat,lng
const atMatch = url.match(/@(-?\d+\.\d+),(-?\d+\.\d+)/);
if (atMatch) {
lat = parseFloat(atMatch[1]);
lng = parseFloat(atMatch[2]);
}
// Format: !3d and !4d
if (!lat || !lng) {
const dMatch = url.match(/!3d(-?\d+\.\d+)!4d(-?\d+\.\d+)/);
if (dMatch) {
lat = parseFloat(dMatch[1]);
lng = parseFloat(dMatch[2]);
}
}
// Format: ll=lat,lng
if (!lat || !lng) {
const llMatch = url.match(/ll=(-?\d+\.\d+),(-?\d+\.\d+)/);
if (llMatch) {
lat = parseFloat(llMatch[1]);
lng = parseFloat(llMatch[2]);
}
}
if (lat && lng && !isNaN(lat) && !isNaN(lng)) {
updateCoordinates(lat, lng);
if (clubMarker && clubMap) {
clubMarker.setLatLng([lat, lng]);
clubMap.setView([lat, lng], 15);
}
if (typeof Toast !== 'undefined') {
Toast.success('Success', 'Coordinates extracted from Google Maps link');
}
} else {
if (typeof Toast !== 'undefined') {
Toast.warning('Warning', 'Could not extract coordinates from the link');
}
}
} catch (error) {
console.error('Error parsing Google Maps link:', error);
if (typeof Toast !== 'undefined') {
Toast.error('Error', 'Invalid Google Maps link');
}
}
}
</script>
@endpush