evOo-local/apps/www/src/components/WaitlistForm.astro
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

112 lines
3.3 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
type Props = {
emailId?: string;
emailLabel?: string;
placeholder?: string;
submitLabel?: string;
joiningLabel?: string;
successLabel?: string;
addedLabel?: string;
errorLabel?: string;
};
const {
emailId = "waitlist-email",
emailLabel = "Email address",
placeholder = "you@company.com",
submitLabel = "Continue",
joiningLabel = "Joining…",
successLabel = "You’re in.",
addedLabel = "Added",
errorLabel = "Couldn’t add you. Try again.",
} = Astro.props;
---
<form
class="waitlist-form"
data-waitlist-form
data-endpoint="/api/waitlist"
data-joining-label={joiningLabel}
data-success-label={successLabel}
data-added-label={addedLabel}
data-error-label={errorLabel}
novalidate
>
<label class="sr-only" for={emailId}>{emailLabel}</label>
<input
id={emailId}
name="email"
type="email"
inputmode="email"
autocomplete="email"
autocapitalize="none"
spellcheck="false"
maxlength="254"
placeholder={placeholder}
required
/>
<div class="waitlist-form__trap" hidden>
<input name="contact-note" type="text" autocomplete="off" tabindex="-1" />
</div>
<button class="button button--primary button--small" type="submit" data-submit-label={submitLabel}>
{submitLabel}
</button>
<p class="waitlist-form__status" data-waitlist-status aria-live="polite"></p>
</form>
<script>
document.querySelectorAll<HTMLFormElement>("[data-waitlist-form]").forEach((form) => {
const email = form.elements.namedItem("email");
const contactNote = form.elements.namedItem("contact-note");
const submit = form.querySelector<HTMLButtonElement>("button[type='submit']");
const status = form.querySelector<HTMLElement>("[data-waitlist-status]");
if (!(email instanceof HTMLInputElement) || !(contactNote instanceof HTMLInputElement) || !submit || !status) {
return;
}
const submitLabel = submit.dataset.submitLabel || "Continue";
const joiningLabel = form.dataset.joiningLabel || "Joining…";
const successLabel = form.dataset.successLabel || "You’re in.";
const addedLabel = form.dataset.addedLabel || "Added";
const errorLabel = form.dataset.errorLabel || "Couldn’t add you. Try again.";
form.addEventListener("submit", async (event) => {
event.preventDefault();
status.classList.remove("is-error", "is-success");
if (!email.checkValidity()) {
email.reportValidity();
return;
}
const endpoint = form.dataset.endpoint;
if (!endpoint) return;
submit.disabled = true;
submit.textContent = joiningLabel;
try {
const response = await fetch(endpoint, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ email: email.value, contactNote: contactNote.value }),
});
if (!response.ok) throw new Error("Waitlist request failed");
form.reset();
status.textContent = successLabel;
status.classList.add("is-success");
submit.textContent = addedLabel;
form.dispatchEvent(new CustomEvent("waitlist:joined", { bubbles: true }));
} catch {
status.textContent = errorLabel;
status.classList.add("is-error");
submit.disabled = false;
submit.textContent = submitLabel;
}
});
});
</script>