Enhance custom map UI and add loading feedback
Improves the custom map interface with a redesigned filter section, quick action buttons, advanced filters, and a statistics display. Adds a loading spinner, disables the submit button during map load, and provides success/error feedback with failsafe timeout. Integrates new CSS for better map and legend styling, and updates JS to support callbacks for AJAX success/error, improving user experience and reliability.
这个提交包含在:
父节点
43e7d9cb9d
当前提交
129cb5986f
共有 4 个文件被更改,包括 615 次插入 和 92 次删除
|
|
@ -784,6 +784,26 @@ if ($this->session->userdata('user_id') != null) {
|
|||
});
|
||||
// Form "submit" //
|
||||
$('.custom-map-QSOs .btn_submit_map_custom').off('click').on('click', function() {
|
||||
// Show loading spinner and disable button
|
||||
const $button = $(this);
|
||||
const $spinner = $('#load-spinner');
|
||||
|
||||
$button.prop('disabled', true);
|
||||
$spinner.removeClass('d-none');
|
||||
|
||||
// Hide any previous alerts
|
||||
$('.warningOnSubmit').hide();
|
||||
$('#map-success-alert').addClass('d-none');
|
||||
|
||||
// Failsafe timeout to prevent stuck spinner (35 seconds)
|
||||
const failsafeTimeout = setTimeout(function() {
|
||||
console.warn('Map loading timed out - forcing spinner hide');
|
||||
$button.prop('disabled', false);
|
||||
$spinner.addClass('d-none');
|
||||
$('.warningOnSubmit .warningOnSubmit_txt').text('Map loading timed out. Please try again.');
|
||||
$('.warningOnSubmit').show();
|
||||
}, 35000);
|
||||
|
||||
var customdata = {
|
||||
'dataPost': {
|
||||
'date_from': $('.custom-map-QSOs input[name="from"]').val(),
|
||||
|
|
@ -795,6 +815,49 @@ if ($this->session->userdata('user_id') != null) {
|
|||
},
|
||||
'map_id': '#custommap'
|
||||
};
|
||||
|
||||
// Add success and error callbacks to the customdata
|
||||
customdata.onSuccess = function(plotjson) {
|
||||
// Clear the failsafe timeout
|
||||
clearTimeout(failsafeTimeout);
|
||||
|
||||
try {
|
||||
// Update statistics
|
||||
if (typeof updateMapStatistics === 'function') {
|
||||
updateMapStatistics(plotjson);
|
||||
}
|
||||
|
||||
// Show success message
|
||||
const qsoCount = plotjson['markers'] ? plotjson['markers'].length : 0;
|
||||
$('#qso-count-display').text(`Loaded ${qsoCount} QSOs successfully`);
|
||||
$('#map-success-alert').removeClass('d-none');
|
||||
|
||||
setTimeout(() => {
|
||||
$('#map-success-alert').addClass('d-none');
|
||||
}, 3000);
|
||||
} catch (error) {
|
||||
console.error('Error in success callback:', error);
|
||||
$('.warningOnSubmit .warningOnSubmit_txt').text('Map loaded but encountered an error displaying statistics.');
|
||||
$('.warningOnSubmit').show();
|
||||
} finally {
|
||||
// Always re-enable button and hide spinner
|
||||
$button.prop('disabled', false);
|
||||
$spinner.addClass('d-none');
|
||||
}
|
||||
};
|
||||
|
||||
customdata.onError = function() {
|
||||
// Clear the failsafe timeout
|
||||
clearTimeout(failsafeTimeout);
|
||||
|
||||
$('.warningOnSubmit .warningOnSubmit_txt').text('Failed to load map data. Please try again.');
|
||||
$('.warningOnSubmit').show();
|
||||
|
||||
// Re-enable button and hide spinner
|
||||
$button.prop('disabled', false);
|
||||
$spinner.addClass('d-none');
|
||||
};
|
||||
|
||||
initplot(qso_loc, customdata);
|
||||
})
|
||||
});
|
||||
|
|
@ -806,6 +869,10 @@ if ($this->session->userdata('user_id') != null) {
|
|||
success: function(data) {
|
||||
document.getElementById('from').value = data;
|
||||
document.getElementById('to').value = new Date().toISOString().split('T')[0];
|
||||
// Update the date range display
|
||||
if (typeof updateDateRangeDisplay === 'function') {
|
||||
updateDateRangeDisplay();
|
||||
}
|
||||
},
|
||||
error: function() {},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,107 +1,474 @@
|
|||
<link rel="stylesheet" href="<?php echo base_url(); ?>assets/css/map-enhancements.css">
|
||||
|
||||
<div class="container custom-map-QSOs">
|
||||
<br>
|
||||
<h2><?php echo $logbook_name; echo strpos($logbook_name, 'logbook') ? '' : ' logbook'; ?> QSOs (Custom Dates)</h2>
|
||||
|
||||
<!-- 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 } ?>
|
||||
|
||||
<form method="post" action="<?php echo site_url('map/custom'); ?>">
|
||||
<div class="row">
|
||||
<div class="mb-3 col-md-3">
|
||||
<label for="from"><?php echo lang('gen_from_date') . ": " ?></label>
|
||||
<input name="from" id="from" type="date" class="form-control w-auto" value="<?php echo $date_from; ?>">
|
||||
</div>
|
||||
|
||||
<div class="mb-3 col-md-3">
|
||||
<label for="to"><?php echo lang('gen_to_date') . ": " ?></label>
|
||||
<input name="to" id="to" type="date" class="form-control w-auto" value="<?php echo $date_to; ?>" max="<?php echo $date_to; ?>">
|
||||
</div>
|
||||
|
||||
<div class="mb-3 col-md-3 d-flex align-items-end">
|
||||
<input class="btn btn-secondary" type="button" value="<?php echo lang('set_log_to_full_dates'); ?>" onclick="get_oldest_qso_date();">
|
||||
</div>
|
||||
<!-- 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>
|
||||
|
||||
<div class="row">
|
||||
<div class="mb-3 col-md-3">
|
||||
<label for="band">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'; ?>>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>
|
||||
<!-- 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>
|
||||
|
||||
<div class="mb-3 col-md-3">
|
||||
<label for="mode">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="mb-3 col-md-3">
|
||||
<label for="selectPropagation">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>
|
||||
<!-- 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 class="row">
|
||||
<div class="mb-2 col-md-2">
|
||||
<input class="btn btn-primary btn_submit_map_custom" type="button" value="Load Map">
|
||||
</div>
|
||||
<div class="mb-4 col-md-4">
|
||||
<div class="alert alert-danger warningOnSubmit" style="display:none;"><span><i class="fas fa-times-circle"></i></span> <span class="warningOnSubmit_txt ms-1">Error</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Map -->
|
||||
<div id="custommap" class="map-leaflet mt-2" style="width: 100%; height: 1000px;"></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" role="alert">Showing QSOs for Custom Dates for Active Logbook <?php echo $logbook_name ?></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');
|
||||
|
||||
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>
|
||||
74
assets/css/map-enhancements.css
普通文件
74
assets/css/map-enhancements.css
普通文件
|
|
@ -0,0 +1,74 @@
|
|||
/* Custom Map Interface Enhancements */
|
||||
|
||||
/* Ensure grid square labels stay together (fix for separated letters) */
|
||||
.leaflet-marker-icon.my-div-icon,
|
||||
.my-div-icon .grid-text,
|
||||
.my-div-icon .grid-text font,
|
||||
span.grid-text,
|
||||
span.grid-text > font {
|
||||
letter-spacing: 0 !important;
|
||||
word-spacing: 0 !important;
|
||||
white-space: nowrap !important;
|
||||
font-family: monospace !important;
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
/* Map container enhancements */
|
||||
#custommap {
|
||||
border-radius: 0 0 0.375rem 0.375rem;
|
||||
overflow: hidden;
|
||||
transition: height 0.3s ease;
|
||||
width: 100%;
|
||||
height: 1000px !important; /* Maintain original screenshot-friendly height */
|
||||
}
|
||||
|
||||
/* Fullscreen map styling */
|
||||
#custommap:fullscreen {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* Statistics badges */
|
||||
.custom-map-QSOs .badge {
|
||||
font-size: 0.85em;
|
||||
padding: 0.5em 0.75em;
|
||||
}
|
||||
|
||||
|
||||
/* Legend styling */
|
||||
.custom-map-QSOs .fas {
|
||||
width: 1em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Responsive improvements */
|
||||
@media (max-width: 768px) {
|
||||
.custom-map-QSOs .card-body {
|
||||
padding: 1rem 0.5rem;
|
||||
}
|
||||
|
||||
.custom-map-QSOs .btn-group .btn {
|
||||
padding: 0.375rem 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* Keep map height at 1000px even on mobile for screenshots */
|
||||
#custommap {
|
||||
height: 1000px !important;
|
||||
min-height: 1000px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation for statistics updates */
|
||||
.custom-map-QSOs .badge {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.custom-map-QSOs .badge.updated {
|
||||
animation: pulse 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.1); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
|
|
@ -68,8 +68,18 @@ function askForPlots(_url_qso, options={}) {
|
|||
removeMarkers();
|
||||
if (typeof options.dataPost !== "undefined") { _dataPost = options.dataPost; } else { _dataPost = {}; }
|
||||
$.ajax({
|
||||
url: _url_qso, type: 'POST', dataType: 'json', data: _dataPost,
|
||||
error: function() { console.log('[ERROR] ajax askForPlots() function return error.'); },
|
||||
url: _url_qso,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: _dataPost,
|
||||
timeout: 30000, // 30 second timeout
|
||||
error: function(xhr, status, error) {
|
||||
console.log('[ERROR] ajax askForPlots() function return error:', status, error);
|
||||
// Call custom error callback if provided
|
||||
if (typeof options.onError === 'function') {
|
||||
options.onError();
|
||||
}
|
||||
},
|
||||
success: function(plotjson) {
|
||||
if ((typeof plotjson['markers'] !== "undefined")&&(plotjson['markers'].length>0)) {
|
||||
for (i=0;i<plotjson['markers'].length;i++) { createPlots(plotjson['markers'][i]); }
|
||||
|
|
@ -80,6 +90,11 @@ function askForPlots(_url_qso, options={}) {
|
|||
$.each(iconsList, function(icon, data){
|
||||
$(options.map_id+' .cspot_'+icon).addClass(data.icon).css("color",data.color);
|
||||
});
|
||||
|
||||
// Call custom success callback if provided
|
||||
if (typeof options.onSuccess === 'function') {
|
||||
options.onSuccess(plotjson);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
正在加载…
在新工单中引用