From c389d82b547ae1fb451c6c2d64ad5deead7a29fe Mon Sep 17 00:00:00 2001 From: bellitabellota <136174454+bellitabellota@users.noreply.github.com> Date: Tue, 2 Dec 2025 12:14:20 +0000 Subject: [PATCH 1/2] Update 'Export Audits' button to be handled by Stimulus controller --- app/views/audits/index.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/audits/index.html.erb b/app/views/audits/index.html.erb index 38871de767..16d4eba5da 100644 --- a/app/views/audits/index.html.erb +++ b/app/views/audits/index.html.erb @@ -47,7 +47,7 @@ <%= clear_filter_button %> - <%= download_button_to(audits_path(format: :csv), {text: "Export Audits", size: "md"}) %> + <%= download_button_to(audits_path(format: :csv), {text: "Export Audits", size: "md", data: {controller: "csv-download-button", disable_with: nil}}) %> <%= new_button_to new_audit_path, {text: "New Audit"} %> <% end # form %> From c75196a830d915a2a7528c7f6d13ea5f3b651954 Mon Sep 17 00:00:00 2001 From: bellitabellota <136174454+bellitabellota@users.noreply.github.com> Date: Tue, 2 Dec 2025 12:14:45 +0000 Subject: [PATCH 2/2] Implement Stimulus controller for CSV download --- .../csv_download_button_controller.js | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 app/javascript/controllers/csv_download_button_controller.js diff --git a/app/javascript/controllers/csv_download_button_controller.js b/app/javascript/controllers/csv_download_button_controller.js new file mode 100644 index 0000000000..462a4b9d3b --- /dev/null +++ b/app/javascript/controllers/csv_download_button_controller.js @@ -0,0 +1,55 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + connect() { + this.element.addEventListener("click", this.handleClick.bind(this)); + } + + handleClick(event) { + event.preventDefault(); + if (this.element.disabled) return; + + this.element.disabled = true; + this.originalButtonText = this.element.textContent; + this.element.textContent = "Please wait..."; + + let filename = "export"; + + const url = this.element.href + fetch(url, { headers: { Accept: "text/csv" } }) + .then(response => { + const contentType = response.headers.get("content-type"); + if (!response.ok) { + throw new Error(`HTTP error. Status: ${response.status}`); + } + if (!contentType.includes("text/csv")) { + throw new Error(`Unexpected content type: ${contentType}`); + } + + if(this.extractFilename(response)) { + filename = this.extractFilename(response); + } + + return response.blob(); + }) + .then(blob => { + const a = document.createElement("a"); + a.href = URL.createObjectURL(blob); + a.download = filename; + a.click(); + URL.revokeObjectURL(a.href); + }) + .catch((error) => console.log(`CSV Download failed: ${error}`) + ) + .finally(() => { + this.element.textContent = this.originalButtonText; + this.element.disabled = false; + }) + } + + extractFilename(response) { + const contentDisposition = response.headers.get("content-disposition"); + const match = contentDisposition.match(/filename="([^"]*)"/); + return match ? match[1] : null; + } +}