plugins_Awards_Assets_js_controllers_rec-add-controller.js

import { Controller } from "@hotwired/stimulus";

/**
 * Awards Recommendation Add Form Controller
 *
 * Handles new recommendation submission with member validation, hierarchical award
 * selection via tabbed interface, and dynamic specialty population.
 *
 * Targets: scaMember, notFound, branch, externalLinks, awardDescriptions, award,
 *          reason, gatherings, specialty
 * Values: publicProfileUrl (String), awardListUrl (String), gatheringsUrl (String)
 */
class AwardsRecommendationAddForm extends Controller {
    static targets = [
        "scaMember",
        "notFound",
        "branch",
        "externalLinks",
        "awardDescriptions",
        "award",
        "reason",
        "gatherings",
        "specialty",
    ];
    static values = {
        publicProfileUrl: String,
        awardListUrl: String,
        gatheringsUrl: String
    };

    /** Enable disabled fields before form submission. */
    submit(event) {
        this.notFoundTarget.disabled = false;
        this.scaMemberTarget.disabled = false;
        this.specialtyTarget.disabled = false;
    }

    /** Handle award tab selection, populate specialties, and update gatherings. */
    setAward(event) {
        let awardId = event.target.dataset.awardId;
        this.awardTarget.value = awardId;
        this.populateSpecialties(event);
        this.updateGatherings(awardId);
    }

    /** Fetch gatherings filtered by award and update checkboxes. */
    updateGatherings(awardId) {
        if (!awardId || !this.hasGatheringsTarget) {
            return;
        }

        // Get member_id if available
        let memberId = this.hasScaMemberTarget ? this.scaMemberTarget.value : '';

        // Build URL with query params
        let url = this.gatheringsUrlValue + '/' + awardId;
        if (memberId) {
            url += '?member_id=' + memberId;
        }

        fetch(url, this.optionsForFetch())
            .then(response => response.json())
            .then(data => {
                if (data.gatherings) {
                    // Get the container and find the fieldset/form-group within
                    const container = this.gatheringsTarget;

                    // Find and preserve the label
                    const label = container.querySelector('label.form-label, legend');
                    const labelText = label ? label.textContent : 'Gatherings/Events They May Attend:';

                    // Clear existing content
                    container.innerHTML = '';

                    // Rebuild with new checkboxes
                    if (data.gatherings.length > 0) {
                        // Add the label back
                        const newLabel = document.createElement('label');
                        newLabel.className = 'form-label';
                        newLabel.textContent = labelText;
                        container.appendChild(newLabel);

                        // Add hidden input for empty submission
                        const hiddenInput = document.createElement('input');
                        hiddenInput.type = 'hidden';
                        hiddenInput.name = 'gatherings[_ids]';
                        hiddenInput.value = '';
                        container.appendChild(hiddenInput);

                        // Add checkbox for each gathering
                        data.gatherings.forEach(gathering => {
                            const checkDiv = document.createElement('div');
                            checkDiv.className = 'form-check';

                            const checkbox = document.createElement('input');
                            checkbox.type = 'checkbox';
                            checkbox.className = 'form-check-input';
                            checkbox.name = 'gatherings[_ids][]';
                            checkbox.value = gathering.id;
                            checkbox.id = 'gatherings-ids-' + gathering.id;

                            const checkLabel = document.createElement('label');
                            checkLabel.className = 'form-check-label';
                            checkLabel.htmlFor = 'gatherings-ids-' + gathering.id;
                            checkLabel.textContent = gathering.display;

                            checkDiv.appendChild(checkbox);
                            checkDiv.appendChild(checkLabel);
                            container.appendChild(checkDiv);
                        });
                    } else {
                        // No gatherings available - show message
                        const newLabel = document.createElement('label');
                        newLabel.className = 'form-label';
                        newLabel.textContent = labelText;
                        container.appendChild(newLabel);

                        const noGatherings = document.createElement('p');
                        noGatherings.className = 'text-muted';
                        noGatherings.textContent = 'No gatherings available for this award.';
                        container.appendChild(noGatherings);
                    }
                }
            })
            .catch(error => {
                console.error('Error fetching gatherings:', error);
            });
    }

