From 73bf7f92f6ef40f36119479fa457c83a51c3f503 Mon Sep 17 00:00:00 2001 From: Rello Date: Sun, 28 Dec 2025 22:07:58 +0700 Subject: [PATCH] Add busy indicator during imports --- CHANGELOG.md | 1 + js/navigation.js | 13 ++++++++++--- js/sidebar.js | 47 ++++++++++++++++++++++++++++++++--------------- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff1d8c62..52fb2c37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Fixed - php 8.5 compatibility fix +- Prevent double submissions by showing busy indicators during report and data imports ## 6.0.2 - 2025-09-19 ### Fixed diff --git a/js/navigation.js b/js/navigation.js index b389f04e..0e91910e 100644 --- a/js/navigation.js +++ b/js/navigation.js @@ -813,18 +813,25 @@ OCA.Analytics.Navigation = { handleImportButton: function () { const fileInput = document.getElementById('importFile'); + const importButton = document.getElementById('importDatasetButton'); fileInput.click(); fileInput.addEventListener('change', async () => { const file = fileInput.files[0]; if (!file) { return; } + OCA.Analytics.Sidebar.Backend.setButtonBusy(importButton, true); const reader = new FileReader(); reader.readAsText(file); reader.onload = async () => { - OCA.Analytics.Sidebar.Report.import(null, reader.result); + OCA.Analytics.Sidebar.Report.import(null, reader.result, importButton); + fileInput.value = ''; }; - }) + reader.onerror = () => { + OCA.Analytics.Notification.notification('error', t('analytics', 'Import failed')); + OCA.Analytics.Sidebar.Backend.setButtonBusy(importButton, false); + }; + }, {once: true}); }, handleSettingsButton: function () { @@ -1212,4 +1219,4 @@ document.addEventListener('DOMContentLoaded', function () { document.getElementById('wizardStart').addEventListener('click', OCA.Analytics.Wizard.showFirstStart); document.getElementById('importDatasetButton').addEventListener('click', OCA.Analytics.Navigation.handleImportButton); -}); \ No newline at end of file +}); diff --git a/js/sidebar.js b/js/sidebar.js index a190e37e..7556999c 100644 --- a/js/sidebar.js +++ b/js/sidebar.js @@ -749,7 +749,7 @@ OCA.Analytics.Sidebar.Report = { window.open(OC.generateUrl('apps/analytics/report/export/') + reportId, '_blank') }, - import: function (path, raw) { + import: function (path, raw, button = null) { if (typeof raw === 'number') raw = null; // file picker is returning some INT which is not helpful in the service let requestUrl = OC.generateUrl('apps/analytics/report/import/'); let headers = new Headers(); @@ -757,7 +757,9 @@ OCA.Analytics.Sidebar.Report = { headers.append('OCS-APIREQUEST', 'true'); headers.append('Content-Type', 'application/json'); - fetch(requestUrl, { + OCA.Analytics.Sidebar.Backend.setButtonBusy(button, true); + + return fetch(requestUrl, { method: 'POST', headers: OCA.Analytics.headers(), body: JSON.stringify({ @@ -768,6 +770,12 @@ OCA.Analytics.Sidebar.Report = { .then(response => response.json()) .then(data => { OCA.Analytics.Navigation.init(); + }) + .catch(error => { + OCA.Analytics.Notification.notification('error', t('analytics', 'Import failed')); + }) + .finally(() => { + OCA.Analytics.Sidebar.Backend.setButtonBusy(button, false); }); }, @@ -1250,6 +1258,19 @@ OCA.Analytics.Threshold = { OCA.Analytics.Sidebar.Backend = { + setButtonBusy: function (button, isBusy) { + if (!button) { + return; + } + if (isBusy) { + button.classList.add('loading'); + button.disabled = true; + } else { + button.classList.remove('loading'); + button.disabled = false; + } + }, + updateData: function () { const reportId = parseInt(document.getElementById('app-sidebar').dataset.id); const button = document.getElementById('updateDataButton'); @@ -1342,9 +1363,8 @@ OCA.Analytics.Sidebar.Backend = { importCsvData: function () { const reportId = parseInt(document.getElementById('app-sidebar').dataset.id); - const button = document.getElementById('importDataClipboardButton'); - button.classList.add('loading'); - button.disabled = true; + const button = document.getElementById('importDataClipboardButtonGo'); + OCA.Analytics.Sidebar.Backend.setButtonBusy(button, true); let requestUrl = OC.generateUrl('apps/analytics/data/importCSV'); fetch(requestUrl, { @@ -1358,8 +1378,6 @@ OCA.Analytics.Sidebar.Backend = { }) .then(response => response.json()) .then(data => { - button.classList.remove('loading'); - button.disabled = false; if (data.error === 0) { OCA.Analytics.Notification.notification('success', data.insert + ' ' + t('analytics', 'records inserted') + ', ' + data.update + ' ' + t('analytics', 'records updated')); if (!OCA.Analytics.isDataset) { @@ -1372,16 +1390,16 @@ OCA.Analytics.Sidebar.Backend = { }) .catch(error => { OCA.Analytics.Notification.notification('error', t('analytics', 'Technical error. Please check the logs.')); - button.classList.remove('loading'); - button.disabled = false; + }) + .finally(() => { + OCA.Analytics.Sidebar.Backend.setButtonBusy(button, false); }); }, importFileData: function (path) { const reportId = parseInt(document.getElementById('app-sidebar').dataset.id); const button = document.getElementById('importDataFileButton'); - button.classList.add('loading'); - button.disabled = true; + OCA.Analytics.Sidebar.Backend.setButtonBusy(button, true); let requestUrl = OC.generateUrl('apps/analytics/data/importFile'); fetch(requestUrl, { @@ -1395,8 +1413,6 @@ OCA.Analytics.Sidebar.Backend = { }) .then(response => response.json()) .then(data => { - button.classList.remove('loading'); - button.disabled = false; if (data.error === 0) { OCA.Analytics.Notification.notification('success', data.insert + ' ' + t('analytics', 'records inserted') + ', ' + data.update + ' ' + t('analytics', 'records updated')); if (!OCA.Analytics.isDataset) { @@ -1409,8 +1425,9 @@ OCA.Analytics.Sidebar.Backend = { }) .catch(error => { OCA.Analytics.Notification.notification('error', t('analytics', 'Technical error. Please check the logs.')); - button.classList.remove('loading'); - button.disabled = false; + }) + .finally(() => { + OCA.Analytics.Sidebar.Backend.setButtonBusy(button, false); }); }, };