Cloudlog/application/views/map/custom_date.php
Peter Goodhall 4db4822c09 Add debug logging and improve map loading UX
Added console logging to map loading callbacks and AJAX requests for better debugging. Reduced failsafe timeout from 35s to 10s to improve user experience. Improved date range display logic to check for element existence before updating.
2025-08-10 22:06:11 +01:00

477 行
无行尾
23 KiB
PHP

<link rel="stylesheet" href="<?php echo base_url(); ?>assets/css/map-enhancements.css">
<div class="container custom-map-QSOs">
<br>
<!-- Enhanced Header Section -->
<div class="row">
<div class="col-12">
<div class="card border-0 mb-4">
<div class="card-body rounded">
<div class="d-flex align-items-center justify-content-between">
<div>
<h2 class="mb-1">
<i class="fas fa-map-marked-alt me-2"></i>
<?php echo $logbook_name; echo strpos($logbook_name, 'logbook') ? '' : ' logbook'; ?> QSOs (Custom Dates)
</h2>
</div>
</div>
</div>
</div>
</div>
</div>
<?php if ($this->session->flashdata('notice')) { ?>
<div class="alert alert-info" role="alert">
<i class="fas fa-info-circle me-2"></i>
<?php echo $this->session->flashdata('notice'); ?>
</div>
<?php } ?>
<!-- Filter Controls Section -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-filter me-2"></i>Filter Controls
</h5>
</div>
<div class="card-body">
<form method="post" action="<?php echo site_url('map/custom'); ?>">
<!-- Quick Action Buttons -->
<div class="row mb-4">
<div class="col-12">
<div class="card border-0">
<div class="card-body py-3">
<h6 class="card-title mb-3">
<i class="fas fa-bolt text-warning me-2"></i>Quick Filters
</h6>
<div class="row g-2">
<div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-primary btn-sm w-100" onclick="setDateRange('today')">
<i class="fas fa-calendar-day me-1"></i>Today
</button>
</div>
<div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-primary btn-sm w-100" onclick="setDateRange('week')">
<i class="fas fa-calendar-week me-1"></i>This Week
</button>
</div>
<div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-primary btn-sm w-100" onclick="setDateRange('month')">
<i class="fas fa-calendar-alt me-1"></i>This Month
</button>
</div>
<div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-primary btn-sm w-100" onclick="setDateRange('year')">
<i class="fas fa-calendar me-1"></i>This Year
</button>
</div>
<div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-primary btn-sm w-100" onclick="get_oldest_qso_date()">
<i class="fas fa-history me-1"></i>All Time
</button>
</div>
<div class="col-6 col-md-2">
<button type="button" class="btn btn-outline-danger btn-sm w-100" onclick="clearFilters()">
<i class="fas fa-eraser me-1"></i>Reset
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Date Range Section -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-body">
<h6 class="card-title text-primary mb-3">
<i class="fas fa-calendar-range me-2"></i>Date Range
</h6>
<div class="row">
<div class="col-md-6">
<label for="from" class="form-label fw-semibold">
<i class="fas fa-play text-success me-1"></i><?php echo lang('gen_from_date') ?>
</label>
<input name="from" id="from" type="date" class="form-control" value="<?php echo $date_from; ?>">
</div>
<div class="col-md-6">
<label for="to" class="form-label fw-semibold">
<i class="fas fa-stop text-danger me-1"></i><?php echo lang('gen_to_date') ?>
</label>
<input name="to" id="to" type="date" class="form-control" value="<?php echo $date_to; ?>" max="<?php echo $date_to; ?>">
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Advanced Filters Section -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-body">
<h6 class="card-title text-secondary mb-3">
<i class="fas fa-sliders-h me-2"></i>Advanced Filters
</h6>
<div class="row">
<div class="col-md-4 mb-3">
<label for="band2" class="form-label fw-semibold">
Band
</label>
<select id="band2" name="band" class="form-select">
<option value="All" <?php if ($this->input->post('band') == "All" || $this->input->method() !== 'post') echo ' selected'; ?>>
<i class="fas fa-asterisk"></i> Every band
</option>
<?php foreach ($worked_bands as $band) {
echo '<option value="' . $band . '"';
if ($this->input->post('band') == $band) echo ' selected';
echo '>' . $band . '</option>' . "\n";
} ?>
</select>
</div>
<div class="col-md-4 mb-3">
<label for="mode" class="form-label fw-semibold">
Mode
</label>
<select id="mode" name="mode" class="form-select">
<option value="All" <?php if ($this->input->post('mode') == "All" || $this->input->method() !== 'post') echo ' selected'; ?>>
<?php echo lang('general_word_all') ?>
</option>
<?php
foreach ($modes as $mode) {
if ($mode->submode ?? '' == '') {
echo '<option value="' . $mode . '">' . strtoupper($mode) . '</option>'."\n";
}
}
?>
</select>
</div>
<?php
// Sort translated propagation modes alphabetically
$prop_modes = ['AS' => lang('gen_hamradio_propagation_AS'),
'AUR' => lang('gen_hamradio_propagation_AUR'),
'AUE' => lang('gen_hamradio_propagation_AUE'),
'BS' => lang('gen_hamradio_propagation_BS'),
'ECH' => lang('gen_hamradio_propagation_ECH'),
'EME' => lang('gen_hamradio_propagation_EME'),
'ES' => lang('gen_hamradio_propagation_ES'),
'FAI' => lang('gen_hamradio_propagation_FAI'),
'F2' => lang('gen_hamradio_propagation_F2'),
'INTERNET' => lang('gen_hamradio_propagation_INTERNET'),
'ION' => lang('gen_hamradio_propagation_ION'),
'IRL' => lang('gen_hamradio_propagation_IRL'),
'MS' => lang('gen_hamradio_propagation_MS'),
'RPT' => lang('gen_hamradio_propagation_RPT'),
'RS' => lang('gen_hamradio_propagation_RS'),
'SAT' => lang('gen_hamradio_propagation_SAT'),
'TEP' => lang('gen_hamradio_propagation_TEP'),
'TR' => lang('gen_hamradio_propagation_TR')];
asort($prop_modes);
?>
<div class="col-md-4 mb-3">
<label for="selectPropagation" class="form-label fw-semibold">
Propagation Mode
</label>
<select class="form-select" id="selectPropagation" name="prop_mode">
<option value="All" <?php if ($this->input->post('prop_mode') == "All" || $this->input->method() !== 'post') echo ' selected'; ?>>
<?php echo lang('general_word_all') ?>
</option>
<option value="" <?php if ($this->input->post('prop_mode') == "" && $this->input->method() == 'post') echo ' selected'; ?>>
<?php echo lang('general_word_undefined') ?>
</option>
<?php
foreach($prop_modes as $key => $label) {
echo '<option value="' . $key . '"';
if ($this->input->post('prop_mode') == $key) echo ' selected';
echo '>' . $label . '</option>';
}
?>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Action Buttons and Status -->
<div class="row">
<div class="col-md-6">
<button class="btn btn-primary btn-sm btn_submit_map_custom me-3" type="button">
<i class="fas fa-map-marker-alt me-2"></i>Load Map
<span class="spinner-border spinner-border-sm ms-2 d-none" id="load-spinner" role="status">
<span class="visually-hidden">Loading...</span>
</span>
</button>
<button class="btn btn-outline-info" type="button" onclick="exportMap()">
<i class="fas fa-download me-2"></i>Export Map
</button>
</div>
<div class="col-md-6">
<div class="alert alert-danger warningOnSubmit" style="display:none;">
<i class="fas fa-exclamation-triangle me-2"></i>
<span class="warningOnSubmit_txt">Error loading map data</span>
</div>
<div class="alert alert-success d-none" id="map-success-alert">
<i class="fas fa-check-circle me-2"></i>
<span id="qso-count-display">Map loaded successfully</span>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<!-- Map Statistics and Controls -->
<div class="row mb-2">
<div class="col-12">
<div class="card border-0">
<div class="card-body py-2">
<div class="row align-items-center">
<div class="col-md-8">
<div class="d-flex align-items-center">
<div class="me-4">
<small class="text-muted">QSOs Displayed:</small>
<span class="badge bg-primary ms-1" id="qso-count">0</span>
</div>
<div class="alert alert-success mb-0 py-1 px-2 d-none" id="map-status">
<small><i class="fas fa-check-circle me-1"></i>Map loaded successfully</small>
</div>
</div>
</div>
<div class="col-md-4 text-end">
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-success" onclick="fitMapToMarkers()" title="Zoom to fit all QSOs">
<i class="fas fa-expand-arrows-alt"></i>
</button>
<button type="button" class="btn btn-outline-success" onclick="toggleGridSquares()" title="Toggle grid squares">
<i class="fas fa-th"></i>
</button>
<button type="button" class="btn btn-outline-success" onclick="toggleFullscreen()" title="Toggle fullscreen">
<i class="fas fa-expand"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Map Container -->
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center py-2">
<h5 class="mb-0">
<i class="fas fa-globe-americas me-2"></i>World Map View
</h5>
<div class="text-muted">
<small>Showing: <strong id="active-logbook-display"><?php echo $logbook_name ?></strong></small>
</div>
</div>
<div class="card-body p-0">
<div id="custommap" class="map-leaflet" style="width: 100%; height: 1000px;"></div>
</div>
</div>
<!-- Map Legend -->
<div class="row mt-3">
<div class="col-12">
<div class="card border-0">
<div class="card-body py-2">
<div class="row align-items-center">
<div class="col-md-6">
<small class="text-muted fw-semibold me-3">Legend:</small>
<span class="me-3">
<i class="fas fa-dot-circle text-danger me-1"></i>
<small>QSO</small>
</span>
<span class="me-3">
<i class="fas fa-home text-primary me-1"></i>
<small>Home Station</small>
</span>
</div>
<div class="col-md-6 text-end">
<small class="text-muted">
<i class="fas fa-info-circle me-1"></i>
Click on markers for QSO details
</small>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Enhanced JavaScript functionality for improved UX
document.addEventListener('DOMContentLoaded', function() {
updateDateRangeDisplay();
// Add event listeners for date changes
document.getElementById('from').addEventListener('change', updateDateRangeDisplay);
document.getElementById('to').addEventListener('change', updateDateRangeDisplay);
});
function updateDateRangeDisplay() {
const fromDate = document.getElementById('from').value;
const toDate = document.getElementById('to').value;
const display = document.getElementById('date-range-display');
// Only update if the display element exists
if (display) {
if (fromDate && toDate) {
if (fromDate === toDate) {
display.textContent = new Date(fromDate).toLocaleDateString();
} else {
display.textContent = new Date(fromDate).toLocaleDateString() + ' - ' + new Date(toDate).toLocaleDateString();
}
} else {
display.textContent = 'Select dates';
}
}
}
function setDateRange(period) {
const today = new Date();
const fromInput = document.getElementById('from');
const toInput = document.getElementById('to');
let fromDate, toDate = today;
switch (period) {
case 'today':
fromDate = new Date(today);
break;
case 'week':
fromDate = new Date(today);
fromDate.setDate(today.getDate() - 7);
break;
case 'month':
// Create first day of current month
fromDate = new Date(today.getFullYear(), today.getMonth(), 1);
console.log('Month calculation - Today:', today.toDateString(), 'First of month:', fromDate.toDateString());
break;
case 'year':
fromDate = new Date(today.getFullYear(), 0, 1);
break;
}
if (fromDate) {
// Use local date formatting to avoid timezone issues
const formatDate = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
fromInput.value = formatDate(fromDate);
toInput.value = formatDate(today);
updateDateRangeDisplay();
}
}
function clearFilters() {
document.getElementById('band2').selectedIndex = 0;
document.getElementById('mode').selectedIndex = 0;
document.getElementById('selectPropagation').selectedIndex = 0;
const today = new Date().toISOString().split('T')[0];
document.getElementById('from').value = today;
document.getElementById('to').value = today;
updateDateRangeDisplay();
}
function exportMap() {
// This would integrate with the existing Leaflet print functionality
if (typeof map !== 'undefined' && map._container) {
// Trigger the existing print functionality
alert('Map export functionality - integrate with existing Leaflet print plugin');
}
}
function fitMapToMarkers() {
if (typeof map !== 'undefined' && plotlayers.length > 0) {
const group = new L.featureGroup(plotlayers);
map.fitBounds(group.getBounds().pad(0.1));
}
}
function toggleGridSquares() {
if (typeof maidenhead !== 'undefined') {
if (map.hasLayer(maidenhead)) {
map.removeLayer(maidenhead);
} else {
map.addLayer(maidenhead);
}
}
}
function toggleFullscreen() {
const mapContainer = document.getElementById('custommap');
if (!document.fullscreenElement) {
mapContainer.requestFullscreen().then(() => {
mapContainer.style.height = '100vh';
if (typeof map !== 'undefined') {
map.invalidateSize();
}
});
} else {
document.exitFullscreen().then(() => {
mapContainer.style.height = '1000px'; // Restore original height
if (typeof map !== 'undefined') {
map.invalidateSize();
}
});
}
}
// Update statistics when map loads
function updateMapStatistics(plotjson) {
if (plotjson && plotjson.markers) {
const qsoCountElement = document.getElementById('qso-count');
if (qsoCountElement) {
qsoCountElement.textContent = plotjson.markers.length;
}
// Count unique countries and gridsquares
const countries = new Set();
const gridsquares = new Set();
plotjson.markers.forEach(marker => {
if (marker.country) countries.add(marker.country);
if (marker.gridsquare) gridsquares.add(marker.gridsquare);
});
// Only update if elements exist
const countryCountElement = document.getElementById('country-count');
if (countryCountElement) {
countryCountElement.textContent = countries.size;
}
const gridCountElement = document.getElementById('grid-count');
if (gridCountElement) {
gridCountElement.textContent = gridsquares.size;
}
// Show success message
const mapStatusElement = document.getElementById('map-status');
if (mapStatusElement) {
mapStatusElement.classList.remove('d-none');
setTimeout(() => {
mapStatusElement.classList.add('d-none');
}, 3000);
}
}
}
</script>