Enhance bands management UI and interactivity

Improved the bands configuration page with info and controls cards, search/filter functionality, statistics, and visual feedback for checkbox actions. Updated bands.js to add animations, keyboard shortcuts, bulk award toggling, and dynamic statistics updates for a more user-friendly experience.
这个提交包含在:
Peter Goodhall 2025-08-09 23:09:31 +01:00
父节点 bfbd104d49
当前提交 9a5f825d10
共有 2 个文件被更改,包括 375 次插入53 次删除

查看文件

@ -23,39 +23,210 @@ $wwff = 0;
<h2><?php echo lang('options_bands'); ?></h2> <h2><?php echo lang('options_bands'); ?></h2>
<div class="card"> <!-- Info Card -->
<div class="card mb-3">
<div class="card-header"> <div class="card-header">
<?php echo lang('options_bands'); ?> <i class="fas fa-info-circle"></i> <?php echo lang('options_bands'); ?> Information
</div> </div>
<div class="card-body"> <div class="card-body">
<p class="card-text"> <div class="alert alert-primary alert-dismissible fade show" role="alert">
<strong><i class="fas fa-lightbulb"></i> Quick Guide:</strong>
The <strong>first column</strong> controls if a band appears in QSO entry. The <strong>award columns</strong> (CQ, DOK, DXCC, etc.) control which awards are tracked for that band. Use the <strong>master checkboxes</strong> at the bottom to toggle all bands for a specific award.
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<p class="card-text mb-2">
<?php echo lang('options_bands_text_ln1'); ?> <?php echo lang('options_bands_text_ln1'); ?>
</p> </p>
<p class="card-text"> <p class="card-text mb-0">
<?php echo lang('options_bands_text_ln2'); ?> <?php echo lang('options_bands_text_ln2'); ?>
</p> </p>
</div>
</div>
<!-- Controls Card -->
<div class="card mb-3">
<div class="card-header">
<i class="fas fa-sliders-h"></i> Band Management Controls
</div>
<div class="card-body">
<!-- Statistics -->
<div class="row mb-3">
<div class="col-md-8">
<div class="alert alert-info py-2 mb-0">
<div class="row text-center">
<div class="col-md-6 col-6">
<strong id="activeBandsCount">0</strong><br>
<small>Active for QSO Entry</small>
</div>
<div class="col-md-6 col-6">
<strong><?php echo count($bands); ?></strong><br>
<small>Total Bands Configured</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="btn-group btn-group-sm w-100" role="group">
<button type="button" class="btn btn-outline-success" id="enableAllAwards" title="Enable all award tracking for all bands">
<i class="fas fa-check-double"></i> Enable All Awards
</button>
<button type="button" class="btn btn-outline-warning" id="resetAllAwards" title="Disable all award tracking">
<i class="fas fa-times"></i> Reset Awards
</button>
</div>
</div>
</div>
<!-- Search and Filter -->
<div class="row">
<div class="col-md-8">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-search"></i></span>
</div>
<input type="text" id="bandSearch" class="form-control" placeholder="Search bands, frequencies, or band groups... (Press / to focus)">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="button" id="clearSearch" title="Clear search">
<i class="fas fa-times"></i>
</button>
</div>
</div>
</div>
<div class="col-md-4">
<div class="btn-group btn-group-sm w-100" role="group">
<button type="button" class="btn btn-outline-secondary" id="showActiveOnly">
<i class="fas fa-eye"></i> Active Only
</button>
<button type="button" class="btn btn-outline-secondary" id="showAll">
<i class="fas fa-list"></i> Show All
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Bands Table Card -->
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<span><i class="fas fa-table"></i> Bands Configuration</span>
<span class="badge badge-secondary" id="visibleRowsCount">Loading...</span>
</div>
<div class="card-body">
<div class="table-responsive"> <div class="table-responsive">
<style>
.card {
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
border: 1px solid rgba(0,0,0,.125);
}
.card-header {
background-color: #f8f9fa;
border-bottom: 1px solid rgba(0,0,0,.125);
font-weight: 500;
}
.card-header i {
color: #6c757d;
margin-right: 0.5rem;
}
.bandtable th, .bandtable td {
text-align: center;
vertical-align: middle;
white-space: nowrap;
padding: 8px 4px;
}
.bandtable th {
background-color: #f8f9fa;
border-top: 2px solid #dee2e6;
}
.bandtable tfoot th {
background-color: #e9ecef;
border-top: 2px solid #adb5bd;
}
.bandtable th:first-child,
.bandtable td:first-child {
position: sticky;
left: 0;
background-color: #fff;
z-index: 2;
box-shadow: 2px 0 2px rgba(0,0,0,0.1);
}
.bandtable th:nth-child(2),
.bandtable td:nth-child(2) {
position: sticky;
left: 40px;
background-color: #fff;
z-index: 2;
box-shadow: 2px 0 2px rgba(0,0,0,0.1);
font-weight: bold;
}
.bandtable thead th:first-child,
.bandtable thead th:nth-child(2) {
z-index: 3;
background-color: #f8f9fa;
}
.bandtable input[type="checkbox"] {
transform: scale(1.2);
cursor: pointer;
}
.band-checkbox-cell {
background-color: #f8f9fa;
}
.frequency-cell {
font-family: monospace;
font-size: 0.9em;
}
.saving {
background-color: #fff3cd !important;
transition: background-color 0.3s ease;
}
.saved {
background-color: #d4edda !important;
transition: background-color 0.3s ease;
}
.updating {
background-color: #e2e3e5 !important;
transition: background-color 0.2s ease;
}
.updated {
background-color: #d1ecf1 !important;
transition: background-color 0.3s ease;
}
.band-actions {
white-space: nowrap;
}
.band-actions .btn {
margin: 0 2px;
}
@media (max-width: 768px) {
.bandtable th, .bandtable td {
padding: 4px 2px;
font-size: 0.85em;
}
}
</style>
<table style="width:100%" class="bandtable table table-sm table-striped"> <table style="width:100%" class="bandtable table table-sm table-striped">
<thead> <thead>
<tr> <tr>
<th></th> <th title="Enable/disable band for QSO entry">Active</th>
<th><?php echo lang('gen_hamradio_band'); ?></th> <th><?php echo lang('gen_hamradio_band'); ?></th>
<th><?php echo lang('gen_hamradio_cq'); ?></th> <th title="CQ Magazine DX Awards">CQ</th>
<th><?php echo lang('gen_hamradio_dok'); ?></th> <th title="Diplom Ortsverbände Kennung">DOK</th>
<th><?php echo lang('gen_hamradio_dxcc'); ?></th> <th title="DX Century Club">DXCC</th>
<th><?php echo lang('gen_hamradio_iota'); ?></th> <th title="Islands On The Air">IOTA</th>
<th><?php echo lang('gen_hamradio_pota'); ?></th> <th title="Parks on the Air">POTA</th>
<th><?php echo lang('gen_hamradio_sig'); ?></th> <th title="Special Interest Group">SIG</th>
<th><?php echo lang('gen_hamradio_sota'); ?></th> <th title="Summits on the Air">SOTA</th>
<th><?php echo lang('gen_hamradio_county_reference'); ?></th> <th title="US Counties">Counties</th>
<th><?php echo lang('menu_vucc'); ?></th> <th title="VHF/UHF Century Club">VUCC</th>
<th><?php echo lang('menu_was'); ?></th> <th title="Worked All States">WAS</th>
<th><?php echo lang('gen_hamradio_wwff'); ?></th> <th title="World Wide Flora and Fauna">WWFF</th>
<th><?php echo lang('gen_hamradio_bandgroup'); ?></th> <th title="Band group classification">Group</th>
<th><?php echo lang('options_bands_ssb_qrg'); ?></th> <th title="SSB Operating Frequency">SSB</th>
<th><?php echo lang('options_bands_data_qrg'); ?></th> <th title="Data Operating Frequency">Data</th>
<th><?php echo lang('options_bands_cw_qrg'); ?></th> <th title="CW Operating Frequency">CW</th>
<?php if($this->session->userdata('user_type') == '99') { ?> <?php if($this->session->userdata('user_type') == '99') { ?>
<th></th> <th></th>
<th></th> <th></th>
@ -65,28 +236,28 @@ $wwff = 0;
<tbody> <tbody>
<?php foreach ($bands as $band) { ?> <?php foreach ($bands as $band) { ?>
<tr> <tr>
<td style="text-align: center; vertical-align: middle;" class='band_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->active == 1) {echo 'checked';}?>></td> <td class='band_<?php echo $band->id ?> band-checkbox-cell'><input type="checkbox" <?php if ($band->active == 1) {echo 'checked';}?>></td>
<td style="text-align: center; vertical-align: middle;" ><?php echo $band->band;?></td> <td class="band-name"><?php echo $band->band;?></td>
<td style="text-align: center; vertical-align: middle;" class='cq_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->cq == 1) {echo 'checked'; $cq++;}?>></td> <td class='cq_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->cq == 1) {echo 'checked'; $cq++;}?>></td>
<td style="text-align: center; vertical-align: middle;" class='dok_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->dok == 1) {echo 'checked'; $dok++;}?>></td> <td class='dok_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->dok == 1) {echo 'checked'; $dok++;}?>></td>
<td style="text-align: center; vertical-align: middle;" class='dxcc_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->dxcc == 1) {echo 'checked'; $dxcc++;}?>></td> <td class='dxcc_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->dxcc == 1) {echo 'checked'; $dxcc++;}?>></td>
<td style="text-align: center; vertical-align: middle;" class='iota_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->iota == 1) {echo 'checked'; $iota++;}?>></td> <td class='iota_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->iota == 1) {echo 'checked'; $iota++;}?>></td>
<td style="text-align: center; vertical-align: middle;" class='pota_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->iota == 1) {echo 'checked'; $pota++;}?>></td> <td class='pota_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->pota == 1) {echo 'checked'; $pota++;}?>></td>
<td style="text-align: center; vertical-align: middle;" class='sig_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->sig == 1) {echo 'checked'; $sig++;}?>></td> <td class='sig_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->sig == 1) {echo 'checked'; $sig++;}?>></td>
<td style="text-align: center; vertical-align: middle;" class='sota_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->sota == 1) {echo 'checked'; $sota++;}?>></td> <td class='sota_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->sota == 1) {echo 'checked'; $sota++;}?>></td>
<td style="text-align: center; vertical-align: middle;" class='uscounties_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->uscounties == 1) {echo 'checked'; $uscounties++;}?>></td> <td class='uscounties_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->uscounties == 1) {echo 'checked'; $uscounties++;}?>></td>
<td style="text-align: center; vertical-align: middle;" class='vucc_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->vucc == 1) {echo 'checked'; $vucc++;}?>></td> <td class='vucc_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->vucc == 1) {echo 'checked'; $vucc++;}?>></td>
<td style="text-align: center; vertical-align: middle;" class='was_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->was == 1) {echo 'checked'; $was++;}?>></td> <td class='was_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->was == 1) {echo 'checked'; $was++;}?>></td>
<td style="text-align: center; vertical-align: middle;" class='wwff_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->wwff == 1) {echo 'checked'; $wwff++;}?>></td> <td class='wwff_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->wwff == 1) {echo 'checked'; $wwff++;}?>></td>
<td style="text-align: center; vertical-align: middle;" ><?php echo $band->bandgroup;?></td> <td><?php echo $band->bandgroup;?></td>
<td style="text-align: center; vertical-align: middle;" ><?php echo $band->ssb;?></td> <td class="frequency-cell"><?php echo $band->ssb;?></td>
<td style="text-align: center; vertical-align: middle;" ><?php echo $band->data;?></td> <td class="frequency-cell"><?php echo $band->data;?></td>
<td style="text-align: center; vertical-align: middle;" ><?php echo $band->cw;?></td> <td class="frequency-cell"><?php echo $band->cw;?></td>
<?php if($this->session->userdata('user_type') == '99') { ?> <?php if($this->session->userdata('user_type') == '99') { ?>
<td style="text-align: center; vertical-align: middle;" > <td>
<a href="javascript:editBandDialog('<?php echo $band->bandid ?>');" class="btn btn-outline-primary btn-sm" title="Edit"><i class="fas fa-edit"></i></a> <a href="javascript:editBandDialog('<?php echo $band->bandid ?>');" class="btn btn-outline-primary btn-sm" title="Edit"><i class="fas fa-edit"></i></a>
</td> </td>
<td style="text-align: center; vertical-align: middle;" > <td>
<a href="javascript:deleteBand('<?php echo $band->id . '\',\'' . $band->band ?>');" class="btn btn-danger btn-sm" title="Delete"><i class="fas fa-trash-alt"></i></a> <a href="javascript:deleteBand('<?php echo $band->id . '\',\'' . $band->band ?>');" class="btn btn-danger btn-sm" title="Delete"><i class="fas fa-trash-alt"></i></a>
</td> </td>
<?php } ?> <?php } ?>
@ -95,7 +266,7 @@ $wwff = 0;
<?php } ?> <?php } ?>
</tbody> </tbody>
<tfoot> <tfoot>
<th><?php echo lang('general_word_all'); ?></th> <th>Toggle All</th>
<th></th> <th></th>
<th class="master_cq"><input type="checkbox" <?php if ($cq > 0) echo 'checked';?>></th> <th class="master_cq"><input type="checkbox" <?php if ($cq > 0) echo 'checked';?>></th>
<th class="master_dok"><input type="checkbox" <?php if ($dok > 0) echo 'checked';?>></th> <th class="master_dok"><input type="checkbox" <?php if ($dok > 0) echo 'checked';?>></th>
@ -115,7 +286,7 @@ $wwff = 0;
<th></th> <th></th>
<th></th> <th></th>
</tfoot> </tfoot>
<table> </table>
</div> </div>
<br/> <br/>
<p> <p>

