diff --git a/application/controllers/Notes.php b/application/controllers/Notes.php index 991b2e591..8e8e5f68b 100644 --- a/application/controllers/Notes.php +++ b/application/controllers/Notes.php @@ -2,7 +2,6 @@ // Notes controller: handles all note actions, with security and input validation class Notes extends CI_Controller { - // API endpoint: check for duplicate note title in category for user // Ensure only authorized users can access Notes controller function __construct() { parent::__construct(); @@ -41,19 +40,34 @@ class Notes extends CI_Controller { function add() { $this->load->model('note'); $this->load->library('form_validation'); - $this->load->library('callbook'); // Used for callsign parsing + $this->load->library('callbook'); // Used for callsign parsing + + // Support prefilled title/category from query string + $prefill_title = $this->input->get('title', TRUE); + $prefill_category = $this->input->get('category', TRUE); $suggested_title = null; // Validate form fields $this->form_validation->set_rules('title', 'Note Title', 'required|callback_contacts_title_unique'); // Custom callback for Contacts category $this->form_validation->set_rules('content', 'Content', 'required'); if ($this->form_validation->run() == FALSE) { + // Use POST if available, otherwise use prefill from query string $category = $this->input->post('category', TRUE); - if ($category === 'Contacts') { - - $suggested_title = strtoupper($this->callbook->get_plaincall($this->input->post('title', TRUE))); + if (empty($category) && !empty($prefill_category)) { + $category = $prefill_category; } + if ($category === 'Contacts') { + $title_input = $this->input->post('title', TRUE); + if (empty($title_input) && !empty($prefill_title)) { + $title_input = $prefill_title; + } + $suggested_title = strtoupper($this->callbook->get_plaincall($title_input)); + } + // Pass prefill values to view $data['suggested_title'] = $suggested_title; + $data['prefill_title'] = $prefill_title; + $data['prefill_category'] = $prefill_category; + $data['category'] = $category; $data['page_title'] = __("Add Notes"); $this->load->view('interface_assets/header', $data); $this->load->view('notes/add'); @@ -241,26 +255,36 @@ class Notes extends CI_Controller { $id = $this->input->get('id', TRUE); // Optional, for edit $check_title = $title; if ($category === 'Contacts') { - $check_title = strtoupper($title); + $this->load->library('callbook'); + $check_title = strtoupper($this->callbook->get_plaincall($title)); } $where = [ - 'category' => $category, + 'cat' => $category, 'user_id' => $user_id, 'title' => $check_title ]; $query = $this->db->get_where('notes', $where); - $duplicate = false; + $exists = false; + $note_id = null; if ($id) { foreach ($query->result() as $note) { if ($note->id != $id) { - $duplicate = true; + $exists = true; + $note_id = $note->id; break; } } } else { - $duplicate = $query->num_rows() > 0; + if ($query->num_rows() > 0) { + $exists = true; + $note_id = $query->row()->id; + } } - $this->output->set_content_type('application/json')->set_output(json_encode(['duplicate' => $duplicate])); + $response = ['exists' => $exists]; + if ($exists && $note_id) { + $response['id'] = $note_id; + } + $this->output->set_content_type('application/json')->set_output(json_encode($response)); } // Form validation callback for add: unique Contacts note title for user, only core callsign diff --git a/application/views/notes/add.php b/application/views/notes/add.php index 177ef3d50..1ad0172b1 100644 --- a/application/views/notes/add.php +++ b/application/views/notes/add.php @@ -29,7 +29,18 @@
- +
diff --git a/application/views/qso/index.php b/application/views/qso/index.php index 93586d393..1aa182d38 100644 --- a/application/views/qso/index.php +++ b/application/views/qso/index.php @@ -154,7 +154,7 @@ switch ($date_format) {
-  " class="fas fa-search"> +  " class="fas fa-search"> " class="fas fa-sticky-note text-secondary">
diff --git a/assets/js/sections/qso.js b/assets/js/sections/qso.js index c363a4e92..71508290c 100644 --- a/assets/js/sections/qso.js +++ b/assets/js/sections/qso.js @@ -73,6 +73,15 @@ function getUTCDateStamp(el) { $(el).attr('value', formatted_date); } +// Note icon state logic +function setNoteIconState(enabled) { + var $icon = $('#note_create_edit'); + if (enabled) { + $icon.removeClass('text-secondary'); + } else { + $icon.addClass('text-secondary'); + } +} $('#stationProfile').on('change', function () { var stationProfile = $('#stationProfile').val(); @@ -399,7 +408,7 @@ function parseUserDate(user_provided_date) { // creates JS-Date out of user-prov month = parseInt(parts[1], 10) - 1; year = parseInt(parts[2], 10); } - if (isNaN(day) || day < 1 || day > 31 || isNaN(month) || month < 0 || month > 11 || isNaN(year)) return null; + if (isNaN(day) || day < 1 || day > 31 || isNaN(month) || month < 0 || month > 11 || isNaN(year)) return null; return new Date(year, month, day); } @@ -891,6 +900,7 @@ function reset_fields() { clearTimeout(); set_timers(); resetTimers(qso_manual); + setNoteIconState(false); // Always gray out note icon on reset } $("#callsign").on("focusout", function () { @@ -921,6 +931,25 @@ $("#callsign").on("focusout", function () { find_callsign = find_callsign.replaceAll('Ø', '0'); const url = `${base_url}index.php/logbook/json/${find_callsign}/${json_band}/${json_mode}/${stationProfile}/${startDate}/${last_qsos_count}`; + // Check note existence for this callsign + $.get( + window.base_url + 'index.php/notes/check_duplicate', + { + category: 'Contacts', + title: callsign + }, + function(data) { + if (typeof data === 'string') { + try { data = JSON.parse(data); } catch (e) { data = {}; } + } + if (data && data.exists === true) { + setNoteIconState(true); + } else { + setNoteIconState(false); + } + } + ); + // Replace / in a callsign with - to stop urls breaking lookupCall = $.getJSON(url, async function (result) { @@ -2173,6 +2202,8 @@ $(document).ready(function () { set_timers(); updateStateDropdown('#dxcc_id', '#stateInputLabel', '#location_us_county', '#stationCntyInputQso'); + setNoteIconState(false); /// Grey-out note icon + // Clear the localStorage for the qrg units, except the quicklogCallsign and a possible backlog clearQrgUnits(); set_qrg(); @@ -2400,6 +2431,39 @@ $(document).ready(function () { }); } + // Note create/edit icon click handler + $('#note_create_edit').on('click', function() { + var callsign = $('#callsign').val().trim(); + if (!callsign) { + alert('Please enter a callsign first.'); + return; + } + // AJAX to check if note exists for this callsign in Contacts category + $.get( + window.base_url + 'index.php/notes/check_duplicate', + { + category: 'Contacts', + title: callsign + }, + function(data) { + // Defensive: try to parse if string + if (typeof data === 'string') { + try { data = JSON.parse(data); } catch (e) { data = {}; } + } + if (data && data.exists === true && data.id) { + window.open(window.base_url + 'index.php/notes/edit/' + data.id, '_blank'); + } else if (data && data.exists === false) { + // Open add with prefilled title and Contacts category + var url = window.base_url + 'index.php/notes/add?title=' + encodeURIComponent(callsign) + '&category=Contacts'; + window.open(url, '_blank'); + } else { + // Unexpected response, show error + alert('Could not check note existence. Please try again.'); + } + } + ); + }); + // everything loaded and ready 2 go bc.postMessage('ready'); });