    /** Get standard fetch options with JSON headers. */
    optionsForFetch() {
        return {
            headers: {
                "X-Requested-With": "XMLHttpRequest",
                "Accept": "application/json"
            }
        }
    }

    /** Fetch awards for domain and create tabbed selection interface. */
    populateAwardDescriptions(event) {
        let url = this.awardListUrlValue + "/" + event.target.value;
        fetch(url, this.optionsForFetch())
            .then(response => response.json())
            .then(data => {
                this.awardDescriptionsTarget.innerHTML = "";

                let tabButtons = document.createElement("ul");
                tabButtons.classList.add("nav", "nav-pills");
                tabButtons.setAttribute("role", "tablist");

                let tabContentArea = document.createElement("div");
                tabContentArea.classList.add("tab-content");
                tabContentArea.classList.add("border");
                tabContentArea.classList.add("border-light-subtle");
                tabContentArea.classList.add("p-2");

                tabContentArea.innerHTML = "";
                this.awardTarget.value = "";
                let active = "active";
                let show = "show";
                let selected = "true";
                let awardList = [];
                if (data.length > 0) {
                    data.forEach(function (award) {
                        //create list item
                        awardList.push({ value: award.id, text: award.name, data: award });
                        //create tab info
                        var tabButton = document.createElement("li");
                        tabButton.classList.add("nav-item");
                        tabButton.setAttribute("role", "presentation");
                        var button = document.createElement("button");
                        button.classList.add("nav-link");
                        if (active == "active") {
                            button.classList.add("active");
                        }
                        button.setAttribute("data-action", "click->awards-rec-add#setAward");
                        button.setAttribute("id", "award_" + award.id + "_btn");
                        button.setAttribute("data-bs-toggle", "tab");
                        button.setAttribute("data-bs-target", "#award_" + award.id);
                        button.setAttribute('data-award-id', award.id);
                        button.setAttribute("type", "button");
                        button.setAttribute("role", "tab");
                        button.setAttribute("aria-controls", "award_" + award.id);
                        button.setAttribute("aria-selected", selected);
                        button.innerHTML = award.name;
                        tabButton.appendChild(button);
                        var tabContent = document.createElement("div");
                        tabContent.classList.add("tab-pane");
                        tabContent.classList.add("fade");
                        if (show == "show") {
                            tabContent.classList.add("show");
                        }
                        if (active == "active") {
                            tabContent.classList.add("active");
                        }
                        tabContent.setAttribute("id", "award_" + award.id);
                        tabContent.setAttribute("role", "tabpanel");
                        tabContent.setAttribute("aria-labelledby", "award_" + award.id + "_btn");
                        tabContent.innerHTML = award.name + ": " + award.description;
                        active = "";
                        show = "";
                        selected = "false";
                        tabButtons.append(tabButton);
                        tabContentArea.append(tabContent);

                    });
                    this.awardDescriptionsTarget.appendChild(tabButtons);
                    this.awardDescriptionsTarget.appendChild(tabContentArea);
                    this.awardTarget.options = awardList;
                    this.awardTarget.disabled = false;
                } else {
                    this.awardTarget.options = [{ value: "No awards available", text: "No awards available" }];
                    this.awardTarget.value = "No awards available";
                    this.awardTarget.disabled = true;
                }
            });
    }
    /** Update specialty dropdown based on selected award's configuration. */
    populateSpecialties(event) {
        let awardId = this.awardTarget.value;
        let options = this.awardTarget.options;
        let award = this.awardTarget.options.find(award => award.value == awardId);
        let specialtyArray = [];
        if (award.data.specialties != null && award.data.specialties.length > 0) {
            award.data.specialties.forEach(function (specialty) {
                specialtyArray.push({ value: specialty, text: specialty });
            });
            this.specialtyTarget.options = specialtyArray;
            this.specialtyTarget.value = "";
            this.specialtyTarget.disabled = false;
            this.specialtyTarget.hidden = false;
        } else {
            this.specialtyTarget.options = [{ value: "No specialties available", text: "No specialties available" }];
            this.specialtyTarget.value = "No specialties available";
            this.specialtyTarget.disabled = true
            this.specialtyTarget.hidden = true;
        }
        // Also update gatherings when award changes via autocomplete selection
        this.updateGatherings(awardId);
    }

