2025-05-16 10:18:55 +04:00

402 lines
14 KiB
JavaScript

/**
* SCADA vs DWG Manifest Comparison Tool
* Main JavaScript functionality
*/
document.addEventListener('DOMContentLoaded', function() {
// Initialize Bootstrap components
initializeBootstrapComponents();
// Add search/filter functionality to tables
initializeTableFilters();
// Add copy to clipboard functionality
initializeClipboardCopy();
// Handle form submissions to prevent resubmission prompts
initializeFormHandlers();
});
/**
* Initialize form handlers to prevent resubmission prompts
*/
function initializeFormHandlers() {
// Handle main comparison form
const comparisonForm = document.querySelector('form[action*="compare"]');
if (comparisonForm) {
comparisonForm.addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
// Show loading indicator
const submitBtn = this.querySelector('button[type="submit"]');
const originalBtnText = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = 'Processing...';
fetch(this.action, {
method: 'POST',
body: formData,
redirect: 'manual'
})
.then(response => {
if (response.type === 'opaqueredirect') {
// Server redirected, follow the redirect
window.location.href = response.url || '/';
return;
}
return response.text();
})
.then(html => {
if (html) {
// If we got HTML back and not a redirect, handle it
// This is a fallback in case redirect didn't work
window.location.reload();
}
})
.catch(error => {
console.error('Error:', error);
alert('An error occurred during form submission. Please try again.');
submitBtn.disabled = false;
submitBtn.innerHTML = originalBtnText;
});
});
}
// Handle update files form
const updateForm = document.querySelector('form[action*="update_files"]');
if (updateForm) {
updateForm.addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
// Show loading indicator
const submitBtn = this.querySelector('button[type="submit"]');
const originalBtnText = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = 'Updating...';
fetch(this.action, {
method: 'POST',
body: formData,
redirect: 'manual'
})
.then(response => {
if (response.type === 'opaqueredirect') {
// Server redirected, follow the redirect
window.location.href = response.url || '/';
return;
}
return response.text();
})
.then(html => {
if (html) {
// If we got HTML back and not a redirect, handle it
window.location.reload();
}
})
.catch(error => {
console.error('Error:', error);
alert('An error occurred during update. Please try again.');
submitBtn.disabled = false;
submitBtn.innerHTML = originalBtnText;
});
});
}
// Handle delete comparison form (the one created dynamically in the results page)
document.addEventListener('submit', function(e) {
if (e.target.action && e.target.action.includes('delete_comparison')) {
e.preventDefault();
fetch(e.target.action, {
method: 'POST',
redirect: 'manual'
})
.then(response => {
if (response.type === 'opaqueredirect') {
window.location.href = response.url || '/';
return;
}
return response.text();
})
.then(html => {
if (html) {
window.location.href = '/';
}
})
.catch(error => {
console.error('Error:', error);
alert('An error occurred while deleting the comparison. Please try again.');
});
}
});
}
/**
* Initialize Bootstrap components like tabs
*/
function initializeBootstrapComponents() {
// Initialize tabs if they exist
const tabEls = document.querySelectorAll('button[data-bs-toggle="tab"]');
if (tabEls.length > 0) {
tabEls.forEach(tabEl => {
new bootstrap.Tab(tabEl);
});
// Add event listener for tab change to persist active tab
tabEls.forEach(tab => {
tab.addEventListener('shown.bs.tab', function (event) {
// Store the currently active tab in local storage
localStorage.setItem('activeTab', event.target.id);
});
});
// Restore active tab from localStorage if available
const activeTabId = localStorage.getItem('activeTab');
if (activeTabId) {
const activeTab = document.getElementById(activeTabId);
if (activeTab) {
new bootstrap.Tab(activeTab).show();
}
}
}
}
/**
* Add search/filter functionality to tables
*/
function initializeTableFilters() {
// Create filter inputs for each table that needs filtering
const tableContainers = document.querySelectorAll('.table-container');
tableContainers.forEach((container, index) => {
const table = container.querySelector('table');
if (!table) return;
// Make the table the first element in a new structure
const originalHTML = container.innerHTML;
container.innerHTML = '';
// Create a wrapper for the sticky elements
const stickyHeader = document.createElement('div');
stickyHeader.className = 'sticky-table-header';
// Create filter input
const filterDiv = document.createElement('div');
filterDiv.className = 'table-filter-container d-flex justify-content-between align-items-center';
// Create the left side with search input
const searchGroup = document.createElement('div');
searchGroup.className = 'input-group';
searchGroup.style.flexBasis = '80%';
const filterInput = document.createElement('input');
filterInput.type = 'text';
filterInput.className = 'form-control table-filter';
filterInput.placeholder = 'Filter items...';
filterInput.setAttribute('data-table-index', index);
const filterClearBtn = document.createElement('button');
filterClearBtn.className = 'btn btn-outline-secondary';
filterClearBtn.innerHTML = '×';
filterClearBtn.type = 'button';
searchGroup.appendChild(filterInput);
searchGroup.appendChild(filterClearBtn);
// Add the search group to the filter container
filterDiv.appendChild(searchGroup);
// The copy button will be added by initializeClipboardCopy()
stickyHeader.appendChild(filterDiv);
// Create a scrollable content div
const scrollableContent = document.createElement('div');
scrollableContent.className = 'scrollable-table-content';
scrollableContent.innerHTML = originalHTML;
// Add both elements to the container
container.appendChild(stickyHeader);
container.appendChild(scrollableContent);
// Fix for header width issue - ensure the table has a consistent width
// and the header cells match the body cells
window.addEventListener('load', function() {
const tableHeader = scrollableContent.querySelector('thead');
const tableBody = scrollableContent.querySelector('tbody');
if (tableHeader && tableBody) {
// Ensure equal cell widths
const headerCells = tableHeader.querySelectorAll('th');
const firstRowCells = tableBody.querySelector('tr') ?
tableBody.querySelector('tr').querySelectorAll('td') : null;
if (headerCells.length > 0 && firstRowCells && firstRowCells.length > 0) {
// Set explicit widths on header cells based on first row
for (let i = 0; i < headerCells.length && i < firstRowCells.length; i++) {
const width = firstRowCells[i].offsetWidth;
headerCells[i].style.minWidth = width + 'px';
}
}
}
});
// Add event listeners
filterInput.addEventListener('input', function() {
// Get the table from the scrollable content
const tableInContent = scrollableContent.querySelector('table');
filterTable(tableInContent, this.value);
});
filterClearBtn.addEventListener('click', function() {
filterInput.value = '';
const tableInContent = scrollableContent.querySelector('table');
filterTable(tableInContent, '');
});
});
}
/**
* Filter table rows based on input
*/
function filterTable(table, filterText) {
const tbody = table.querySelector('tbody');
if (!tbody) return;
const rows = tbody.querySelectorAll('tr');
const lowerFilterText = filterText.toLowerCase();
rows.forEach(row => {
const text = row.textContent.toLowerCase();
if (text.includes(lowerFilterText)) {
row.style.display = '';
} else {
row.style.display = 'none';
}
});
// Update counter if there is one
const cardHeader = table.closest('.card').querySelector('.card-header h5');
if (cardHeader) {
const originalText = cardHeader.getAttribute('data-original-text');
if (!originalText) {
cardHeader.setAttribute('data-original-text', cardHeader.textContent);
}
if (filterText) {
const visibleRowCount = [...rows].filter(row => row.style.display !== 'none').length;
const matches = cardHeader.textContent.match(/\((\d+)\)/);
if (matches && matches[1]) {
cardHeader.textContent = cardHeader.textContent.replace(
`(${matches[1]})`,
`(${visibleRowCount} of ${matches[1]})`
);
}
} else if (originalText) {
cardHeader.textContent = originalText;
}
}
}
/**
* Add ability to copy table contents to clipboard
*/
function initializeClipboardCopy() {
const tableContainers = document.querySelectorAll('.table-container');
tableContainers.forEach(container => {
// Find the filter container in the sticky header
const filterContainer = container.querySelector('.table-filter-container');
if (!filterContainer) return;
// Create copy button
const copyBtn = document.createElement('button');
copyBtn.className = 'btn btn-primary ms-2 copy-btn';
copyBtn.innerHTML = '<i class="bi bi-clipboard"></i> Copy';
// Insert button into the filter container
filterContainer.appendChild(copyBtn);
// Add event listener
copyBtn.addEventListener('click', function() {
// Find the table in the scrollable content
const scrollableContent = container.querySelector('.scrollable-table-content');
const table = scrollableContent ? scrollableContent.querySelector('table') : container.querySelector('table');
if (!table) return;
let text = '';
const rows = table.querySelectorAll('tbody tr');
rows.forEach(row => {
if (row.style.display !== 'none') {
const cells = row.querySelectorAll('td');
cells.forEach(cell => {
text += cell.textContent.trim() + '\t';
});
text += '\n';
}
});
copyToClipboard(text);
// Visual feedback
const originalText = copyBtn.innerHTML;
copyBtn.innerHTML = '<i class="bi bi-check"></i> Copied!';
copyBtn.classList.add('btn-success');
copyBtn.classList.remove('btn-primary');
setTimeout(() => {
copyBtn.innerHTML = originalText;
copyBtn.classList.remove('btn-success');
copyBtn.classList.add('btn-primary');
}, 1500);
});
});
}
/**
* Helper function to copy text to clipboard
*/
function copyToClipboard(text) {
// Create a temporary textarea
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.setAttribute('readonly', '');
textarea.style.position = 'absolute';
textarea.style.left = '-9999px';
document.body.appendChild(textarea);
// Select and copy
textarea.select();
document.execCommand('copy');
// Clean up
document.body.removeChild(textarea);
}
/**
* Handle file upload previews and validation
*/
document.querySelectorAll('input[type="file"]').forEach(fileInput => {
fileInput.addEventListener('change', function(e) {
const fileName = e.target.files[0]?.name;
const fileLabel = fileInput.nextElementSibling;
if (fileName) {
// Show the filename
if (fileLabel) {
fileLabel.textContent = fileName;
}
// Basic validation for Excel files
if (!fileName.endsWith('.xlsx') && !fileName.endsWith('.xls')) {
alert('Please upload a valid Excel file (.xlsx or .xls)');
fileInput.value = '';
if (fileLabel) {
fileLabel.textContent = 'Choose file';
}
}
}
});
});