mirror of
https://github.com/magicbug/Cloudlog
synced 2026-08-13 17:49:35 -04:00
Add scan preview/apply for call history
Adds a workflow to scan uploaded call history files for matching QSOs and apply SIG/SIG_INFO backfills. Controller Callhistory: loads logbooks, adds scan_preview and scan_apply actions, CSV parsing helpers, and safety checks to ensure files are readable and station ownership matches. Model Callhistory_model: adds get_station_ids_for_logbook, get_qsos_for_callsigns, and apply_sig_backfill to fetch candidate QSOs within a user's station/logbook scope and apply updates in a DB transaction. View callhistory/index.php: introduces a preview UI with selection controls, logbook scope selector, and client-side JS to manage selections and submission. Changes include normalization/heuristics for extracting callsigns/exchanges and only propose updates where existing SIG fields are blank.
This commit is contained in:
parent
a049d33b3a
commit
ed0578cd18
3 changed files with 480 additions and 0 deletions
|
|
@ -21,8 +21,10 @@ class Callhistory extends CI_Controller {
|
|||
{
|
||||
$user_id = (int)$this->session->userdata('user_id');
|
||||
|
||||
$this->load->model('logbooks_model');
|
||||
$data['page_title'] = 'Call History';
|
||||
$data['files'] = $this->callhistory_model->get_all_for_user($user_id);
|
||||
$data['logbooks'] = $this->logbooks_model->show_all()->result();
|
||||
|
||||
$this->load->view('interface_assets/header', $data);
|
||||
$this->load->view('callhistory/index', $data);
|
||||
|
|
@ -167,6 +169,147 @@ class Callhistory extends CI_Controller {
|
|||
redirect('callhistory');
|
||||
}
|
||||
|
||||
public function scan_preview()
|
||||
{
|
||||
if (strtolower($this->input->method()) !== 'post') {
|
||||
redirect('callhistory');
|
||||
return;
|
||||
}
|
||||
|
||||
$user_id = (int)$this->session->userdata('user_id');
|
||||
$file_id = (int)$this->input->post('file_id', TRUE);
|
||||
$logbook_id = $this->input->post('logbook_id', TRUE);
|
||||
$logbook_id = ($logbook_id !== '' && $logbook_id !== FALSE) ? (int)$logbook_id : NULL;
|
||||
|
||||
$file = $this->callhistory_model->get_for_user_by_id($user_id, $file_id);
|
||||
if (!$file) {
|
||||
$this->session->set_flashdata('notice', 'Call history file not found.');
|
||||
redirect('callhistory');
|
||||
return;
|
||||
}
|
||||
|
||||
$path = FCPATH . 'uploads/callhistory/' . $user_id . '/' . $file->stored_filename;
|
||||
if (!is_file($path) || !is_readable($path)) {
|
||||
$this->session->set_flashdata('notice', 'Uploaded file is not readable.');
|
||||
redirect('callhistory');
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse every callsign in the file
|
||||
$all_callsigns = $this->get_all_callsigns_in_file($path);
|
||||
if (empty($all_callsigns)) {
|
||||
$this->session->set_flashdata('notice', 'No callsigns found in the selected file.');
|
||||
redirect('callhistory');
|
||||
return;
|
||||
}
|
||||
|
||||
// Look up matching QSOs for all those callsigns
|
||||
$qsos = $this->callhistory_model->get_qsos_for_callsigns($user_id, array_keys($all_callsigns), $logbook_id);
|
||||
|
||||
$preview = array();
|
||||
foreach ($qsos as $qso) {
|
||||
$normalized_call = $this->normalize_callsign($qso->COL_CALL);
|
||||
if (!isset($all_callsigns[$normalized_call])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$call_data = $all_callsigns[$normalized_call];
|
||||
$proposed_sig = $file->organization_label;
|
||||
$proposed_sig_info = $call_data['exch1'];
|
||||
|
||||
$current_sig = trim((string)($qso->COL_SIG ?? ''));
|
||||
$current_sig_info = trim((string)($qso->COL_SIG_INFO ?? ''));
|
||||
|
||||
// Only propose changes where both SIG fields are currently blank
|
||||
if ($current_sig !== '' || $current_sig_info !== '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($proposed_sig === '' && $proposed_sig_info === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$preview[] = array(
|
||||
'qso_id' => (int)$qso->COL_PRIMARY_KEY,
|
||||
'station_id' => (int)$qso->station_id,
|
||||
'callsign' => $qso->COL_CALL,
|
||||
'time_on' => $qso->COL_TIME_ON,
|
||||
'band' => $qso->COL_BAND,
|
||||
'mode' => $qso->COL_SUBMODE !== '' ? $qso->COL_SUBMODE : $qso->COL_MODE,
|
||||
'station_location' => $qso->station_profile_name . ' (' . $qso->station_callsign . ')',
|
||||
'current_sig' => $current_sig,
|
||||
'current_sig_info' => $current_sig_info,
|
||||
'new_sig' => $proposed_sig,
|
||||
'new_sig_info' => $proposed_sig_info,
|
||||
);
|
||||
}
|
||||
|
||||
$this->load->model('logbooks_model');
|
||||
$data['page_title'] = 'Call History - Scan Preview';
|
||||
$data['files'] = $this->callhistory_model->get_all_for_user($user_id);
|
||||
$data['preview'] = $preview;
|
||||
$data['scan_file'] = $file;
|
||||
$data['logbooks'] = $this->logbooks_model->show_all()->result();
|
||||
$data['selected_logbook_id'] = $logbook_id;
|
||||
|
||||
$this->load->view('interface_assets/header', $data);
|
||||
$this->load->view('callhistory/index', $data);
|
||||
$this->load->view('interface_assets/footer');
|
||||
}
|
||||
|
||||
public function scan_apply()
|
||||
{
|
||||
if (strtolower($this->input->method()) !== 'post') {
|
||||
redirect('callhistory');
|
||||
return;
|
||||
}
|
||||
|
||||
$user_id = (int)$this->session->userdata('user_id');
|
||||
$raw_changes = $this->input->post('changes', TRUE);
|
||||
|
||||
if (empty($raw_changes) || !is_array($raw_changes)) {
|
||||
$this->session->set_flashdata('notice', 'No changes submitted.');
|
||||
redirect('callhistory');
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate that every station_id belongs to this user before applying
|
||||
$station_ids = $this->callhistory_model->get_station_ids_for_logbook($user_id);
|
||||
$station_ids_set = array_flip($station_ids);
|
||||
|
||||
$safe_changes = array();
|
||||
foreach ($raw_changes as $change) {
|
||||
$qso_id = isset($change['qso_id']) ? (int)$change['qso_id'] : 0;
|
||||
$station_id = isset($change['station_id']) ? (int)$change['station_id'] : 0;
|
||||
|
||||
if ($qso_id <= 0 || $station_id <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($station_ids_set[$station_id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$safe_changes[] = array(
|
||||
'qso_id' => $qso_id,
|
||||
'station_id' => $station_id,
|
||||
'new_sig' => $this->security->xss_clean((string)($change['new_sig'] ?? '')),
|
||||
'new_sig_info' => $this->security->xss_clean((string)($change['new_sig_info'] ?? '')),
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($safe_changes)) {
|
||||
$this->session->set_flashdata('notice', 'No valid changes to apply.');
|
||||
redirect('callhistory');
|
||||
return;
|
||||
}
|
||||
|
||||
$applied = $this->callhistory_model->apply_sig_backfill($safe_changes);
|
||||
|
||||
$this->session->set_flashdata('notice', $applied . ' QSO(s) updated with SIG data.');
|
||||
redirect('callhistory');
|
||||
}
|
||||
|
||||
public function lookup()
|
||||
{
|
||||
if (strtolower($this->input->method()) !== 'post') {
|
||||
|
|
@ -213,6 +356,75 @@ class Callhistory extends CI_Controller {
|
|||
echo json_encode($response);
|
||||
}
|
||||
|
||||
private function get_all_callsigns_in_file($file_path)
|
||||
{
|
||||
$callsigns = array();
|
||||
$header_map = null;
|
||||
|
||||
if (($handle = fopen($file_path, 'r')) === FALSE) {
|
||||
return $callsigns;
|
||||
}
|
||||
|
||||
while (($row = fgetcsv($handle, 0, ',')) !== FALSE) {
|
||||
if (empty($row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$row = array_map('trim', $row);
|
||||
if (count($row) === 1 && $row[0] === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->is_comment_row($row)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->looks_like_header($row)) {
|
||||
$header_map = $this->build_header_map($row);
|
||||
continue;
|
||||
}
|
||||
|
||||
$row_callsign = $this->extract_by_map_or_index($row, $header_map, array('call', 'callsign', 'callsigns'), 0);
|
||||
$row_callsign = $this->normalize_callsign($row_callsign);
|
||||
|
||||
if ($row_callsign === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Strip portable/alternative suffixes to get base call for matching
|
||||
$base_callsign = $row_callsign;
|
||||
if (strpos($base_callsign, '/') !== FALSE) {
|
||||
$parts = explode('/', $base_callsign);
|
||||
// Use the longest segment as the base callsign
|
||||
usort($parts, function($a, $b) { return strlen($b) - strlen($a); });
|
||||
$base_callsign = $parts[0];
|
||||
}
|
||||
|
||||
$name = $this->extract_name($row, $header_map);
|
||||
$exch1 = $this->extract_by_map_or_index($row, $header_map, array('exch1', 'exchange1', 'exchange', 'member', 'membership'), 8);
|
||||
|
||||
if ($exch1 === '' && is_null($header_map)) {
|
||||
$exch1 = $this->guess_exch1_without_header($row);
|
||||
}
|
||||
|
||||
$callsigns[$base_callsign] = array(
|
||||
'name' => $name,
|
||||
'exch1' => $exch1,
|
||||
);
|
||||
|
||||
// Also index by original callsign if it differs (e.g. contains /)
|
||||
if ($row_callsign !== $base_callsign) {
|
||||
$callsigns[$row_callsign] = array(
|
||||
'name' => $name,
|
||||
'exch1' => $exch1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
return $callsigns;
|
||||
}
|
||||
|
||||
private function find_matches_in_file($file_path, $callsign, $file)
|
||||
{
|
||||
$matches = array();
|
||||
|
|
|
|||
|
|
@ -57,4 +57,115 @@ class Callhistory_model extends CI_Model {
|
|||
$this->db->where('id', (int)$id);
|
||||
return $this->db->delete('callhistory_files');
|
||||
}
|
||||
|
||||
public function get_station_ids_for_logbook($user_id, $logbook_id = NULL)
|
||||
{
|
||||
if ($logbook_id === NULL || $logbook_id === '' || $logbook_id === 0 || $logbook_id === '0') {
|
||||
$this->db->select('station_id');
|
||||
$this->db->from('station_profile');
|
||||
$this->db->where('user_id', (int)$user_id);
|
||||
|
||||
$query = $this->db->get();
|
||||
$station_ids = array();
|
||||
foreach ($query->result() as $row) {
|
||||
$station_ids[] = (int)$row->station_id;
|
||||
}
|
||||
|
||||
return $station_ids;
|
||||
}
|
||||
|
||||
$this->db->select('station_location_id');
|
||||
$this->db->from('station_logbooks_relationship');
|
||||
$this->db->where('station_logbook_id', (int)$logbook_id);
|
||||
$query = $this->db->get();
|
||||
|
||||
$candidate_ids = array();
|
||||
foreach ($query->result() as $row) {
|
||||
$candidate_ids[] = (int)$row->station_location_id;
|
||||
}
|
||||
|
||||
if (empty($candidate_ids)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$this->db->select('station_id');
|
||||
$this->db->from('station_profile');
|
||||
$this->db->where('user_id', (int)$user_id);
|
||||
$this->db->where_in('station_id', $candidate_ids);
|
||||
$verified_query = $this->db->get();
|
||||
|
||||
$verified_ids = array();
|
||||
foreach ($verified_query->result() as $row) {
|
||||
$verified_ids[] = (int)$row->station_id;
|
||||
}
|
||||
|
||||
return $verified_ids;
|
||||
}
|
||||
|
||||
public function get_qsos_for_callsigns($user_id, $callsigns, $logbook_id = NULL)
|
||||
{
|
||||
$callsigns = array_values(array_unique(array_filter(array_map('strval', (array)$callsigns))));
|
||||
if (empty($callsigns)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$station_ids = $this->get_station_ids_for_logbook($user_id, $logbook_id);
|
||||
if (empty($station_ids)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$table = $this->config->item('table_name');
|
||||
$results = array();
|
||||
|
||||
foreach (array_chunk($callsigns, 500) as $callsign_chunk) {
|
||||
$escaped_callsigns = array();
|
||||
foreach ($callsign_chunk as $callsign) {
|
||||
$escaped_callsigns[] = $this->db->escape(strtoupper($callsign));
|
||||
}
|
||||
|
||||
$this->db->select($table . '.COL_PRIMARY_KEY, ' . $table . '.COL_CALL, ' . $table . '.COL_TIME_ON, ' . $table . '.COL_BAND, ' . $table . '.COL_MODE, ' . $table . '.COL_SUBMODE, ' . $table . '.COL_SIG, ' . $table . '.COL_SIG_INFO, ' . $table . '.station_id, station_profile.station_profile_name, station_profile.station_callsign');
|
||||
$this->db->from($table);
|
||||
$this->db->join('station_profile', 'station_profile.station_id = ' . $table . '.station_id');
|
||||
$this->db->where('station_profile.user_id', (int)$user_id);
|
||||
$this->db->where_in($table . '.station_id', $station_ids);
|
||||
$this->db->where('UPPER(REPLACE(' . $table . '.COL_CALL, "Ø", "0")) IN (' . implode(',', $escaped_callsigns) . ')', NULL, FALSE);
|
||||
$this->db->order_by($table . '.COL_TIME_ON', 'DESC');
|
||||
|
||||
$query = $this->db->get();
|
||||
$results = array_merge($results, $query->result());
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
public function apply_sig_backfill($changes)
|
||||
{
|
||||
if (empty($changes)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$table = $this->config->item('table_name');
|
||||
$applied = 0;
|
||||
|
||||
$this->db->trans_start();
|
||||
foreach ($changes as $change) {
|
||||
$this->db->where('COL_PRIMARY_KEY', (int)$change['qso_id']);
|
||||
$this->db->where('station_id', (int)$change['station_id']);
|
||||
$this->db->update($table, array(
|
||||
'COL_SIG' => $change['new_sig'],
|
||||
'COL_SIG_INFO' => $change['new_sig_info'],
|
||||
));
|
||||
|
||||
if ($this->db->affected_rows() >= 0) {
|
||||
$applied++;
|
||||
}
|
||||
}
|
||||
$this->db->trans_complete();
|
||||
|
||||
if (!$this->db->trans_status()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $applied;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,100 @@
|
|||
<h2>Call History</h2>
|
||||
<p class="text-muted">Upload N1MM call history files (.txt/.csv), assign an optional organization label (e.g., FOC), and set file priority for matching.</p>
|
||||
|
||||
<?php if (!empty($preview)) { ?>
|
||||
<div class="card mb-3 border-warning">
|
||||
<div class="card-header bg-warning text-dark">
|
||||
<strong><i class="fas fa-search"></i> Scan Preview — <?php echo htmlspecialchars($scan_file->original_filename); ?></strong>
|
||||
<span class="badge bg-dark ms-2"><?php echo count($preview); ?> QSO(s) with blank SIG fields found</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted small mb-2">Only QSOs with <strong>no existing SIG data</strong> are shown. Review the proposed values below, then click <strong>Apply Selected</strong> to write them.</p>
|
||||
<form method="post" action="<?php echo site_url('callhistory/scan_apply'); ?>" id="apply-form">
|
||||
<div class="mb-2 d-flex gap-2">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" id="select-all-btn"><i class="fas fa-check-double"></i> Select All</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" id="deselect-all-btn"><i class="fas fa-times"></i> Deselect All</button>
|
||||
<button type="submit" class="btn btn-sm btn-success" id="apply-btn"><i class="fas fa-save"></i> Apply Selected (<span id="selected-count"><?php echo count($preview); ?></span>)</button>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table id="callhistory-preview-table" class="table table-sm table-striped table-hover w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:30px;"><input type="checkbox" id="check-all" checked></th>
|
||||
<th>Callsign</th>
|
||||
<th>Date/Time</th>
|
||||
<th>Band</th>
|
||||
<th>Mode</th>
|
||||
<th>Station Location</th>
|
||||
<th>SIG →</th>
|
||||
<th>SIG Info →</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($preview as $i => $row) { ?>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="checkbox" class="row-check" name="changes[<?php echo $i; ?>][qso_id]"
|
||||
value="<?php echo (int)$row['qso_id']; ?>" checked>
|
||||
<input type="hidden" name="changes[<?php echo $i; ?>][station_id]" value="<?php echo (int)$row['station_id']; ?>">
|
||||
<input type="hidden" name="changes[<?php echo $i; ?>][new_sig]" value="<?php echo htmlspecialchars($row['new_sig']); ?>">
|
||||
<input type="hidden" name="changes[<?php echo $i; ?>][new_sig_info]" value="<?php echo htmlspecialchars($row['new_sig_info']); ?>">
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($row['callsign']); ?></td>
|
||||
<td><?php echo htmlspecialchars($row['time_on']); ?></td>
|
||||
<td><?php echo htmlspecialchars($row['band']); ?></td>
|
||||
<td><?php echo htmlspecialchars($row['mode']); ?></td>
|
||||
<td><?php echo htmlspecialchars($row['station_location']); ?></td>
|
||||
<td><span class="badge bg-primary"><?php echo htmlspecialchars($row['new_sig']); ?></span></td>
|
||||
<td><span class="badge bg-secondary"><?php echo htmlspecialchars($row['new_sig_info']); ?></span></td>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php } elseif (isset($scan_file)) { ?>
|
||||
<div class="alert alert-info"><i class="fas fa-info-circle"></i> No blank-SIG matches found for <strong><?php echo htmlspecialchars($scan_file->original_filename); ?></strong> in the selected logbook scope.</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (!empty($files)) { ?>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<strong><i class="fas fa-search-plus"></i> Scan Logbook</strong>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="<?php echo site_url('callhistory/scan_preview'); ?>" class="row g-3 align-items-end">
|
||||
<div class="col-md-4">
|
||||
<label for="scan_file_id" class="form-label">Call History File</label>
|
||||
<select class="form-select" id="scan_file_id" name="file_id" required>
|
||||
<?php foreach ($files as $f) { ?>
|
||||
<option value="<?php echo (int)$f->id; ?>" <?php echo (isset($scan_file) && (int)$scan_file->id === (int)$f->id) ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($f->file_label ?: $f->original_filename); ?>
|
||||
<?php if (!empty($f->organization_label)) { echo '(' . htmlspecialchars($f->organization_label) . ')'; } ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="scan_logbook_id" class="form-label">Station Location Scope</label>
|
||||
<select class="form-select" id="scan_logbook_id" name="logbook_id">
|
||||
<option value="">All my station locations</option>
|
||||
<?php if (!empty($logbooks)) { foreach ($logbooks as $lb) { ?>
|
||||
<option value="<?php echo (int)$lb->logbook_id; ?>" <?php echo (isset($selected_logbook_id) && (int)$selected_logbook_id === (int)$lb->logbook_id) ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($lb->logbook_name); ?>
|
||||
</option>
|
||||
<?php } } ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<button type="submit" class="btn btn-warning"><i class="fas fa-search"></i> Preview Changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<strong>Upload Call History File</strong>
|
||||
|
|
@ -99,3 +193,66 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($preview)) { ?>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
var table = $('#callhistory-preview-table').DataTable({
|
||||
"pageLength": 25,
|
||||
"order": [[2, "asc"]],
|
||||
"columnDefs": [
|
||||
{ "orderable": false, "targets": 0 },
|
||||
{ "orderable": false, "targets": 6 },
|
||||
{ "orderable": false, "targets": 7 }
|
||||
],
|
||||
"language": {
|
||||
url: getDataTablesLanguageUrl()
|
||||
}
|
||||
});
|
||||
|
||||
function updateSelectedCount() {
|
||||
var count = $('#callhistory-preview-table tbody .row-check:checked').length;
|
||||
$('#selected-count').text(count);
|
||||
}
|
||||
|
||||
$('#check-all').on('change', function () {
|
||||
var checked = $(this).prop('checked');
|
||||
$('#callhistory-preview-table tbody .row-check').prop('checked', checked);
|
||||
updateSelectedCount();
|
||||
});
|
||||
|
||||
$('#select-all-btn').on('click', function () {
|
||||
$('#callhistory-preview-table tbody .row-check').prop('checked', true);
|
||||
$('#check-all').prop('checked', true);
|
||||
updateSelectedCount();
|
||||
});
|
||||
|
||||
$('#deselect-all-btn').on('click', function () {
|
||||
$('#callhistory-preview-table tbody .row-check').prop('checked', false);
|
||||
$('#check-all').prop('checked', false);
|
||||
updateSelectedCount();
|
||||
});
|
||||
|
||||
$('#callhistory-preview-table tbody').on('change', '.row-check', function () {
|
||||
updateSelectedCount();
|
||||
});
|
||||
|
||||
$('#apply-form').on('submit', function (e) {
|
||||
// Disable checkboxes that are unchecked so their hidden inputs are not submitted
|
||||
$('#callhistory-preview-table tbody .row-check:not(:checked)').each(function () {
|
||||
var idx = $(this).attr('name').replace('[qso_id]', '');
|
||||
$('[name="' + idx + '[station_id]"],' +
|
||||
'[name="' + idx + '[new_sig]"],' +
|
||||
'[name="' + idx + '[new_sig_info]"]').prop('disabled', true);
|
||||
$(this).prop('disabled', true);
|
||||
});
|
||||
|
||||
var count = $('#apply-form input.row-check:not(:disabled)').length;
|
||||
if (count === 0) {
|
||||
e.preventDefault();
|
||||
alert('No rows selected.');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php } ?>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue