plugins_Waivers_assets_js_controllers_waiver-attestation-controller.js

import { Controller } from "@hotwired/stimulus"

/**
 * Waiver Attestation Controller
 * 
 * Manages the modal for attesting that a waiver is not needed for a specific
 * activity/waiver type combination. Handles:
 * - Modal display with configurable reasons
 * - Reason selection
 * - Form submission via AJAX
 * - Success/error feedback
 * 
 * **Data Values**:
 * - activityId: Gathering activity ID
 * - waiverTypeId: Waiver type ID
 * - gatheringId: Gathering ID
 * - reasons: JSON array of exemption reasons
 * 
 * **Targets**:
 * - modal: The Bootstrap modal element
 * - reasonList: Container for reason radio buttons
 * - notes: Textarea for optional notes
 * - submitBtn: Submit button
 * - error: Error message container
 * - success: Success message container
 * 
 * **Actions**:
 * - showModal: Displays the modal with reasons
 * - submitAttestation: Submits the attestation form
 * 
 * @see GatheringWaiversController.attest() Server endpoint
 */
class WaiverAttestationController extends Controller {
    static targets = ["modal", "reasonList", "notes", "submitBtn", "error", "success"]
    static values = {
        activityId: Number,
        waiverTypeId: Number,
        gatheringId: Number,
        reasons: Array
    }

    /**
     * Initialize Bootstrap modal instance
     */
    connect() {
        if (this.hasModalTarget) {
            this.modalInstance = new bootstrap.Modal(this.modalTarget)
        }
    }

    /**
     * Show the modal and populate with reasons
     * Called when user clicks "Attest Not Needed" button
     */
    showModal(event) {
        event.preventDefault()
        
        // Get values from the button that was clicked
        const btn = event.currentTarget
        this.activityIdValue = parseInt(btn.dataset.activityId)
        this.waiverTypeIdValue = parseInt(btn.dataset.waiverTypeId)
        this.gatheringIdValue = parseInt(btn.dataset.gatheringId)
        
        try {
            this.reasonsValue = JSON.parse(btn.dataset.reasons)
        } catch (e) {
            console.error('Failed to parse reasons:', e)
            this.reasonsValue = []
        }

        // Populate reasons
        this.populateReasons()

        // Clear previous state
        this.clearMessages()
        if (this.hasNotesTarget) {
            this.notesTarget.value = ''
        }

        // Show modal
        if (this.modalInstance) {
            this.modalInstance.show()
        }
    }

    /**
     * Populate the reason selection radio buttons
     */
    populateReasons() {
        if (!this.hasReasonListTarget) return

        const reasons = this.reasonsValue || []
        
        if (reasons.length === 0) {
            this.reasonListTarget.innerHTML = `
                <div class="alert alert-warning">
                    <i class="bi bi-exclamation-triangle"></i>
                    No exemption reasons have been configured for this waiver type.
                </div>
            `
            if (this.hasSubmitBtnTarget) {
                this.submitBtnTarget.disabled = true
            }
            return
        }

        // Build radio buttons
        let html = '<div class="list-group">'
        reasons.forEach((reason, index) => {
            const id = `reason_${index}`
            html += `
                <label class="list-group-item list-group-item-action">
                    <input class="form-check-input me-2" type="radio" name="attestation_reason" 
                           id="${id}" value="${this.escapeHtml(reason)}">
                    ${this.escapeHtml(reason)}
                </label>
            `
        })
        html += '</div>'

        this.reasonListTarget.innerHTML = html

        if (this.hasSubmitBtnTarget) {
            this.submitBtnTarget.disabled = false
        }
    }

    /**
     * Submit the attestation form
     */
    async submitAttestation(event) {
        event.preventDefault()

        // Get selected reason
        const selectedReason = this.reasonListTarget.querySelector('input[name="attestation_reason"]:checked')
        
        if (!selectedReason) {
            this.showError('Please select a reason for the exemption.')
            return
        }

        const reason = selectedReason.value
        const notes = this.hasNotesTarget ? this.notesTarget.value : ''

        // Disable submit button
        if (this.hasSubmitBtnTarget) {
            this.submitBtnTarget.disabled = true
            this.submitBtnTarget.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> Submitting...'
        }

        this.clearMessages()

        try {
            // Submit via AJAX
            const response = await fetch('/waivers/gathering-waivers/attest', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-Requested-With': 'XMLHttpRequest',
                    'X-CSRF-Token': this.getCsrfToken()
                },
                body: JSON.stringify({
                    gathering_activity_id: this.activityIdValue,
                    waiver_type_id: this.waiverTypeIdValue,
                    gathering_id: this.gatheringIdValue,
                    reason: reason,
                    notes: notes
                })
            })

            const data = await response.json()

            if (response.ok && data.success) {
                this.showSuccess(data.message || 'Attestation recorded successfully.')
                
                // Reload page after short delay
                setTimeout(() => {
                    window.location.reload()
                }, 1500)
            } else {
                this.showError(data.message || 'Failed to record attestation.')
                this.resetSubmitButton()
            }
        } catch (error) {
            console.error('Error submitting attestation:', error)
            this.showError('An error occurred while submitting the attestation.')
            this.resetSubmitButton()
        }
    }

    /**
     * Show error message
     */
    showError(message) {
        if (this.hasErrorTarget) {
            this.errorTarget.textContent = message
            this.errorTarget.classList.remove('d-none')
        }
        if (this.hasSuccessTarget) {
            this.successTarget.classList.add('d-none')
        }
    }

    /**
     * Show success message
     */
    showSuccess(message) {
        if (this.hasSuccessTarget) {
            this.successTarget.textContent = message
            this.successTarget.classList.remove('d-none')
        }
        if (this.hasErrorTarget) {
            this.errorTarget.classList.add('d-none')
        }
    }

    /**
     * Clear all messages
     */
    clearMessages() {
        if (this.hasErrorTarget) {
            this.errorTarget.classList.add('d-none')
        }
        if (this.hasSuccessTarget) {
            this.successTarget.classList.add('d-none')
        }
    }

    /**
     * Reset submit button to original state
     */
    resetSubmitButton() {
        if (this.hasSubmitBtnTarget) {
            this.submitBtnTarget.disabled = false
            this.submitBtnTarget.innerHTML = '<i class="bi bi-shield-check"></i> Submit Attestation'
        }
    }

    /**
     * Get CSRF token from meta tag
     */
    getCsrfToken() {
        const meta = document.querySelector('meta[name="csrf-token"]')
        return meta ? meta.getAttribute('content') : ''
    }

    /**
     * Escape HTML to prevent XSS
     */
    escapeHtml(text) {
        const div = document.createElement('div')
        div.textContent = text
        return div.innerHTML
    }
}

// Register controller
if (!window.Controllers) {
    window.Controllers = {}
}
window.Controllers["waivers-waiver-attestation"] = WaiverAttestationController