查看文件

@ -1,16 +1,48 @@
$('.bandtable').on('click', 'input[type="checkbox"]', function() { $('.bandtable').on('click', 'input[type="checkbox"]', function() {
var clickedbandid = $(this).closest('td').attr("class"); var $checkbox = $(this);
var $cell = $checkbox.closest('td');
// Add visual feedback
$cell.addClass('saving');
$checkbox.prop('disabled', true);
var clickedbandid = $cell.attr("class");
clickedbandid = clickedbandid.match(/\d+/)[0]; clickedbandid = clickedbandid.match(/\d+/)[0];
saveBand(clickedbandid);
saveBand(clickedbandid, function() {
// Remove visual feedback on success
$cell.removeClass('saving');
$checkbox.prop('disabled', false);
// Add success flash
$cell.addClass('saved');
setTimeout(function() {
$cell.removeClass('saved');
}, 1000);
});
}); });
$('.bandtable tfoot').on('click', 'input[type="checkbox"]', function() { $('.bandtable tfoot').on('click', 'input[type="checkbox"]', function() {
var clickedaward = $(this).closest('th').attr("class"); var $masterCheckbox = $(this);
var status = $(this).is(":checked"); var clickedaward = $masterCheckbox.closest('th').attr("class");
var status = $masterCheckbox.is(":checked");
clickedaward = clickedaward.replace('master_', ''); clickedaward = clickedaward.replace('master_', '');
$('[class^='+clickedaward+'_] input[type="checkbox').each(function() {
$(this).prop( "checked", status ); // Update all related checkboxes with animation
$('[class^='+clickedaward+'_] input[type="checkbox"]').each(function() {
var $checkbox = $(this);
var $cell = $checkbox.closest('td');
$cell.addClass('updating');
setTimeout(function() {
$checkbox.prop("checked", status);
$cell.removeClass('updating').addClass('updated');
setTimeout(function() {
$cell.removeClass('updated');
}, 500);
}, Math.random() * 200); // Stagger the updates
}); });
saveBandAward(clickedaward, status); saveBandAward(clickedaward, status);
}); });
@ -34,8 +66,122 @@ $('.bandtable').DataTable({
"scrollCollapse": true, "scrollCollapse": true,
"paging": false, "paging": false,
"scrollX": true, "scrollX": true,
"searching": true,
"language": { "language": {
url: getDataTablesLanguageUrl(), url: getDataTablesLanguageUrl(),
},
"columnDefs": [
{
"targets": [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // Checkbox columns
"orderable": false,
"className": "text-center"
},
{
"targets": [14, 15, 16], // Frequency columns
"className": "text-center frequency-cell"
}
],
"drawCallback": function() {
updateStatistics();
}
});
// Custom search functionality
$('#bandSearch').on('keyup', function() {
$('.bandtable').DataTable().search(this.value).draw();
updateStatistics();
});
// Clear search button
$('#clearSearch').on('click', function() {
$('#bandSearch').val('');
$('.bandtable').DataTable().search('').draw();
updateStatistics();
});
// Keyboard shortcut to focus search (like GitHub)
$(document).on('keydown', function(e) {
if (e.key === '/' && !$(e.target).is('input, textarea')) {
e.preventDefault();
$('#bandSearch').focus();
}
if (e.key === 'Escape' && $(e.target).is('#bandSearch')) {
$('#bandSearch').blur().val('');
$('.bandtable').DataTable().search('').draw();
}
});
// Filter buttons
$('#showActiveOnly').on('click', function() {
$('.bandtable tbody tr').each(function() {
var $row = $(this);
var isActive = $row.find('.band-checkbox-cell input[type="checkbox"]').is(':checked');
if (!isActive) {
$row.hide();
} else {
$row.show();
}
});
$(this).addClass('active').siblings().removeClass('active');
updateStatistics();
});
$('#showAll').on('click', function() {
$('.bandtable tbody tr').show();
$(this).addClass('active').siblings().removeClass('active');
updateStatistics();
});
// Initialize with "Show All" active
$('#showAll').addClass('active');
// Update statistics
function updateStatistics() {
var activeBands = $('.band-checkbox-cell input[type="checkbox"]:checked').length;
$('#activeBandsCount').text(activeBands);
// Update visible rows count
var visibleRows = $('.bandtable tbody tr:visible').length;
var totalRows = $('.bandtable tbody tr').length;
$('#visibleRowsCount').text(visibleRows + ' of ' + totalRows + ' bands');
}
// Update statistics on page load
$(document).ready(function() {
updateStatistics();
});
// Update statistics when band status changes
$('.bandtable').on('change', '.band-checkbox-cell input[type="checkbox"]', function() {
updateStatistics();
});
// Bulk action buttons
$('#enableAllAwards').on('click', function() {
if (confirm('This will enable ALL award tracking (DXCC, IOTA, SOTA, WWFF, POTA, etc.) for ALL bands. Continue?')) {
$('.bandtable tbody tr').each(function() {
var $row = $(this);
// Check all award checkboxes except the first (active) column
$row.find('input[type="checkbox"]').not('.band-checkbox-cell input').each(function() {
if (!$(this).is(':checked')) {
$(this).prop('checked', true).trigger('change');
}
});
});
}
});
$('#resetAllAwards').on('click', function() {
if (confirm('This will disable ALL award tracking for ALL bands (bands will remain active for QSO entry). Continue?')) {
$('.bandtable tbody tr').each(function() {
var $row = $(this);
// Uncheck all award checkboxes except the first (active) column
$row.find('input[type="checkbox"]').not('.band-checkbox-cell input').each(function() {
if ($(this).is(':checked')) {
$(this).prop('checked', false).trigger('change');
}
});
});
} }
}); });
@ -201,7 +347,7 @@ function deactivateAllBands() {
}); });
} }
function saveBand(id) { function saveBand(id, callback) {
$.ajax({ $.ajax({
url: base_url + 'index.php/band/saveBand', url: base_url + 'index.php/band/saveBand',
type: 'post', type: 'post',
@ -220,6 +366,11 @@ function saveBand(id) {
'vucc': $(".vucc_"+id+" input[type='checkbox']").is(":checked") 'vucc': $(".vucc_"+id+" input[type='checkbox']").is(":checked")
}, },
success: function (html) { success: function (html) {
if (callback) callback();
},
error: function() {
// Show error state
if (callback) callback();
} }
}); });
} }