Desplegables dependientes en formulario de registro

Hola.

He logrado crear un código con la ayuda de GROK y ChatGPT, que inserté en la pestaña JS de los componentes, después de haber creado algunos campos de usuario personalizados en el menú de registro.

He conseguido que la funcionalidad del menú desplegable dependiente funcione, pero no es posible enviar el formulario.

El código está disponible actualmente para revisión en foorum.maarahvas.ee/signup

Aquí está el código JS que se está utilizando allí:

// dependent-dropdown.js
import { apiInitializer } from "discourse/lib/api";

export default apiInitializer("0.8", api => {
  if (window.__DEPENDENT_DROPDOWN_INITIALIZED) {
    console.debug("Dependent dropdown script already initialized, skipping.");
    return;
  }
  window.__DEPENDENT_DROPDOWN_INITIALIZED = true;

  const landParishMap = {
    "Virumaa": ["Ebavere (Väike-Maarja)", "Haljala", "Jõvi (Jõhvi)", "Katkuküla (Simuna)", "Lüganuse", "Mahu (Viru-Nigula)", "Rakvere", "Rõhu (Viru-Jaagupi)", "Torvastvere (Kadrina)", "Tärevere (Iisaku)", "Vaivara"],
    "Järvamaa": ["Ambla", "Keika (Järva-Jaani)", "Koeru", "Kullamäe (Järva-Madise)", "Nõstvere (Anna)", "Paide", "Türi", "Ämbra (Peetri)"],
    "Harjumaa": ["Hageri", "Juuru", "Jõelähtme", "Keila", "Kose", "Kuusalu", "Külaselja (Risti)", "Nissi", "Padise (Harju-Madise)", "Rapla", "Sahataguse (Harju-Jaani)", "Vaskjala (Jüri)"],
    "Läänemaa": ["Emaste (Emmaste)", "Hanila", "Karuse", "Kirbla", "Kullamaa", "Käina", "Lihula", "Märjamaa", "Noarootsi", "Pöhelepe (Pühalepa)", "Pööna (Lääne-Nigula)", "Reigi", "Rõdali (Ridala)", "Soontaga (Mihkli)", "Umra (Martna)", "Varbla", "Vigala", "Vormsi"],
    "Saaremaa": ["Anseküla", "Jämaja", "Kaarma", "Karja", "Kihelkonna", "Kärla", "Muhu", "Mustjala", "Para (Jaani)", "Pöide", "Püha", "Ruhnu", "Valjala"],
    "Pärnumaa": ["Aliste (Halliste)", "Audru", "Häädemeeste", "Karksi", "Kõrve (Pärnu-Jaagupi)", "Pärnu", "Ruhja", "Saarde", "Salatsi", "Soontaga (Mihkli)", "Tori", "Tõstamaa", "Vändra"],
    "Wiljandimaa": ["Helme", "Kolga (Kolga-Jaani)", "Kõpu", "Paistu", "Pilistvere", "Põltsamaa", "Tarvastu", "Valula (Suure-Jaani)", "Viljandi"],
    "Tartumaa": ["Kambja", "Kodavere", "Kursi", "Kõrenduse (Maarja-Magdaleena)", "Laiuse", "Nõo", "Otepää", "Palamuse", "Puhja", "Rannu", "Rõngu", "Sangaste", "Tartu (Tartu-Maarja)", "Tõrma (Torma)", "Võnnu", "Äksi"],
    "Võrumaa": ["Hargla", "Kanepi", "Karula", "Luke", "Põlva", "Rõugu (Rõuge)", "Räpinä (Räpina)", "Urvaste", "Vahtseliina (Vastseliina)", "Valga"],
    "Setomaa": ["Setomaa"]
  };

  const LAND_FIELD_NAME = "user_fields[6]";
  const PARISH_FIELD_NAME = "user_fields[8]";

  function findSignupForm() {
    return (
      document.querySelector(".user-signup-form") ||
      document.querySelector("form[action*='signup']") ||
      document.querySelector("form[id*='new-account']") ||
      document.querySelector("form:has(.user-field)")
    );
  }

  function resetSelectKitState(header, body, fieldName) {
    header.classList.remove("is-expanded", "is-invalid", "is-disabled");
    header.classList.add("is-valid");
    header.setAttribute("tabindex", "0");
    header.setAttribute("aria-expanded", "false");
    body.style.display = "";
    body.classList.remove("is-expanded");
    console.debug(`Resetting SelectKit state for ${fieldName}:`, {
      headerClasses: header.className,
      bodyDisplay: body.style.display,
      ariaExpanded: header.getAttribute("aria-expanded")
    });
    setTimeout(() => {
      body.style.display = "";
      header.classList.remove("is-expanded");
      header.focus();
    }, 100);
  }

  function initializeDropdowns() {
    try {
      if (!window.location.pathname.includes("/signup")) {
        console.debug("Not on sign-up page, skipping initialization");
        return;
      }

      const signupForm = findSignupForm();
      if (!signupForm) {
        console.error("Sign-up form not found");
        return;
      }

      const landField = Array.from(signupForm.querySelectorAll(".user-field")).find(field =>
        field.querySelector("label")?.textContent.trim() === "Maa"
      );
      const parishField = Array.from(signupForm.querySelectorAll(".user-field")).find(field =>
        field.querySelector("label")?.textContent.trim() === "Kodukihelkond"
      );

      const landHeader = landField?.querySelector(".select-kit-header");
      const parishHeader = parishField?.querySelector(".select-kit-header");
      const landBody = landField?.querySelector(".select-kit-body");
      const parishBody = parishField?.querySelector(".select-kit-body");

      console.debug("Form and field details:", {
        signupForm: !!signupForm,
        landField: !!landField,
        parishField: !!parishField,
        landHeader: !!landHeader,
        parishHeader: !!parishHeader,
        landBody: !!landBody,
        parishBody: !!parishBody
      });

      if (!landField || !parishField || !landHeader || !parishHeader || !landBody || !parishBody) {
        console.error("Required elements not found");
        return;
      }

      const landLabel = landField.querySelector("label");
      const parishLabel = parishField.querySelector("label");
      if (landLabel) landLabel.setAttribute("for", "user_fields_6");
      if (parishLabel) parishLabel.setAttribute("for", "user_fields_8");

      function getOrCreateInputField(fieldContainer, fieldName, inputId) {
        let inputField = signupForm.querySelector(`input[name='${fieldName}']`);
        if (!inputField) {
          console.warn(`No input field found for ${fieldName}, creating one`);
          inputField = document.createElement("input");
          inputField.type = "hidden";
          inputField.name = fieldName;
          inputField.id = inputId;
          inputField.value = "";
          inputField.setAttribute("required", "required");
          inputField.setAttribute("data-select-kit", "true");
          const controls = fieldContainer.querySelector(".controls");
          if (controls) {
            controls.appendChild(inputField);
          } else {
            fieldContainer.appendChild(inputField);
          }
        }
        console.debug(`Input field details for ${fieldName}:`, {
          name: inputField.name,
          value: inputField.value,
          id: inputField.id,
          parent: inputField.parentElement?.className || inputField.parentElement?.tagName
        });
        return inputField;
      }

      const landInput = getOrCreateInputField(landField, LAND_FIELD_NAME, "user_fields_6");
      const parishInput = getOrCreateInputField(parishField, PARISH_FIELD_NAME, "user_fields_8");

      const usernameInput = signupForm.querySelector("input[name='username']");
      const emailInput = signupForm.querySelector("input[name='email']");
      if (usernameInput) usernameInput.setAttribute("autocomplete", "username");
      if (emailInput) emailInput.setAttribute("autocomplete", "email");

      function updateParishOptions(selectedLand) {
        if (!parishBody) {
          console.error("Parish SelectKit body not found");
          return;
        }

        parishBody.innerHTML = "";
        const defaultOption = document.createElement("div");
        defaultOption.className = "select-kit-row is-none";
        defaultOption.setAttribute("data-value", "");
        defaultOption.setAttribute("data-name", "Vali kihelkond");
        defaultOption.innerHTML = '<span class="name">Vali kihelkond</span>';
        parishBody.appendChild(defaultOption);

        if (selectedLand && landParishMap[selectedLand]) {
          landParishMap[selectedLand].forEach(parish => {
            const option = document.createElement("div");
            option.className = "select-kit-row";
            option.setAttribute("data-value", parish);
            option.setAttribute("data-name", parish);
            option.innerHTML = `<span class="name">${parish}</span>`;
            parishBody.appendChild(option);
          });

          parishHeader.setAttribute("data-value", "");
          parishHeader.setAttribute("data-name", "Vali kihelkond");
          const selectedName = parishHeader.querySelector(".select-kit-selected-name");
          if (selectedName) {
            selectedName.innerHTML = '<span class="name">Vali kihelkond</span>';
          }
          parishHeader.setAttribute("aria-label", "Vali kihelkond");
          parishHeader.classList.remove("is-disabled", "is-invalid");
          parishHeader.classList.add("is-valid");
          parishHeader.setAttribute("tabindex", "0");

          parishInput.value = "";
          parishInput.classList.remove("invalid");
          parishInput.dispatchEvent(new Event("change", { bubbles: true }));
          parishInput.dispatchEvent(new Event("input", { bubbles: true }));
          parishInput.dispatchEvent(new Event("blur", { bubbles: true }));
        } else {
          parishHeader.classList.add("is-disabled");
          parishHeader.setAttribute("tabindex", "-1");
          parishInput.value = "";
          parishInput.dispatchEvent(new Event("change", { bubbles: true }));
        }

        resetSelectKitState(parishHeader, parishBody, "Kodukihelkond");
      }

      parishBody.addEventListener("click", (event) => {
        const row = event.target.closest(".select-kit-row:not(.is-none)");
        if (!row) return;

        const value = row.getAttribute("data-value");
        const name = row.getAttribute("data-name");
        parishHeader.setAttribute("data-value", value);
        parishHeader.setAttribute("data-name", name);
        const selectedName = parishHeader.querySelector(".select-kit-selected-name");
        if (selectedName) {
          selectedName.innerHTML = `<span class="name">${name}</span>`;
        }
        parishHeader.setAttribute("aria-label", `Valitud: ${name}`);
        parishHeader.classList.remove("is-invalid");
        parishHeader.classList.add("is-valid");
        parishHeader.closest(".select-kit")?.classList.remove("is-invalid");

        parishInput.value = value;
        parishInput.classList.remove("invalid");
        parishInput.dispatchEvent(new Event("change", { bubbles: true }));
        parishInput.dispatchEvent(new Event("input", { bubbles: true }));
        parishInput.dispatchEvent(new Event("blur", { bubbles: true }));
        const customEvent = new CustomEvent("select-kit:change", { bubbles: true, detail: { value } });
        parishInput.dispatchEvent(customEvent);
        signupForm.dispatchEvent(new Event("change", { bubbles: true }));

        console.debug("Parish input updated:", {
          value,
          valid: parishInput.checkValidity(),
          formValid: signupForm.checkValidity(),
          parishHeaderClasses: parishHeader.className,
          parishBodyDisplay: parishBody.style.display
        });

        resetSelectKitState(parishHeader, parishBody, "Kodukihelkond");
      });

      const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
          if (mutation.attributeName === "data-value") {
            const selectedLand = landHeader.getAttribute("data-value");
            landInput.value = selectedLand || "";
            landInput.classList.remove("invalid");
            landInput.dispatchEvent(new Event("change", { bubbles: true }));
            landInput.dispatchEvent(new Event("input", { bubbles: true }));
            landInput.dispatchEvent(new Event("blur", { bubbles: true }));
            console.debug("Land input updated:", {
              value: selectedLand,
              valid: landInput.checkValidity(),
              formValid: signupForm.checkValidity()
            });
            updateParishOptions(selectedLand);
          }
        });
      });

      observer.observe(landHeader, {
        attributes: true,
        attributeFilter: ["data-value"]
      });

      const initialLand = landHeader.getAttribute("data-value");
      landInput.value = initialLand || "";
      updateParishOptions(initialLand);

      const formObserver = new MutationObserver(() => {
        if (!signupForm.querySelector(`input[name='${LAND_FIELD_NAME}']`)) {
          console.warn("Land input missing, recreating");
          getOrCreateInputField(landField, LAND_FIELD_NAME, "user_fields_6");
        }
        if (!signupForm.querySelector(`input[name='${PARISH_FIELD_NAME}']`)) {
          console.warn("Parish input missing, recreating");
          getOrCreateInputField(parishField, PARISH_FIELD_NAME, "user_fields_8");
        }
      });

      formObserver.observe(signupForm, { childList: true, subtree: true });

      signupForm.addEventListener("submit", () => {
        console.debug("Form submission attempted:", {
          landValue: landInput.value,
          landValid: landInput.checkValidity(),
          parishValue: parishInput.value,
          parishValid: parishInput.checkValidity(),
          formValid: signupForm.checkValidity(),
          formErrors: Array.from(signupForm.querySelectorAll(".invalid")).map(el => el.name || el.id)
        });
      });

      signupForm.addEventListener("ajax:error", (event) => {
        console.debug("Form submission error:", event.detail);
      });

      console.log("Dependent dropdowns initialized successfully.");
    } catch (error) {
      console.error("Error initializing dependent dropdowns:", error);
    }
  }

  const formObserver = new MutationObserver((mutations, obs) => {
    const signupForm = findSignupForm();
    if (signupForm && signupForm.querySelector(".user-field")) {
      initializeDropdowns();
      obs.disconnect();
    }
  });

  formObserver.observe(document.body, {
    childList: true,
    subtree: true
  });

  setTimeout(initializeDropdowns, 5000);
});

¿Alguien tiene alguna idea de qué está mal allí y cómo se puede solucionar?
Gracias

1 me gusta