assets_js_controllers_csv-download-controller.js

import { Controller } from "@hotwired/stimulus"

/**
 * CsvDownload Stimulus Controller
 * 
 * Handles CSV file downloads with AJAX requests and automatic file saving.
 * Provides seamless CSV export functionality with error handling and 
 * proper resource cleanup.
 * 
 * Features:
 * - AJAX-based file download
 * - Automatic file saving via browser download
 * - Configurable filename and URL
 * - Error handling with user feedback
 * - Automatic resource cleanup
 * 
 * Values:
 * - url: String - URL to download CSV from
 * - filename: String - Name for downloaded file
 * 
 * Targets:
 * - button: Optional button element for click handling
 * 
 * Usage:
 * <button data-controller="csv-download" 
 *         data-csv-download-url-value="/export.csv"
 *         data-csv-download-filename-value="members.csv">
 *   Download CSV
 * </button>
 */
class CsvDownloadController extends Controller {
    static values = {
        url: String,
        filename: String
    }
    static targets = ["button"]

    /**
     * Download CSV file via AJAX and trigger browser download
     * Handles the complete download workflow with error handling
     * 
     * @param {Event} event - Click event from download trigger
     */
    async download(event) {
        event.preventDefault();
        const url = this.urlValue || this.element.getAttribute('href') || this.element.dataset.url;
        if (!url) {
            alert("No CSV URL provided.");
            return;
        }
        try {
            const response = await fetch(url, {
                headers: { 'X-Requested-With': 'XMLHttpRequest' }
            });
            if (!response.ok) {
                throw new Error(`Failed to download CSV: ${response.status}`);
            }
            const blob = await response.blob();
            const downloadUrl = window.URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = downloadUrl;
            a.download = this.filenameValue || 'export.csv';
            document.body.appendChild(a);
            a.click();
            setTimeout(() => {
                window.URL.revokeObjectURL(downloadUrl);
                a.remove();
            }, 100);
        } catch (error) {
            alert("Error downloading CSV: " + error.message);
        }
    }

    /**
     * Connect controller to DOM
     * Sets up event listeners for download triggering
     */
    connect() {
        if (this.hasButtonTarget) {
            this.buttonTarget.addEventListener('click', this.download.bind(this));
        } else {
            this.element.addEventListener('click', this.download.bind(this));
        }
    }

    /**
     * Disconnect controller from DOM
     * Cleans up event listeners
     */
    disconnect() {
        if (this.hasButtonTarget) {
            this.buttonTarget.removeEventListener('click', this.download.bind(this));
        } else {
            this.element.removeEventListener('click', this.download.bind(this));
        }
    }
}

if (!window.Controllers) {
    window.Controllers = {};
}
window.Controllers["csv-download"] = CsvDownloadController;