AkurAI Build
Menu

Public repository / 0c236c0a909a7e427ba31e516b9f4fa15cbcd6ce

AkurAI-Platform / frontend/contact.js

Latest change f0014e563961e05e2913923cda58289ad20d27a7 - feat: persist acquisition attribution with durable CRM handoff (AKURAI-PLATFORM-25) by Ólafur Búi Ólafsson

// contact.js — Contact/demo/waitlist inquiry form with attribution capture
const modal = document.getElementById("contact-modal");
const dialog = modal?.querySelector("[role=dialog]");
const form = document.getElementById("contact-form");
const submit = document.getElementById("contact-submit");
const consent = document.getElementById("contact-consent");
const confirmation = document.getElementById("contact-confirmation");
const locale = document.documentElement.lang || "en";

function isIcelandic() {
  return locale.startsWith("is");
}

function captureAttribution() {
  const params = new URLSearchParams(location.search);
  const attr = {};
  const utmKeys = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
  for (const key of utmKeys) {
    const val = params.get(key);
    if (val) attr[key.replace(/_([a-z])/g, (_, c) => c.toUpperCase())] = val;
  }
  const ref = document.referrer;
  if (ref && !ref.startsWith(location.origin)) attr.referrer = ref;
  attr.landingPage = location.pathname + location.search;
  return attr;
}

function openModal() {
  if (!modal || !dialog) return;
  modal.hidden = false;
  if (confirmation) confirmation.hidden = true;
  if (submit) { submit.disabled = false; submit.textContent = isIcelandic() ? "Senda" : "Send inquiry"; }
  if (form) form.reset();
  if (consent) consent.setAttribute("aria-checked", "false");
  const firstInput = form?.querySelector("input[name=name]");
  if (firstInput instanceof HTMLInputElement) firstInput.focus();
}

function closeModal() {
  if (!modal) return;
  modal.hidden = true;
}

function showConfirmation(msg) {
  if (!confirmation) return;
  confirmation.textContent = msg;
  confirmation.hidden = false;
  if (submit) submit.hidden = true;
}

// Wire trigger buttons
document.querySelectorAll("[data-contact-trigger]").forEach((btn) => {
  btn.addEventListener("click", openModal);
});

// Wire close buttons
if (modal) {
  modal.querySelectorAll("[data-contact-close]").forEach((btn) => {
    btn.addEventListener("click", closeModal);
  });
  modal.addEventListener("keydown", (event) => {
    if (event.key === "Escape") closeModal();
  });
  // Prevent backdrop click from closing during active state
  modal.addEventListener("click", (event) => {
    if (event.target === modal) closeModal();
  });
}

// Consent toggle
if (consent) {
  consent.addEventListener("click", () => {
    const checked = consent.getAttribute("aria-checked") !== "true";
    consent.setAttribute("aria-checked", String(checked));
  });
}

// Form submission
if (form) {
  form.addEventListener("submit", async (event) => {
    event.preventDefault();
    const name = form.elements.name.value.trim();
    const email = form.elements.email.value.trim();
    const outcome = form.elements.outcome?.value || "contact";
    const company = form.elements.company?.value?.trim() || "";
    const intent = form.elements.intent?.value?.trim() || "";
    if (!name || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
      if (!name) form.elements.name.focus();
      else form.elements.email.focus();
      return;
    }
    if (consent && consent.getAttribute("aria-checked") !== "true") {
      consent.focus();
      return;
    }

    const originalLabel = submit.textContent;
    submit.disabled = true;
    submit.textContent = isIcelandic() ? "Sendi…" : "Sending…";

    try {
      const attribution = captureAttribution();
      const response = await fetch("/api/contact", {
        method: "POST",
        headers: { "content-type": "application/json", accept: "application/json" },
        body: JSON.stringify({
          name, email, company, outcome, intent,
          locale,
          productIds: [],
          consent: {
            noticeVersion: "v1",
            grants: ["contact"],
            explicit: true,
          },
          ...attribution,
        }),
      });
      if (!response.ok) throw new Error("contact_failed");
      const data = await response.json();
      showConfirmation(data.message || (
        isIcelandic() ? "Skráning móttekin. Við höfum samband innan skamms."
          : "Your inquiry has been received. We will be in touch shortly."
      ));
    } catch {
      submit.disabled = false;
      submit.textContent = originalLabel;
    }
  });
}