2.7.0
这个提交包含在:
当前提交
4620cc3a72
共有 35 个文件被更改,包括 3099 次插入 和 886 次删除
11
Dockerfile
11
Dockerfile
|
|
@ -24,5 +24,16 @@ RUN apt-get update && apt-get install -y \
|
|||
&& docker-php-ext-install xml \
|
||||
&& a2enmod rewrite
|
||||
|
||||
# Copy script.sh and make it executable
|
||||
COPY script.sh /usr/local/bin/startup.sh
|
||||
RUN sed -i 's/\r$//' /usr/local/bin/startup.sh && chmod +x /usr/local/bin/startup.sh
|
||||
|
||||
# Configure PHP for larger file uploads (30MB)
|
||||
RUN echo "upload_max_filesize = 30M" >> /usr/local/etc/php/conf.d/uploads.ini \
|
||||
&& echo "post_max_size = 35M" >> /usr/local/etc/php/conf.d/uploads.ini \
|
||||
&& echo "memory_limit = 64M" >> /usr/local/etc/php/conf.d/uploads.ini \
|
||||
&& echo "max_execution_time = 300" >> /usr/local/etc/php/conf.d/uploads.ini \
|
||||
&& echo "max_input_time = 300" >> /usr/local/etc/php/conf.d/uploads.ini
|
||||
|
||||
# Expose port 80
|
||||
EXPOSE 80
|
||||
|
|
@ -4,5 +4,9 @@ FROM mariadb:latest
|
|||
# Add the install.sql file to the docker image
|
||||
ADD install/assets/install.sql /docker-entrypoint-initdb.d
|
||||
|
||||
# Create a healthcheck script that uses mariadb-admin
|
||||
RUN echo '#!/bin/bash\nmariadb-admin ping -h "localhost" --silent' > /usr/local/bin/healthcheck.sh \
|
||||
&& chmod +x /usr/local/bin/healthcheck.sh
|
||||
|
||||
# Expose port 3306
|
||||
EXPOSE 3306
|
||||
|
|
@ -22,7 +22,7 @@ $config['migration_enabled'] = TRUE;
|
|||
|
|
||||
*/
|
||||
|
||||
$config['migration_version'] = 204;
|
||||
$config['migration_version'] = 205;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -13,16 +13,6 @@ class adif extends CI_Controller {
|
|||
if(!$this->user_model->authorize(2)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); }
|
||||
}
|
||||
|
||||
public function test() {
|
||||
if(validateADIFDate('20120228') == true){
|
||||
echo "valid date";
|
||||
} else {
|
||||
echo "date incorrect";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* Shows Export Views */
|
||||
public function export() {
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,16 @@
|
|||
class Dashboard extends CI_Controller
|
||||
{
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
// Load common models that are used across multiple methods
|
||||
$this->load->model('user_model');
|
||||
$this->load->model('logbook_model');
|
||||
$this->load->model('logbooks_model');
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
// If environment is set to development then show the debug toolbar
|
||||
|
|
@ -13,10 +23,6 @@ class Dashboard extends CI_Controller
|
|||
// Load language files
|
||||
$this->lang->load('lotw');
|
||||
|
||||
// Database connections
|
||||
$this->load->model('logbook_model');
|
||||
$this->load->model('user_model');
|
||||
|
||||
// LoTW infos
|
||||
$this->load->model('LotwCert');
|
||||
|
||||
|
|
@ -31,7 +37,6 @@ class Dashboard extends CI_Controller
|
|||
redirect('user/login');
|
||||
}
|
||||
|
||||
$this->load->model('logbooks_model');
|
||||
$logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
|
||||
|
||||
/*
|
||||
|
|
@ -63,9 +68,11 @@ class Dashboard extends CI_Controller
|
|||
$this->load->model('stations');
|
||||
$this->load->model('setup_model');
|
||||
|
||||
$data['countryCount'] = $this->setup_model->getCountryCount();
|
||||
$data['logbookCount'] = $this->setup_model->getLogbookCount();
|
||||
$data['locationCount'] = $this->setup_model->getLocationCount();
|
||||
// Use consolidated setup counts instead of 3 separate queries
|
||||
$setup_counts = $this->setup_model->getAllSetupCounts();
|
||||
$data['countryCount'] = $setup_counts['country_count'];
|
||||
$data['logbookCount'] = $setup_counts['logbook_count'];
|
||||
$data['locationCount'] = $setup_counts['location_count'];
|
||||
|
||||
$data['current_active'] = $this->stations->find_active();
|
||||
|
||||
|
|
@ -85,19 +92,21 @@ class Dashboard extends CI_Controller
|
|||
|
||||
$data['radio_status'] = $this->cat->recent_status();
|
||||
|
||||
// Store info
|
||||
$data['todays_qsos'] = $this->logbook_model->todays_qsos($logbooks_locations_array);
|
||||
$data['total_qsos'] = $this->logbook_model->total_qsos($logbooks_locations_array);
|
||||
$data['month_qsos'] = $this->logbook_model->month_qsos($logbooks_locations_array);
|
||||
$data['year_qsos'] = $this->logbook_model->year_qsos($logbooks_locations_array);
|
||||
// Store info - Use consolidated query for QSO statistics
|
||||
$qso_stats = $this->logbook_model->get_qso_statistics_consolidated($logbooks_locations_array);
|
||||
$data['todays_qsos'] = $qso_stats['todays_qsos'];
|
||||
$data['total_qsos'] = $qso_stats['total_qsos'];
|
||||
$data['month_qsos'] = $qso_stats['month_qsos'];
|
||||
$data['year_qsos'] = $qso_stats['year_qsos'];
|
||||
|
||||
// Load Countries Breakdown data into array
|
||||
$CountriesBreakdown = $this->logbook_model->total_countries_confirmed($logbooks_locations_array);
|
||||
|
||||
$data['total_countries'] = $CountriesBreakdown['Countries_Worked'];
|
||||
$data['total_countries_confirmed_paper'] = $CountriesBreakdown['Countries_Worked_QSL'];
|
||||
$data['total_countries_confirmed_eqsl'] = $CountriesBreakdown['Countries_Worked_EQSL'];
|
||||
$data['total_countries_confirmed_lotw'] = $CountriesBreakdown['Countries_Worked_LOTW'];
|
||||
// Use consolidated countries statistics instead of separate queries
|
||||
$countries_stats = $this->logbook_model->get_countries_statistics_consolidated($logbooks_locations_array);
|
||||
|
||||
$data['total_countries'] = $countries_stats['Countries_Worked'];
|
||||
$data['total_countries_confirmed_paper'] = $countries_stats['Countries_Worked_QSL'];
|
||||
$data['total_countries_confirmed_eqsl'] = $countries_stats['Countries_Worked_EQSL'];
|
||||
$data['total_countries_confirmed_lotw'] = $countries_stats['Countries_Worked_LOTW'];
|
||||
$current_countries = $countries_stats['Countries_Current'];
|
||||
|
||||
$data['dashboard_upcoming_dx_card'] = false;
|
||||
$data['dashboard_qslcard_card'] = false;
|
||||
|
|
@ -107,53 +116,23 @@ class Dashboard extends CI_Controller
|
|||
|
||||
$dashboard_options = $this->user_options_model->get_options('dashboard')->result();
|
||||
|
||||
// Optimize options processing - convert to associative array for O(1) lookup
|
||||
$options_map = array();
|
||||
foreach ($dashboard_options as $item) {
|
||||
$option_name = $item->option_name;
|
||||
$option_key = $item->option_key;
|
||||
$option_value = $item->option_value;
|
||||
|
||||
if ($option_name == 'dashboard_upcoming_dx_card' && $option_key == 'enabled') {
|
||||
if($option_value == 'true') {
|
||||
$data['dashboard_upcoming_dx_card'] = true;
|
||||
} else {
|
||||
$data['dashboard_upcoming_dx_card'] = false;
|
||||
}
|
||||
}
|
||||
$options_map[$item->option_name][$item->option_key] = $item->option_value;
|
||||
}
|
||||
|
||||
if ($option_name == 'dashboard_qslcards_card' && $option_key == 'enabled') {
|
||||
if($item->option_value == 'true') {
|
||||
$data['dashboard_qslcard_card'] = true;
|
||||
} else {
|
||||
$data['dashboard_qslcard_card'] = false;
|
||||
}
|
||||
}
|
||||
// Quick lookups instead of nested loops
|
||||
$data['dashboard_upcoming_dx_card'] = isset($options_map['dashboard_upcoming_dx_card']['enabled']) && $options_map['dashboard_upcoming_dx_card']['enabled'] == 'true';
|
||||
$data['dashboard_qslcard_card'] = isset($options_map['dashboard_qslcards_card']['enabled']) && $options_map['dashboard_qslcards_card']['enabled'] == 'true';
|
||||
$data['dashboard_eqslcard_card'] = isset($options_map['dashboard_eqslcards_card']['enabled']) && $options_map['dashboard_eqslcards_card']['enabled'] == 'true';
|
||||
$data['dashboard_lotw_card'] = isset($options_map['dashboard_lotw_card']['enabled']) && $options_map['dashboard_lotw_card']['enabled'] == 'true';
|
||||
$data['dashboard_vuccgrids_card'] = isset($options_map['dashboard_vuccgrids_card']['enabled']) && $options_map['dashboard_vuccgrids_card']['enabled'] == 'true';
|
||||
|
||||
if ($option_name == 'dashboard_eqslcards_card' && $option_key == 'enabled') {
|
||||
if($item->option_value == 'true') {
|
||||
$data['dashboard_eqslcard_card'] = true;
|
||||
} else {
|
||||
$data['dashboard_eqslcard_card'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($option_name == 'dashboard_lotw_card' && $option_key == 'enabled') {
|
||||
if($item->option_value == 'true') {
|
||||
$data['dashboard_lotw_card'] = true;
|
||||
} else {
|
||||
$data['dashboard_lotw_card'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($option_name == 'dashboard_vuccgrids_card' && $option_key == 'enabled') {
|
||||
if($item->option_value == 'true') {
|
||||
$data['dashboard_vuccgrids_card'] = true;
|
||||
|
||||
$data['vucc'] = $this->vucc->fetchVuccSummary();
|
||||
$data['vuccSAT'] = $this->vucc->fetchVuccSummary('SAT');
|
||||
} else {
|
||||
$data['dashboard_vuccgrids_card'] = false;
|
||||
}
|
||||
}
|
||||
// Only load VUCC data if the card is actually enabled
|
||||
if ($data['dashboard_vuccgrids_card']) {
|
||||
$data['vucc'] = $this->vucc->fetchVuccSummary();
|
||||
$data['vuccSAT'] = $this->vucc->fetchVuccSummary('SAT');
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -185,12 +164,10 @@ class Dashboard extends CI_Controller
|
|||
|
||||
$data['page_title'] = "Dashboard";
|
||||
|
||||
// Optimize DXCC calculation - get count directly instead of loading all records
|
||||
$this->load->model('dxcc');
|
||||
$dxcc = $this->dxcc->list_current();
|
||||
|
||||
$current = $this->logbook_model->total_countries_current($logbooks_locations_array);
|
||||
|
||||
$data['total_countries_needed'] = count($dxcc->result()) - $current;
|
||||
$total_dxcc_count = $this->dxcc->get_total_dxcc_count();
|
||||
$data['total_countries_needed'] = $total_dxcc_count - $current_countries;
|
||||
|
||||
$this->load->view('interface_assets/header', $data);
|
||||
$this->load->view('dashboard/index');
|
||||
|
|
@ -199,30 +176,23 @@ class Dashboard extends CI_Controller
|
|||
}
|
||||
|
||||
public function todays_qso_component() {
|
||||
$this->load->model('user_model');
|
||||
|
||||
if ($this->user_model->validate_session() == 0) {
|
||||
// User is not logged in
|
||||
} else {
|
||||
$this->load->model('logbook_model');
|
||||
$this->load->model('logbooks_model');
|
||||
return;
|
||||
}
|
||||
|
||||
$logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
|
||||
|
||||
$data['todays_qsos'] = $this->logbook_model->todays_qsos($logbooks_locations_array);
|
||||
// Use consolidated query instead of individual todays_qsos call
|
||||
$qso_stats = $this->logbook_model->get_qso_statistics_consolidated($logbooks_locations_array);
|
||||
$data['todays_qsos'] = $qso_stats['todays_qsos'];
|
||||
$this->load->view('components/dashboard_todays_qsos', $data);
|
||||
|
||||
}
|
||||
|
||||
public function logbook_display_component() {
|
||||
$this->load->model('user_model');
|
||||
|
||||
if ($this->user_model->validate_session() == 0) {
|
||||
// User is not logged in
|
||||
} else {
|
||||
$this->load->model('logbook_model');
|
||||
$this->load->model('logbooks_model');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get Logbook Locations
|
||||
|
|
|
|||
|
|
@ -219,6 +219,16 @@ class Update extends CI_Controller {
|
|||
$this->update_status("DONE");
|
||||
}
|
||||
|
||||
public function get_status() {
|
||||
$status_file = $this->make_update_path("status.html");
|
||||
if (file_exists($status_file)) {
|
||||
$content = file_get_contents($status_file);
|
||||
echo $content;
|
||||
} else {
|
||||
echo "No status available";
|
||||
}
|
||||
}
|
||||
|
||||
public function update_status($done=""){
|
||||
|
||||
if ($done != "Downloading file"){
|
||||
|
|
@ -234,7 +244,16 @@ class Update extends CI_Controller {
|
|||
$html = $done."....<br/>";
|
||||
}
|
||||
|
||||
file_put_contents($this->make_update_path("status.html"), $html);
|
||||
$status_file = $this->make_update_path("status.html");
|
||||
if (file_put_contents($status_file, $html) === FALSE) {
|
||||
log_message('error', 'Failed to write status file: ' . $status_file);
|
||||
// Try to create the directory if it doesn't exist
|
||||
$dir = dirname($status_file);
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
file_put_contents($status_file, $html);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -29,70 +29,81 @@ class Workabledxcc extends CI_Controller
|
|||
|
||||
public function dxcclist()
|
||||
{
|
||||
|
||||
$json = file_get_contents($this->optionslib->get_option('dxped_url'));
|
||||
|
||||
// Decode the JSON data into a PHP array
|
||||
$dataResult = json_decode($json, true);
|
||||
|
||||
// Initialize an empty array to store the required data
|
||||
$requiredData = array();
|
||||
if (empty($dataResult)) {
|
||||
$data['dxcclist'] = array();
|
||||
$this->load->view('/workabledxcc/components/dxcclist', $data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get Date format
|
||||
if ($this->session->userdata('user_date_format')) {
|
||||
// If Logged in and session exists
|
||||
$custom_date_format = $this->session->userdata('user_date_format');
|
||||
} else {
|
||||
// Get Default date format from /config/cloudlog.php
|
||||
$custom_date_format = $this->config->item('qso_date_format');
|
||||
}
|
||||
|
||||
// Iterate through the decoded JSON data
|
||||
foreach ($dataResult as $item) {
|
||||
// Create a new array with the required fields and add it to the main array
|
||||
$oldStartDate = DateTime::createFromFormat('Y-m-d', $item['0']);
|
||||
// Load models once
|
||||
$this->load->model('logbook_model');
|
||||
$this->load->model('Workabledxcc_model');
|
||||
|
||||
// Get all DXCC entities for all callsigns in one batch
|
||||
$callsigns = array_column($dataResult, 'callsign');
|
||||
$dates = array_column($dataResult, '0');
|
||||
$dxccEntities = $this->Workabledxcc_model->batchDxccLookup($callsigns, $dates);
|
||||
|
||||
// Get worked/confirmed status for all entities in batch
|
||||
$uniqueEntities = array_unique(array_filter($dxccEntities));
|
||||
$dxccStatus = $this->Workabledxcc_model->batchDxccWorkedStatus($uniqueEntities);
|
||||
|
||||
// If JSON contains iota fields, batch process IOTA status
|
||||
$iotas = [];
|
||||
foreach ($dataResult as $item) {
|
||||
if (!empty($item['iota'])) {
|
||||
$iotas[] = $item['iota'];
|
||||
}
|
||||
}
|
||||
$uniqueIotas = array_unique($iotas);
|
||||
$iotaStatus = [];
|
||||
if (!empty($uniqueIotas)) {
|
||||
$iotaStatus = $this->Workabledxcc_model->batchIotaWorkedStatus($uniqueIotas);
|
||||
}
|
||||
|
||||
// Process results
|
||||
$requiredData = array();
|
||||
foreach ($dataResult as $index => $item) {
|
||||
$oldStartDate = DateTime::createFromFormat('Y-m-d', $item['0']);
|
||||
$StartDate = $oldStartDate->format($custom_date_format);
|
||||
|
||||
$oldEndDate = DateTime::createFromFormat('Y-m-d', $item['1']);
|
||||
|
||||
$EndDate = $oldEndDate->format($custom_date_format);
|
||||
|
||||
$oldStartDate1 = DateTime::createFromFormat('Y-m-d', $item['0']);
|
||||
|
||||
$StartDate1 = $oldStartDate1->format('Y-m-d');
|
||||
|
||||
|
||||
$this->load->model('logbook_model');
|
||||
$dxccInfo = $this->logbook_model->dxcc_lookup($item['callsign'], $StartDate1);
|
||||
|
||||
// Call DXCC Worked function to check if the DXCC has been worked before
|
||||
if (isset($dxccInfo['entity'])) {
|
||||
$dxccWorked = $this->dxccWorked($dxccInfo['entity']);
|
||||
} else {
|
||||
// Handle the case where 'entity' is not set in $dxccInfo
|
||||
$dxccWorked = array(
|
||||
'workedBefore' => false,
|
||||
'confirmed' => false,
|
||||
);
|
||||
}
|
||||
// Get DXCC status for this callsign
|
||||
$entity = $dxccEntities[$index] ?? null;
|
||||
$worked = $entity && isset($dxccStatus[$entity]) ? $dxccStatus[$entity] : [
|
||||
'workedBefore' => false,
|
||||
'confirmed' => false,
|
||||
'workedViaSatellite' => false
|
||||
];
|
||||
|
||||
$requiredData[] = array(
|
||||
'clean_date' => $item['0'],
|
||||
'start_date' => $StartDate,
|
||||
'end_date' => $EndDate,
|
||||
'country' => $item['2'],
|
||||
'iota' => isset($item['iota']) ? $item['iota'] : null,
|
||||
'iota_status' => (isset($item['iota']) && isset($iotaStatus[$item['iota']])) ? $iotaStatus[$item['iota']] : null,
|
||||
'notes' => $item['6'],
|
||||
'callsign' => $item['callsign'],
|
||||
'workedBefore' => $dxccWorked['workedBefore'],
|
||||
'confirmed' => $dxccWorked['confirmed'],
|
||||
'workedBefore' => $worked['workedBefore'],
|
||||
'confirmed' => $worked['confirmed'],
|
||||
'workedViaSatellite' => $worked['workedViaSatellite'],
|
||||
);
|
||||
}
|
||||
|
||||
$data['dxcclist'] = $requiredData;
|
||||
|
||||
// Return the array with the required data
|
||||
|
||||
$this->load->view('/workabledxcc/components/dxcclist', $data);
|
||||
}
|
||||
|
||||
|
|
@ -102,6 +113,7 @@ class Workabledxcc extends CI_Controller
|
|||
$return = [
|
||||
"workedBefore" => false,
|
||||
"confirmed" => false,
|
||||
"workedViaSatellite" => false,
|
||||
];
|
||||
|
||||
$user_default_confirmation = $this->session->userdata('user_default_confirmation');
|
||||
|
|
@ -110,16 +122,28 @@ class Workabledxcc extends CI_Controller
|
|||
$this->load->model('logbook_model');
|
||||
|
||||
if (!empty($logbooks_locations_array)) {
|
||||
// Check terrestrial contacts
|
||||
$this->db->where('COL_PROP_MODE !=', 'SAT');
|
||||
|
||||
$this->db->where_in('station_id', $logbooks_locations_array);
|
||||
$this->db->where('COL_COUNTRY', urldecode($country));
|
||||
// Fix case sensitivity issue for DXCC country matching
|
||||
$this->db->where('UPPER(COL_COUNTRY) = UPPER(?)', urldecode($country));
|
||||
|
||||
$query = $this->db->get($this->config->item('table_name'), 1, 0);
|
||||
foreach ($query->result() as $workedBeforeRow) {
|
||||
$return['workedBefore'] = true;
|
||||
}
|
||||
|
||||
// Check satellite contacts
|
||||
$this->db->where('COL_PROP_MODE', 'SAT');
|
||||
$this->db->where_in('station_id', $logbooks_locations_array);
|
||||
$this->db->where('UPPER(COL_COUNTRY) = UPPER(?)', urldecode($country));
|
||||
|
||||
$query = $this->db->get($this->config->item('table_name'), 1, 0);
|
||||
foreach ($query->result() as $satelliteRow) {
|
||||
$return['workedViaSatellite'] = true;
|
||||
}
|
||||
|
||||
$extrawhere = '';
|
||||
if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'Q') !== false) {
|
||||
$extrawhere = "COL_QSL_RCVD='Y'";
|
||||
|
|
@ -155,7 +179,8 @@ class Workabledxcc extends CI_Controller
|
|||
|
||||
|
||||
$this->db->where_in('station_id', $logbooks_locations_array);
|
||||
$this->db->where('COL_COUNTRY', urldecode($country));
|
||||
// Fix case sensitivity issue for DXCC country matching
|
||||
$this->db->where('UPPER(COL_COUNTRY) = UPPER(?)', urldecode($country));
|
||||
|
||||
$query = $this->db->get($this->config->item('table_name'), 1, 0);
|
||||
foreach ($query->result() as $workedBeforeRow) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<?php defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
<?php defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Migration_modify_eQSL_url extends CI_Migration {
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Migration_add_workable_dxcc_indexes extends CI_Migration {
|
||||
|
||||
public function up()
|
||||
{
|
||||
// Add composite index for workable DXCC queries
|
||||
// This will greatly improve performance for COL_COUNTRY + station_id + COL_PROP_MODE queries
|
||||
$this->db->db_debug = false;
|
||||
|
||||
// Check if index already exists
|
||||
$index_exists = $this->db->query("SHOW INDEX FROM ".$this->config->item('table_name')." WHERE Key_name = 'idx_workable_dxcc'")->num_rows();
|
||||
|
||||
if ($index_exists == 0) {
|
||||
$sql = "ALTER TABLE ".$this->config->item('table_name')." ADD INDEX `idx_workable_dxcc` (`COL_COUNTRY`, `station_id`, `COL_PROP_MODE`)";
|
||||
$this->db->query($sql);
|
||||
}
|
||||
|
||||
// Add index for confirmation status columns
|
||||
$conf_index_exists = $this->db->query("SHOW INDEX FROM ".$this->config->item('table_name')." WHERE Key_name = 'idx_qsl_confirmations'")->num_rows();
|
||||
|
||||
if ($conf_index_exists == 0) {
|
||||
$sql = "ALTER TABLE ".$this->config->item('table_name')." ADD INDEX `idx_qsl_confirmations` (`COL_QSL_RCVD`, `COL_LOTW_QSL_RCVD`, `COL_EQSL_QSL_RCVD`, `COL_QRZCOM_QSO_DOWNLOAD_STATUS`)";
|
||||
$this->db->query($sql);
|
||||
}
|
||||
|
||||
$this->db->db_debug = true;
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
$this->db->db_debug = false;
|
||||
|
||||
// Drop the indexes if they exist
|
||||
$index_exists = $this->db->query("SHOW INDEX FROM ".$this->config->item('table_name')." WHERE Key_name = 'idx_workable_dxcc'")->num_rows();
|
||||
if ($index_exists > 0) {
|
||||
$this->db->query("ALTER TABLE ".$this->config->item('table_name')." DROP INDEX `idx_workable_dxcc`");
|
||||
}
|
||||
|
||||
$conf_index_exists = $this->db->query("SHOW INDEX FROM ".$this->config->item('table_name')." WHERE Key_name = 'idx_qsl_confirmations'")->num_rows();
|
||||
if ($conf_index_exists > 0) {
|
||||
$this->db->query("ALTER TABLE ".$this->config->item('table_name')." DROP INDEX `idx_qsl_confirmations`");
|
||||
}
|
||||
|
||||
$this->db->db_debug = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -58,6 +58,12 @@ class DXCC extends CI_Model {
|
|||
return $this->db->get('dxcc_entities');
|
||||
}
|
||||
|
||||
/* Optimized method to get count of current DXCC entities without loading all records */
|
||||
function get_total_dxcc_count() {
|
||||
$this->db->where('end', null);
|
||||
return $this->db->count_all_results('dxcc_entities');
|
||||
}
|
||||
|
||||
function get_dxcc_array($dxccArray, $bands, $postdata) {
|
||||
$CI =& get_instance();
|
||||
$CI->load->model('logbooks_model');
|
||||
|
|
|
|||
|
|
@ -2079,7 +2079,7 @@ class Logbook_model extends CI_Model
|
|||
$this->db->where_in('station_id', $logbooks_locations_array);
|
||||
$this->db->group_start();
|
||||
$this->db->like('SUBSTRING(COL_GRIDSQUARE, 1, 4)', substr($grid, 0, 4));
|
||||
$this->db->or_like('SUBSTRING(COL_VUCC_GRIDS, 1, 4)', substr($grid, 0, 4));
|
||||
$this->db->or_like('COL_VUCC_GRIDS', substr($grid, 0, 4));
|
||||
$this->db->group_end();
|
||||
|
||||
if ($band != null && $band != 'SAT') {
|
||||
|
|
@ -2621,95 +2621,99 @@ class Logbook_model extends CI_Model
|
|||
}
|
||||
|
||||
if (!empty($logbooks_locations_array)) {
|
||||
$this->db->select('
|
||||
COUNT(IF(COL_QSL_SENT="Y",COL_QSL_SENT,null)) as QSL_Sent,
|
||||
COUNT(IF(COL_QSL_RCVD="Y",COL_QSL_RCVD,null)) as QSL_Received,
|
||||
COUNT(IF(COL_QSL_SENT IN("Q", "R") ,COL_QSL_SENT,null)) as QSL_Requested,
|
||||
COUNT(IF(COL_EQSL_QSL_SENT="Y",COL_EQSL_QSL_SENT,null)) as eQSL_Sent,
|
||||
COUNT(IF(COL_EQSL_QSL_RCVD="Y",COL_EQSL_QSL_RCVD,null)) as eQSL_Received,
|
||||
COUNT(IF(COL_LOTW_QSL_SENT="Y",COL_LOTW_QSL_SENT,null)) as LoTW_Sent,
|
||||
COUNT(IF(COL_LOTW_QSL_RCVD="Y",COL_LOTW_QSL_RCVD,null)) as LoTW_Received,
|
||||
COUNT(IF(COL_QRZCOM_QSO_UPLOAD_STATUS="Y",COL_QRZCOM_QSO_UPLOAD_STATUS,null)) as QRZ_Sent,
|
||||
COUNT(IF(COL_QRZCOM_QSO_DOWNLOAD_STATUS="Y",COL_QRZCOM_QSO_DOWNLOAD_STATUS,null)) as QRZ_Received,
|
||||
COUNT(IF(COL_QSL_SENT="Y" and DATE(COL_QSLSDATE)=DATE(SYSDATE()),COL_QSL_SENT,null)) as QSL_Sent_today,
|
||||
COUNT(IF(COL_QSL_RCVD="Y" and DATE(COL_QSLRDATE)=DATE(SYSDATE()),COL_QSL_RCVD,null)) as QSL_Received_today,
|
||||
COUNT(IF(COL_QSL_SENT IN("Q", "R") and DATE(COL_QSLSDATE)=DATE(SYSDATE()) ,COL_QSL_SENT,null)) as QSL_Requested_today,
|
||||
COUNT(IF(COL_EQSL_QSL_SENT="Y" and DATE(COL_EQSL_QSLSDATE)=DATE(SYSDATE()),COL_EQSL_QSL_SENT,null)) as eQSL_Sent_today,
|
||||
COUNT(IF(COL_EQSL_QSL_RCVD="Y" and DATE(COL_EQSL_QSLRDATE)=DATE(SYSDATE()),COL_EQSL_QSL_RCVD,null)) as eQSL_Received_today,
|
||||
COUNT(IF(COL_LOTW_QSL_SENT="Y" and DATE(COL_LOTW_QSLSDATE)=DATE(SYSDATE()),COL_LOTW_QSL_SENT,null)) as LoTW_Sent_today,
|
||||
COUNT(IF(COL_LOTW_QSL_RCVD="Y" and DATE(COL_LOTW_QSLRDATE)=DATE(SYSDATE()),COL_LOTW_QSL_RCVD,null)) as LoTW_Received_today,
|
||||
COUNT(IF(COL_QRZCOM_QSO_UPLOAD_STATUS="Y" and DATE(COL_QRZCOM_QSO_UPLOAD_DATE)=DATE(SYSDATE()),COL_QRZCOM_QSO_UPLOAD_STATUS,null)) as QRZ_Sent_today,
|
||||
COUNT(IF(COL_QRZCOM_QSO_DOWNLOAD_STATUS="Y" and DATE(COL_QRZCOM_QSO_DOWNLOAD_DATE)=DATE(SYSDATE()),COL_QRZCOM_QSO_DOWNLOAD_STATUS,null)) as QRZ_Received_today
|
||||
');
|
||||
// Pre-calculate today's date for better performance
|
||||
$today_date = date('Y-m-d');
|
||||
|
||||
$this->db->select("
|
||||
COUNT(IF(COL_QSL_SENT='Y',COL_QSL_SENT,null)) as QSL_Sent,
|
||||
COUNT(IF(COL_QSL_RCVD='Y',COL_QSL_RCVD,null)) as QSL_Received,
|
||||
COUNT(IF(COL_QSL_SENT IN('Q', 'R') ,COL_QSL_SENT,null)) as QSL_Requested,
|
||||
COUNT(IF(COL_EQSL_QSL_SENT='Y',COL_EQSL_QSL_SENT,null)) as eQSL_Sent,
|
||||
COUNT(IF(COL_EQSL_QSL_RCVD='Y',COL_EQSL_QSL_RCVD,null)) as eQSL_Received,
|
||||
COUNT(IF(COL_LOTW_QSL_SENT='Y',COL_LOTW_QSL_SENT,null)) as LoTW_Sent,
|
||||
COUNT(IF(COL_LOTW_QSL_RCVD='Y',COL_LOTW_QSL_RCVD,null)) as LoTW_Received,
|
||||
COUNT(IF(COL_QRZCOM_QSO_UPLOAD_STATUS='Y',COL_QRZCOM_QSO_UPLOAD_STATUS,null)) as QRZ_Sent,
|
||||
COUNT(IF(COL_QRZCOM_QSO_DOWNLOAD_STATUS='Y',COL_QRZCOM_QSO_DOWNLOAD_STATUS,null)) as QRZ_Received,
|
||||
COUNT(IF(COL_QSL_SENT='Y' and DATE(COL_QSLSDATE)='$today_date',COL_QSL_SENT,null)) as QSL_Sent_today,
|
||||
COUNT(IF(COL_QSL_RCVD='Y' and DATE(COL_QSLRDATE)='$today_date',COL_QSL_RCVD,null)) as QSL_Received_today,
|
||||
COUNT(IF(COL_QSL_SENT IN('Q', 'R') and DATE(COL_QSLSDATE)='$today_date' ,COL_QSL_SENT,null)) as QSL_Requested_today,
|
||||
COUNT(IF(COL_EQSL_QSL_SENT='Y' and DATE(COL_EQSL_QSLSDATE)='$today_date',COL_EQSL_QSL_SENT,null)) as eQSL_Sent_today,
|
||||
COUNT(IF(COL_EQSL_QSL_RCVD='Y' and DATE(COL_EQSL_QSLRDATE)='$today_date',COL_EQSL_QSL_RCVD,null)) as eQSL_Received_today,
|
||||
COUNT(IF(COL_LOTW_QSL_SENT='Y' and DATE(COL_LOTW_QSLSDATE)='$today_date',COL_LOTW_QSL_SENT,null)) as LoTW_Sent_today,
|
||||
COUNT(IF(COL_LOTW_QSL_RCVD='Y' and DATE(COL_LOTW_QSLRDATE)='$today_date',COL_LOTW_QSL_RCVD,null)) as LoTW_Received_today,
|
||||
COUNT(IF(COL_QRZCOM_QSO_UPLOAD_STATUS='Y' and DATE(COL_QRZCOM_QSO_UPLOAD_DATE)='$today_date',COL_QRZCOM_QSO_UPLOAD_STATUS,null)) as QRZ_Sent_today,
|
||||
COUNT(IF(COL_QRZCOM_QSO_DOWNLOAD_STATUS='Y' and DATE(COL_QRZCOM_QSO_DOWNLOAD_DATE)='$today_date',COL_QRZCOM_QSO_DOWNLOAD_STATUS,null)) as QRZ_Received_today
|
||||
", FALSE);
|
||||
$this->db->where_in('station_id', $logbooks_locations_array);
|
||||
|
||||
if ($query = $this->db->get($this->config->item('table_name'))) {
|
||||
$this->db->last_query();
|
||||
foreach ($query->result() as $row) {
|
||||
$QSLBreakdown['QSL_Sent'] = $row->QSL_Sent;
|
||||
$QSLBreakdown['QSL_Received'] = $row->QSL_Received;
|
||||
$QSLBreakdown['QSL_Requested'] = $row->QSL_Requested;
|
||||
$QSLBreakdown['eQSL_Sent'] = $row->eQSL_Sent;
|
||||
$QSLBreakdown['eQSL_Received'] = $row->eQSL_Received;
|
||||
$QSLBreakdown['LoTW_Sent'] = $row->LoTW_Sent;
|
||||
$QSLBreakdown['LoTW_Received'] = $row->LoTW_Received;
|
||||
$QSLBreakdown['QRZ_Sent'] = $row->QRZ_Sent;
|
||||
$QSLBreakdown['QRZ_Received'] = $row->QRZ_Received;
|
||||
$QSLBreakdown['QSL_Sent_today'] = $row->QSL_Sent_today;
|
||||
$QSLBreakdown['QSL_Received_today'] = $row->QSL_Received_today;
|
||||
$QSLBreakdown['QSL_Requested_today'] = $row->QSL_Requested_today;
|
||||
$QSLBreakdown['eQSL_Sent_today'] = $row->eQSL_Sent_today;
|
||||
$QSLBreakdown['eQSL_Received_today'] = $row->eQSL_Received_today;
|
||||
$QSLBreakdown['LoTW_Sent_today'] = $row->LoTW_Sent_today;
|
||||
$QSLBreakdown['LoTW_Received_today'] = $row->LoTW_Received_today;
|
||||
$QSLBreakdown['QRZ_Sent_today'] = $row->QRZ_Sent_today;
|
||||
$QSLBreakdown['QRZ_Received_today'] = $row->QRZ_Received_today;
|
||||
if ($query->num_rows() > 0) {
|
||||
$row = $query->row();
|
||||
return array(
|
||||
'QSL_Sent' => (int)$row->QSL_Sent,
|
||||
'QSL_Received' => (int)$row->QSL_Received,
|
||||
'QSL_Requested' => (int)$row->QSL_Requested,
|
||||
'eQSL_Sent' => (int)$row->eQSL_Sent,
|
||||
'eQSL_Received' => (int)$row->eQSL_Received,
|
||||
'LoTW_Sent' => (int)$row->LoTW_Sent,
|
||||
'LoTW_Received' => (int)$row->LoTW_Received,
|
||||
'QRZ_Sent' => (int)$row->QRZ_Sent,
|
||||
'QRZ_Received' => (int)$row->QRZ_Received,
|
||||
'QSL_Sent_today' => (int)$row->QSL_Sent_today,
|
||||
'QSL_Received_today' => (int)$row->QSL_Received_today,
|
||||
'QSL_Requested_today' => (int)$row->QSL_Requested_today,
|
||||
'eQSL_Sent_today' => (int)$row->eQSL_Sent_today,
|
||||
'eQSL_Received_today' => (int)$row->eQSL_Received_today,
|
||||
'LoTW_Sent_today' => (int)$row->LoTW_Sent_today,
|
||||
'LoTW_Received_today' => (int)$row->LoTW_Received_today,
|
||||
'QRZ_Sent_today' => (int)$row->QRZ_Sent_today,
|
||||
'QRZ_Received_today' => (int)$row->QRZ_Received_today
|
||||
);
|
||||
}
|
||||
|
||||
return $QSLBreakdown;
|
||||
} else {
|
||||
$QSLBreakdown['QSL_Sent'] = 0;
|
||||
$QSLBreakdown['QSL_Received'] = 0;
|
||||
$QSLBreakdown['QSL_Requested'] = 0;
|
||||
$QSLBreakdown['eQSL_Sent'] = 0;
|
||||
$QSLBreakdown['eQSL_Received'] = 0;
|
||||
$QSLBreakdown['LoTW_Sent'] = 0;
|
||||
$QSLBreakdown['LoTW_Received'] = 0;
|
||||
$QSLBreakdown['QRZ_Sent'] = 0;
|
||||
$QSLBreakdown['QRZ_Received'] = 0;
|
||||
$QSLBreakdown['QSL_Sent_today'] = 0;
|
||||
$QSLBreakdown['QSL_Received_today'] = 0;
|
||||
$QSLBreakdown['QSL_Requested_today'] = 0;
|
||||
$QSLBreakdown['eQSL_Sent_today'] = 0;
|
||||
$QSLBreakdown['eQSL_Received_today'] = 0;
|
||||
$QSLBreakdown['LoTW_Sent_today'] = 0;
|
||||
$QSLBreakdown['LoTW_Received_today'] = 0;
|
||||
$QSLBreakdown['QRZ_Sent_today'] = 0;
|
||||
$QSLBreakdown['QRZ_Received_today'] = 0;
|
||||
|
||||
return $QSLBreakdown;
|
||||
}
|
||||
} else {
|
||||
$QSLBreakdown['QSL_Sent'] = 0;
|
||||
$QSLBreakdown['QSL_Received'] = 0;
|
||||
$QSLBreakdown['QSL_Requested'] = 0;
|
||||
$QSLBreakdown['eQSL_Sent'] = 0;
|
||||
$QSLBreakdown['eQSL_Received'] = 0;
|
||||
$QSLBreakdown['LoTW_Sent'] = 0;
|
||||
$QSLBreakdown['LoTW_Received'] = 0;
|
||||
$QSLBreakdown['QRZ_Sent'] = 0;
|
||||
$QSLBreakdown['QRZ_Received'] = 0;
|
||||
$QSLBreakdown['QSL_Sent_today'] = 0;
|
||||
$QSLBreakdown['QSL_Received_today'] = 0;
|
||||
$QSLBreakdown['QSL_Requested_today'] = 0;
|
||||
$QSLBreakdown['eQSL_Sent_today'] = 0;
|
||||
$QSLBreakdown['eQSL_Received_today'] = 0;
|
||||
$QSLBreakdown['LoTW_Sent_today'] = 0;
|
||||
$QSLBreakdown['LoTW_Received_today'] = 0;
|
||||
$QSLBreakdown['QRZ_Sent_today'] = 0;
|
||||
$QSLBreakdown['QRZ_Received_today'] = 0;
|
||||
|
||||
return $QSLBreakdown;
|
||||
// Return default values if no results
|
||||
return array(
|
||||
'QSL_Sent' => 0,
|
||||
'QSL_Received' => 0,
|
||||
'QSL_Requested' => 0,
|
||||
'eQSL_Sent' => 0,
|
||||
'eQSL_Received' => 0,
|
||||
'LoTW_Sent' => 0,
|
||||
'LoTW_Received' => 0,
|
||||
'QRZ_Sent' => 0,
|
||||
'QRZ_Received' => 0,
|
||||
'QSL_Sent_today' => 0,
|
||||
'QSL_Received_today' => 0,
|
||||
'QSL_Requested_today' => 0,
|
||||
'eQSL_Sent_today' => 0,
|
||||
'eQSL_Received_today' => 0,
|
||||
'LoTW_Sent_today' => 0,
|
||||
'LoTW_Received_today' => 0,
|
||||
'QRZ_Sent_today' => 0,
|
||||
'QRZ_Received_today' => 0
|
||||
);
|
||||
} else {
|
||||
return array(
|
||||
'QSL_Sent' => 0,
|
||||
'QSL_Received' => 0,
|
||||
'QSL_Requested' => 0,
|
||||
'eQSL_Sent' => 0,
|
||||
'eQSL_Received' => 0,
|
||||
'LoTW_Sent' => 0,
|
||||
'LoTW_Received' => 0,
|
||||
'QRZ_Sent' => 0,
|
||||
'QRZ_Received' => 0,
|
||||
'QSL_Sent_today' => 0,
|
||||
'QSL_Received_today' => 0,
|
||||
'QSL_Requested_today' => 0,
|
||||
'eQSL_Sent_today' => 0,
|
||||
'eQSL_Received_today' => 0,
|
||||
'LoTW_Sent_today' => 0,
|
||||
'LoTW_Received_today' => 0,
|
||||
'QRZ_Sent_today' => 0,
|
||||
'QRZ_Received_today' => 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2986,6 +2990,53 @@ class Logbook_model extends CI_Model
|
|||
}
|
||||
}
|
||||
|
||||
// Consolidated method to get all country statistics in one query
|
||||
function get_countries_statistics_consolidated($StationLocationsArray = null) {
|
||||
if ($StationLocationsArray == null) {
|
||||
$CI = &get_instance();
|
||||
$CI->load->model('logbooks_model');
|
||||
$logbooks_locations_array = $CI->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
|
||||
} else {
|
||||
$logbooks_locations_array = $StationLocationsArray;
|
||||
}
|
||||
|
||||
if (!empty($logbooks_locations_array)) {
|
||||
// Get both confirmed countries and current countries in one query
|
||||
$this->db->select('
|
||||
COUNT(DISTINCT COL_COUNTRY) as Countries_Worked,
|
||||
COUNT(DISTINCT IF(COL_QSL_RCVD = "Y", COL_COUNTRY, NULL)) as Countries_Worked_QSL,
|
||||
COUNT(DISTINCT IF(COL_EQSL_QSL_RCVD = "Y", COL_COUNTRY, NULL)) as Countries_Worked_EQSL,
|
||||
COUNT(DISTINCT IF(COL_LOTW_QSL_RCVD = "Y", COL_COUNTRY, NULL)) as Countries_Worked_LOTW,
|
||||
COUNT(DISTINCT IF(dxcc_entities.end IS NULL, COL_COUNTRY, NULL)) as Countries_Current
|
||||
');
|
||||
$this->db->join('dxcc_entities', 'dxcc_entities.adif = ' . $this->config->item('table_name') . '.col_dxcc', 'left');
|
||||
$this->db->where_in('station_id', $logbooks_locations_array);
|
||||
$this->db->where('COL_COUNTRY !=', 'Invalid');
|
||||
$this->db->where('COL_DXCC >', '0');
|
||||
|
||||
if ($query = $this->db->get($this->config->item('table_name'))) {
|
||||
if ($query->num_rows() > 0) {
|
||||
$row = $query->row();
|
||||
return array(
|
||||
'Countries_Worked' => $row->Countries_Worked,
|
||||
'Countries_Worked_QSL' => $row->Countries_Worked_QSL,
|
||||
'Countries_Worked_EQSL' => $row->Countries_Worked_EQSL,
|
||||
'Countries_Worked_LOTW' => $row->Countries_Worked_LOTW,
|
||||
'Countries_Current' => $row->Countries_Current
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'Countries_Worked' => 0,
|
||||
'Countries_Worked_QSL' => 0,
|
||||
'Countries_Worked_EQSL' => 0,
|
||||
'Countries_Worked_LOTW' => 0,
|
||||
'Countries_Current' => 0
|
||||
);
|
||||
}
|
||||
|
||||
/* Return total number of countries confirmed with paper QSL */
|
||||
function total_countries_confirmed_paper()
|
||||
{
|
||||
|
|
@ -4806,6 +4857,7 @@ class Logbook_model extends CI_Model
|
|||
$plot = array('lat' => 0, 'lng' => 0, 'html' => '', 'label' => '', 'flag' => '', 'confirmed' => 'N');
|
||||
|
||||
$plot['label'] = $row->COL_CALL;
|
||||
$plot['callsign'] = $row->COL_CALL;
|
||||
$flag = strtolower($CI->dxccflag->getISO($row->COL_DXCC));
|
||||
$plot['flag'] = '<span data-bs-toggle="tooltip" title="' . ucwords(strtolower(($row->name==null?"- NONE -":$row->name))) . '"><span class="fi fi-' . $flag .'"></span></span> ';
|
||||
$plot['html'] = ($row->COL_GRIDSQUARE != null ? "<b>Grid:</b> " . $row->COL_GRIDSQUARE . "<br />" : "");
|
||||
|
|
@ -4980,6 +5032,63 @@ class Logbook_model extends CI_Model
|
|||
// If all parsing fails, return the original input and let the database handle it
|
||||
return $date_input;
|
||||
}
|
||||
|
||||
/* Consolidated QSO Statistics - Get all basic counts in a single query */
|
||||
function get_qso_statistics_consolidated($StationLocationsArray = null)
|
||||
{
|
||||
if ($StationLocationsArray == null) {
|
||||
$this->load->model('logbooks_model');
|
||||
$logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
|
||||
} else {
|
||||
$logbooks_locations_array = $StationLocationsArray;
|
||||
}
|
||||
|
||||
if (!$logbooks_locations_array) {
|
||||
return array(
|
||||
'total_qsos' => 0,
|
||||
'todays_qsos' => 0,
|
||||
'month_qsos' => 0,
|
||||
'year_qsos' => 0
|
||||
);
|
||||
}
|
||||
|
||||
// Calculate date ranges
|
||||
$today_morning = date('Y-m-d 00:00:00');
|
||||
$today_night = date('Y-m-d 23:59:59');
|
||||
$month_morning = date('Y-m-01 00:00:00');
|
||||
$date = new DateTime('now');
|
||||
$date->modify('last day of this month');
|
||||
$month_night = $date->format('Y-m-d') . " 23:59:59";
|
||||
$year_morning = date('Y-01-01 00:00:00');
|
||||
$year_night = date('Y-12-31 23:59:59');
|
||||
|
||||
// Build the consolidated query
|
||||
$this->db->select("
|
||||
COUNT(*) as total_qsos,
|
||||
COUNT(CASE WHEN COL_TIME_ON >= '$today_morning' AND COL_TIME_ON <= '$today_night' THEN 1 END) as todays_qsos,
|
||||
COUNT(CASE WHEN COL_TIME_ON >= '$month_morning' AND COL_TIME_ON <= '$month_night' THEN 1 END) as month_qsos,
|
||||
COUNT(CASE WHEN COL_TIME_ON >= '$year_morning' AND COL_TIME_ON <= '$year_night' THEN 1 END) as year_qsos
|
||||
", FALSE);
|
||||
$this->db->where_in('station_id', $logbooks_locations_array);
|
||||
$query = $this->db->get($this->config->item('table_name'));
|
||||
|
||||
if ($query->num_rows() > 0) {
|
||||
$row = $query->row();
|
||||
return array(
|
||||
'total_qsos' => (int)$row->total_qsos,
|
||||
'todays_qsos' => (int)$row->todays_qsos,
|
||||
'month_qsos' => (int)$row->month_qsos,
|
||||
'year_qsos' => (int)$row->year_qsos
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'total_qsos' => 0,
|
||||
'todays_qsos' => 0,
|
||||
'month_qsos' => 0,
|
||||
'year_qsos' => 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to validate ADIF date format
|
||||
|
|
|
|||
|
|
@ -24,6 +24,25 @@ class Setup_model extends CI_Model {
|
|||
|
||||
return $query->row()->count;
|
||||
}
|
||||
|
||||
// Consolidated method to get all setup counts in one query
|
||||
function getAllSetupCounts() {
|
||||
$userid = xss_clean($this->session->userdata('user_id'));
|
||||
|
||||
$sql = "SELECT
|
||||
(SELECT COUNT(*) FROM dxcc_entities) as country_count,
|
||||
(SELECT COUNT(*) FROM station_logbooks WHERE user_id = {$userid}) as logbook_count,
|
||||
(SELECT COUNT(*) FROM station_profile WHERE user_id = {$userid}) as location_count";
|
||||
|
||||
$query = $this->db->query($sql);
|
||||
$row = $query->row();
|
||||
|
||||
return array(
|
||||
'country_count' => $row->country_count,
|
||||
'logbook_count' => $row->logbook_count,
|
||||
'location_count' => $row->location_count
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
|||
|
|
@ -88,10 +88,20 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Initialize bandcalls array with all bands set to 0
|
||||
foreach ($bands as $band) {
|
||||
$bandcalls[$band] = 0;
|
||||
}
|
||||
|
||||
foreach ($bandunique as $band) {
|
||||
$bandcalls[$band->band] = $band->calls;
|
||||
}
|
||||
|
||||
// Initialize modecalls array with all modes set to 0
|
||||
foreach ($modes as $mode) {
|
||||
$modecalls[$mode] = 0;
|
||||
}
|
||||
|
||||
foreach ($modeunique as $mode) {
|
||||
//if ($mode->col_submode == null) {
|
||||
if ($mode->col_submode == null || $mode->col_submode == "") {
|
||||
|
|
|
|||
|
|
@ -2,87 +2,421 @@
|
|||
|
||||
class Workabledxcc_model extends CI_Model
|
||||
{
|
||||
// Cache for DXCC lookups to avoid repeated queries
|
||||
private $dxccCache = array();
|
||||
private $workedCache = array();
|
||||
|
||||
/**
|
||||
* Batch DXCC lookup for multiple callsigns
|
||||
* @param array $callsigns Array of callsigns
|
||||
* @param array $dates Array of dates corresponding to callsigns
|
||||
* @return array Array of DXCC entities indexed by callsign index
|
||||
*/
|
||||
public function batchDxccLookup($callsigns, $dates)
|
||||
{
|
||||
$this->load->model('logbook_model');
|
||||
$entities = array();
|
||||
|
||||
foreach ($callsigns as $index => $callsign) {
|
||||
$cacheKey = $callsign . '_' . $dates[$index];
|
||||
|
||||
if (!isset($this->dxccCache[$cacheKey])) {
|
||||
$dxccInfo = $this->logbook_model->dxcc_lookup($callsign, $dates[$index]);
|
||||
$this->dxccCache[$cacheKey] = isset($dxccInfo['entity']) ? $dxccInfo['entity'] : null;
|
||||
}
|
||||
|
||||
$entities[$index] = $this->dxccCache[$cacheKey];
|
||||
}
|
||||
|
||||
return $entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch check if DXCC entities have been worked/confirmed
|
||||
* @param array $entities Array of unique DXCC entities
|
||||
* @return array Array of worked/confirmed status indexed by entity
|
||||
*/
|
||||
public function batchDxccWorkedStatus($entities)
|
||||
{
|
||||
if (empty($entities)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$user_default_confirmation = $this->session->userdata('user_default_confirmation');
|
||||
$this->load->model('logbooks_model');
|
||||
$logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
|
||||
|
||||
if (empty($logbooks_locations_array)) {
|
||||
return array_fill_keys($entities, [
|
||||
'workedBefore' => false,
|
||||
'confirmed' => false,
|
||||
'workedViaSatellite' => false
|
||||
]);
|
||||
}
|
||||
|
||||
$results = array();
|
||||
|
||||
// Build confirmation criteria once
|
||||
$confirmationCriteria = $this->buildConfirmationCriteria($user_default_confirmation);
|
||||
|
||||
// Debug: Log entities being checked
|
||||
log_message('debug', 'Workable DXCC: Checking entities: ' . implode(', ', $entities));
|
||||
|
||||
// Batch query for worked status (terrestrial)
|
||||
$workedResults = $this->batchWorkedQuery($entities, $logbooks_locations_array);
|
||||
|
||||
// Batch query for confirmed status (terrestrial)
|
||||
$confirmedResults = $this->batchConfirmedQuery($entities, $logbooks_locations_array, $confirmationCriteria);
|
||||
|
||||
// Batch query for satellite contacts
|
||||
$satelliteResults = $this->batchSatelliteQuery($entities, $logbooks_locations_array);
|
||||
|
||||
// Debug: Log results
|
||||
log_message('debug', 'Workable DXCC: Worked results: ' . json_encode($workedResults));
|
||||
log_message('debug', 'Workable DXCC: Confirmed results: ' . json_encode($confirmedResults));
|
||||
log_message('debug', 'Workable DXCC: Satellite results: ' . json_encode($satelliteResults));
|
||||
|
||||
// Combine results
|
||||
foreach ($entities as $entity) {
|
||||
$results[$entity] = [
|
||||
'workedBefore' => isset($workedResults[$entity]),
|
||||
'confirmed' => isset($confirmedResults[$entity]),
|
||||
'workedViaSatellite' => isset($satelliteResults[$entity])
|
||||
];
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch check IOTA worked/confirmed status
|
||||
* @param array $iotas Array of unique IOTA tags
|
||||
* @return array Array indexed by IOTA tag with ['worked'=>bool,'confirmed'=>bool]
|
||||
*/
|
||||
public function batchIotaWorkedStatus($iotas)
|
||||
{
|
||||
if (empty($iotas)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$user_default_confirmation = $this->session->userdata('user_default_confirmation');
|
||||
$this->load->model('logbooks_model');
|
||||
$logbooks_locations_array = $this->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
|
||||
|
||||
if (empty($logbooks_locations_array)) {
|
||||
$out = [];
|
||||
foreach ($iotas as $i) {
|
||||
$out[$i] = ['worked' => false, 'confirmed' => false];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
// Build confirmation criteria once
|
||||
$confirmationCriteria = $this->buildConfirmationCriteria($user_default_confirmation);
|
||||
|
||||
// Build case-insensitive WHERE conditions for COL_IOTA
|
||||
$whereConditions = array();
|
||||
foreach ($iotas as $iota) {
|
||||
$whereConditions[] = "UPPER(COL_IOTA) = UPPER('" . $this->db->escape_str($iota) . "')";
|
||||
}
|
||||
|
||||
if (empty($whereConditions)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$whereClause = '(' . implode(' OR ', $whereConditions) . ')';
|
||||
|
||||
// Worked query (any mode)
|
||||
$this->db->select('COL_IOTA')
|
||||
->distinct()
|
||||
->from($this->config->item('table_name'))
|
||||
->where_in('station_id', $logbooks_locations_array)
|
||||
->where($whereClause);
|
||||
|
||||
$workedQuery = $this->db->get();
|
||||
log_message('debug', 'Workable DXCC IOTA worked query: ' . $this->db->last_query());
|
||||
|
||||
$workedResults = array();
|
||||
foreach ($workedQuery->result() as $row) {
|
||||
foreach ($iotas as $iota) {
|
||||
if (strtoupper($row->COL_IOTA) === strtoupper($iota)) {
|
||||
$workedResults[$iota] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Confirmed query (apply confirmation criteria, exclude satellite confirmations if desired)
|
||||
$confirmedResults = array();
|
||||
if ($confirmationCriteria !== '1=0') {
|
||||
$this->db->select('COL_IOTA')
|
||||
->distinct()
|
||||
->from($this->config->item('table_name'))
|
||||
->where($confirmationCriteria)
|
||||
->where_in('station_id', $logbooks_locations_array)
|
||||
->where($whereClause);
|
||||
|
||||
$confirmedQuery = $this->db->get();
|
||||
foreach ($confirmedQuery->result() as $row) {
|
||||
foreach ($iotas as $iota) {
|
||||
if (strtoupper($row->COL_IOTA) === strtoupper($iota)) {
|
||||
$confirmedResults[$iota] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$out = array();
|
||||
foreach ($iotas as $iota) {
|
||||
$out[$iota] = [
|
||||
'worked' => isset($workedResults[$iota]),
|
||||
'confirmed' => isset($confirmedResults[$iota])
|
||||
];
|
||||
}
|
||||
|
||||
// Debug
|
||||
log_message('debug', 'Workable DXCC: IOTA worked results: ' . json_encode($workedResults));
|
||||
log_message('debug', 'Workable DXCC: IOTA confirmed results: ' . json_encode($confirmedResults));
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch query to check which entities have been worked via satellite
|
||||
*/
|
||||
private function batchSatelliteQuery($entities, $logbooks_locations_array)
|
||||
{
|
||||
// Create case-insensitive matching for DXCC entities
|
||||
$whereConditions = array();
|
||||
foreach ($entities as $entity) {
|
||||
$whereConditions[] = "UPPER(COL_COUNTRY) = UPPER('" . $this->db->escape_str($entity) . "')";
|
||||
}
|
||||
|
||||
if (empty($whereConditions)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$whereClause = '(' . implode(' OR ', $whereConditions) . ')';
|
||||
|
||||
$this->db->select('COL_COUNTRY')
|
||||
->distinct()
|
||||
->from($this->config->item('table_name'))
|
||||
->where('COL_PROP_MODE', 'SAT') // Only satellite contacts
|
||||
->where_in('station_id', $logbooks_locations_array)
|
||||
->where($whereClause);
|
||||
|
||||
$query = $this->db->get();
|
||||
|
||||
// Debug: Log the SQL query
|
||||
log_message('debug', 'Workable DXCC satellite query: ' . $this->db->last_query());
|
||||
|
||||
$results = array();
|
||||
|
||||
foreach ($query->result() as $row) {
|
||||
// Store with the original entity case for lookup
|
||||
foreach ($entities as $entity) {
|
||||
if (strtoupper($row->COL_COUNTRY) === strtoupper($entity)) {
|
||||
$results[$entity] = true;
|
||||
log_message('debug', 'Workable DXCC: Found satellite match: ' . $entity . ' matches ' . $row->COL_COUNTRY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build confirmation criteria SQL based on user preferences
|
||||
*/
|
||||
private function buildConfirmationCriteria($user_default_confirmation)
|
||||
{
|
||||
$criteria = array();
|
||||
|
||||
if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'Q') !== false) {
|
||||
$criteria[] = "COL_QSL_RCVD='Y'";
|
||||
}
|
||||
if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'L') !== false) {
|
||||
$criteria[] = "COL_LOTW_QSL_RCVD='Y'";
|
||||
}
|
||||
if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'E') !== false) {
|
||||
$criteria[] = "COL_EQSL_QSL_RCVD='Y'";
|
||||
}
|
||||
if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'Z') !== false) {
|
||||
$criteria[] = "COL_QRZCOM_QSO_DOWNLOAD_STATUS='Y'";
|
||||
}
|
||||
|
||||
return empty($criteria) ? '1=0' : '(' . implode(' OR ', $criteria) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch query to check which entities have been worked
|
||||
*/
|
||||
private function batchWorkedQuery($entities, $logbooks_locations_array)
|
||||
{
|
||||
// Create case-insensitive matching for DXCC entities
|
||||
$whereConditions = array();
|
||||
foreach ($entities as $entity) {
|
||||
$whereConditions[] = "UPPER(COL_COUNTRY) = UPPER('" . $this->db->escape_str($entity) . "')";
|
||||
}
|
||||
|
||||
if (empty($whereConditions)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$whereClause = '(' . implode(' OR ', $whereConditions) . ')';
|
||||
|
||||
$this->db->select('COL_COUNTRY')
|
||||
->distinct()
|
||||
->from($this->config->item('table_name'))
|
||||
->where('COL_PROP_MODE !=', 'SAT')
|
||||
->where_in('station_id', $logbooks_locations_array)
|
||||
->where($whereClause);
|
||||
|
||||
$query = $this->db->get();
|
||||
|
||||
// Debug: Log the SQL query
|
||||
log_message('debug', 'Workable DXCC worked query: ' . $this->db->last_query());
|
||||
|
||||
$results = array();
|
||||
|
||||
foreach ($query->result() as $row) {
|
||||
// Store with the original entity case for lookup
|
||||
foreach ($entities as $entity) {
|
||||
if (strtoupper($row->COL_COUNTRY) === strtoupper($entity)) {
|
||||
$results[$entity] = true;
|
||||
log_message('debug', 'Workable DXCC: Found worked match: ' . $entity . ' matches ' . $row->COL_COUNTRY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch query to check which entities have been confirmed
|
||||
*/
|
||||
private function batchConfirmedQuery($entities, $logbooks_locations_array, $confirmationCriteria)
|
||||
{
|
||||
if ($confirmationCriteria === '1=0') {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Create case-insensitive matching for DXCC entities
|
||||
$whereConditions = array();
|
||||
foreach ($entities as $entity) {
|
||||
$whereConditions[] = "UPPER(COL_COUNTRY) = UPPER('" . $this->db->escape_str($entity) . "')";
|
||||
}
|
||||
|
||||
if (empty($whereConditions)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$whereClause = '(' . implode(' OR ', $whereConditions) . ')';
|
||||
|
||||
$this->db->select('COL_COUNTRY')
|
||||
->distinct()
|
||||
->from($this->config->item('table_name'))
|
||||
->where('COL_PROP_MODE !=', 'SAT')
|
||||
->where($confirmationCriteria)
|
||||
->where_in('station_id', $logbooks_locations_array)
|
||||
->where($whereClause);
|
||||
|
||||
$query = $this->db->get();
|
||||
$results = array();
|
||||
|
||||
foreach ($query->result() as $row) {
|
||||
// Store with the original entity case for lookup
|
||||
foreach ($entities as $entity) {
|
||||
if (strtoupper($row->COL_COUNTRY) === strtoupper($entity)) {
|
||||
$results[$entity] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function GetThisWeek()
|
||||
{
|
||||
$json = file_get_contents($this->optionslib->get_option('dxped_url'));
|
||||
|
||||
// Step 2: Convert the JSON data to an array.
|
||||
$json = file_get_contents($this->optionslib->get_option('dxped_url'));
|
||||
$data = json_decode($json, true);
|
||||
|
||||
// Step 3: Create a new array to hold the records for this week.
|
||||
$thisWeekRecords = [];
|
||||
if (empty($data)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Get the start and end of this week.
|
||||
$thisWeekRecords = [];
|
||||
$startOfWeek = (new DateTime())->setISODate((new DateTime())->format('o'), (new DateTime())->format('W'), 1);
|
||||
$endOfWeek = (clone $startOfWeek)->modify('+6 days');
|
||||
|
||||
// Step 4: Iterate over the array.
|
||||
foreach ($data as $record) {
|
||||
// Get Date format
|
||||
if ($this->session->userdata('user_date_format')) {
|
||||
$custom_date_format = $this->session->userdata('user_date_format');
|
||||
} else {
|
||||
$custom_date_format = $this->config->item('qso_date_format');
|
||||
}
|
||||
|
||||
// Convert "0" and "1" to DateTime objects.
|
||||
// First pass: filter records for this week
|
||||
$weekRecords = array();
|
||||
foreach ($data as $record) {
|
||||
$startDate = new DateTime($record['0']);
|
||||
$endDate = new DateTime($record['1']);
|
||||
|
||||
// Step 5: Check if the start date or end date is within this week.
|
||||
if (($startDate >= $startOfWeek && $startDate <= $endOfWeek) || ($endDate >= $startOfWeek && $endDate <= $endOfWeek)) {
|
||||
$endDate = new DateTime($record['1']);
|
||||
$now = new DateTime();
|
||||
$interval = $now->diff($endDate);
|
||||
$daysLeft = $interval->days;
|
||||
|
||||
// If daysLeft is 0, set it to "Last day"
|
||||
if ($daysLeft == 0) {
|
||||
$daysLeft = "Last day";
|
||||
} else {
|
||||
$daysLeft = $daysLeft . " days left";
|
||||
}
|
||||
|
||||
// Add daysLeft to record
|
||||
$record['daysLeft'] = $daysLeft;
|
||||
// Get Date format
|
||||
if ($this->session->userdata('user_date_format')) {
|
||||
// If Logged in and session exists
|
||||
$custom_date_format = $this->session->userdata('user_date_format');
|
||||
} else {
|
||||
// Get Default date format from /config/cloudlog.php
|
||||
$custom_date_format = $this->config->item('qso_date_format');
|
||||
}
|
||||
|
||||
// Create a new array with the required fields and add it to the main array
|
||||
$oldStartDate = DateTime::createFromFormat('Y-m-d', $record['0']);
|
||||
|
||||
$StartDate = $oldStartDate->format($custom_date_format);
|
||||
$record['startDate'] = $StartDate;
|
||||
|
||||
$oldEndDate = DateTime::createFromFormat('Y-m-d', $record['1']);
|
||||
$EndDate = $oldEndDate->format($custom_date_format);
|
||||
$record['endDate'] = $EndDate;
|
||||
|
||||
$record['confirmed'] = true; // or false, depending on your logic
|
||||
|
||||
$CI = &get_instance();
|
||||
$CI->load->model('logbook_model');
|
||||
$dxccInfo = $CI->logbook_model->dxcc_lookup($record['callsign'], $startDate->format('Y-m-d'));
|
||||
|
||||
// Call DXCC Worked function to check if the DXCC has been worked before
|
||||
if (isset($dxccInfo['entity'])) {
|
||||
$dxccWorkedData = $this->dxccWorked($dxccInfo['entity']);
|
||||
$record = array_merge($record, $dxccWorkedData);
|
||||
} else {
|
||||
// Handle the case where 'entity' is not set in $dxccInfo
|
||||
$itemsToAdd = array(
|
||||
'workedBefore' => false,
|
||||
'confirmed' => false,
|
||||
);
|
||||
$record = array_merge($record, $itemsToAdd);
|
||||
}
|
||||
|
||||
$thisWeekRecords[] = $record;
|
||||
if (($startDate >= $startOfWeek && $startDate <= $endOfWeek) ||
|
||||
($endDate >= $startOfWeek && $endDate <= $endOfWeek)) {
|
||||
$weekRecords[] = $record;
|
||||
}
|
||||
}
|
||||
return $thisWeekRecords;
|
||||
|
||||
if (empty($weekRecords)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Batch process DXCC lookups
|
||||
$callsigns = array_column($weekRecords, 'callsign');
|
||||
$dates = array_column($weekRecords, '0');
|
||||
$dxccEntities = $this->batchDxccLookup($callsigns, $dates);
|
||||
|
||||
// Get worked/confirmed status for all entities in batch
|
||||
$uniqueEntities = array_unique(array_filter($dxccEntities));
|
||||
$dxccStatus = $this->batchDxccWorkedStatus($uniqueEntities);
|
||||
|
||||
// Process results
|
||||
foreach ($weekRecords as $index => $record) {
|
||||
$endDate = new DateTime($record['1']);
|
||||
$now = new DateTime();
|
||||
$interval = $now->diff($endDate);
|
||||
$daysLeft = $interval->days;
|
||||
|
||||
$daysLeft = ($daysLeft == 0) ? "Last day" : $daysLeft . " days left";
|
||||
$record['daysLeft'] = $daysLeft;
|
||||
|
||||
$oldStartDate = DateTime::createFromFormat('Y-m-d', $record['0']);
|
||||
$record['startDate'] = $oldStartDate->format($custom_date_format);
|
||||
|
||||
$oldEndDate = DateTime::createFromFormat('Y-m-d', $record['1']);
|
||||
$record['endDate'] = $oldEndDate->format($custom_date_format);
|
||||
|
||||
// Get DXCC status for this callsign
|
||||
$entity = $dxccEntities[$index] ?? null;
|
||||
$worked = $entity && isset($dxccStatus[$entity]) ? $dxccStatus[$entity] : [
|
||||
'workedBefore' => false,
|
||||
'confirmed' => false,
|
||||
'workedViaSatellite' => false
|
||||
];
|
||||
|
||||
$record['workedBefore'] = $worked['workedBefore'];
|
||||
$record['confirmed'] = $worked['confirmed'];
|
||||
$record['workedViaSatellite'] = $worked['workedViaSatellite'];
|
||||
|
||||
$thisWeekRecords[] = $record;
|
||||
}
|
||||
|
||||
return $thisWeekRecords;
|
||||
}
|
||||
|
||||
function dxccWorked($country)
|
||||
|
|
@ -91,6 +425,7 @@ class Workabledxcc_model extends CI_Model
|
|||
$return = [
|
||||
"workedBefore" => false,
|
||||
"confirmed" => false,
|
||||
"workedViaSatellite" => false,
|
||||
];
|
||||
|
||||
$user_default_confirmation = $this->session->userdata('user_default_confirmation');
|
||||
|
|
@ -99,16 +434,28 @@ class Workabledxcc_model extends CI_Model
|
|||
$this->load->model('logbook_model');
|
||||
|
||||
if (!empty($logbooks_locations_array)) {
|
||||
// Check for terrestrial contacts
|
||||
$this->db->where('COL_PROP_MODE !=', 'SAT');
|
||||
|
||||
$this->db->where_in('station_id', $logbooks_locations_array);
|
||||
$this->db->where('COL_COUNTRY', urldecode($country));
|
||||
// Fix case sensitivity issue for DXCC country matching
|
||||
$this->db->where('UPPER(COL_COUNTRY) = UPPER(?)', urldecode($country));
|
||||
|
||||
$query = $this->db->get($this->config->item('table_name'), 1, 0);
|
||||
foreach ($query->result() as $workedBeforeRow) {
|
||||
$return['workedBefore'] = true;
|
||||
}
|
||||
|
||||
// Check for satellite contacts
|
||||
$this->db->where('COL_PROP_MODE', 'SAT');
|
||||
$this->db->where_in('station_id', $logbooks_locations_array);
|
||||
$this->db->where('UPPER(COL_COUNTRY) = UPPER(?)', urldecode($country));
|
||||
|
||||
$query = $this->db->get($this->config->item('table_name'), 1, 0);
|
||||
foreach ($query->result() as $satelliteRow) {
|
||||
$return['workedViaSatellite'] = true;
|
||||
}
|
||||
|
||||
$extrawhere = '';
|
||||
if (isset($user_default_confirmation) && strpos($user_default_confirmation, 'Q') !== false) {
|
||||
$extrawhere = "COL_QSL_RCVD='Y'";
|
||||
|
|
@ -144,7 +491,8 @@ class Workabledxcc_model extends CI_Model
|
|||
|
||||
|
||||
$this->db->where_in('station_id', $logbooks_locations_array);
|
||||
$this->db->where('COL_COUNTRY', urldecode($country));
|
||||
// Fix case sensitivity issue for DXCC country matching
|
||||
$this->db->where('UPPER(COL_COUNTRY) = UPPER(?)', urldecode($country));
|
||||
|
||||
$query = $this->db->get($this->config->item('table_name'), 1, 0);
|
||||
foreach ($query->result() as $workedBeforeRow) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
<div class="container adif" id="adif_import">
|
||||
|
||||
<h2><?php echo $page_title; ?></h2>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2><i class="fas fa-file-import me-2"></i><?php echo $page_title; ?></h2>
|
||||
<p class="text-muted mb-4">Import and export your amateur radio logs in ADIF format</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$showtab = '';
|
||||
if (isset($tab)) {
|
||||
|
|
@ -8,260 +14,420 @@
|
|||
}
|
||||
?>
|
||||
|
||||
<div class="card">
|
||||
<style>
|
||||
/* Fix text-muted visibility in dark themes */
|
||||
.text-muted {
|
||||
color: var(--bs-secondary-color) !important;
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header">
|
||||
<ul class="nav nav-tabs card-header-tabs pull-right" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?php if ($showtab == '' || $showtab == 'adif') {
|
||||
echo 'active';
|
||||
} ?>" id="import-tab" data-bs-toggle="tab" href="#import" role="tab" aria-controls="import" aria-selected="<?php if ($showtab == '' || $showtab == 'adif') {
|
||||
echo 'true';
|
||||
} else {
|
||||
echo 'false';
|
||||
} ?>"><?php echo lang('adif_import') ?></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="export-tab" data-bs-toggle="tab" href="#export" role="tab" aria-controls="export" aria-selected="false"><?php echo lang('adif_export') ?></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="lotw-tab" data-bs-toggle="tab" href="#lotw" role="tab" aria-controls="lotw" aria-selected="false"><?php echo lang('lotw_title') ?></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link <?php if ($showtab == 'dcl') {
|
||||
echo 'active';
|
||||
} ?>" id="dcl-tab" data-bs-toggle="tab" href="#dcl" role="tab" aria-controls="dcl" aria-selected="<?php if ($showtab == 'dcl') {
|
||||
echo 'true';
|
||||
} else {
|
||||
echo 'false';
|
||||
} ?>"><?php echo lang('darc_dcl') ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
<nav class="card-header-tabs">
|
||||
<div class="nav nav-tabs" id="nav-tab" role="tablist">
|
||||
<a class="nav-item nav-link <?php if ($showtab == '' || $showtab == 'adif') echo 'active'; ?>"
|
||||
id="import-tab" data-bs-toggle="tab" href="#import" role="tab" aria-controls="import"
|
||||
aria-selected="<?php echo ($showtab == '' || $showtab == 'adif') ? 'true' : 'false'; ?>">
|
||||
<i class="fas fa-upload me-1"></i><?php echo lang('adif_import') ?>
|
||||
</a>
|
||||
<a class="nav-item nav-link" id="export-tab" data-bs-toggle="tab" href="#export" role="tab"
|
||||
aria-controls="export" aria-selected="false">
|
||||
<i class="fas fa-download me-1"></i><?php echo lang('adif_export') ?>
|
||||
</a>
|
||||
<a class="nav-item nav-link" id="lotw-tab" data-bs-toggle="tab" href="#lotw" role="tab"
|
||||
aria-controls="lotw" aria-selected="false">
|
||||
<i class="fas fa-globe me-1"></i><?php echo lang('lotw_title') ?>
|
||||
</a>
|
||||
<a class="nav-item nav-link <?php if ($showtab == 'dcl') echo 'active'; ?>"
|
||||
id="dcl-tab" data-bs-toggle="tab" href="#dcl" role="tab" aria-controls="dcl"
|
||||
aria-selected="<?php echo ($showtab == 'dcl') ? 'true' : 'false'; ?>">
|
||||
<i class="fas fa-flag me-1"></i><?php echo lang('darc_dcl') ?>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane <?php if ($showtab == '' || $showtab == 'adif') {
|
||||
echo 'active';
|
||||
echo 'active show';
|
||||
} else {
|
||||
echo 'fade';
|
||||
} ?>" id="import" role="tabpanel" aria-labelledby="home-tab">
|
||||
} ?>" id="import" role="tabpanel" aria-labelledby="import-tab">
|
||||
|
||||
<?php if (isset($error) && ($showtab == '' || $showtab == 'adif')) { ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<?php echo $error; ?>
|
||||
<i class="fas fa-exclamation-triangle me-2"></i><?php echo $error; ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<p><span class="badge text-bg-warning"><?php echo lang('general_word_important') ?></span> <?php echo lang('adif_alert_log_files_type') ?></p>
|
||||
<p><span class="badge text-bg-warning"><?php echo lang('general_word_warning') ?></span> <?php echo lang('gen_max_file_upload_size') ?><?php echo $max_upload; ?>B.</p>
|
||||
<!-- Important Notices -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-warning d-flex align-items-center" role="alert">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
<div>
|
||||
<strong><?php echo lang('general_word_important') ?>:</strong> <?php echo lang('adif_alert_log_files_type') ?><br>
|
||||
<strong><?php echo lang('general_word_warning') ?>:</strong> <?php echo lang('gen_max_file_upload_size') ?><?php echo $max_upload; ?>B.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="form" action="<?php echo site_url('adif/import'); ?>" method="post" enctype="multipart/form-data">
|
||||
<select name="station_profile" class="form-select mb-2 me-sm-2" style="width: 20%;">
|
||||
<option value="0"><?php echo lang('adif_select_stationlocation') ?></option>
|
||||
<?php foreach ($station_profile->result() as $station) { ?>
|
||||
<option value="<?php echo $station->station_id; ?>" <?php if ($station->station_id == $this->stations->find_active()) {
|
||||
echo " selected =\"selected\"";
|
||||
} ?>><?php echo lang('gen_hamradio_callsign') . ": " ?><?php echo $station->station_callsign; ?> (<?php echo $station->station_profile_name; ?>)</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
<label class="visually-hidden" for="inlineFormInputName2"><?php echo lang('adif_file_label') ?></label>
|
||||
<input class="file-input mb-2 me-sm-2" type="file" name="userfile" size="20" />
|
||||
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="skipDuplicate" value="1" id="skipDuplicate">
|
||||
<label class="form-check-label" for="skipDuplicate"><?php echo lang('adif_import_dup') ?></label>
|
||||
|
||||
<!-- Station Selection and File Upload -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<h5 class="mb-3"><i class="fas fa-tower-broadcast me-2"></i>Station Selection</h5>
|
||||
<div class="mb-3">
|
||||
<label for="station_profile" class="form-label">Select Station Location</label>
|
||||
<select name="station_profile" id="station_profile" class="form-select">
|
||||
<option value="0"><?php echo lang('adif_select_stationlocation') ?></option>
|
||||
<?php foreach ($station_profile->result() as $station) { ?>
|
||||
<option value="<?php echo $station->station_id; ?>" <?php if ($station->station_id == $this->stations->find_active()) {
|
||||
echo " selected =\"selected\"";
|
||||
} ?>>
|
||||
<?php echo lang('gen_hamradio_callsign') . ": " ?><?php echo $station->station_callsign; ?> (<?php echo $station->station_profile_name; ?>)
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h5 class="mb-3"><i class="fas fa-file me-2"></i>File Upload</h5>
|
||||
<div class="mb-3">
|
||||
<label for="userfile" class="form-label">Choose ADIF File</label>
|
||||
<input class="form-control" type="file" name="userfile" id="userfile" accept=".adi,.ADI,.adif,.ADIF" />
|
||||
<div class="form-text">Supported formats: .adi, .adif</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="markLotw" value="1" id="markLotwImport">
|
||||
<label class="form-check-label" for="markLotwImport"><?php echo lang('adif_mark_imported_lotw') ?></label>
|
||||
<!-- Import Options -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<h5 class="mb-3"><i class="fas fa-cogs me-2"></i>Import Options</h5>
|
||||
|
||||
<!-- Basic Options -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header" style="background-color: var(--bs-secondary-bg); border-bottom: 1px solid var(--bs-border-color);">
|
||||
<h6 class="mb-0">Basic Settings</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="skipDuplicate" value="1" id="skipDuplicate">
|
||||
<label class="form-check-label" for="skipDuplicate">
|
||||
<strong><?php echo lang('adif_import_dup') ?></strong>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="dxccAdif" value="1" id="dxccAdif">
|
||||
<label class="form-check-label" for="dxccAdif">
|
||||
<strong><?php echo lang('adif_dxcc_from_adif') ?></strong>
|
||||
<br><small class="text-muted"><?php echo lang('adif_dxcc_from_adif_hint') ?></small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="operatorName" value="1" id="operatorName">
|
||||
<label class="form-check-label" for="operatorName">
|
||||
<strong><?php echo lang('adif_always_use_login_call_as_op') ?></strong>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="skipStationCheck" value="1" id="skipStationCheck">
|
||||
<label class="form-check-label" for="skipStationCheck">
|
||||
<span class="badge bg-danger me-1"><?php echo lang('general_word_danger') ?></span>
|
||||
<strong><?php echo lang('adif_ignore_station_call') ?></strong>
|
||||
<br><small class="text-muted"><?php echo lang('adif_ignore_station_call_hint') ?></small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="small form-text text-muted"><?php echo lang('adif_hint_no_info_in_file') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="markHrd" value="1" id="markHrdImport">
|
||||
<label class="form-check-label" for="markHrdImport"><?php echo lang('adif_mark_imported_hrdlog') ?></label>
|
||||
</div>
|
||||
<div class="small form-text text-muted"><?php echo lang('adif_hint_no_info_in_file') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="markQrz" value="1" id="markQrzImport">
|
||||
<label class="form-check-label" for="markQrzImport"><?php echo lang('adif_mark_imported_qrz') ?></label>
|
||||
</div>
|
||||
<div class="small form-text text-muted"><?php echo lang('adif_hint_no_info_in_file') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="markClublog" value="1" id="markClublogImport">
|
||||
<label class="form-check-label" for="markClublogImport"><?php echo lang('adif_mark_imported_clublog') ?></label>
|
||||
</div>
|
||||
<div class="small form-text text-muted"><?php echo lang('adif_hint_no_info_in_file') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="dxccAdif" value="1" id="dxccAdif">
|
||||
<label class="form-check-label" for="dxccAdif"><?php echo lang('adif_dxcc_from_adif') ?></label>
|
||||
</div>
|
||||
<div class="small form-text text-muted"><?php echo lang('adif_dxcc_from_adif_hint') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="operatorName" value="1" id="operatorName">
|
||||
<label class="form-check-label" for="operatorName"><?php echo lang('adif_always_use_login_call_as_op') ?></label>
|
||||
<!-- Logbook Upload Markers -->
|
||||
<div class="card">
|
||||
<div class="card-header" style="background-color: var(--bs-secondary-bg); border-bottom: 1px solid var(--bs-border-color);">
|
||||
<h6 class="mb-0">Mark as Uploaded to Online Logbooks</h6> </div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="markLotw" value="1" id="markLotwImport">
|
||||
<label class="form-check-label" for="markLotwImport">
|
||||
<strong><?php echo lang('adif_mark_imported_lotw') ?></strong>
|
||||
<br><small class="text-muted"><?php echo lang('adif_hint_no_info_in_file') ?></small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="markHrd" value="1" id="markHrdImport">
|
||||
<label class="form-check-label" for="markHrdImport">
|
||||
<strong><?php echo lang('adif_mark_imported_hrdlog') ?></strong>
|
||||
<br><small class="text-muted"><?php echo lang('adif_hint_no_info_in_file') ?></small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="markQrz" value="1" id="markQrzImport">
|
||||
<label class="form-check-label" for="markQrzImport">
|
||||
<strong><?php echo lang('adif_mark_imported_qrz') ?></strong>
|
||||
<br><small class="text-muted"><?php echo lang('adif_hint_no_info_in_file') ?></small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="markClublog" value="1" id="markClublogImport">
|
||||
<label class="form-check-label" for="markClublogImport">
|
||||
<strong><?php echo lang('adif_mark_imported_clublog') ?></strong>
|
||||
<br><small class="text-muted"><?php echo lang('adif_hint_no_info_in_file') ?></small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="skipStationCheck" value="1" id="skipStationCheck">
|
||||
<label class="form-check-label" for="skipStationCheck"><span class="badge text-bg-warning"><?php echo lang('general_word_danger') ?></span> <?php echo lang('adif_ignore_station_call') ?></label>
|
||||
</div>
|
||||
<div class="small form-text text-muted"><?php echo lang('adif_ignore_station_call_hint') ?></div>
|
||||
<!-- Submit Button -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<button type="submit" class="btn btn-primary btn-lg">
|
||||
<i class="fas fa-upload me-2"></i><?php echo lang('adif_upload') ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-sm btn-primary mb-2" value="Upload"><?php echo lang('adif_upload') ?></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="export" role="tabpanel" aria-labelledby="home-tab">
|
||||
<div class="tab-pane fade" id="export" role="tabpanel" aria-labelledby="export-tab">
|
||||
|
||||
<!-- Custom Export Section -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<h5 class="mb-3"><i class="fas fa-download me-2"></i><?php echo lang('adif_export_take_it_anywhere') ?></h5>
|
||||
<p class="text-muted mb-4"><?php echo lang('adif_export_take_it_anywhere_hint') ?></p>
|
||||
|
||||
<form class="form" action="<?php echo site_url('adif/export_custom'); ?>" method="post" enctype="multipart/form-data">
|
||||
<div class="card">
|
||||
<div class="card-header" style="background-color: var(--bs-secondary-bg); border-bottom: 1px solid var(--bs-border-color);">
|
||||
<h6 class="mb-0">Export Settings</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="station_profile_export" class="form-label">Station Location</label>
|
||||
<select name="station_profile" id="station_profile_export" class="form-select">
|
||||
<option value="0"><?php echo lang('adif_select_stationlocation') ?></option>
|
||||
<?php foreach ($station_profile->result() as $station) { ?>
|
||||
<option value="<?php echo $station->station_id; ?>" <?php if ($station->station_id == $this->stations->find_active()) {
|
||||
echo " selected =\"selected\"";
|
||||
} ?>>
|
||||
<?php echo lang('gen_hamradio_callsign') . ": " ?><?php echo $station->station_callsign; ?> (<?php echo $station->station_profile_name; ?>)
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="from_export" class="form-label"><?php echo lang('gen_from_date') ?></label>
|
||||
<input name="from" id="from_export" type="date" class="form-control">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="to_export" class="form-label"><?php echo lang('gen_to_date') ?></label>
|
||||
<input name="to" id="to_export" type="date" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6 class="mb-3">Export Options</h6>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="markLotw" value="1" id="markLotwExport">
|
||||
<label class="form-check-label" for="markLotwExport">
|
||||
<strong><?php echo lang('adif_mark_exported_lotw') ?></strong>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="exportLotw" value="1" id="exportLotw">
|
||||
<label class="form-check-label" for="exportLotw">
|
||||
<strong><?php echo lang('adif_mark_exported_no_lotw') ?></strong>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-download me-2"></i><?php echo lang('adif_export_qso') ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="form" action="<?php echo site_url('adif/export_custom'); ?>" method="post" enctype="multipart/form-data">
|
||||
<h5 class="card-title"><?php echo lang('adif_export_take_it_anywhere') ?> </h5>
|
||||
<p class="card-text"><?php echo lang('adif_export_take_it_anywhere_hint') ?> </p>
|
||||
<select name="station_profile" class="form-select mb-2 me-sm-2" style="width: 20%;">
|
||||
<option value="0"><?php echo lang('adif_select_stationlocation') ?></option>
|
||||
<?php foreach ($station_profile->result() as $station) { ?>
|
||||
<option value="<?php echo $station->station_id; ?>" <?php if ($station->station_id == $this->stations->find_active()) {
|
||||
echo " selected =\"selected\"";
|
||||
} ?>><?php echo lang('gen_hamradio_callsign') . ": " ?><?php echo $station->station_callsign; ?> (<?php echo $station->station_profile_name; ?>)</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
<br>
|
||||
<label for="from"><?php echo lang('gen_from_date') . ": " ?></label>
|
||||
<input name="from" id="from" type="date" class="form-control w-auto">
|
||||
<br>
|
||||
<label for="to"><?php echo lang('gen_to_date') . ": " ?></label>
|
||||
<input name="to" id="to" type="date" class="form-control w-auto">
|
||||
|
||||
<br>
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="markLotw" value="1" id="markLotwExport">
|
||||
<label class="form-check-label" for="markLotwExport"><?php echo lang('adif_mark_exported_lotw') ?></label>
|
||||
<!-- Satellite Export Section -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header" style="background-color: var(--bs-secondary-bg); border-bottom: 1px solid var(--bs-border-color);">
|
||||
<h6 class="mb-0"><i class="fas fa-satellite me-2"></i><?php echo lang('adif_export_sat_only_qso') ?></h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p class="mb-3">Export all satellite contacts from your logbook</p>
|
||||
<a href="<?php echo site_url('adif/exportsat'); ?>" title="Export All Satellite Contacts" target="_blank" class="btn btn-outline-primary">
|
||||
<i class="fas fa-satellite me-2"></i><?php echo lang('adif_export_sat_only_qso_all') ?>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<p class="mb-3">Export satellite contacts confirmed on LoTW</p>
|
||||
<a href="<?php echo site_url('adif/exportsatlotw'); ?>" title="Export All Satellite QSOs Confirmed on LoTW" target="_blank" class="btn btn-outline-primary">
|
||||
<i class="fas fa-check-circle me-2"></i><?php echo lang('adif_export_sat_only_qso_lotw') ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="exportLotw" value="1" id="exportLotw">
|
||||
<label class="form-check-label" for="exportLotw"><?php echo lang('adif_mark_exported_no_lotw') ?></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-sm btn-primary" value="Export"><?php echo lang('adif_export_qso') ?></button>
|
||||
</form>
|
||||
|
||||
<br><br>
|
||||
|
||||
<h5><?php echo lang('adif_export_sat_only_qso') ?></h5>
|
||||
<p><a href="<?php echo site_url('adif/exportsat'); ?>" title="Export All Satellite Contacts" target="_blank" class="btn btn-sm btn-primary"><?php echo lang('adif_export_sat_only_qso_all') ?></a></p>
|
||||
|
||||
<p><a href="<?php echo site_url('adif/exportsatlotw'); ?>" title="Export All Satellite QSOs Confirmed on LoTW" target="_blank" class="btn btn-sm btn-primary"><?php echo lang('adif_export_sat_only_qso_lotw') ?></a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane fade" id="lotw" role="tabpanel" aria-labelledby="home-tab">
|
||||
<form class="form" action="<?php echo site_url('adif/mark_lotw'); ?>" method="post" enctype="multipart/form-data">
|
||||
<select name="station_profile" class="form-select mb-2 me-sm-2" style="width: 20%;">
|
||||
<option value="0"><?php echo lang('adif_select_stationlocation') ?></option>
|
||||
<?php foreach ($station_profile->result() as $station) { ?>
|
||||
<option value="<?php echo $station->station_id; ?>"><?php echo lang('gen_hamradio_callsign') . ": " ?><?php echo $station->station_callsign; ?> (<?php echo $station->station_profile_name; ?>)</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
<p><span class="badge text-bg-warning"><?php echo lang('general_word_warning') ?></span> <?php echo lang('adif_lotw_export_if_selected') ?></p>
|
||||
<br>
|
||||
<label for="from"><?php echo lang('gen_from_date') . ": " ?></label>
|
||||
<input name="from" id="from" type="date" class="form-control w-auto">
|
||||
<br>
|
||||
<label for="to"><?php echo lang('gen_to_date') . ": " ?></label>
|
||||
<input name="to" id="to" type="date" class="form-control w-auto">
|
||||
<br>
|
||||
<button type="button" class="btn btn-sm btn-primary" id="markExportedToLotw" value="Export"><?php echo lang('adif_mark_qso_as_exported_to_lotw') ?></button>
|
||||
</form>
|
||||
<div class="tab-pane fade" id="lotw" role="tabpanel" aria-labelledby="lotw-tab">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h5 class="mb-3"><i class="fas fa-globe me-2"></i>LoTW Export Management</h5>
|
||||
|
||||
<div class="alert alert-warning d-flex align-items-center" role="alert">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
<div>
|
||||
<strong><?php echo lang('general_word_warning') ?>:</strong> <?php echo lang('adif_lotw_export_if_selected') ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="form" action="<?php echo site_url('adif/mark_lotw'); ?>" method="post" enctype="multipart/form-data">
|
||||
<div class="card">
|
||||
<div class="card-header" style="background-color: var(--bs-secondary-bg); border-bottom: 1px solid var(--bs-border-color);">
|
||||
<h6 class="mb-0">Mark QSOs as Exported to LoTW</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="station_profile_lotw" class="form-label">Station Location</label>
|
||||
<select name="station_profile" id="station_profile_lotw" class="form-select">
|
||||
<option value="0"><?php echo lang('adif_select_stationlocation') ?></option>
|
||||
<?php foreach ($station_profile->result() as $station) { ?>
|
||||
<option value="<?php echo $station->station_id; ?>">
|
||||
<?php echo lang('gen_hamradio_callsign') . ": " ?><?php echo $station->station_callsign; ?> (<?php echo $station->station_profile_name; ?>)
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="mb-3">
|
||||
<label for="from_lotw" class="form-label"><?php echo lang('gen_from_date') ?></label>
|
||||
<input name="from" id="from_lotw" type="date" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="mb-3">
|
||||
<label for="to_lotw" class="form-label"><?php echo lang('gen_to_date') ?></label>
|
||||
<input name="to" id="to_lotw" type="date" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="button" class="btn btn-primary" id="markExportedToLotw">
|
||||
<i class="fas fa-check me-2"></i><?php echo lang('adif_mark_qso_as_exported_to_lotw') ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane <?php if ($showtab == 'dcl') {
|
||||
echo 'active';
|
||||
echo 'active show';
|
||||
} else {
|
||||
echo 'fade';
|
||||
} ?>" id="dcl" role="tabpanel" aria-labelledby="home-tab">
|
||||
} ?>" id="dcl" role="tabpanel" aria-labelledby="dcl-tab">
|
||||
|
||||
<?php if (isset($error) && $showtab == 'dcl') { ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<?php echo $error; ?>
|
||||
<i class="fas fa-exclamation-triangle me-2"></i><?php echo $error; ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<p class="card-text"><?php echo lang('adif_dcl_text_pre') ?> <a href="http://dcl.darc.de/dml/export_adif_form.php" target="_blank"><?php echo lang('darc_dcl') ?></a> <?php echo lang('adif_dcl_text_post') ?></p>
|
||||
<form class="form" action="<?php echo site_url('adif/dcl'); ?>" method="post" enctype="multipart/form-data">
|
||||
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="onlyConfirmed" value="1" id="onlyConfirmed" checked>
|
||||
<label class="form-check-label" for="onlyConfirmed"><?php echo lang('only_confirmed_qsos') ?></label>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h5 class="mb-3"><i class="fas fa-flag me-2"></i>DARC DCL Import</h5>
|
||||
|
||||
<div class="alert alert-info d-flex align-items-center" role="alert">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
<div>
|
||||
<?php echo lang('adif_dcl_text_pre') ?> <a href="http://dcl.darc.de/dml/export_adif_form.php" target="_blank" class="alert-link"><?php echo lang('darc_dcl') ?></a> <?php echo lang('adif_dcl_text_post') ?>
|
||||
</div>
|
||||
<div class="small form-text text-muted"><?php echo lang('only_confirmed_qsos_hint') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="overwriteDok" value="1" id="overwriteDok">
|
||||
<label class="form-check-label" for="overwriteDok"><span class="badge text-bg-warning"><?php echo lang('general_word_warning') ?></span> <?php echo lang('overwrite_by_dcl') ?></label>
|
||||
|
||||
<form class="form" action="<?php echo site_url('adif/dcl'); ?>" method="post" enctype="multipart/form-data">
|
||||
<div class="card">
|
||||
<div class="card-header" style="background-color: var(--bs-secondary-bg); border-bottom: 1px solid var(--bs-border-color);">
|
||||
<h6 class="mb-0">DCL Import Settings</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="mb-3">
|
||||
<label for="userfile_dcl" class="form-label">Choose DCL ADIF File</label>
|
||||
<input class="form-control" type="file" name="userfile" id="userfile_dcl" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6 class="mb-3">Import Options</h6>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="onlyConfirmed" value="1" id="onlyConfirmed" checked>
|
||||
<label class="form-check-label" for="onlyConfirmed">
|
||||
<strong><?php echo lang('only_confirmed_qsos') ?></strong>
|
||||
<br><small class="text-muted"><?php echo lang('only_confirmed_qsos_hint') ?></small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="overwriteDok" value="1" id="overwriteDok">
|
||||
<label class="form-check-label" for="overwriteDok">
|
||||
<span class="badge bg-warning text-dark me-1"><?php echo lang('general_word_warning') ?></span>
|
||||
<strong><?php echo lang('overwrite_by_dcl') ?></strong>
|
||||
<br><small class="text-muted"><?php echo lang('overwrite_by_dcl_hint') ?></small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" name="ignoreAmbiguous" value="1" id="ignoreAmbiguous" checked>
|
||||
<label class="form-check-label" for="ignoreAmbiguous">
|
||||
<strong><?php echo lang('ignore_ambiguous') ?></strong>
|
||||
<br><small class="text-muted"><?php echo lang('ignore_ambiguous_hint') ?></small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-upload me-2"></i><?php echo lang('adif_upload') ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="small form-text text-muted"><?php echo lang('overwrite_by_dcl_hint') ?></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<div class="col-md-10">
|
||||
<div class="form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="ignoreAmbiguous" value="1" id="ignoreAmbiguous" checked>
|
||||
<label class="form-check-label" for="ignoreAmbiguous"><?php echo lang('ignore_ambiguous') ?></label>
|
||||
</div>
|
||||
<div class="small form-text text-muted"><?php echo lang('ignore_ambiguous_hint') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<input class="file-input mb-2 me-sm-2" type="file" name="userfile" size="20" />
|
||||
<button type="submit" class="btn btn-sm btn-primary mb-2" value="Upload"><?php echo lang('adif_upload') ?></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -23,39 +23,203 @@ $wwff = 0;
|
|||
|
||||
<h2><?php echo lang('options_bands'); ?></h2>
|
||||
|
||||
<!-- Info Card -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle me-2"></i><?php echo lang('options_bands'); ?> Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<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'); ?>
|
||||
</p>
|
||||
<p class="card-text mb-0">
|
||||
<?php echo lang('options_bands_text_ln2'); ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Controls Card -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0"><i class="fas fa-sliders-h me-2"></i>Band Management Controls</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Statistics -->
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-body py-2">
|
||||
<div class="row text-center">
|
||||
<div class="col-md-6 col-6">
|
||||
<strong id="activeBandsCount" class="text-primary">0</strong><br>
|
||||
<small class="text-muted">Active for QSO Entry</small>
|
||||
</div>
|
||||
<div class="col-md-6 col-6">
|
||||
<strong class="text-primary"><?php echo count($bands); ?></strong><br>
|
||||
<small class="text-muted">Total Bands Configured</small>
|
||||
</div>
|
||||
</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">
|
||||
<?php echo lang('options_bands'); ?>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="fas fa-table me-2"></i>Bands Configuration</h5>
|
||||
<span class="badge bg-secondary" id="visibleRowsCount">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-text">
|
||||
<?php echo lang('options_bands_text_ln1'); ?>
|
||||
</p>
|
||||
<p class="card-text">
|
||||
<?php echo lang('options_bands_text_ln2'); ?>
|
||||
</p>
|
||||
|
||||
<div class="table-responsive">
|
||||
<style>
|
||||
.bandtable th, .bandtable td {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
padding: 8px 4px;
|
||||
}
|
||||
.bandtable th {
|
||||
border-top: 2px solid #dee2e6;
|
||||
background-color: var(--bs-secondary-bg, #f8f9fa);
|
||||
}
|
||||
.bandtable tfoot th {
|
||||
border-top: 2px solid #adb5bd;
|
||||
background-color: var(--bs-tertiary-bg, #e9ecef);
|
||||
}
|
||||
.bandtable th:first-child,
|
||||
.bandtable td:first-child {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
box-shadow: 2px 0 2px rgba(0,0,0,0.1);
|
||||
background-color: var(--bs-body-bg, #fff);
|
||||
}
|
||||
.bandtable th:nth-child(2),
|
||||
.bandtable td:nth-child(2) {
|
||||
position: sticky;
|
||||
left: 40px;
|
||||
z-index: 2;
|
||||
box-shadow: 2px 0 2px rgba(0,0,0,0.1);
|
||||
font-weight: bold;
|
||||
background-color: var(--bs-body-bg, #fff);
|
||||
}
|
||||
.bandtable thead th:first-child,
|
||||
.bandtable thead th:nth-child(2) {
|
||||
z-index: 3;
|
||||
}
|
||||
.bandtable th:nth-child(3) {
|
||||
border-left: 1px solid var(--bs-border-color, #dee2e6);
|
||||
}
|
||||
.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">
|
||||
<thead>
|
||||
<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_cq'); ?></th>
|
||||
<th><?php echo lang('gen_hamradio_dok'); ?></th>
|
||||
<th><?php echo lang('gen_hamradio_dxcc'); ?></th>
|
||||
<th><?php echo lang('gen_hamradio_iota'); ?></th>
|
||||
<th><?php echo lang('gen_hamradio_pota'); ?></th>
|
||||
<th><?php echo lang('gen_hamradio_sig'); ?></th>
|
||||
<th><?php echo lang('gen_hamradio_sota'); ?></th>
|
||||
<th><?php echo lang('gen_hamradio_county_reference'); ?></th>
|
||||
<th><?php echo lang('menu_vucc'); ?></th>
|
||||
<th><?php echo lang('menu_was'); ?></th>
|
||||
<th><?php echo lang('gen_hamradio_wwff'); ?></th>
|
||||
<th><?php echo lang('gen_hamradio_bandgroup'); ?></th>
|
||||
<th><?php echo lang('options_bands_ssb_qrg'); ?></th>
|
||||
<th><?php echo lang('options_bands_data_qrg'); ?></th>
|
||||
<th><?php echo lang('options_bands_cw_qrg'); ?></th>
|
||||
<th title="CQ Magazine DX Awards">CQ</th>
|
||||
<th title="Diplom Ortsverbände Kennung">DOK</th>
|
||||
<th title="DX Century Club">DXCC</th>
|
||||
<th title="Islands On The Air">IOTA</th>
|
||||
<th title="Parks on the Air">POTA</th>
|
||||
<th title="Special Interest Group">SIG</th>
|
||||
<th title="Summits on the Air">SOTA</th>
|
||||
<th title="US Counties">Counties</th>
|
||||
<th title="VHF/UHF Century Club">VUCC</th>
|
||||
<th title="Worked All States">WAS</th>
|
||||
<th title="World Wide Flora and Fauna">WWFF</th>
|
||||
<th title="Band group classification">Group</th>
|
||||
<th title="SSB Operating Frequency">SSB</th>
|
||||
<th title="Data Operating Frequency">Data</th>
|
||||
<th title="CW Operating Frequency">CW</th>
|
||||
<?php if($this->session->userdata('user_type') == '99') { ?>
|
||||
<th></th>
|
||||
<th></th>
|
||||
|
|
@ -65,28 +229,28 @@ $wwff = 0;
|
|||
<tbody>
|
||||
<?php foreach ($bands as $band) { ?>
|
||||
<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 style="text-align: center; vertical-align: middle;" ><?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 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 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 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 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 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 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 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 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 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 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 style="text-align: center; vertical-align: middle;" ><?php echo $band->bandgroup;?></td>
|
||||
<td style="text-align: center; vertical-align: middle;" ><?php echo $band->ssb;?></td>
|
||||
<td style="text-align: center; vertical-align: middle;" ><?php echo $band->data;?></td>
|
||||
<td style="text-align: center; vertical-align: middle;" ><?php echo $band->cw;?></td>
|
||||
<td class='band_<?php echo $band->id ?> band-checkbox-cell'><input type="checkbox" <?php if ($band->active == 1) {echo 'checked';}?>></td>
|
||||
<td class="band-name"><?php echo $band->band;?></td>
|
||||
<td class='cq_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->cq == 1) {echo 'checked'; $cq++;}?>></td>
|
||||
<td class='dok_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->dok == 1) {echo 'checked'; $dok++;}?>></td>
|
||||
<td class='dxcc_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->dxcc == 1) {echo 'checked'; $dxcc++;}?>></td>
|
||||
<td class='iota_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->iota == 1) {echo 'checked'; $iota++;}?>></td>
|
||||
<td class='pota_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->pota == 1) {echo 'checked'; $pota++;}?>></td>
|
||||
<td class='sig_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->sig == 1) {echo 'checked'; $sig++;}?>></td>
|
||||
<td class='sota_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->sota == 1) {echo 'checked'; $sota++;}?>></td>
|
||||
<td class='uscounties_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->uscounties == 1) {echo 'checked'; $uscounties++;}?>></td>
|
||||
<td class='vucc_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->vucc == 1) {echo 'checked'; $vucc++;}?>></td>
|
||||
<td class='was_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->was == 1) {echo 'checked'; $was++;}?>></td>
|
||||
<td class='wwff_<?php echo $band->id ?>'><input type="checkbox" <?php if ($band->wwff == 1) {echo 'checked'; $wwff++;}?>></td>
|
||||
<td><?php echo $band->bandgroup;?></td>
|
||||
<td class="frequency-cell"><?php echo $band->ssb;?></td>
|
||||
<td class="frequency-cell"><?php echo $band->data;?></td>
|
||||
<td class="frequency-cell"><?php echo $band->cw;?></td>
|
||||
<?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>
|
||||
</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>
|
||||
</td>
|
||||
<?php } ?>
|
||||
|
|
@ -95,7 +259,7 @@ $wwff = 0;
|
|||
<?php } ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<th><?php echo lang('general_word_all'); ?></th>
|
||||
<th>Toggle All</th>
|
||||
<th></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>
|
||||
|
|
@ -115,7 +279,7 @@ $wwff = 0;
|
|||
<th></th>
|
||||
<th></th>
|
||||
</tfoot>
|
||||
<table>
|
||||
</table>
|
||||
</div>
|
||||
<br/>
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -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 (60 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();
|
||||
}, 60000);
|
||||
|
||||
var customdata = {
|
||||
'dataPost': {
|
||||
'date_from': $('.custom-map-QSOs input[name="from"]').val(),
|
||||
|
|
@ -795,6 +815,58 @@ if ($this->session->userdata('user_id') != null) {
|
|||
},
|
||||
'map_id': '#custommap'
|
||||
};
|
||||
|
||||
// Add success and error callbacks to the customdata
|
||||
customdata.onSuccess = function(plotjson) {
|
||||
console.log('Map loading success callback called with data:', plotjson);
|
||||
|
||||
// Clear the failsafe timeout
|
||||
clearTimeout(failsafeTimeout);
|
||||
|
||||
try {
|
||||
// Update statistics
|
||||
if (typeof updateMapStatistics === 'function') {
|
||||
console.log('Calling updateMapStatistics function');
|
||||
updateMapStatistics(plotjson);
|
||||
} else {
|
||||
console.warn('updateMapStatistics function not found');
|
||||
}
|
||||
|
||||
// Show success message
|
||||
const qsoCount = plotjson['markers'] ? plotjson['markers'].length : 0;
|
||||
console.log('QSO count:', qsoCount);
|
||||
$('#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
|
||||
console.log('Re-enabling button and hiding spinner');
|
||||
$button.prop('disabled', false);
|
||||
$spinner.addClass('d-none');
|
||||
}
|
||||
};
|
||||
|
||||
customdata.onError = function() {
|
||||
console.log('Map loading error callback called');
|
||||
|
||||
// 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 +878,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() {},
|
||||
});
|
||||
|
|
@ -1927,21 +2003,59 @@ if ($this->session->userdata('user_id') != null) {
|
|||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#btn_update_dxcc').bind('click', function() {
|
||||
$('#dxcc_update_status').show();
|
||||
// Show spinner and disable button
|
||||
$('#dxcc_spinner').show();
|
||||
$('#btn_text').text('Updating...');
|
||||
$('#btn_update_dxcc').prop('disabled', true);
|
||||
|
||||
// Show initial status immediately
|
||||
$('#dxcc_update_status').show().html('Starting update...<br/>');
|
||||
|
||||
$.ajax({
|
||||
url: "update/dxcc"
|
||||
});
|
||||
setTimeout(update_stats, 5000);
|
||||
|
||||
// Start polling immediately, then every 2 seconds for more responsive updates
|
||||
setTimeout(update_stats, 1000);
|
||||
});
|
||||
|
||||
function update_stats() {
|
||||
$('#dxcc_update_status').load('<?php echo base_url() ?>updates/status.html', function(val) {
|
||||
$('#dxcc_update_staus').html(val);
|
||||
|
||||
if ((val === null) || (val.substring(0, 4) != "DONE")) {
|
||||
setTimeout(update_stats, 5000);
|
||||
}
|
||||
});
|
||||
console.log('Polling status at: ' + new Date().toLocaleTimeString());
|
||||
|
||||
$.get('<?php echo base_url() ?>index.php/update/get_status')
|
||||
.done(function(response) {
|
||||
console.log('Status response:', response);
|
||||
|
||||
// Always update the status display with the response
|
||||
if (response && response.trim() !== '') {
|
||||
$('#dxcc_update_status').html(response);
|
||||
}
|
||||
|
||||
// Check if update is complete
|
||||
if (response && typeof response === 'string' && response.substring(0, 4) === "DONE") {
|
||||
console.log('Update completed!');
|
||||
// Update is done - hide spinner and re-enable button
|
||||
$('#dxcc_spinner').hide();
|
||||
$('#btn_text').text('Update DXCC Data');
|
||||
$('#btn_update_dxcc').prop('disabled', false);
|
||||
} else {
|
||||
// Continue polling if not done - reduced interval for better responsiveness
|
||||
setTimeout(update_stats, 2000);
|
||||
}
|
||||
})
|
||||
.fail(function(xhr, status, error) {
|
||||
console.log('Error loading status: ' + status + ' - ' + error);
|
||||
$('#dxcc_update_status').html('Error loading status... Retrying...<br/>');
|
||||
|
||||
// Reset button state on persistent error
|
||||
if (xhr.status === 0 || xhr.status >= 500) {
|
||||
$('#dxcc_spinner').hide();
|
||||
$('#btn_text').text('Update DXCC Data');
|
||||
$('#btn_update_dxcc').prop('disabled', false);
|
||||
}
|
||||
|
||||
setTimeout(update_stats, 5000); // Retry in 5 seconds on error
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -200,8 +200,8 @@
|
|||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="<?php echo site_url('bandmap/list'); ?>" title="Bandmap"><i class="fa fa-bezier-curve"></i> <?php echo lang('menu_bandmap'); ?></a>
|
||||
<?php } ?>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="<?php echo site_url('sattimers'); ?>" title="SAT Timers"><i class="fas fa-satellite"></i> SAT Timers</a>
|
||||
<!-- <div class="dropdown-divider"></div> -->
|
||||
<!-- a class="dropdown-item" href="<?php echo site_url('sattimers'); ?>" title="SAT Timers"><i class="fas fa-satellite"></i> SAT Timers</a> -->
|
||||
</div>
|
||||
</li>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,87 +1,157 @@
|
|||
<div class="container">
|
||||
<div class="container logbooks-management">
|
||||
|
||||
<br>
|
||||
<?php if($this->session->flashdata('message')) { ?>
|
||||
<!-- Display Message -->
|
||||
<div class="alert-message error">
|
||||
<p><?php echo $this->session->flashdata('message'); ?></p>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="fas fa-check-circle me-2"></i><?php echo $this->session->flashdata('message'); ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<h2><?php echo $page_title; ?></h2>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<?php echo lang('station_logbooks_description_header')?>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-text"><?php echo lang('station_logbooks_description_text')?></p>
|
||||
</div>
|
||||
<!-- Page Header -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h2><i class="fas fa-book me-2"></i><?php echo $page_title; ?></h2>
|
||||
<p class="text-muted mb-0">Organize your QSOs into separate logbooks</p>
|
||||
</div>
|
||||
<a href="<?php echo site_url('logbooks/create'); ?>" class="btn btn-primary">
|
||||
<i class="fas fa-plus me-2"></i><?php echo lang('station_logbooks_create')?></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top: 20px;">
|
||||
<div class="card-header">
|
||||
<?php echo lang('station_logbooks')?> <a class="btn btn-primary float-end" href="<?php echo site_url('logbooks/create'); ?>"><i class="fas fa-plus"></i> <?php echo lang('station_logbooks_create')?></a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="station_logbooks">
|
||||
<?php if ($my_logbooks->num_rows() > 0) { ?>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table id="station_logbooks_table" class="table table-sm table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col"><?php echo lang('general_word_name')?></th>
|
||||
<th scope="col"><?php echo lang('station_logbooks_status')?></th>
|
||||
<th scope="col"><?php echo lang('admin_edit')?></th>
|
||||
<th scope="col"><?php echo lang('admin_delete')?></th>
|
||||
<th scope="col"><?php echo lang('station_logbooks_link')?></th>
|
||||
<th scope="col"><?php echo lang('station_logbooks_public_search')?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($my_logbooks->result() as $row) { ?>
|
||||
<tr>
|
||||
<td style="text-align: center; vertical-align: middle;">
|
||||
<?php echo $row->logbook_name;?><br>
|
||||
</td>
|
||||
|
||||
<td style="text-align: center; vertical-align: middle;">
|
||||
<?php if($this->session->userdata('active_station_logbook') != $row->logbook_id) { ?>
|
||||
<a href="<?php echo site_url('logbooks/set_active')."/".$row->logbook_id; ?>" class="btn btn-outline-primary btn-sm"><?php echo lang('station_logbooks_set_active')?></a>
|
||||
<?php } else {
|
||||
echo "<span class='badge text-bg-success'>" . lang('station_logbooks_active_logbook') . "</span>";
|
||||
}?>
|
||||
</td>
|
||||
<td style="text-align: center; vertical-align: middle;">
|
||||
<a href="<?php echo site_url('logbooks/edit')."/".$row->logbook_id; ?>" class="btn btn-outline-primary btn-sm"><i class="fas fa-edit" title="<?php echo lang('station_logbooks_edit_logbook') . ': ' . $row->logbook_name;?>"></i> </a>
|
||||
</td>
|
||||
<td style="text-align: center; vertical-align: middle;">
|
||||
<?php if($this->session->userdata('active_station_logbook') != $row->logbook_id) { ?>
|
||||
<a href="<?php echo site_url('Logbooks/delete')."/".$row->logbook_id; ?>" class="btn btn-danger btn-sm" onclick="return confirm('<?php echo lang('station_logbooks_confirm_delete') . $row->logbook_name; ?>');"><i class="fas fa-trash-alt"></i></a>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<td style="text-align: center; vertical-align: middle;">
|
||||
<?php if($row->public_slug != '') { ?>
|
||||
<a target="_blank" href="<?php echo site_url('visitor')."/".$row->public_slug; ?>" class="btn btn-outline-primary btn-sm" ><i class="fas fa-globe" title="<?php echo lang('station_logbooks_view_public') . $row->logbook_name;?>"></i> </a>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<td style="text-align: center; vertical-align: middle;">
|
||||
<?php if ($row->public_search == 1) {
|
||||
echo "<span class='badge text-bg-success'>" . lang('general_word_enabled') . "</span>";
|
||||
} else {
|
||||
echo "<span class='badge text-bg-dark'>" . lang('general_word_disabled') . "</span>";
|
||||
} ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Information Card -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle me-2"></i><?php echo lang('station_logbooks_description_header')?></h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="mb-0"><?php echo lang('station_logbooks_description_text')?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Station Logbooks Table -->
|
||||
<?php if ($my_logbooks->num_rows() > 0) { ?>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header bg-light">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="fas fa-list me-2"></i><?php echo lang('station_logbooks')?> (<?php echo $my_logbooks->num_rows(); ?>)</h5>
|
||||
<small class="text-muted">Click to set a logbook as active</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-3">
|
||||
<div class="table-responsive">
|
||||
<table id="station_logbooks_table" class="table table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th scope="col"><i class="fas fa-tag me-1"></i><?php echo lang('general_word_name')?></th>
|
||||
<th scope="col" class="text-center"><i class="fas fa-power-off me-1"></i><?php echo lang('station_logbooks_status')?></th>
|
||||
<th scope="col" class="text-center"><i class="fas fa-globe me-1"></i><?php echo lang('station_logbooks_link')?></th>
|
||||
<th scope="col" class="text-center"><i class="fas fa-search me-1"></i><?php echo lang('station_logbooks_public_search')?></th>
|
||||
<th scope="col" class="text-center"><i class="fas fa-tools me-1"></i>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($my_logbooks->result() as $row) { ?>
|
||||
<tr <?php if($this->session->userdata('active_station_logbook') == $row->logbook_id) { echo 'class="table-success"'; } ?>>
|
||||
<td class="align-middle">
|
||||
<div class="d-flex align-items-center">
|
||||
<div>
|
||||
<strong><?php echo $row->logbook_name;?></strong>
|
||||
<?php if($this->session->userdata('active_station_logbook') == $row->logbook_id) { ?>
|
||||
<span class="badge bg-success ms-2">
|
||||
<i class="fas fa-check me-1"></i><?php echo lang('station_logbooks_active_logbook'); ?>
|
||||
</span>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-middle text-center">
|
||||
<?php if($this->session->userdata('active_station_logbook') != $row->logbook_id) { ?>
|
||||
<a href="<?php echo site_url('logbooks/set_active')."/".$row->logbook_id; ?>"
|
||||
class="btn btn-outline-success btn-sm"
|
||||
title="<?php echo lang('station_logbooks_set_active')?>">
|
||||
<i class="fas fa-power-off me-1"></i><?php echo lang('station_logbooks_set_active')?>
|
||||
</a>
|
||||
<?php } else { ?>
|
||||
<span class="badge bg-success">
|
||||
<i class="fas fa-check me-1"></i><?php echo lang('station_logbooks_active_logbook'); ?>
|
||||
</span>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<td class="align-middle text-center">
|
||||
<?php if($row->public_slug != '') { ?>
|
||||
<a target="_blank" href="<?php echo site_url('visitor')."/".$row->public_slug; ?>"
|
||||
class="btn btn-info btn-sm"
|
||||
title="<?php echo lang('station_logbooks_view_public') . $row->logbook_name;?>">
|
||||
<i class="fas fa-globe me-1"></i>View Public
|
||||
</a>
|
||||
<?php } else { ?>
|
||||
<span class="text-muted">-</span>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<td class="align-middle text-center">
|
||||
<?php if ($row->public_search == 1) { ?>
|
||||
<span class="badge bg-success">
|
||||
<i class="fas fa-check me-1"></i><?php echo lang('general_word_enabled'); ?>
|
||||
</span>
|
||||
<?php } else { ?>
|
||||
<span class="badge bg-secondary">
|
||||
<i class="fas fa-times me-1"></i><?php echo lang('general_word_disabled'); ?>
|
||||
</span>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<td class="align-middle text-center">
|
||||
<div class="d-flex justify-content-center gap-1">
|
||||
<a href="<?php echo site_url('logbooks/edit')."/".$row->logbook_id; ?>"
|
||||
class="btn btn-primary btn-sm"
|
||||
title="<?php echo lang('station_logbooks_edit_logbook') . ': ' . $row->logbook_name;?>">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<?php if($this->session->userdata('active_station_logbook') != $row->logbook_id) { ?>
|
||||
<a href="<?php echo site_url('Logbooks/delete')."/".$row->logbook_id; ?>"
|
||||
class="btn btn-danger btn-sm"
|
||||
title="<?php echo lang('admin_delete')?>"
|
||||
onclick="return confirm('<?php echo lang('station_logbooks_confirm_delete') . $row->logbook_name; ?>');">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } else { ?>
|
||||
<!-- Empty State -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-body text-center py-5">
|
||||
<i class="fas fa-book text-muted mb-3" style="font-size: 3rem;"></i>
|
||||
<h5 class="text-muted mb-3">No Logbooks</h5>
|
||||
<p class="text-muted mb-4">You haven't created any logbooks yet. Create your first logbook to organize your QSOs.</p>
|
||||
<a href="<?php echo site_url('logbooks/create'); ?>" class="btn btn-primary btn-lg">
|
||||
<i class="fas fa-plus me-2"></i><?php echo lang('station_logbooks_create')?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -122,7 +122,11 @@
|
|||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<button class="btn btn-outline-success" hx-get="<?php echo site_url('lotw/lotw_upload'); ?>" hx-target="#lotw_manual_results">
|
||||
<button id="manual-sync-btn" class="btn btn-outline-success"
|
||||
hx-get="<?php echo site_url('lotw/lotw_upload'); ?>"
|
||||
hx-target="#lotw_manual_results"
|
||||
hx-indicator="#sync-spinner">
|
||||
<span id="sync-spinner" class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true" style="display: none;"></span>
|
||||
<?php echo lang('lotw_btn_manual_sync'); ?>
|
||||
</button>
|
||||
|
||||
|
|
@ -131,3 +135,22 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const syncBtn = document.getElementById('manual-sync-btn');
|
||||
const spinner = document.getElementById('sync-spinner');
|
||||
|
||||
// Show spinner when htmx request starts
|
||||
syncBtn.addEventListener('htmx:beforeRequest', function() {
|
||||
spinner.style.display = 'inline-block';
|
||||
syncBtn.disabled = true;
|
||||
});
|
||||
|
||||
// Hide spinner when htmx request completes (success or error)
|
||||
syncBtn.addEventListener('htmx:afterRequest', function() {
|
||||
spinner.style.display = 'none';
|
||||
syncBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,107 +1,557 @@
|
|||
<link rel="stylesheet" href="<?php echo base_url(); ?>assets/css/map-enhancements.css">
|
||||
|
||||
<style>
|
||||
.callsign-label-text {
|
||||
background: rgba(0, 0, 0, 0.8) !important;
|
||||
color: white !important;
|
||||
font-family: Arial, sans-serif !important;
|
||||
font-weight: bold !important;
|
||||
padding: 1px 3px !important;
|
||||
border-radius: 2px !important;
|
||||
border: 1px solid #fff !important;
|
||||
white-space: nowrap !important;
|
||||
text-align: center !important;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.5) !important;
|
||||
display: inline-block !important;
|
||||
line-height: 1 !important;
|
||||
margin: 0 !important;
|
||||
min-width: auto !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
transform-origin: center !important;
|
||||
font-size: 12px !important; /* Ensure consistent font size */
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<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_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 Container -->
|
||||
<div class="card">
|
||||
<div class="card-header py-3">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-4">
|
||||
<h5 class="mb-0">
|
||||
<i class="fas fa-globe-americas me-2"></i>World Map View
|
||||
</h5>
|
||||
<small class="text-muted">Showing: <strong id="active-logbook-display"><?php echo $logbook_name ?></strong></small>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="d-flex align-items-center justify-content-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="toggleCallsignLabels()" title="Toggle callsign labels" id="callsign-labels-btn">
|
||||
<i class="fas fa-tags"></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 class="card-body p-0">
|
||||
<div id="custommap" class="map-leaflet" style="width: 100%; height: 1000px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-success" role="alert">Showing QSOs for Custom Dates for Active Logbook <?php echo $logbook_name ?></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 • Use
|
||||
<i class="fas fa-tags"></i> to toggle callsign labels
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Enhanced JavaScript functionality for improved UX
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Add event listeners for date changes - removed updateDateRangeDisplay calls
|
||||
// since the date-range-display element doesn't exist
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Global variable to track if callsign labels are shown
|
||||
let callsignLabelsVisible = false;
|
||||
let callsignLabels = [];
|
||||
|
||||
function clearCallsignLabels() {
|
||||
// Remove all existing callsign labels from the map
|
||||
callsignLabels.forEach(function(labelMarker) {
|
||||
if (map.hasLayer(labelMarker)) {
|
||||
map.removeLayer(labelMarker);
|
||||
}
|
||||
});
|
||||
|
||||
// Clear the labels array
|
||||
callsignLabels = [];
|
||||
|
||||
// Reset the visibility state
|
||||
callsignLabelsVisible = false;
|
||||
|
||||
// Reset button appearance
|
||||
const button = document.getElementById('callsign-labels-btn');
|
||||
if (button) {
|
||||
button.classList.remove('btn-success');
|
||||
button.classList.add('btn-outline-success');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleCallsignLabels() {
|
||||
const button = document.getElementById('callsign-labels-btn');
|
||||
|
||||
if (!callsignLabelsVisible) {
|
||||
// Show callsign labels
|
||||
if (typeof plotlayers !== 'undefined' && plotlayers.length > 0) {
|
||||
plotlayers.forEach(function(marker) {
|
||||
if (marker.data && marker.data.callsign) {
|
||||
// Only create labels when callsign is specifically provided
|
||||
const callsign = marker.data.callsign;
|
||||
|
||||
// Try a different approach - create a DivIcon instead of tooltip
|
||||
const labelIcon = L.divIcon({
|
||||
className: 'callsign-label-icon',
|
||||
html: `<div class="callsign-label-text">${callsign}</div>`,
|
||||
iconSize: [0, 0], // No fixed size, let CSS handle it
|
||||
iconAnchor: [0, -5] // Position above the marker with minimal offset
|
||||
});
|
||||
|
||||
// Create a separate marker for the label
|
||||
const labelMarker = L.marker(marker.getLatLng(), {
|
||||
icon: labelIcon,
|
||||
interactive: false,
|
||||
zIndexOffset: 1000
|
||||
});
|
||||
|
||||
map.addLayer(labelMarker);
|
||||
callsignLabels.push(labelMarker);
|
||||
}
|
||||
});
|
||||
|
||||
callsignLabelsVisible = true;
|
||||
button.classList.remove('btn-outline-success');
|
||||
button.classList.add('btn-success');
|
||||
}
|
||||
} else {
|
||||
// Hide callsign labels
|
||||
callsignLabels.forEach(function(labelMarker) {
|
||||
map.removeLayer(labelMarker);
|
||||
});
|
||||
|
||||
callsignLabels = [];
|
||||
callsignLabelsVisible = false;
|
||||
button.classList.remove('btn-success');
|
||||
button.classList.add('btn-outline-success');
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Handle callsign labels when new data is loaded
|
||||
const labelsWereVisible = callsignLabelsVisible;
|
||||
|
||||
// Clear any existing callsign labels before processing new data
|
||||
clearCallsignLabels();
|
||||
|
||||
// Reapply callsign labels if they were previously enabled
|
||||
if (labelsWereVisible) {
|
||||
setTimeout(() => {
|
||||
toggleCallsignLabels();
|
||||
}, 100); // Small delay to ensure markers are fully rendered
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,99 +1,228 @@
|
|||
<div class="container">
|
||||
<div class="container station-management">
|
||||
|
||||
<br>
|
||||
<?php if($this->session->flashdata('message')) { ?>
|
||||
<!-- Display Message -->
|
||||
<div class="alert-message error">
|
||||
<p><?php echo $this->session->flashdata('message'); ?></p>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="fas fa-check-circle me-2"></i><?php echo $this->session->flashdata('message'); ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h2><?php echo $page_title; ?></h2>
|
||||
<a href="<?php echo site_url('station/create'); ?>" class="btn btn-primary"><i class="fas fa-plus"></i> <?php echo lang('station_location_create'); ?></a>
|
||||
<!-- Page Header -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h2><i class="fas fa-tower-broadcast me-2"></i><?php echo $page_title; ?></h2>
|
||||
<p class="text-muted mb-0">Manage your station locations.</p>
|
||||
</div>
|
||||
<a href="<?php echo site_url('station/create'); ?>" class="btn btn-primary">
|
||||
<i class="fas fa-plus me-2"></i><?php echo lang('station_location_create'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p class="card-text"><?php echo lang('station_location_header_ln1'); ?></p>
|
||||
<p class="card-text"><?php echo lang('station_location_header_ln2'); ?></p>
|
||||
<p class="card-text"><?php echo lang('station_location_header_ln3'); ?></p>
|
||||
|
||||
<?php if ($stations->num_rows() > 0) { ?>
|
||||
|
||||
<?php if($current_active == 0) { ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<?php echo lang('station_location_warning'); ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (($is_there_qsos_with_no_station_id >= 1) && ($is_admin)) { ?>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<span class="badge rounded-pill text-bg-warning"><?php echo lang('general_word_warning'); ?></span> <?php echo lang('station_location_warning_reassign'); ?> <br>
|
||||
<?php echo lang('station_location_reassign_at'); ?> <a href="<?php echo site_url('maintenance/'); ?>" class="btn btn-warning"><i class="fas fa-sync"></i><?php echo lang('account_word_admin') . "/" . lang('general_word_maintenance'); ?></a>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table id="station_locations_table" class="table table-sm table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col"><?php echo lang('station_location_name'); ?></th>
|
||||
<th scope="col"><?php echo lang('station_location_callsign'); ?></th>
|
||||
<th scope="col"><?php echo lang('general_word_country'); ?></th>
|
||||
<th scope="col"><?php echo lang('gen_hamradio_gridsquare'); ?></th>
|
||||
<th></th>
|
||||
<th scope="col"><?php echo lang('admin_edit'); ?></th>
|
||||
<th scope="col"><?php echo lang('admin_copy'); ?></th>
|
||||
<th scope="col"><?php echo lang('station_location_emptylog'); ?></th>
|
||||
<th scope="col"><?php echo lang('admin_delete'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($stations->result() as $row) { ?>
|
||||
<tr>
|
||||
<td style="text-align: center; vertical-align: middle;">
|
||||
<?php echo $row->station_profile_name;?><br>
|
||||
</td>
|
||||
<td style="text-align: center; vertical-align: middle;"><?php echo $row->station_callsign;?></td>
|
||||
<td style="text-align: center; vertical-align: middle;"><?php echo $row->station_country == '' ? '- NONE -' : $row->station_country; if ($row->dxcc_end != NULL) { echo ' <span class="badge text-bg-danger">'.lang('gen_hamradio_deleted_dxcc').'</span>'; } ?></td>
|
||||
<td style="text-align: center; vertical-align: middle;"><?php echo $row->station_gridsquare;?></td>
|
||||
<td style="text-align: center" data-order="<?php echo $row->station_id;?>">
|
||||
<?php if($row->station_active != 1) { ?>
|
||||
<a href="<?php echo site_url('station/set_active/').$current_active."/".$row->station_id; ?>" class="btn btn-outline-secondary btn-sm" onclick="return confirm('<?php echo lang('station_location_confirm_active'); ?> <?php echo $row->station_profile_name; ?>');"><?php echo lang('station_location_set_active'); ?></a>
|
||||
<?php } else { ?>
|
||||
<span class="badge text-bg-success"><?php echo lang('station_location_active'); ?></span>
|
||||
<?php } ?>
|
||||
|
||||
<br>
|
||||
<span class="badge text-bg-info">ID: <?php echo $row->station_id;?></span>
|
||||
<span class="badge text-bg-light"><?php echo $row->qso_total;?> <?php echo lang('gen_hamradio_qso'); ?></span>
|
||||
</td>
|
||||
<td style="text-align: center; vertical-align: middle;">
|
||||
<?php if($row->user_id == "") { ?>
|
||||
<a href="<?php echo site_url('station/claim_user')."/".$row->station_id; ?>" class="btn btn-outline-primary btn-sm"><i class="fas fa-user-plus"></i> <?php echo lang('station_location_claim_ownership'); ?></a>
|
||||
<?php } ?>
|
||||
<a href="<?php echo site_url('station/edit')."/".$row->station_id; ?>" title="<?php echo lang('admin_edit'); ?>" class="btn btn-outline-primary btn-sm"><i class="fas fa-edit"></i></a>
|
||||
</td>
|
||||
<td style="text-align: center; vertical-align: middle;">
|
||||
<a href="<?php echo site_url('station/copy')."/".$row->station_id; ?>" title="<?php echo lang('admin_copy'); ?>" class="btn btn-outline-primary btn-sm"><i class="fas fa-copy"></i></a>
|
||||
</td>
|
||||
<td style="text-align: center; vertical-align: middle;"> <a href="<?php echo site_url('station/deletelog')."/".$row->station_id; ?>" class="btn btn-danger btn-sm" title="<?php echo lang('station_location_emptylog'); ?>" onclick="return confirm('<?php echo lang('station_location_confirm_del_qso'); ?>');"><i class="fas fa-trash-alt"></i></a>
|
||||
</td>
|
||||
<td style="text-align: center; vertical-align: middle;">
|
||||
<?php if($row->station_active != 1) { ?>
|
||||
<a href="<?php echo site_url('station/delete')."/".$row->station_id; ?>" class="btn btn-danger btn-sm" title="<?php echo lang('admin_delete'); ?>" onclick="return confirm('<?php echo lang('station_location_confirm_del_stationlocation'); ?> <?php echo $row->station_profile_name; ?> <?php echo lang('station_location_confirm_del_stationlocation_qso'); ?>');"><i class="fas fa-trash-alt"></i></a>
|
||||
<?php } ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<!-- Information Card -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0"><i class="fas fa-info-circle me-2"></i>About Station Locations</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="d-flex align-items-start mb-3">
|
||||
<i class="fas fa-map-marker-alt text-primary me-2 mt-1"></i>
|
||||
<div>
|
||||
<h6 class="mb-1">Location Management</h6>
|
||||
<p class="mb-0 text-muted small"><?php echo lang('station_location_header_ln1'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="d-flex align-items-start mb-3">
|
||||
<i class="fas fa-radio text-primary me-2 mt-1"></i>
|
||||
<div>
|
||||
<h6 class="mb-1">Multiple Locations</h6>
|
||||
<p class="mb-0 text-muted small"><?php echo lang('station_location_header_ln2'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="d-flex align-items-start mb-3">
|
||||
<i class="fas fa-cog text-primary me-2 mt-1"></i>
|
||||
<div>
|
||||
<h6 class="mb-1">Active Location</h6>
|
||||
<p class="mb-0 text-muted small"><?php echo lang('station_location_header_ln3'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Warnings Section -->
|
||||
<?php if($current_active == 0 || (($is_there_qsos_with_no_station_id >= 1) && ($is_admin))) { ?>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<?php if($current_active == 0) { ?>
|
||||
<div class="alert alert-danger d-flex align-items-center" role="alert">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
<div><?php echo lang('station_location_warning'); ?></div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (($is_there_qsos_with_no_station_id >= 1) && ($is_admin)) { ?>
|
||||
<div class="alert alert-warning d-flex align-items-center" role="alert">
|
||||
<i class="fas fa-exclamation-triangle me-2"></i>
|
||||
<div>
|
||||
<strong><?php echo lang('general_word_warning'); ?>:</strong> <?php echo lang('station_location_warning_reassign'); ?><br>
|
||||
<div class="mt-2">
|
||||
<?php echo lang('station_location_reassign_at'); ?>
|
||||
<a href="<?php echo site_url('maintenance/'); ?>" class="btn btn-warning btn-sm">
|
||||
<i class="fas fa-sync me-1"></i><?php echo lang('account_word_admin') . "/" . lang('general_word_maintenance'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<!-- Station Locations Table -->
|
||||
<?php if ($stations->num_rows() > 0) { ?>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header bg-light">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0"><i class="fas fa-list me-2"></i>Station Locations (<?php echo $stations->num_rows(); ?>)</h5>
|
||||
<small class="text-muted">Click on a station name to set it as active</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-3">
|
||||
<div class="table-responsive">
|
||||
<table id="station_locations_table" class="table table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th scope="col"><i class="fas fa-tag me-1"></i><?php echo lang('station_location_name'); ?></th>
|
||||
<th scope="col"><i class="fas fa-microphone me-1"></i><?php echo lang('station_location_callsign'); ?></th>
|
||||
<th scope="col"><i class="fas fa-flag me-1"></i><?php echo lang('general_word_country'); ?></th>
|
||||
<th scope="col"><i class="fas fa-th me-1"></i><?php echo lang('gen_hamradio_gridsquare'); ?></th>
|
||||
<th scope="col" class="text-center"><i class="fas fa-power-off me-1"></i>Status</th>
|
||||
<th scope="col" class="text-center"><i class="fas fa-tools me-1"></i>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($stations->result() as $row) { ?>
|
||||
<tr <?php if($row->station_active == 1) { echo 'class="table-success"'; } ?>>
|
||||
<td class="align-middle">
|
||||
<div class="d-flex align-items-center">
|
||||
<div>
|
||||
<strong><?php echo $row->station_profile_name;?></strong>
|
||||
<?php if($row->station_active == 1) { ?>
|
||||
<span class="badge bg-success ms-2">
|
||||
<i class="fas fa-check me-1"></i><?php echo lang('station_location_active'); ?>
|
||||
</span>
|
||||
<?php } ?>
|
||||
<br>
|
||||
<small class="text-muted">
|
||||
ID: <?php echo $row->station_id;?> •
|
||||
<?php echo $row->qso_total;?> <?php echo lang('gen_hamradio_qso'); ?>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<strong><?php echo $row->station_callsign;?></strong>
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<?php echo $row->station_country == '' ? '<span class="text-muted">- NONE -</span>' : $row->station_country; ?>
|
||||
<?php if ($row->dxcc_end != NULL) { echo '<br><span class="badge bg-danger">'.lang('gen_hamradio_deleted_dxcc').'</span>'; } ?>
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<?php echo $row->station_gridsquare == '' ? '<span class="text-muted">-</span>' : $row->station_gridsquare;?>
|
||||
</td>
|
||||
<td class="align-middle text-center" data-order="<?php echo $row->station_id;?>">
|
||||
<?php if($row->station_active != 1) { ?>
|
||||
<a href="<?php echo site_url('station/set_active/').$current_active."/".$row->station_id; ?>"
|
||||
class="btn btn-outline-success btn-sm"
|
||||
onclick="return confirm('<?php echo lang('station_location_confirm_active'); ?> <?php echo $row->station_profile_name; ?>');",
|
||||
title="<?php echo lang('station_location_set_active'); ?>">
|
||||
<i class="fas fa-power-off me-1"></i><?php echo lang('station_location_set_active'); ?>
|
||||
</a>
|
||||
<?php } else { ?>
|
||||
<span class="badge bg-success">
|
||||
<i class="fas fa-check me-1"></i><?php echo lang('station_location_active'); ?>
|
||||
</span>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<td class="align-middle text-center">
|
||||
<div class="d-flex justify-content-center gap-1">
|
||||
<?php if($row->user_id == "") { ?>
|
||||
<a href="<?php echo site_url('station/claim_user')."/".$row->station_id; ?>"
|
||||
class="btn btn-info btn-sm"
|
||||
title="<?php echo lang('station_location_claim_ownership'); ?>">
|
||||
<i class="fas fa-user-plus"></i>
|
||||
</a>
|
||||
<?php } ?>
|
||||
<a href="<?php echo site_url('station/edit')."/".$row->station_id; ?>"
|
||||
title="<?php echo lang('admin_edit'); ?>"
|
||||
class="btn btn-primary btn-sm">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a href="<?php echo site_url('station/copy')."/".$row->station_id; ?>"
|
||||
title="<?php echo lang('admin_copy'); ?>"
|
||||
class="btn btn-secondary btn-sm">
|
||||
<i class="fas fa-copy"></i>
|
||||
</a>
|
||||
<a href="<?php echo site_url('station/deletelog')."/".$row->station_id; ?>"
|
||||
class="btn btn-warning btn-sm"
|
||||
title="<?php echo lang('station_location_emptylog'); ?>"
|
||||
onclick="return confirm('<?php echo lang('station_location_confirm_del_qso'); ?>');">
|
||||
<i class="fas fa-broom"></i>
|
||||
</a>
|
||||
<?php if($row->station_active != 1) { ?>
|
||||
<a href="<?php echo site_url('station/delete')."/".$row->station_id; ?>"
|
||||
class="btn btn-danger btn-sm"
|
||||
title="<?php echo lang('admin_delete'); ?>"
|
||||
onclick="return confirm('<?php echo lang('station_location_confirm_del_stationlocation'); ?> <?php echo $row->station_profile_name; ?> <?php echo lang('station_location_confirm_del_stationlocation_qso'); ?>');">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } else { ?>
|
||||
<!-- Empty State -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-body text-center py-5">
|
||||
<i class="fas fa-tower-broadcast text-muted mb-3" style="font-size: 3rem;"></i>
|
||||
<h5 class="text-muted mb-3">No Station Locations</h5>
|
||||
<p class="text-muted mb-4">You haven't created any station locations yet. Create your first station location to get started.</p>
|
||||
<a href="<?php echo site_url('station/create'); ?>" class="btn btn-primary btn-lg">
|
||||
<i class="fas fa-plus me-2"></i><?php echo lang('station_location_create'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ if ($qsoarray) {
|
|||
foreach ($value as $key => $val) {
|
||||
echo '<td>' . $val . '</td>';
|
||||
}
|
||||
echo '<th>' . $modetotal[$mode] . '</th>';
|
||||
echo '<th>' . (isset($modetotal[$mode]) ? $modetotal[$mode] : 0) . '</th>';
|
||||
echo '</tr>';
|
||||
}
|
||||
echo '</tbody><tfoot><tr><th>'.lang('statistics_total').'</th>';
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ if ($qsoarray) {
|
|||
foreach ($value as $key => $val) {
|
||||
echo '<td>' . $val . '</td>';
|
||||
}
|
||||
echo '<th>' . $modeunique[$mode] . '</th>';
|
||||
echo '<th>' . (isset($modeunique[$mode]) ? $modeunique[$mode] : 0) . '</th>';
|
||||
echo '</tr>';
|
||||
}
|
||||
echo '</tbody><tfoot><tr><th>'.lang('statistics_total').'</th>';
|
||||
|
||||
foreach($bands as $band) {
|
||||
echo '<th>' . $bandunique[$band] . '</th>';
|
||||
echo '<th>' . (isset($bandunique[$band]) ? $bandunique[$band] : 0) . '</th>';
|
||||
}
|
||||
echo '<th>' . $total->calls . '</th>';
|
||||
echo '</tr></tfoot></table>';
|
||||
|
|
|
|||
|
|
@ -25,9 +25,14 @@
|
|||
</div>
|
||||
<?php } else { ?>
|
||||
<h5>Check for DXCC Data Updates</h5>
|
||||
<input type="submit" class="btn btn-primary" id="btn_update_dxcc" value="Update DXCC Data" />
|
||||
<button type="button" class="btn btn-primary" id="btn_update_dxcc">
|
||||
<span class="spinner-border spinner-border-sm me-2" id="dxcc_spinner" role="status" aria-hidden="true" style="display: none;"></span>
|
||||
<span id="btn_text">Update DXCC Data</span>
|
||||
</button>
|
||||
|
||||
<div id="dxcc_update_status">Status:</br></div>
|
||||
<div id="dxcc_update_status" class="mt-3 p-2 border rounded bg-light" style="min-height: 60px;">
|
||||
<strong>Status:</strong> Ready to update
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
|
@ -45,11 +50,6 @@
|
|||
This function can be used to update QSO continent information for all QSOs in Cloudlog missing that information.
|
||||
</p>
|
||||
<p><a class="btn btn-primary" href="<?php echo site_url('update/check_missing_continent'); ?>">Check QSOs missing continent data</a></p>
|
||||
<style>
|
||||
#dxcc_update_status {
|
||||
display: None;
|
||||
}
|
||||
</style>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="distance" role="tabpanel" aria-labelledby="distance-tab">
|
||||
|
|
|
|||
|
|
@ -9,39 +9,7 @@
|
|||
|
||||
<script src="<?php echo base_url('assets/js/showdown.min.js'); ?>"></script>
|
||||
|
||||
<script>
|
||||
function convertMarkdownToHTML() {
|
||||
// Check if the required elements exist
|
||||
var markdownDiv = document.getElementById('markdownDiv');
|
||||
var formattedHTMLDiv = document.getElementById('formattedHTMLDiv');
|
||||
|
||||
if (!markdownDiv || !formattedHTMLDiv) {
|
||||
console.warn('Required elements for markdown conversion not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the Markdown content from the div
|
||||
var markdownContent = markdownDiv.innerText;
|
||||
|
||||
// Create a new Showdown Converter with simplifiedAutoLink option enabled
|
||||
var converter = new showdown.Converter({
|
||||
simplifiedAutoLink: true
|
||||
});
|
||||
|
||||
// Convert Markdown to HTML
|
||||
var html = converter.makeHtml(markdownContent);
|
||||
|
||||
// Set the HTML content of the div
|
||||
formattedHTMLDiv.innerHTML = html;
|
||||
}
|
||||
|
||||
// Only call the function if the DOM is ready and elements exist
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
convertMarkdownToHTML();
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
<?php
|
||||
$versionDialogMode = isset($this->optionslib) ? $this->optionslib->get_option('version_dialog') : 'release_notes';
|
||||
if ($versionDialogMode == 'custom_text' || $versionDialogMode == 'both') {
|
||||
?>
|
||||
|
|
@ -75,22 +43,72 @@
|
|||
$data = json_decode($response, true);
|
||||
|
||||
$current_version = $this->optionslib->get_option('version');
|
||||
|
||||
if ($data !== null && !empty($data)) {
|
||||
$firstRelease = null;
|
||||
foreach ($data as $singledata) {
|
||||
if ($singledata['tag_name'] == $current_version) {
|
||||
$firstRelease = $singledata;
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$releaseBody = isset($firstRelease['body']) ? $firstRelease['body'] : 'No release information available';
|
||||
$htmlReleaseBody = htmlspecialchars($releaseBody);
|
||||
$htmlReleaseBodyWithLinks = preg_replace('/(https?:\/\/[^\s<]+)/', '<a href="$1" target="_blank">$1</a>', $htmlReleaseBody);
|
||||
if ($firstRelease !== null) {
|
||||
$releaseBody = isset($firstRelease['body']) ? $firstRelease['body'] : 'No release information available';
|
||||
|
||||
$releaseName = isset($firstRelease['name']) ? $firstRelease['name'] : 'No version name information available';
|
||||
echo "<h4>v" . $releaseName . "</h4>";
|
||||
echo "<div id='markdownDiv' style='display: none;'>" . $releaseBody . "</div>";
|
||||
echo "<div id='formattedHTMLDiv'></div>";
|
||||
$releaseName = isset($firstRelease['name']) ? $firstRelease['name'] : 'No version name information available';
|
||||
echo "<h4>v" . $releaseName . "</h4>";
|
||||
|
||||
// Convert markdown to HTML using PHP
|
||||
$htmlContent = $releaseBody;
|
||||
|
||||
// Escape HTML first to prevent issues
|
||||
$htmlContent = htmlspecialchars($htmlContent);
|
||||
|
||||
// Convert headers
|
||||
$htmlContent = preg_replace('/^## (.+)$/m', '<h3>$1</h3>', $htmlContent);
|
||||
$htmlContent = preg_replace('/^# (.+)$/m', '<h2>$1</h2>', $htmlContent);
|
||||
|
||||
// Convert bullet points to list items
|
||||
// First, find all bullet point sections and convert them to proper lists
|
||||
$htmlContent = preg_replace_callback(
|
||||
'/(?:^[ ]*\* .+(?:\r?\n|$))+/m',
|
||||
function($matches) {
|
||||
$listContent = $matches[0];
|
||||
// Convert each bullet point to <li>, removing any trailing newlines
|
||||
$listContent = preg_replace('/^[ ]*\* (.+?)(?:\r?\n|$)/m', '<li>$1</li>', $listContent);
|
||||
// Wrap in <ul> tags
|
||||
return '<ul>' . trim($listContent) . '</ul>';
|
||||
},
|
||||
$htmlContent
|
||||
);
|
||||
|
||||
// Convert links (markdown style)
|
||||
$htmlContent = preg_replace('/\[([^\]]+)\]\(([^)]+)\)/', '<a href="$2" target="_blank">$1</a>', $htmlContent);
|
||||
|
||||
// Convert plain URLs to links
|
||||
$htmlContent = preg_replace('/(https?:\/\/[^\s<]+)/', '<a href="$1" target="_blank">$1</a>', $htmlContent);
|
||||
|
||||
// Convert GitHub usernames (@username) to profile links
|
||||
$htmlContent = preg_replace('/@([a-zA-Z0-9_-]+)/', '<a href="https://github.com/$1" target="_blank">@$1</a>', $htmlContent);
|
||||
|
||||
// Convert bold text
|
||||
$htmlContent = preg_replace('/\*\*([^*]+)\*\*/', '<strong>$1</strong>', $htmlContent);
|
||||
|
||||
// Convert line breaks to <br> tags
|
||||
$htmlContent = nl2br($htmlContent);
|
||||
|
||||
// Clean up: remove <br> tags that appear right before or after list tags
|
||||
$htmlContent = preg_replace('/<br\s*\/?>\s*(<\/?ul>)/', '$1', $htmlContent);
|
||||
$htmlContent = preg_replace('/(<\/?ul>)\s*<br\s*\/?>/', '$1', $htmlContent);
|
||||
$htmlContent = preg_replace('/<br\s*\/?>\s*(<\/?li>)/', '$1', $htmlContent);
|
||||
$htmlContent = preg_replace('/(<\/?li>)\s*<br\s*\/?>/', '$1', $htmlContent);
|
||||
|
||||
echo "<div class='release-notes mt-3'>" . $htmlContent . "</div>";
|
||||
} else {
|
||||
echo '<h4>v' . $current_version . '</h4>';
|
||||
echo '<p>No release information found for this version on GitHub.</p>';
|
||||
}
|
||||
} else {
|
||||
echo 'Error decoding JSON data or received empty response.';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,36 @@ foreach ($grouped as $month => $dxccs) {
|
|||
echo '<span class="badge bg-primary">Confirmed</span>';
|
||||
}
|
||||
|
||||
// Add satellite badge if worked via satellite
|
||||
if (isset($dxcc['workedViaSatellite']) && $dxcc['workedViaSatellite']) {
|
||||
echo ' <span class="badge bg-info">Worked via Satellite</span>';
|
||||
}
|
||||
|
||||
// IOTA handling: show badge if JSON contained an iota field
|
||||
if (isset($dxcc['iota']) && !empty($dxcc['iota'])) {
|
||||
$iotaTag = $dxcc['iota'];
|
||||
$mapUrl = 'https://www.iota-world.org/iotamaps/?grpref=' . $iotaTag;
|
||||
// Anchor inside badge should inherit readable text colour
|
||||
$iotaAnchor = '<a href="' . $mapUrl . '" target="_blank" class="text-white text-decoration-none">' . $iotaTag . '</a>';
|
||||
|
||||
if (isset($dxcc['iota_status'])) {
|
||||
$s = $dxcc['iota_status'];
|
||||
|
||||
if (!empty($s) && isset($s['worked']) && $s['worked']) {
|
||||
echo ' <span class="badge bg-success">IOTA ' . $iotaAnchor . ' Worked</span>';
|
||||
} else {
|
||||
echo ' <span class="badge bg-danger">IOTA ' . $iotaAnchor . ' Needed</span>';
|
||||
}
|
||||
|
||||
if (!empty($s) && isset($s['confirmed']) && $s['confirmed']) {
|
||||
echo ' <span class="badge bg-primary">Confirmed</span>';
|
||||
}
|
||||
} else {
|
||||
// No status; render a neutral badge containing the link
|
||||
echo ' <span class="badge bg-secondary">' . $iotaAnchor . '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
echo '</td>
|
||||
<td>' . $dxcc['notes'] . '</td>
|
||||
</tr>';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,16 @@
|
|||
<div class="container">
|
||||
|
||||
<h2><?php echo $page_title; ?></h2>
|
||||
<div id="dxcclist_display" hx-get="<?php echo site_url('workabledxcc/dxcclist'); ?>" hx-trigger="load"></div>
|
||||
<div id="dxcclist_display" hx-get="<?php echo site_url('workabledxcc/dxcclist'); ?>" hx-trigger="load">
|
||||
<!-- Loading spinner and message shown while HTMX loads content -->
|
||||
<div class="d-flex justify-content-center align-items-center py-5">
|
||||
<div class="text-center">
|
||||
<div class="spinner-border text-primary mb-3" role="status" style="width: 3rem; height: 3rem;">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
<h5 class="text-muted">Generating Table</h5>
|
||||
<p class="text-muted">Processing DXPedition data and checking your logbook...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -74,6 +74,200 @@ thead>tr>td {
|
|||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.adif .card {
|
||||
border: none;
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
.adif .card-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.adif .form-check {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.adif .form-check-input {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.adif .form-check-label {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.adif .badge {
|
||||
font-size: 0.7rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
.adif .btn {
|
||||
border-radius: 0.375rem;
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.adif .btn-lg {
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.adif .form-control,
|
||||
.adif .form-select {
|
||||
border-radius: 0.375rem;
|
||||
border-color: #ced4da;
|
||||
}
|
||||
|
||||
.adif .form-control:focus,
|
||||
.adif .form-select:focus {
|
||||
border-color: #86b7fe;
|
||||
box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
|
||||
}
|
||||
|
||||
.adif .card .card-header h6 {
|
||||
margin-bottom: 0;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.adif .alert {
|
||||
border-radius: 0.5rem;
|
||||
border: none;
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
/* Station Management Page Styling */
|
||||
.station-management {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.station-management .table-success {
|
||||
background-color: rgba(25, 135, 84, 0.1);
|
||||
}
|
||||
|
||||
.station-management .card {
|
||||
border: none;
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
.station-management .card-header {
|
||||
background-color: #f8f9fa;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
padding: 1rem 1.25rem;
|
||||
}
|
||||
|
||||
.station-management .badge {
|
||||
font-size: 0.7rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
.station-management .btn-sm {
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
/* DataTable styling within cards */
|
||||
.station-management .dataTables_wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.station-management .dataTables_length,
|
||||
.station-management .dataTables_filter {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.station-management .dataTables_info,
|
||||
.station-management .dataTables_paginate {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.station-management .dataTables_length label,
|
||||
.station-management .dataTables_filter label {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.station-management .dataTables_filter input {
|
||||
margin-left: 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid #ced4da;
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
.station-management .dataTables_length select {
|
||||
margin: 0 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid #ced4da;
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
/* Logbooks Management Page Styling */
|
||||
.logbooks-management {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.logbooks-management .table-success {
|
||||
background-color: rgba(25, 135, 84, 0.1);
|
||||
}
|
||||
|
||||
.logbooks-management .card {
|
||||
border: none;
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
.logbooks-management .card-header {
|
||||
background-color: #f8f9fa;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
padding: 1rem 1.25rem;
|
||||
}
|
||||
|
||||
.logbooks-management .badge {
|
||||
font-size: 0.7rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
|
||||
.logbooks-management .btn-sm {
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
/* DataTable styling within cards */
|
||||
.logbooks-management .dataTables_wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.logbooks-management .dataTables_length,
|
||||
.logbooks-management .dataTables_filter {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.logbooks-management .dataTables_info,
|
||||
.logbooks-management .dataTables_paginate {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.logbooks-management .dataTables_length label,
|
||||
.logbooks-management .dataTables_filter label {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.logbooks-management .dataTables_filter input {
|
||||
margin-left: 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid #ced4da;
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
.logbooks-management .dataTables_length select {
|
||||
margin: 0 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid #ced4da;
|
||||
padding: 0.375rem 0.75rem;
|
||||
}
|
||||
|
||||
.api .alert p {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
|
|
|||
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); }
|
||||
}
|
||||
|
|
@ -65,12 +65,25 @@ function initplot(_url_qso, options={}) {
|
|||
}
|
||||
|
||||
function askForPlots(_url_qso, options={}) {
|
||||
console.log('askForPlots called with URL:', _url_qso, 'options:', 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: 50000, // 50 second timeout
|
||||
error: function(xhr, status, error) {
|
||||
console.log('[ERROR] ajax askForPlots() function return error:', status, error, xhr);
|
||||
// Call custom error callback if provided
|
||||
if (typeof options.onError === 'function') {
|
||||
console.log('Calling custom error callback');
|
||||
options.onError();
|
||||
}
|
||||
},
|
||||
success: function(plotjson) {
|
||||
console.log('askForPlots AJAX success, plotjson:', plotjson);
|
||||
if ((typeof plotjson['markers'] !== "undefined")&&(plotjson['markers'].length>0)) {
|
||||
for (i=0;i<plotjson['markers'].length;i++) { createPlots(plotjson['markers'][i]); }
|
||||
}
|
||||
|
|
@ -80,6 +93,14 @@ 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') {
|
||||
console.log('Calling custom success callback');
|
||||
options.onSuccess(plotjson);
|
||||
} else {
|
||||
console.warn('No custom success callback provided');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -103,4 +124,9 @@ function removeMarkers() {
|
|||
map.removeLayer(plotlayers[i]);
|
||||
}
|
||||
plotlayers=[];
|
||||
|
||||
// Clear callsign labels if the function exists (for custom map page)
|
||||
if (typeof clearCallsignLabels === 'function') {
|
||||
clearCallsignLabels();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,48 @@
|
|||
$('.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];
|
||||
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() {
|
||||
var clickedaward = $(this).closest('th').attr("class");
|
||||
var status = $(this).is(":checked");
|
||||
var $masterCheckbox = $(this);
|
||||
var clickedaward = $masterCheckbox.closest('th').attr("class");
|
||||
var status = $masterCheckbox.is(":checked");
|
||||
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);
|
||||
});
|
||||
|
||||
|
|
@ -34,8 +66,136 @@ $('.bandtable').DataTable({
|
|||
"scrollCollapse": true,
|
||||
"paging": false,
|
||||
"scrollX": true,
|
||||
"searching": true,
|
||||
"language": {
|
||||
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();
|
||||
},
|
||||
"initComplete": function() {
|
||||
// Ensure statistics are updated when table is fully initialized
|
||||
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;
|
||||
|
||||
// Fallback: if the class-based selector doesn't work, try alternative selectors
|
||||
if (activeBands === 0) {
|
||||
// Try finding by column position (first column checkboxes)
|
||||
activeBands = $('.bandtable tbody tr td:first-child 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() {
|
||||
// Wait for table to be fully rendered before calculating stats
|
||||
setTimeout(function() {
|
||||
updateStatistics();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// 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 +361,7 @@ function deactivateAllBands() {
|
|||
});
|
||||
}
|
||||
|
||||
function saveBand(id) {
|
||||
function saveBand(id, callback) {
|
||||
$.ajax({
|
||||
url: base_url + 'index.php/band/saveBand',
|
||||
type: 'post',
|
||||
|
|
@ -220,6 +380,11 @@ function saveBand(id) {
|
|||
'vucc': $(".vucc_"+id+" input[type='checkbox']").is(":checked")
|
||||
},
|
||||
success: function (html) {
|
||||
if (callback) callback();
|
||||
},
|
||||
error: function() {
|
||||
// Show error state
|
||||
if (callback) callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
version: "3.8"
|
||||
|
||||
networks:
|
||||
mynet:
|
||||
|
||||
|
|
@ -12,9 +10,10 @@ services:
|
|||
- "80:80"
|
||||
volumes:
|
||||
- ./:/var/www/html:rw
|
||||
command: ["./script.sh"]
|
||||
command: ["/usr/local/bin/startup.sh"]
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- mynet
|
||||
restart: on-failure
|
||||
|
|
@ -30,11 +29,11 @@ services:
|
|||
networks:
|
||||
- mynet
|
||||
healthcheck:
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
timeout: 30s
|
||||
retries: 20
|
||||
interval: 15s
|
||||
start_period: 60s
|
||||
test: ["CMD", "/usr/local/bin/healthcheck.sh"]
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
interval: 10s
|
||||
start_period: 40s
|
||||
|
||||
volumes:
|
||||
db_data: {}
|
||||
|
|
|
|||
7
package-lock.json
自动生成的
7
package-lock.json
自动生成的
|
|
@ -1923,10 +1923,11 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tmp": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
|
||||
"integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz",
|
||||
"integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.14"
|
||||
}
|
||||
|
|
|
|||
57
script.sh
57
script.sh
|
|
@ -60,32 +60,51 @@ if [ ! -d "$DEST_DIR" ]; then
|
|||
mkdir -p $DEST_DIR
|
||||
fi
|
||||
|
||||
# Check if configuration has already been processed
|
||||
if [ -f "$DEST_DIR/database.php" ] && [ -f "$DEST_DIR/config.php" ]; then
|
||||
echo "Configuration files already exist, skipping processing..."
|
||||
else
|
||||
echo "Processing configuration files..."
|
||||
|
||||
# Use sed with a different delimiter (`|`) to avoid conflicts with special characters
|
||||
# Strip any trailing whitespace/newlines from variables before using them
|
||||
CLEAN_DATABASE=$(echo "${MYSQL_DATABASE}" | tr -d '\r\n')
|
||||
CLEAN_USER=$(echo "${MYSQL_USER}" | tr -d '\r\n')
|
||||
CLEAN_PASSWORD=$(echo "${MYSQL_PASSWORD}" | tr -d '\r\n')
|
||||
CLEAN_HOST=$(echo "${MYSQL_HOST}" | tr -d '\r\n')
|
||||
CLEAN_LOCATOR=$(echo "${BASE_LOCATOR}" | tr -d '\r\n')
|
||||
CLEAN_URL=$(echo "${WEBSITE_URL}" | tr -d '\r\n')
|
||||
CLEAN_DIRECTORY=$(echo "${DIRECTORY}" | tr -d '\r\n')
|
||||
|
||||
sed -i "s|%DATABASE%|${CLEAN_DATABASE}|g" $DATABASE_FILE
|
||||
sed -i "s|%USERNAME%|${CLEAN_USER}|g" $DATABASE_FILE
|
||||
sed -i "s|%PASSWORD%|${CLEAN_PASSWORD}|g" $DATABASE_FILE
|
||||
sed -i "s|%HOSTNAME%|${CLEAN_HOST}|g" $DATABASE_FILE
|
||||
sed -i "s|%baselocator%|${CLEAN_LOCATOR}|g" $CONFIG_FILE
|
||||
sed -i "s|%websiteurl%|${CLEAN_URL}|g" $CONFIG_FILE
|
||||
sed -i "s|%directory%|${CLEAN_DIRECTORY}|g" $CONFIG_FILE
|
||||
|
||||
# Use sed with a different delimiter (`|`) to avoid conflicts with special characters
|
||||
sed -i "s|%DATABASE%|${MYSQL_DATABASE}|g" $DATABASE_FILE
|
||||
sed -i "s|%USERNAME%|${MYSQL_USER}|g" $DATABASE_FILE
|
||||
sed -i "s|%PASSWORD%|${MYSQL_PASSWORD}|g" $DATABASE_FILE
|
||||
sed -i "s|%HOSTNAME%|${MYSQL_HOST}|g" $DATABASE_FILE
|
||||
sed -i "s|%baselocator%|${BASE_LOCATOR}|g" $CONFIG_FILE
|
||||
sed -i "s|%websiteurl%|${WEBSITE_URL}|g" $CONFIG_FILE
|
||||
sed -i "s|%directory%|${DIRECTORY}|g" $CONFIG_FILE
|
||||
# Move the files to the destination directory
|
||||
mv $CONFIG_FILE $DEST_DIR
|
||||
mv $DATABASE_FILE $DEST_DIR
|
||||
|
||||
# Move the files to the destination directory
|
||||
mv $CONFIG_FILE $DEST_DIR
|
||||
mv $DATABASE_FILE $DEST_DIR
|
||||
|
||||
# Delete the /install directory
|
||||
rm -rf /install
|
||||
# Delete the /install directory
|
||||
rm -rf /install
|
||||
fi
|
||||
|
||||
echo "Replacement complete."
|
||||
|
||||
# Wait for database to be ready
|
||||
echo "Waiting for database to be ready..."
|
||||
until mysql -h"$MYSQL_HOST" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -e "SELECT 1" > /dev/null 2>&1; do
|
||||
echo "Database is not ready yet. Waiting 5 seconds..."
|
||||
sleep 5
|
||||
done
|
||||
echo "Database is ready!"
|
||||
echo "Connecting to: Host=$MYSQL_HOST, User=$MYSQL_USER, Database=$MYSQL_DATABASE"
|
||||
|
||||
# Give the database a moment, then test connection once
|
||||
sleep 2
|
||||
if mariadb -h"$MYSQL_HOST" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -D"$MYSQL_DATABASE" -e "SELECT 1;" >/dev/null 2>&1; then
|
||||
echo "Database is ready!"
|
||||
else
|
||||
echo "Database connection failed, but continuing anyway since healthcheck passed..."
|
||||
fi
|
||||
|
||||
# Set Permissions
|
||||
chown -R root:www-data /var/www/html/application/config/
|
||||
|
|
|
|||
正在加载…
在新工单中引用