Files
enviPy-bayer/static/js/alpine/pathway.js
Tobias O 5f5ae76182 [Fix] Fix Prediction Spinner, ensure proper pathway status is set
Fixes Spinner and status message display on pathway page

Reviewed-on: enviPath/enviPy#291
Co-authored-by: Tobias O <tobias.olenyi@envipath.com>
Co-committed-by: Tobias O <tobias.olenyi@envipath.com>
2026-01-15 23:09:12 +13:00

135 lines
3.9 KiB
JavaScript

/**
* Pathway Viewer Alpine.js Component
*
* Provides reactive status management and polling for pathway predictions.
* Handles status updates, change detection, and update notices.
*/
document.addEventListener('alpine:init', () => {
/**
* Pathway Viewer Component
*
* Usage:
* <div x-data="pathwayViewer({
* status: 'running',
* modified: '2024-01-01T00:00:00Z',
* statusUrl: '/pathway/123?status=true'
* })" x-init="init()">
* ...
* </div>
*/
Alpine.data('pathwayViewer', (config) => ({
status: config.status,
modified: config.modified,
modifiedDate: null,
statusUrl: config.statusUrl,
emptyDueToThreshold: config.emptyDueToThreshold === "True",
showUpdateNotice: false,
showEmptyDueToThresholdNotice: false,
emptyDueToThresholdMessage: 'The Pathway is empty due to the selected threshold. Please try a different threshold.',
updateMessage: '',
pollInterval: null,
get statusTooltip() {
const tooltips = {
'completed': 'Pathway prediction completed.',
'failed': 'Pathway prediction failed.',
'running': 'Pathway prediction running.'
};
return tooltips[this.status] || '';
},
init() {
this.modifiedDate = this.parseDate(this.modified);
if (this.status === 'running') {
this.startPolling();
}
if (this.emptyDueToThreshold) {
this.showEmptyDueToThresholdNotice = true;
}
},
startPolling() {
if (this.pollInterval) {
return;
}
this.pollInterval = setInterval(() => this.checkStatus(), 5000);
},
async checkStatus() {
try {
const response = await fetch(this.statusUrl);
const data = await response.json();
if (data.emptyDueToThreshold) {
this.emptyDueToThreshold = true;
this.showEmptyDueToThresholdNotice = true;
}
const nextModifiedDate = this.parseDate(data.modified);
const modifiedChanged = this.hasNewerTimestamp(nextModifiedDate, this.modifiedDate);
const statusChanged = data.status !== this.status;
if ((modifiedChanged || statusChanged) && !this.emptyDueToThreshold) {
this.showUpdateNotice = true;
this.updateMessage = this.getUpdateMessage(data.status, modifiedChanged, statusChanged);
}
this.modified = data.modified;
this.modifiedDate = nextModifiedDate;
this.status = data.status;
if (data.status !== 'running' && this.pollInterval) {
clearInterval(this.pollInterval);
this.pollInterval = null;
}
} catch (err) {
console.error('Polling error:', err);
}
},
getUpdateMessage(status, modifiedChanged, statusChanged) {
// Prefer explicit status change messaging, otherwise fall back to modified change copy
if (statusChanged) {
if (status === 'completed') {
return 'Prediction completed. Reload the page to see the updated Pathway.';
}
if (status === 'failed') {
return 'Prediction failed. Reload the page to see the latest status.';
}
}
let msg = 'Prediction ';
if (status === 'running') {
msg += 'is still running. But the Pathway was updated.';
} else if (status === 'completed') {
msg += 'is completed. Reload the page to see the updated Pathway.';
} else if (status === 'failed') {
msg += 'failed. Reload the page to see the current shape.';
}
return msg;
},
parseDate(dateString) {
// Normalize "YYYY-MM-DD HH:mm:ss" into an ISO-compatible string to avoid locale issues
if (!dateString) return null;
return new Date(dateString.replace(' ', 'T'));
},
hasNewerTimestamp(nextDate, currentDate) {
if (!nextDate) return false;
if (!currentDate) return true;
return nextDate.getTime() > currentDate.getTime();
},
reloadPage() {
location.reload();
}
}));
});