133 lines
6.0 KiB
JavaScript
133 lines
6.0 KiB
JavaScript
const locationInput = document.querySelector('#location-input');
|
||
const latitudeInput = document.querySelector('#latitude-input');
|
||
const longitudeInput = document.querySelector('#longitude-input');
|
||
const options = document.querySelector('#location-options');
|
||
let locationTimer;
|
||
|
||
if (locationInput) {
|
||
locationInput.addEventListener('input', () => {
|
||
latitudeInput.value = '';
|
||
longitudeInput.value = '';
|
||
clearTimeout(locationTimer);
|
||
const query = locationInput.value.trim();
|
||
if (query.length < 3) { options.hidden = true; return; }
|
||
locationTimer = setTimeout(async () => {
|
||
try {
|
||
const response = await fetch(`/api/locations?q=${encodeURIComponent(query)}`);
|
||
const places = await response.json();
|
||
options.replaceChildren();
|
||
places.forEach(place => {
|
||
const button = document.createElement('button');
|
||
button.type = 'button';
|
||
button.textContent = place.label;
|
||
button.addEventListener('click', () => {
|
||
locationInput.value = place.label;
|
||
latitudeInput.value = place.latitude;
|
||
longitudeInput.value = place.longitude;
|
||
options.hidden = true;
|
||
});
|
||
options.append(button);
|
||
});
|
||
if (places.length === 0) {
|
||
const message = document.createElement('div');
|
||
message.className = 'location-message';
|
||
message.textContent = 'No geographic locations found. Try a suburb, town, or postcode.';
|
||
options.append(message);
|
||
}
|
||
options.hidden = false;
|
||
} catch (_) { options.hidden = true; }
|
||
}, 300);
|
||
});
|
||
document.addEventListener('click', event => { if (!event.target.closest('.location-field')) options.hidden = true; });
|
||
}
|
||
|
||
const tagToggle = document.querySelector('#tag-toggle');
|
||
const tagOptions = document.querySelector('#tag-options');
|
||
if (tagToggle && tagOptions) {
|
||
tagToggle.addEventListener('click', () => { tagOptions.hidden = !tagOptions.hidden; });
|
||
tagOptions.addEventListener('change', () => {
|
||
const count = tagOptions.querySelectorAll('input:checked').length;
|
||
tagToggle.textContent = count ? `${count} selected` : 'All types';
|
||
});
|
||
document.addEventListener('click', event => {
|
||
if (!event.target.closest('.tag-field')) tagOptions.hidden = true;
|
||
});
|
||
}
|
||
|
||
const tabs = document.querySelectorAll('.tab');
|
||
let map;
|
||
tabs.forEach(tab => tab.addEventListener('click', () => {
|
||
tabs.forEach(item => item.classList.toggle('active', item === tab));
|
||
document.querySelectorAll('.view-panel').forEach(panel => panel.classList.toggle('active', panel.id === tab.dataset.view));
|
||
if (tab.dataset.view === 'map-view') initialiseMap();
|
||
}));
|
||
|
||
const eventRows = [...document.querySelectorAll('[data-event-start]')];
|
||
const visibleCount = document.querySelector('#visible-event-count');
|
||
const clearDay = document.querySelector('#clear-day');
|
||
const dayEmpty = document.querySelector('#day-empty');
|
||
const rangeStatus = document.querySelector('#range-status');
|
||
let rangeStart = '';
|
||
let rangeEnd = '';
|
||
|
||
function formatDay(dayKey) {
|
||
return new Date(`${dayKey}T00:00:00`).toLocaleDateString(undefined, { day: 'numeric', month: 'short' });
|
||
}
|
||
|
||
function filterToRange(startDay, endDay = startDay) {
|
||
let count = 0;
|
||
eventRows.forEach(row => {
|
||
row.hidden = Boolean(startDay) && (row.dataset.eventStart > endDay || row.dataset.eventEnd < startDay);
|
||
if (!row.hidden) count += 1;
|
||
});
|
||
document.querySelectorAll('.calendar-day').forEach(day => {
|
||
const inRange = Boolean(startDay) && day.dataset.day >= startDay && day.dataset.day <= endDay;
|
||
day.classList.toggle('selected', inRange);
|
||
day.classList.toggle('range-start', day.dataset.day === startDay);
|
||
day.classList.toggle('range-end', day.dataset.day === endDay && endDay !== startDay);
|
||
});
|
||
if (visibleCount) visibleCount.textContent = count;
|
||
if (clearDay) clearDay.hidden = !startDay;
|
||
if (dayEmpty) dayEmpty.hidden = count !== 0;
|
||
}
|
||
|
||
document.querySelectorAll('.calendar-day').forEach(day => day.addEventListener('click', event => {
|
||
event.preventDefault();
|
||
const clicked = day.dataset.day;
|
||
if (!rangeStart || rangeEnd) {
|
||
rangeStart = clicked;
|
||
rangeEnd = '';
|
||
filterToRange(rangeStart);
|
||
if (rangeStatus) rangeStatus.textContent = `${formatDay(rangeStart)} selected · choose an end date`;
|
||
} else {
|
||
rangeEnd = clicked;
|
||
if (rangeEnd < rangeStart) [rangeStart, rangeEnd] = [rangeEnd, rangeStart];
|
||
filterToRange(rangeStart, rangeEnd);
|
||
if (rangeStatus) rangeStatus.textContent = `${formatDay(rangeStart)} – ${formatDay(rangeEnd)}`;
|
||
document.querySelector('#event-list')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||
}
|
||
}));
|
||
if (clearDay) clearDay.addEventListener('click', event => {
|
||
event.preventDefault(); rangeStart = ''; rangeEnd = ''; filterToRange('');
|
||
if (rangeStatus) rangeStatus.textContent = 'Select a start date';
|
||
});
|
||
|
||
function initialiseMap() {
|
||
if (map || !window.maplibregl) return;
|
||
const events = JSON.parse(document.querySelector('#map-events').textContent);
|
||
const center = events.length ? [events[0].longitude, events[0].latitude] : [150.8931, -34.4278];
|
||
map = new maplibregl.Map({ container: 'events-map', style: { version: 8, sources: { osm: { type: 'raster', tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'], tileSize: 256, attribution: '© OpenStreetMap contributors' } }, layers: [{ id: 'osm', type: 'raster', source: 'osm' }] }, center, zoom: 12 });
|
||
map.addControl(new maplibregl.NavigationControl(), 'top-right');
|
||
const bounds = new maplibregl.LngLatBounds();
|
||
events.forEach(event => {
|
||
const popup = new maplibregl.Popup({ offset: 20 }).setHTML(`<strong>${escapeHtml(event.title)}</strong><br>${escapeHtml(event.venue)}<br><a href="/events/${event.id}">View event</a>`);
|
||
new maplibregl.Marker({ color: '#dd5b32' }).setLngLat([event.longitude, event.latitude]).setPopup(popup).addTo(map);
|
||
bounds.extend([event.longitude, event.latitude]);
|
||
});
|
||
if (events.length > 1) map.fitBounds(bounds, { padding: 60, maxZoom: 14 });
|
||
}
|
||
|
||
function escapeHtml(value) {
|
||
const node = document.createElement('div'); node.textContent = value; return node.innerHTML;
|
||
}
|