fix preserve selected download format

Show a spinner and disable the selected download button while its file is prepared. Use the clicked button action so XLSX downloads do not fall back to CSV.

Commit
83ac166cdc52e187849f7e032439e184165f67e2
Author
GPT-5 medium <codex@openai.com>
Author date
Committer
GPT-5 medium <codex@openai.com>
Committer date
Changed files
roles/dashboard/public/js/downloads.js
index 15f7fce3..c1ad5bf7 100644..100644
@@ -4,6 +4,7 @@
4 4 const downloadTo = document.querySelector("#download-to");
5 5 const errorBanner = document.querySelector("[data-downloads-error]");
6 6 const form = document.querySelector("form.downloads-form");
7 Added: let submittedButton = null;
7 8
8 9 downloadRangeButtons.forEach(option => {
9 10 option.addEventListener("change", () => {
@@ -57,13 +58,31 @@
57 58 return match ? match[1] : "readings.csv";
58 59 }
59 60
61 Added: function setDownloadPending(button, pending) {
62 Added: if (!button) return;
63 Added:
64 Added: button.disabled = pending;
65 Added: button.setAttribute("aria-busy", pending ? "true" : "false");
66 Added: button.querySelector("[data-download-spinner]")?.classList.toggle("d-none", !pending);
67 Added: }
68 Added:
60 69 if (form) {
70 Added: form.querySelectorAll("[data-download-button]").forEach(button => {
71 Added: button.addEventListener("click", () => {
72 Added: submittedButton = button;
73 Added: });
74 Added: });
75 Added:
61 76 form.addEventListener("submit", async event => {
62 77 event.preventDefault();
63 78 clearError();
64 79
80 Added: const button = event.submitter || submittedButton || form.querySelector("[data-download-button]");
65 81 const params = new URLSearchParams(new FormData(form));
66 Removed: const url = `${form.action}?${params}`;
82 Added: const action = button?.getAttribute("formaction") || form.action;
83 Added: const url = new URL(action, window.location.origin);
84 Added: url.search = params;
85 Added: setDownloadPending(button, true);
67 86
68 87 try {
69 88 const response = await fetch(url);
@@ -78,6 +97,9 @@
78 97 triggerDownload(blob, filenameFromResponse(response));
79 98 } catch (err) {
80 99 showError("Could not reach the server. Check your connection and try again.");
100 Added: } finally {
101 Added: setDownloadPending(button, false);
102 Added: submittedButton = null;
81 103 }
82 104 });
83 105 }
roles/dashboard/t/06-downloads.t
index 52b75f8b..5ee45ef0 100644..100644
@@ -29,10 +29,15 @@
29 29 '[data-custom-date-fields][hidden] input[name="from"][type="date"]')
30 30 ->element_exists(
31 31 '[data-custom-date-fields][hidden] input[name="to"][type="date"]')
32 Removed: ->text_is( 'button.downloads-button[type="submit"]', 'Download CSV' )
33 Removed: ->text_is(
32 Added: ->text_like( 'button.downloads-button[type="submit"]', qr/Download CSV/ )
33 Added: ->element_exists(
34 Added: 'button.downloads-button[type="submit"][data-download-button] [data-download-spinner].d-none')
35 Added: ->text_like(
34 36 'button.downloads-button[type="submit"][formaction="/downloads/readings.xlsx"]',
35 Removed: 'Download .xlsx' );
37 Added: qr/Download \.xlsx/ )
38 Added: ->element_exists(
39 Added: 'button.downloads-button[type="submit"][formaction="/downloads/readings.xlsx"][data-download-button] [data-download-spinner].d-none'
40 Added: );
36 41
37 42 for my $probe (qw(ph do orp ec)) {
38 43 $t->element_exists(qq{select#download-probe option[value="$probe"]});
roles/dashboard/templates/dashboard/downloads.html.ep
index 4e86f4b4..31bc6071 100644..100644
@@ -55,8 +55,14 @@
55 55 </fieldset>
56 56
57 57 <div>
58 Removed: <button class="btn btn-success downloads-button" type="submit">Download CSV</button>
59 Removed: <button class="btn btn-outline-success downloads-button" type="submit" formaction="/downloads/readings.xlsx">Download .xlsx</button>
58 Added: <button class="btn btn-success downloads-button" type="submit" data-download-button>
59 Added: <span class="spinner-border spinner-border-sm d-none" aria-hidden="true" data-download-spinner></span>
60 Added: Download CSV
61 Added: </button>
62 Added: <button class="btn btn-outline-success downloads-button" type="submit" formaction="/downloads/readings.xlsx" data-download-button>
63 Added: <span class="spinner-border spinner-border-sm d-none" aria-hidden="true" data-download-spinner></span>
64 Added: Download .xlsx
65 Added: </button>
60 66 </div>
61 67
62 68 </form>