Load contact form JS as external script
This commit is contained in:
parent
481c6a1627
commit
4c0bdbdbac
2 changed files with 263 additions and 203 deletions
55
public/contact-form.js
Normal file
55
public/contact-form.js
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
(() => {
|
||||||
|
const form = document.querySelector("[data-contact-form]");
|
||||||
|
const message = document.querySelector("[data-form-message]");
|
||||||
|
const submitButton = document.querySelector("[data-form-submit]");
|
||||||
|
|
||||||
|
if (!form || !message || !submitButton) return;
|
||||||
|
|
||||||
|
form.addEventListener("submit", async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (submitButton.disabled) return;
|
||||||
|
|
||||||
|
submitButton.disabled = true;
|
||||||
|
submitButton.textContent = "Sending...";
|
||||||
|
message.textContent = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(form.action, {
|
||||||
|
method: "POST",
|
||||||
|
body: new FormData(form),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
let errorMessage = "Request failed";
|
||||||
|
const errorData = await response.json().catch(() => null);
|
||||||
|
if (errorData?.error) {
|
||||||
|
errorMessage = errorData.details
|
||||||
|
? `${errorData.error} (${errorData.details})`
|
||||||
|
: errorData.error;
|
||||||
|
} else {
|
||||||
|
const errorText = await response.text().catch(() => "");
|
||||||
|
if (errorText.trim()) {
|
||||||
|
errorMessage = `${response.status} ${response.statusText}: ${errorText}`;
|
||||||
|
} else {
|
||||||
|
errorMessage = `${response.status} ${response.statusText}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
message.textContent = "Thanks! Your message has been sent.";
|
||||||
|
form.reset();
|
||||||
|
if (window.turnstile && typeof window.turnstile.reset === "function") {
|
||||||
|
window.turnstile.reset();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.textContent =
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Sorry, something went wrong. Please try again.";
|
||||||
|
} finally {
|
||||||
|
submitButton.disabled = false;
|
||||||
|
submitButton.textContent = "Send message";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
@ -19,10 +19,13 @@ const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
|
||||||
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
|
||||||
async
|
async
|
||||||
defer
|
defer
|
||||||
></script>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
<Fragment slot="head">
|
||||||
|
<script src="/contact-form.js" defer></script>
|
||||||
|
</Fragment>
|
||||||
<div class="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
|
<div class="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
|
||||||
<main class="w-full">
|
<main class="w-full">
|
||||||
<section class="w-full max-w-[1200px] mx-auto px-0 sm:px-8 mt-0">
|
<section class="w-full max-w-[1200px] mx-auto px-0 sm:px-8 mt-0">
|
||||||
|
|
@ -31,19 +34,35 @@ const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
|
||||||
style="background-image: url(/222957077_6028824210491349_4146132303877993139_n-1920w.webp);"
|
style="background-image: url(/222957077_6028824210491349_4146132303877993139_n-1920w.webp);"
|
||||||
>
|
>
|
||||||
<div class="absolute inset-0 bg-black/45"></div>
|
<div class="absolute inset-0 bg-black/45"></div>
|
||||||
<SiteHeader navigationItems={navigationItems} theme="dark" className="px-6 sm:px-4" />
|
<SiteHeader
|
||||||
<div class="relative px-6 sm:px-8 pb-32 pt-6 sm:pb-24 sm:pt-8">
|
navigationItems={navigationItems}
|
||||||
<div class="max-w-none sm:max-w-[760px] rounded-[7px] bg-black/45 px-5 py-5 sm:px-6 sm:py-6 backdrop-blur-sm">
|
theme="dark"
|
||||||
<p class="[font-family:'Public_Sans',Helvetica] font-semibold text-xs sm:text-sm uppercase tracking-[0.2em] text-white/90">
|
className="px-6 sm:px-4"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="relative px-6 sm:px-8 pb-32 pt-6 sm:pb-24 sm:pt-8"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="max-w-none sm:max-w-[760px] rounded-[7px] bg-black/45 px-5 py-5 sm:px-6 sm:py-6 backdrop-blur-sm"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="[font-family:'Public_Sans',Helvetica] font-semibold text-xs sm:text-sm uppercase tracking-[0.2em] text-white/90"
|
||||||
|
>
|
||||||
Get in touch
|
Get in touch
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-3 h-[2px] w-10 bg-white/70"></div>
|
<div class="mt-3 h-[2px] w-10 bg-white/70"></div>
|
||||||
<h1 class="mt-4 [font-family:'Merriweather',Helvetica] font-bold text-white text-[34px] sm:text-[44px] leading-[1.15]">
|
<h1
|
||||||
|
class="mt-4 [font-family:'Merriweather',Helvetica] font-bold text-white text-[34px] sm:text-[44px] leading-[1.15]"
|
||||||
|
>
|
||||||
Contact Highland Group RDA
|
Contact Highland Group RDA
|
||||||
</h1>
|
</h1>
|
||||||
<p class="mt-4 [font-family:'Public_Sans',Helvetica] font-semibold text-white text-base sm:text-lg leading-[1.6] max-w-[720px]">
|
<p
|
||||||
We would love to hear from you about sessions, volunteering, or ways to support the group. Reach out
|
class="mt-4 [font-family:'Public_Sans',Helvetica] font-semibold text-white text-base sm:text-lg leading-[1.6] max-w-[720px]"
|
||||||
and we will get back to you as soon as we can.
|
>
|
||||||
|
We would love to hear from you about sessions,
|
||||||
|
volunteering, or ways to support the group.
|
||||||
|
Reach out and we will get back to you as soon as
|
||||||
|
we can.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -53,22 +72,37 @@ const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
<img class="w-full h-full object-contain" alt="Donate via JustGiving" src="/Button.webp" />
|
<img
|
||||||
|
class="w-full h-full object-contain"
|
||||||
|
alt="Donate via JustGiving"
|
||||||
|
src="/Button.webp"
|
||||||
|
/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="w-full max-w-[1200px] mx-auto px-0 sm:px-8 mt-0 pb-0">
|
<section
|
||||||
|
class="w-full max-w-[1200px] mx-auto px-0 sm:px-8 mt-0 pb-0"
|
||||||
|
>
|
||||||
<div class="bg-[#d6d6d6] px-6 sm:px-8 py-6 sm:py-8">
|
<div class="bg-[#d6d6d6] px-6 sm:px-8 py-6 sm:py-8">
|
||||||
<div class="grid grid-cols-1 gap-8 sm:grid-cols-[1.1fr_0.9fr]">
|
<div
|
||||||
|
class="grid grid-cols-1 gap-8 sm:grid-cols-[1.1fr_0.9fr]"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="[font-family:'Merriweather',Helvetica] font-bold text-black text-2xl sm:text-3xl">
|
<h2
|
||||||
|
class="[font-family:'Merriweather',Helvetica] font-bold text-black text-2xl sm:text-3xl"
|
||||||
|
>
|
||||||
Contact details
|
Contact details
|
||||||
</h2>
|
</h2>
|
||||||
<p class="mt-4 [font-family:'Public_Sans',Helvetica] font-semibold text-black text-base sm:text-lg leading-[1.6]">
|
<p
|
||||||
Highland Group RDA, Sandycroft, Reelig, Kirkhill, IV5 7PP
|
class="mt-4 [font-family:'Public_Sans',Helvetica] font-semibold text-black text-base sm:text-lg leading-[1.6]"
|
||||||
|
>
|
||||||
|
Highland Group RDA, Sandycroft, Reelig,
|
||||||
|
Kirkhill, IV5 7PP
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-3 [font-family:'Public_Sans',Helvetica] font-semibold text-black text-base sm:text-lg leading-[1.6]">
|
<p
|
||||||
|
class="mt-3 [font-family:'Public_Sans',Helvetica] font-semibold text-black text-base sm:text-lg leading-[1.6]"
|
||||||
|
>
|
||||||
Tel: +447500 203226
|
Tel: +447500 203226
|
||||||
<br />
|
<br />
|
||||||
Email: info@highlandgrouprda.org.uk
|
Email: info@highlandgrouprda.org.uk
|
||||||
|
|
@ -84,19 +118,31 @@ const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-white/70 px-6 sm:px-8 py-6 sm:py-8">
|
<div class="bg-white/70 px-6 sm:px-8 py-6 sm:py-8">
|
||||||
<h2 class="[font-family:'Merriweather',Helvetica] font-bold text-black text-2xl sm:text-3xl">
|
<h2
|
||||||
|
class="[font-family:'Merriweather',Helvetica] font-bold text-black text-2xl sm:text-3xl"
|
||||||
|
>
|
||||||
Send a message
|
Send a message
|
||||||
</h2>
|
</h2>
|
||||||
<form class="mt-4 space-y-4" action="/api/contact" method="post" data-contact-form>
|
<form
|
||||||
|
class="mt-4 space-y-4"
|
||||||
|
action="/api/contact"
|
||||||
|
method="post"
|
||||||
|
data-contact-form
|
||||||
|
>
|
||||||
{
|
{
|
||||||
!turnstileSiteKey && (
|
!turnstileSiteKey && (
|
||||||
<p class="rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-sm font-semibold text-amber-900">
|
<p class="rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-sm font-semibold text-amber-900">
|
||||||
Turnstile is not configured. Set PUBLIC_TURNSTILE_SITE_KEY and redeploy to enable the form.
|
Turnstile is not configured. Set
|
||||||
|
PUBLIC_TURNSTILE_SITE_KEY and
|
||||||
|
redeploy to enable the form.
|
||||||
</p>
|
</p>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-semibold text-black">Name</label>
|
<label
|
||||||
|
class="block text-sm font-semibold text-black"
|
||||||
|
>Name</label
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
class="mt-2 w-full rounded-md border border-black/20 bg-white px-3 py-2 text-black"
|
class="mt-2 w-full rounded-md border border-black/20 bg-white px-3 py-2 text-black"
|
||||||
type="text"
|
type="text"
|
||||||
|
|
@ -106,7 +152,10 @@ const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-semibold text-black">Email</label>
|
<label
|
||||||
|
class="block text-sm font-semibold text-black"
|
||||||
|
>Email</label
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
class="mt-2 w-full rounded-md border border-black/20 bg-white px-3 py-2 text-black"
|
class="mt-2 w-full rounded-md border border-black/20 bg-white px-3 py-2 text-black"
|
||||||
type="email"
|
type="email"
|
||||||
|
|
@ -116,16 +165,26 @@ const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-semibold text-black">Message</label>
|
<label
|
||||||
|
class="block text-sm font-semibold text-black"
|
||||||
|
>Message</label
|
||||||
|
>
|
||||||
<textarea
|
<textarea
|
||||||
class="mt-2 w-full rounded-md border border-black/20 bg-white px-3 py-2 text-black"
|
class="mt-2 w-full rounded-md border border-black/20 bg-white px-3 py-2 text-black"
|
||||||
name="message"
|
name="message"
|
||||||
rows="4"
|
rows="4"
|
||||||
required
|
required></textarea>
|
||||||
></textarea>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="cf-turnstile" data-sitekey={turnstileSiteKey}></div>
|
<div
|
||||||
<p class="text-sm font-semibold text-black" data-form-message></p>
|
class="cf-turnstile"
|
||||||
|
data-sitekey={turnstileSiteKey}
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="text-sm font-semibold text-black"
|
||||||
|
data-form-message
|
||||||
|
>
|
||||||
|
</p>
|
||||||
<button
|
<button
|
||||||
class="inline-flex items-center rounded-full border border-black/20 px-5 py-2 text-sm font-semibold uppercase tracking-wide text-black"
|
class="inline-flex items-center rounded-full border border-black/20 px-5 py-2 text-sm font-semibold uppercase tracking-wide text-black"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|
@ -138,10 +197,14 @@ const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="w-full max-w-[1200px] mx-auto px-0 sm:px-8 mt-0 pb-0">
|
<section
|
||||||
|
class="w-full max-w-[1200px] mx-auto px-0 sm:px-8 mt-0 pb-0"
|
||||||
|
>
|
||||||
<div class="bg-[#d6d6d6] px-6 sm:px-8 py-6 sm:py-8">
|
<div class="bg-[#d6d6d6] px-6 sm:px-8 py-6 sm:py-8">
|
||||||
<div class="h-px w-full bg-black/20 mb-6"></div>
|
<div class="h-px w-full bg-black/20 mb-6"></div>
|
||||||
<div class="overflow-hidden rounded-[7px] border border-black/10">
|
<div
|
||||||
|
class="overflow-hidden rounded-[7px] border border-black/10"
|
||||||
|
>
|
||||||
<iframe
|
<iframe
|
||||||
title="Highland Group RDA location"
|
title="Highland Group RDA location"
|
||||||
src="https://www.google.com/maps?q=IV5%207PP&z=11&output=embed"
|
src="https://www.google.com/maps?q=IV5%207PP&z=11&output=embed"
|
||||||
|
|
@ -155,62 +218,4 @@ const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
|
||||||
<SiteFooter fullBleedOnMobile />
|
<SiteFooter fullBleedOnMobile />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="module">
|
|
||||||
const turnstileSiteKey = {JSON.stringify(turnstileSiteKey)};
|
|
||||||
|
|
||||||
const form = document.querySelector("[data-contact-form]");
|
|
||||||
const message = document.querySelector("[data-form-message]");
|
|
||||||
const submitButton = document.querySelector("[data-form-submit]");
|
|
||||||
|
|
||||||
if (form && message && submitButton) {
|
|
||||||
form.addEventListener("submit", async (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
if (submitButton.disabled) return;
|
|
||||||
|
|
||||||
submitButton.disabled = true;
|
|
||||||
submitButton.textContent = "Sending...";
|
|
||||||
message.textContent = "";
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(form.action, {
|
|
||||||
method: "POST",
|
|
||||||
body: new FormData(form),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
let errorMessage = "Request failed";
|
|
||||||
const errorData = await response.json().catch(() => null);
|
|
||||||
if (errorData?.error) {
|
|
||||||
errorMessage = errorData.details
|
|
||||||
? `${errorData.error} (${errorData.details})`
|
|
||||||
: errorData.error;
|
|
||||||
} else {
|
|
||||||
const errorText = await response.text().catch(() => "");
|
|
||||||
if (errorText.trim()) {
|
|
||||||
errorMessage = `${response.status} ${response.statusText}: ${errorText}`;
|
|
||||||
} else {
|
|
||||||
errorMessage = `${response.status} ${response.statusText}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
message.textContent = "Thanks! Your message has been sent.";
|
|
||||||
form.reset();
|
|
||||||
if (window.turnstile && typeof window.turnstile.reset === "function") {
|
|
||||||
window.turnstile.reset();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
message.textContent =
|
|
||||||
error instanceof Error
|
|
||||||
? error.message
|
|
||||||
: "Sorry, something went wrong. Please try again.";
|
|
||||||
} finally {
|
|
||||||
submitButton.disabled = false;
|
|
||||||
submitButton.textContent = "Send message";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</Base>
|
</Base>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue