532 lines
24 KiB
JavaScript
532 lines
24 KiB
JavaScript
// --- UI Update Functions ---
|
|
|
|
// Store references to frequently used DOM elements
|
|
const uiElements = {
|
|
overallScadaProgress: document.getElementById('overall-scada-progress'),
|
|
scadaPanelsProgress: document.getElementById('scada-panels-progress'),
|
|
overallDrawingProgress: document.getElementById('overall-drawing-progress'),
|
|
drawingPanelsProgress: document.getElementById('drawing-panels-progress'),
|
|
panelsConflicts: document.getElementById('panels-conflicts'),
|
|
overallScadaText: document.getElementById('overall-scada-text'),
|
|
overallDrawingText: document.getElementById('overall-drawing-text'),
|
|
conflictCountBadge: document.getElementById('conflict-count'),
|
|
statusBarProjectName: document.getElementById('selected-project-status-name'),
|
|
statusBarMessage: document.getElementById('status-message'),
|
|
statusBarCommit: document.getElementById('last-commit'),
|
|
scadaContent: document.getElementById('scada-content'),
|
|
drawingsContent: document.getElementById('drawings-content'),
|
|
conflictsContent: document.getElementById('conflicts-content'),
|
|
navLinks: document.querySelectorAll('.nav-link'),
|
|
projectNameDisplays: document.querySelectorAll('.project-name-display'),
|
|
// Status message elements for Manage Files modal
|
|
manageFilesStatus: document.getElementById('manageFilesStatus'),
|
|
uploadStatus: document.getElementById('uploadStatus'),
|
|
analysisTriggerStatus: document.getElementById('analysisTriggerStatus'),
|
|
deleteProjectStatus: document.getElementById('deleteProjectStatus'),
|
|
uploadManifestStatus: document.getElementById('uploadManifestStatus')
|
|
};
|
|
|
|
// --- Loading Indicators ---
|
|
|
|
/**
|
|
* Displays a loading indicator within a container element.
|
|
* @param {HTMLElement} containerElement - The DOM element to show the indicator in.
|
|
* @param {string} message - The message to display below the spinner.
|
|
*/
|
|
function uiShowLoadingIndicator(containerElement, message = "Processing data...") {
|
|
if (!containerElement) return;
|
|
|
|
let indicatorDiv = containerElement.querySelector('.processing-indicator');
|
|
|
|
// --- Check if projects exist ---
|
|
const projectSelector = document.getElementById('projectSelector');
|
|
const noProjectsExist = projectSelector && projectSelector.options.length === 1 && projectSelector.options[0].disabled;
|
|
// Determine appropriate message if none was explicitly provided
|
|
let displayMessage = message;
|
|
if (message === "Processing data..." || message === "Select a project" || message === "Loading data...") { // Check against default/generic messages
|
|
displayMessage = noProjectsExist ? "No projects available. Please add one." : (selectedProjectName ? "Loading data..." : "Select a project");
|
|
}
|
|
// --- End Check ---
|
|
|
|
if (!indicatorDiv) {
|
|
// If indicator doesn't exist, clear the container first
|
|
containerElement.innerHTML = '';
|
|
// Create and add the indicator
|
|
indicatorDiv = document.createElement('div');
|
|
indicatorDiv.className = 'text-center p-4 processing-indicator';
|
|
indicatorDiv.innerHTML = `
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Loading...</span>
|
|
</div>
|
|
<p class="mt-2 fst-italic text-muted"></p>
|
|
`;
|
|
containerElement.prepend(indicatorDiv);
|
|
} else {
|
|
// If indicator already exists, ensure it's visible (it might have been hidden)
|
|
indicatorDiv.style.display = 'block';
|
|
}
|
|
|
|
// Update message (simple text sanitization)
|
|
const messageElement = indicatorDiv.querySelector('p');
|
|
if (messageElement) {
|
|
// Use the determined displayMessage
|
|
const safeMessage = displayMessage.replace(/</g, "<").replace(/>/g, ">");
|
|
messageElement.textContent = safeMessage;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes the loading indicator from a container element and restores content visibility.
|
|
* @param {HTMLElement} containerElement - The DOM element to clear the indicator from.
|
|
*/
|
|
function uiClearLoadingIndicator(containerElement) {
|
|
if (!containerElement) return;
|
|
|
|
const indicatorDiv = containerElement.querySelector('.processing-indicator');
|
|
if (indicatorDiv) {
|
|
// Remove the indicator element completely
|
|
indicatorDiv.remove();
|
|
}
|
|
|
|
// No longer need to manage 'content-hidden-by-loader' class
|
|
|
|
// Remove default "Loading panel data..." placeholders if they still exist
|
|
const placeholderP = Array.from(containerElement.querySelectorAll('p.fst-italic')).find(p =>
|
|
p.textContent.toLowerCase().includes('loading') &&
|
|
!p.closest('.processing-indicator')
|
|
);
|
|
if (placeholderP) {
|
|
placeholderP.remove();
|
|
}
|
|
}
|
|
|
|
// --- Overall UI State ---
|
|
|
|
/**
|
|
* Sets the entire UI to reflect a processing state, showing loading indicators.
|
|
* @param {string} statusMsg - The status message to display in the indicators.
|
|
*/
|
|
function uiShowProcessingState(statusMsg = "Loading data...") {
|
|
console.log(`[UI] Setting processing state: "${statusMsg}"`);
|
|
const containers = [
|
|
uiElements.overallScadaProgress,
|
|
uiElements.scadaPanelsProgress,
|
|
uiElements.overallDrawingProgress,
|
|
uiElements.drawingPanelsProgress,
|
|
uiElements.panelsConflicts
|
|
];
|
|
|
|
// Destroy existing charts (delegated to chartManager)
|
|
chartManagerDestroyAll();
|
|
|
|
// Show loading indicator in all main content containers
|
|
containers.forEach(container => {
|
|
if (container) {
|
|
uiShowLoadingIndicator(container, statusMsg);
|
|
}
|
|
});
|
|
|
|
// Clear text content that is updated directly
|
|
if (uiElements.overallScadaText) uiElements.overallScadaText.textContent = '';
|
|
if (uiElements.overallDrawingText) uiElements.overallDrawingText.textContent = '';
|
|
if (uiElements.conflictCountBadge) {
|
|
uiElements.conflictCountBadge.textContent = '...';
|
|
uiElements.conflictCountBadge.style.display = 'inline-block'; // Show badge while loading
|
|
}
|
|
|
|
// Ensure the currently selected section's container is visible
|
|
// (uiShowSection handles this, but good belt-and-braces)
|
|
const sectionId = currentVisibleSection; // Assumes currentVisibleSection is accessible (global state)
|
|
if (sectionId === 'scada' && uiElements.scadaContent) uiElements.scadaContent.style.display = 'block';
|
|
else if (sectionId === 'drawings' && uiElements.drawingsContent) uiElements.drawingsContent.style.display = 'block';
|
|
else if (sectionId === 'conflicts' && uiElements.conflictsContent) uiElements.conflictsContent.style.display = 'block';
|
|
}
|
|
|
|
/**
|
|
* Clears loading indicators and prepares the UI for content rendering.
|
|
* Note: Actual content rendering is triggered by calling core update functions.
|
|
*/
|
|
function uiClearProcessingState() {
|
|
console.log(`[UI] Clearing processing state.`);
|
|
const containers = [
|
|
uiElements.overallScadaProgress,
|
|
uiElements.scadaPanelsProgress,
|
|
uiElements.overallDrawingProgress,
|
|
uiElements.drawingPanelsProgress,
|
|
uiElements.panelsConflicts
|
|
];
|
|
containers.forEach(container => {
|
|
if (container) uiClearLoadingIndicator(container);
|
|
});
|
|
}
|
|
|
|
// --- Section/Tab Navigation ---
|
|
|
|
/**
|
|
* Shows the specified content section and hides others. Updates active nav link.
|
|
* @param {string} sectionId - 'scada', 'drawings', or 'conflicts'.
|
|
*/
|
|
function uiShowSection(sectionId) {
|
|
console.log("[UI] Showing section:", sectionId);
|
|
// Hide all sections first
|
|
if (uiElements.scadaContent) uiElements.scadaContent.style.display = 'none';
|
|
if (uiElements.drawingsContent) uiElements.drawingsContent.style.display = 'none';
|
|
if (uiElements.conflictsContent) uiElements.conflictsContent.style.display = 'none';
|
|
|
|
let elementToShow = null;
|
|
if (sectionId === 'scada' && uiElements.scadaContent) elementToShow = uiElements.scadaContent;
|
|
else if (sectionId === 'drawings' && uiElements.drawingsContent) elementToShow = uiElements.drawingsContent;
|
|
else if (sectionId === 'conflicts' && uiElements.conflictsContent) elementToShow = uiElements.conflictsContent;
|
|
|
|
if (elementToShow) {
|
|
elementToShow.style.display = 'block';
|
|
// Update global state (This might move to an event handler later)
|
|
currentVisibleSection = sectionId;
|
|
console.log(`[UI] Current visible section set to: ${currentVisibleSection}`);
|
|
|
|
// Trigger redraw of the newly visible section
|
|
// Needs access to global state: currentProjectData, selectedProjectName, isAnalysisGloballyActive
|
|
const projectData = currentProjectData[selectedProjectName];
|
|
// Check if analysis is globally marked as active OR if the specific project status indicates processing
|
|
const analysisIsActive = typeof isAnalysisGloballyActive !== 'undefined' && isAnalysisGloballyActive;
|
|
const projectIsProcessing = projectData && isProcessing(projectData.status); // isProcessing needs to be accessible
|
|
|
|
if (analysisIsActive || projectIsProcessing) {
|
|
const statusMsg = projectData?.status || "Analysis in progress..."; // Use project status if available, otherwise generic message
|
|
console.log(`[UI] Section ${sectionId} shown, project processing. Status: "${statusMsg}"`);
|
|
// Ensure loading indicator is visible in the right place
|
|
if (sectionId === 'scada') {
|
|
uiShowLoadingIndicator(uiElements.overallScadaProgress, statusMsg);
|
|
uiShowLoadingIndicator(uiElements.scadaPanelsProgress, statusMsg);
|
|
} else if (sectionId === 'drawings') {
|
|
uiShowLoadingIndicator(uiElements.overallDrawingProgress, statusMsg);
|
|
uiShowLoadingIndicator(uiElements.drawingPanelsProgress, statusMsg);
|
|
} else if (sectionId === 'conflicts') {
|
|
uiShowLoadingIndicator(uiElements.panelsConflicts, statusMsg);
|
|
}
|
|
chartManagerDestroyAll(); // Ensure charts are gone if processing
|
|
|
|
} else if (projectData) { // Project exists and is NOT processing
|
|
// Project is ready, draw the content immediately
|
|
console.log(`[UI] Section ${sectionId} shown, project ready. Drawing content.`);
|
|
|
|
// Double check status before actually drawing (good practice)
|
|
const currentData = currentProjectData[selectedProjectName];
|
|
const stillNotProcessing = currentData && !isProcessing(currentData.status);
|
|
const stillGloballyInactive = typeof isAnalysisGloballyActive !== 'undefined' && !isAnalysisGloballyActive;
|
|
|
|
if (stillNotProcessing && stillGloballyInactive) {
|
|
// Explicitly clear any loading indicators before drawing
|
|
uiClearProcessingState();
|
|
|
|
// Call core update functions directly
|
|
if (sectionId === 'scada') updateUIScadaCore(currentData);
|
|
else if (sectionId === 'drawings') updateUIDrawingCore(currentData);
|
|
else if (sectionId === 'conflicts') updateUIConflictsCore(currentData);
|
|
} else {
|
|
// If state somehow changed back to processing *just* as we switched tabs, show loading
|
|
const currentStatusMsg = currentData?.status || (isAnalysisGloballyActive ? "Analysis in progress..." : "State changed...");
|
|
console.log(`[UI] Status changed back to processing just before drawing for ${sectionId}. Status: "${currentStatusMsg}"`);
|
|
uiShowProcessingState(currentStatusMsg);
|
|
}
|
|
|
|
} else { // No project data found for the selected project
|
|
console.log(`[UI] Section ${sectionId} shown, but no data for project ${selectedProjectName}. Showing loading.`);
|
|
// Show loading indicator as data is missing or project not selected
|
|
// --- Check if projects exist ---
|
|
const projectSelectorCheck = document.getElementById('projectSelector');
|
|
const noProjectsExistCheck = projectSelectorCheck && projectSelectorCheck.options.length === 1 && projectSelectorCheck.options[0].disabled;
|
|
const msg = noProjectsExistCheck ? "No projects available. Please add one." : (selectedProjectName ? "Loading data..." : "Select a project");
|
|
// --- End Check ---
|
|
if (sectionId === 'scada') { uiShowLoadingIndicator(uiElements.overallScadaProgress, msg); uiShowLoadingIndicator(uiElements.scadaPanelsProgress, msg); }
|
|
else if (sectionId === 'drawings') { uiShowLoadingIndicator(uiElements.overallDrawingProgress, msg); uiShowLoadingIndicator(uiElements.drawingPanelsProgress, msg); }
|
|
else if (sectionId === 'conflicts') { uiShowLoadingIndicator(uiElements.panelsConflicts, msg); }
|
|
}
|
|
} else {
|
|
console.error("[UI] Attempted to show unknown section:", sectionId);
|
|
// Default back to SCADA maybe?
|
|
if(uiElements.scadaContent) uiElements.scadaContent.style.display = 'block';
|
|
currentVisibleSection = 'scada';
|
|
}
|
|
|
|
// Update active nav link state
|
|
uiElements.navLinks.forEach(link => {
|
|
link.classList.remove('active');
|
|
if (link.getAttribute('data-view') === sectionId) {
|
|
link.classList.add('active');
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
// --- Content Updates ---
|
|
|
|
/**
|
|
* Updates all elements displaying the current project name.
|
|
* @param {string} projectName - The name of the project.
|
|
*/
|
|
function uiUpdateProjectNameDisplay(projectName) {
|
|
uiElements.projectNameDisplays.forEach(el => el.textContent = projectName || '...');
|
|
}
|
|
|
|
/**
|
|
* Updates the status bar content.
|
|
* @param {string} projectName - Name of the current project.
|
|
* @param {string} statusMsg - Status message to display.
|
|
* @param {string} commitHash - Last commit hash (abbreviated).
|
|
*/
|
|
function uiUpdateStatusBar(projectName, statusMsg, commitHash) {
|
|
if (uiElements.statusBarProjectName) uiElements.statusBarProjectName.textContent = projectName || '...';
|
|
if (uiElements.statusBarMessage) uiElements.statusBarMessage.textContent = statusMsg || 'N/A';
|
|
if (uiElements.statusBarCommit) uiElements.statusBarCommit.textContent = commitHash ? commitHash.substring(0, 7) : 'N/A';
|
|
}
|
|
|
|
/**
|
|
* Updates the text displaying overall statistics for SCADA or Drawing views.
|
|
* @param {string} context - 'scada' or 'drawing'.
|
|
* @param {number} foundCount - Number of items found.
|
|
* @param {number} totalCount - Total number of items.
|
|
* @param {number} percentage - Calculated percentage found.
|
|
*/
|
|
function uiUpdateOverallStatsText(context, foundCount, totalCount, percentage) {
|
|
const element = context === 'scada' ? uiElements.overallScadaText : uiElements.overallDrawingText;
|
|
if (element) {
|
|
element.textContent = context === 'scada'
|
|
? `Found in SCADA: ${foundCount}/${totalCount} (${percentage}%)`
|
|
: `Found in Drawing: ${foundCount}/${totalCount} (${percentage}%)`;
|
|
} else {
|
|
console.warn(`[UI] Element not found for overall stats text: ${context}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the conflicts table display.
|
|
* @param {object} panelsData - The panels data containing conflict lists.
|
|
*/
|
|
function uiUpdateConflictsTable(panelsData) {
|
|
const container = uiElements.panelsConflicts;
|
|
if (!container) return;
|
|
|
|
container.innerHTML = ''; // Clear previous content
|
|
|
|
let totalConflicts = 0;
|
|
let panelsWithConflicts = 0;
|
|
|
|
if (!panelsData || Object.keys(panelsData).length === 0) {
|
|
container.innerHTML = '<p class="text-center fst-italic">No panel data available yet.</p>';
|
|
} else {
|
|
const sortedPanels = Object.keys(panelsData).sort();
|
|
sortedPanels.forEach(panelName => {
|
|
const panel = panelsData[panelName];
|
|
const conflictsList = panel.found_scada_only_list || [];
|
|
if (conflictsList.length > 0) {
|
|
panelsWithConflicts++;
|
|
totalConflicts += conflictsList.length;
|
|
|
|
const panelHeader = document.createElement('h4');
|
|
panelHeader.className = 'mt-4 mb-2';
|
|
panelHeader.textContent = `${panelName} (${conflictsList.length} conflicts)`;
|
|
container.appendChild(panelHeader);
|
|
|
|
const table = document.createElement('table');
|
|
table.className = 'table table-sm table-striped table-hover table-bordered';
|
|
table.innerHTML = `
|
|
<thead>
|
|
<tr><th>Alias</th><th>Panel</th><th>SCADA Status</th><th>Drawing Status</th><th>Equipment Type</th><th>Type of Conveyor</th></tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
`;
|
|
const tbody = table.querySelector('tbody');
|
|
conflictsList.sort((a, b) => a.alias.localeCompare(b.alias)).forEach(item => {
|
|
const row = tbody.insertRow();
|
|
row.classList.add('table-warning');
|
|
row.insertCell().textContent = item.alias;
|
|
row.insertCell().textContent = item.control_panel;
|
|
row.insertCell().innerHTML = '<span class="status-yes">Yes</span>';
|
|
row.insertCell().innerHTML = '<span class="status-no">No</span>';
|
|
row.insertCell().textContent = item.equipment_type || 'N/A';
|
|
row.insertCell().textContent = item.conveyor_type || 'N/A';
|
|
});
|
|
container.appendChild(table);
|
|
}
|
|
});
|
|
if (panelsWithConflicts === 0) {
|
|
container.innerHTML = '<p class="text-center fst-italic">No conflicts found across all panels.</p>';
|
|
}
|
|
}
|
|
// Update total count badge
|
|
if (uiElements.conflictCountBadge) {
|
|
uiElements.conflictCountBadge.textContent = totalConflicts;
|
|
uiElements.conflictCountBadge.style.display = totalConflicts > 0 ? 'inline-block' : 'none';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clears all project-specific display areas (charts, text, tables).
|
|
*/
|
|
function uiClearProjectDisplay() {
|
|
console.log("[UI] Clearing project display.");
|
|
// Clear charts (delegated)
|
|
chartManagerDestroyAll();
|
|
|
|
// Clear text
|
|
if (uiElements.overallScadaText) uiElements.overallScadaText.textContent = 'Found in SCADA: N/A';
|
|
if (uiElements.overallDrawingText) uiElements.overallDrawingText.textContent = 'Found in Drawing: N/A';
|
|
if (uiElements.conflictCountBadge) {
|
|
uiElements.conflictCountBadge.textContent = '0';
|
|
uiElements.conflictCountBadge.style.display = 'none';
|
|
}
|
|
|
|
// Clear containers
|
|
// --- Check if projects exist ---
|
|
const projectSelector = document.getElementById('projectSelector');
|
|
const noProjectsExist = projectSelector && projectSelector.options.length === 1 && projectSelector.options[0].disabled;
|
|
const defaultMsg = noProjectsExist
|
|
? "<p class=\"text-center fst-italic\">No projects loaded. Use 'Add Project' to create one.</p>"
|
|
: "<p class=\"text-center fst-italic\">Select a project to view data.</p>";
|
|
// --- End Check ---
|
|
|
|
if(uiElements.scadaPanelsProgress) uiElements.scadaPanelsProgress.innerHTML = defaultMsg;
|
|
if(uiElements.drawingPanelsProgress) uiElements.drawingPanelsProgress.innerHTML = defaultMsg;
|
|
if(uiElements.panelsConflicts) uiElements.panelsConflicts.innerHTML = defaultMsg;
|
|
}
|
|
|
|
// --- Status Messages ---
|
|
|
|
/**
|
|
* Shows a status message in a specific element, optionally auto-clearing.
|
|
* @param {HTMLElement} element - The status display element.
|
|
* @param {string} message - The message to show.
|
|
* @param {string} type - 'info', 'success', 'warning', 'danger'.
|
|
* @param {boolean} autoClear - Whether to hide the message after a delay.
|
|
* @param {number} delay - Delay in milliseconds for auto-clear.
|
|
*/
|
|
function uiShowStatusMessage(element, message, type = 'info', autoClear = true, delay = 5000) {
|
|
if (!element) return;
|
|
// Use text-based classes for inline messages, alert classes for block messages
|
|
const isAlert = element.classList.contains('alert');
|
|
if (isAlert) {
|
|
element.className = `mt-3 alert alert-${type}`;
|
|
} else {
|
|
element.className = `mt-2 text-${type}`;
|
|
if (type === 'danger' || type === 'warning' || type === 'success') {
|
|
element.className += ' fw-bold';
|
|
}
|
|
}
|
|
element.textContent = message;
|
|
element.style.display = 'block';
|
|
|
|
// Clear previous timeouts if any
|
|
if (element.timeoutId) clearTimeout(element.timeoutId);
|
|
element.timeoutId = null; // Clear the stored ID
|
|
|
|
if (autoClear) {
|
|
element.timeoutId = setTimeout(() => {
|
|
element.style.display = 'none';
|
|
element.timeoutId = null;
|
|
}, delay);
|
|
}
|
|
}
|
|
|
|
function uiShowManageFilesStatus(message, type = 'info') {
|
|
uiShowStatusMessage(uiElements.manageFilesStatus, message, type, false); // Not auto-clearing by default
|
|
}
|
|
|
|
function uiShowUploadStatus(message, type = 'info', autoClear = true) {
|
|
uiShowStatusMessage(uiElements.uploadStatus, message, type, autoClear, 5000);
|
|
}
|
|
|
|
function uiShowAnalysisTriggerStatus(message, type = 'info', autoClear = true) {
|
|
uiShowStatusMessage(uiElements.analysisTriggerStatus, message, type, autoClear, 8000);
|
|
}
|
|
|
|
function uiShowDeleteProjectStatus(message, type = 'info', autoClear = true) {
|
|
uiShowStatusMessage(uiElements.deleteProjectStatus, message, type, autoClear, 6000);
|
|
}
|
|
|
|
function uiShowUploadManifestStatus(message, type = 'info', autoClear = true) {
|
|
uiShowStatusMessage(uiElements.uploadManifestStatus, message, type, autoClear, 5000);
|
|
}
|
|
|
|
/** Clears all status messages within the Manage Files modal. */
|
|
function uiClearManageFilesStatusMessages() {
|
|
const elements = [
|
|
uiElements.manageFilesStatus,
|
|
uiElements.uploadStatus,
|
|
uiElements.analysisTriggerStatus,
|
|
uiElements.deleteProjectStatus,
|
|
uiElements.uploadManifestStatus
|
|
];
|
|
elements.forEach(element => {
|
|
if (element) {
|
|
element.style.display = 'none';
|
|
if (element.timeoutId) {
|
|
clearTimeout(element.timeoutId);
|
|
element.timeoutId = null;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
// --- Project Selector Update ---
|
|
/**
|
|
* Rebuilds the project selector dropdown based on the provided list.
|
|
* Tries to maintain the current selection if possible.
|
|
* @param {Array<string>} incomingProjects - List of project names.
|
|
* @returns {boolean} - True if the selection was changed/reset, false otherwise.
|
|
*/
|
|
function uiUpdateProjectSelector(incomingProjects) {
|
|
const projectSelector = document.getElementById('projectSelector'); // Get fresh reference
|
|
if (!projectSelector) return false;
|
|
|
|
const currentOptions = Array.from(projectSelector.options).map(opt => opt.value);
|
|
const previouslySelected = projectSelector.value;
|
|
let selectionChanged = false;
|
|
|
|
// Check if the list of projects has actually changed
|
|
const hasChanged = incomingProjects.length !== currentOptions.length ||
|
|
incomingProjects.some(p => !currentOptions.includes(p)) ||
|
|
currentOptions.some(p => !incomingProjects.includes(p));
|
|
|
|
if (hasChanged) {
|
|
console.log("[UI] Project list changed. Rebuilding project selector.");
|
|
projectSelector.innerHTML = ''; // Clear existing options
|
|
|
|
if (incomingProjects.length > 0) {
|
|
incomingProjects.sort().forEach(projName => {
|
|
const option = document.createElement('option');
|
|
option.value = projName;
|
|
option.textContent = projName;
|
|
projectSelector.appendChild(option);
|
|
});
|
|
// Try to re-select the previous project if it still exists
|
|
if (incomingProjects.includes(previouslySelected)) {
|
|
projectSelector.value = previouslySelected;
|
|
} else {
|
|
projectSelector.selectedIndex = 0; // Select the first one
|
|
selectionChanged = true; // Selection was reset to the first item
|
|
}
|
|
} else {
|
|
// Handle empty project list
|
|
const option = document.createElement('option');
|
|
option.value = '';
|
|
option.textContent = 'No projects found';
|
|
option.disabled = true;
|
|
projectSelector.appendChild(option);
|
|
selectionChanged = true; // Selection is now empty/disabled
|
|
}
|
|
}
|
|
|
|
// Update button state based on current selection (even if list didn't change)
|
|
const manageFilesBtn = document.getElementById('manageFilesBtn'); // Get fresh reference
|
|
if (manageFilesBtn) {
|
|
manageFilesBtn.disabled = !projectSelector.value;
|
|
}
|
|
|
|
return selectionChanged;
|
|
}
|
|
|
|
|
|
// --- Export (if using modules in the future) ---
|
|
// export { uiShowLoadingIndicator, uiClearLoadingIndicator, ... };
|