assets_js_controllers_app-setting-modal-controller.js

import { Controller } from "@hotwired/stimulus"

/**
 * AppSettingModal Stimulus Controller
 * 
 * Manages the edit modal for app settings, handling:
 * - Loading edit form via turbo-frame when edit button is clicked
 * - Coordinating with outlet-btn controller for data passing
 * - Managing modal state and turbo-frame loading
 * 
 * Features:
 * - Dynamic form loading via turbo-frame
 * - Integration with outlet-btn for row data
 * - Bootstrap modal coordination
 * - Loading state management
 * 
 * Values:
 * - editUrl: String - Base URL for edit action
 * - modalId: String - ID of the modal element (default: editAppSettingModal)
 * - frameId: String - ID of the turbo-frame element (default: editAppSettingFrame)
 * 
 * Note: This controller uses direct DOM queries for the modal and frame elements
 * because they are rendered in the modals block which is outside the controller's
 * DOM scope. Stimulus targets only work within the controller's element tree.
 * 
 * Usage:
 * <div data-controller="app-setting-modal"
 *      data-app-setting-modal-edit-url-value="/app-settings/edit"
 *      data-app-setting-modal-modal-id-value="editAppSettingModal"
 *      data-app-setting-modal-frame-id-value="editAppSettingFrame">
 *   <!-- Grid with edit buttons -->
 * </div>
 * <!-- Modal rendered separately in modals block -->
 * <div id="editAppSettingModal" class="modal">
 *   <turbo-frame id="editAppSettingFrame">
 *     <!-- Content loaded here -->
 *   </turbo-frame>
 * </div>
 */
class AppSettingModalController extends Controller {
    static values = {
        editUrl: String,
        modalId: { type: String, default: 'editAppSettingModal' },
        frameId: { type: String, default: 'editAppSettingFrame' }
    }

    /**
     * Initialize controller
     */
    initialize() {
        this.modalInstance = null
        this.boundHandleOutletClick = this.handleOutletClick.bind(this)
    }

    /**
     * Get the modal element by ID
     * Uses direct DOM query since modal is outside controller scope
     */
    get modalElement() {
        return document.getElementById(this.modalIdValue)
    }

    /**
     * Get the frame element by ID
     * Uses direct DOM query since frame is outside controller scope
     */
    get frameElement() {
        return document.getElementById(this.frameIdValue)
    }

    /**
     * Connect - set up event listeners
     */
    connect() {
        console.log('AppSettingModal controller connected')

        // Initialize Bootstrap modal (deferred until first use)
        // Listen for outlet-btn clicks from the grid
        document.addEventListener('outlet-btn:outlet-button-clicked', this.boundHandleOutletClick)
    }

    /**
     * Disconnect - clean up event listeners
     */
    disconnect() {
        document.removeEventListener('outlet-btn:outlet-button-clicked', this.boundHandleOutletClick)
    }

    /**
     * Handle outlet button click event
     * Loads the edit form for the clicked setting
     * 
     * @param {CustomEvent} event - The outlet-button-clicked event
     */
    handleOutletClick(event) {
        const data = event.detail

        // Check if this is for our modal (the button target is our modal)
        const clickedButton = event.target
        if (!clickedButton) return

        const modalTarget = clickedButton.getAttribute('data-bs-target')
        if (modalTarget !== `#${this.modalIdValue}`) return

        console.log('AppSettingModal: Edit clicked for setting:', data)

        if (data && data.id) {
            this.loadEditForm(data.id)
        }
    }

    /**
     * Load the edit form into the turbo-frame
     * 
     * @param {string|number} id - The app setting ID to edit
     */
    loadEditForm(id) {
        const frameEl = this.frameElement
        if (!frameEl) {
            console.error('AppSettingModal: Frame element not found with ID:', this.frameIdValue)
            return
        }

        // Build the edit URL with the setting ID
        const editUrl = `${this.editUrlValue}/${id}`
        console.log('AppSettingModal: Loading edit form from:', editUrl)

        // Reset frame content to show loading state first
        frameEl.innerHTML = `
            <div class="modal-header">
                <h5 class="modal-title">Edit App Setting</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <div class="text-center p-5">
                    <div class="spinner-border text-primary" role="status">
                        <span class="visually-hidden">Loading...</span>
                    </div>
                    <p class="mt-2">Loading setting...</p>
                </div>
            </div>
        `

        // Set the src to trigger turbo-frame load
        frameEl.src = editUrl
    }
}

// Add to global controllers registry
if (!window.Controllers) {
    window.Controllers = {};
}
window.Controllers["app-setting-modal"] = AppSettingModalController;