    /** Handle member field change, load profile or show branch field if not found. */
    loadScaMemberInfo(event) {
        //reset member metadata area
        this.externalLinksTarget.innerHTML = "";
        let memberPublicId = event.target.value;
        if (memberPublicId && memberPublicId.length > 0) {
            this.notFoundTarget.checked = false;
            this.branchTarget.hidden = true;
            this.branchTarget.disabled = true;
            this.loadMember(memberPublicId);
        } else {
            this.notFoundTarget.checked = true;
            this.branchTarget.hidden = false;
            this.branchTarget.disabled = false;
            this.branchTarget.focus();
        }

    }

    /** Fetch and display member profile external links. */
    loadMember(memberPublicId) {
        let url = this.publicProfileUrlValue + "/" + memberPublicId;
        fetch(url, this.optionsForFetch())
            .then(response => response.json())
            .then(data => {
                this.externalLinksTarget.innerHTML = "";
                let keys = Object.keys(data.external_links);
                if (keys.length > 0) {
                    var LinksTitle = document.createElement("div");
                    LinksTitle.innerHTML = "<h5>Public Links</h5>";
                    LinksTitle.classList.add("col-12");
                    this.externalLinksTarget.appendChild(LinksTitle);
                    for (let key in data.external_links) {
                        let div = document.createElement("div");
                        div.classList.add("col-12");
                        let a = document.createElement("a");
                        a.href = data.external_links[key];
                        a.text = key;
                        a.target = "_blank";
                        div.appendChild(a);
                        this.externalLinksTarget.appendChild(div);
                    }
                } else {
                    var noLink = document.createElement("div");
                    noLink.innerHTML = "<h5>No links available</h5>";
                    noLink.classList.add("col-12");
                    this.externalLinksTarget.appendChild(noLink);
                }
            });
    }

    /** Initialize field state when autocomplete connects. */
    acConnected(event) {
        var target = event.detail["awardsRecAddTarget"];
        switch (target) {
            case "branch":
                this.branchTarget.disabled = true;
                this.branchTarget.hidden = true;
                this.branchTarget.value = "";
                break;
            case "award":
                this.awardTarget.disabled = true;
                this.awardTarget.value = "Select Award Type First";
                break;
            case "scaMember":
                this.scaMemberTarget.value = "";
                break;
            case "specialty":
                this.specialtyTarget.value = "Select Award First";
                this.specialtyTarget.disabled = true;
                this.specialtyTarget.hidden = true;
                break;
            default:
                event.target.value = "";
                break;
        }
    }

    /** Initialize form state with disabled fields and empty values. */
    connect() {
        this.notFoundTarget.checked = false;
        this.notFoundTarget.disabled = true;
        this.reasonTarget.value = "";
        //this.personToNotifyTarget.value = "";
        if (this.hasGatheringsTarget) {
            // Disable all checkboxes within the gatherings container
            this.gatheringsTarget.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
                checkbox.checked = false;
                checkbox.disabled = true;
            });
        }
    }
}
// add to window.Controllers with a name of the controller
if (!window.Controllers) {
    window.Controllers = {};
}
window.Controllers["awards-rec-add"] = AwardsRecommendationAddForm;