Load contact form JS as external script

This commit is contained in:
Calum Muir 2025-12-28 13:04:14 +00:00
parent 481c6a1627
commit 4c0bdbdbac
2 changed files with 263 additions and 203 deletions

55
public/contact-form.js Normal file
View 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";
}
});
})();

View file

@ -8,209 +8,214 @@ const turnstileSiteKey = import.meta.env.PUBLIC_TURNSTILE_SITE_KEY ?? "";
---
<Base
title="Contact"
path="/contact"
description="Contact Highland Group RDA for sessions, volunteering, and support."
title="Contact"
path="/contact"
description="Contact Highland Group RDA for sessions, volunteering, and support."
>
{
turnstileSiteKey && (
<Fragment slot="head">
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
async
defer
></script>
</Fragment>
)
}
<div class="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
<main class="w-full">
<section class="w-full max-w-[1200px] mx-auto px-0 sm:px-8 mt-0">
<div
class="relative w-full sm:rounded-[7px] overflow-hidden bg-cover bg-[50%_35%]"
style="background-image: url(/222957077_6028824210491349_4146132303877993139_n-1920w.webp);"
>
<div class="absolute inset-0 bg-black/45"></div>
<SiteHeader navigationItems={navigationItems} theme="dark" 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
</p>
<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]">
Contact Highland Group RDA
</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]">
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>
</div>
</div>
<a
class="absolute bottom-4 left-4 sm:bottom-6 sm:left-6 w-[190px] sm:w-[240px] h-[48px] sm:h-[50px]"
href="https://www.justgiving.com/charity/highlandgrouprda"
target="_blank"
rel="noreferrer"
>
<img class="w-full h-full object-contain" alt="Donate via JustGiving" src="/Button.webp" />
</a>
</div>
</section>
<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="grid grid-cols-1 gap-8 sm:grid-cols-[1.1fr_0.9fr]">
<div>
<h2 class="[font-family:'Merriweather',Helvetica] font-bold text-black text-2xl sm:text-3xl">
Contact details
</h2>
<p 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 class="mt-3 [font-family:'Public_Sans',Helvetica] font-semibold text-black text-base sm:text-lg leading-[1.6]">
Tel: +447500 203226
<br />
Email: info@highlandgrouprda.org.uk
</p>
<div class="mt-6">
<a
class="inline-flex items-center rounded-full border border-black/20 px-4 py-2 text-sm font-semibold uppercase tracking-wide text-black"
href="mailto:info@highlandgrouprda.org.uk"
>
Email us
</a>
</div>
</div>
<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">
Send a message
</h2>
<form class="mt-4 space-y-4" action="/api/contact" method="post" data-contact-form>
{
!turnstileSiteKey && (
<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.
</p>
)
}
<div>
<label class="block text-sm font-semibold text-black">Name</label>
<input
class="mt-2 w-full rounded-md border border-black/20 bg-white px-3 py-2 text-black"
type="text"
name="name"
autocomplete="name"
required
/>
</div>
<div>
<label class="block text-sm font-semibold text-black">Email</label>
<input
class="mt-2 w-full rounded-md border border-black/20 bg-white px-3 py-2 text-black"
type="email"
name="email"
autocomplete="email"
required
/>
</div>
<div>
<label class="block text-sm font-semibold text-black">Message</label>
<textarea
class="mt-2 w-full rounded-md border border-black/20 bg-white px-3 py-2 text-black"
name="message"
rows="4"
required
></textarea>
</div>
<div class="cf-turnstile" data-sitekey={turnstileSiteKey}></div>
<p class="text-sm font-semibold text-black" data-form-message></p>
<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"
type="submit"
data-form-submit
>
Send message
</button>
</form>
</div>
</div>
</div>
</section>
<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="h-px w-full bg-black/20 mb-6"></div>
<div class="overflow-hidden rounded-[7px] border border-black/10">
<iframe
title="Highland Group RDA location"
src="https://www.google.com/maps?q=IV5%207PP&z=11&output=embed"
class="w-full h-[300px] sm:h-[380px] border-0"
loading="lazy"
referrerpolicy="no-referrer-when-downgrade"
></iframe>
</div>
</div>
</section>
<SiteFooter fullBleedOnMobile />
</main>
</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";
}
});
{
turnstileSiteKey && (
<Fragment slot="head">
<script
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
async
defer
/>
</Fragment>
)
}
</script>
<Fragment slot="head">
<script src="/contact-form.js" defer></script>
</Fragment>
<div class="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
<main class="w-full">
<section class="w-full max-w-[1200px] mx-auto px-0 sm:px-8 mt-0">
<div
class="relative w-full sm:rounded-[7px] overflow-hidden bg-cover bg-[50%_35%]"
style="background-image: url(/222957077_6028824210491349_4146132303877993139_n-1920w.webp);"
>
<div class="absolute inset-0 bg-black/45"></div>
<SiteHeader
navigationItems={navigationItems}
theme="dark"
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
</p>
<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]"
>
Contact Highland Group RDA
</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]"
>
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>
</div>
</div>
<a
class="absolute bottom-4 left-4 sm:bottom-6 sm:left-6 w-[190px] sm:w-[240px] h-[48px] sm:h-[50px]"
href="https://www.justgiving.com/charity/highlandgrouprda"
target="_blank"
rel="noreferrer"
>
<img
class="w-full h-full object-contain"
alt="Donate via JustGiving"
src="/Button.webp"
/>
</a>
</div>
</section>
<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="grid grid-cols-1 gap-8 sm:grid-cols-[1.1fr_0.9fr]"
>
<div>
<h2
class="[font-family:'Merriweather',Helvetica] font-bold text-black text-2xl sm:text-3xl"
>
Contact details
</h2>
<p
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
class="mt-3 [font-family:'Public_Sans',Helvetica] font-semibold text-black text-base sm:text-lg leading-[1.6]"
>
Tel: +447500 203226
<br />
Email: info@highlandgrouprda.org.uk
</p>
<div class="mt-6">
<a
class="inline-flex items-center rounded-full border border-black/20 px-4 py-2 text-sm font-semibold uppercase tracking-wide text-black"
href="mailto:info@highlandgrouprda.org.uk"
>
Email us
</a>
</div>
</div>
<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"
>
Send a message
</h2>
<form
class="mt-4 space-y-4"
action="/api/contact"
method="post"
data-contact-form
>
{
!turnstileSiteKey && (
<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.
</p>
)
}
<div>
<label
class="block text-sm font-semibold text-black"
>Name</label
>
<input
class="mt-2 w-full rounded-md border border-black/20 bg-white px-3 py-2 text-black"
type="text"
name="name"
autocomplete="name"
required
/>
</div>
<div>
<label
class="block text-sm font-semibold text-black"
>Email</label
>
<input
class="mt-2 w-full rounded-md border border-black/20 bg-white px-3 py-2 text-black"
type="email"
name="email"
autocomplete="email"
required
/>
</div>
<div>
<label
class="block text-sm font-semibold text-black"
>Message</label
>
<textarea
class="mt-2 w-full rounded-md border border-black/20 bg-white px-3 py-2 text-black"
name="message"
rows="4"
required></textarea>
</div>
<div
class="cf-turnstile"
data-sitekey={turnstileSiteKey}
>
</div>
<p
class="text-sm font-semibold text-black"
data-form-message
>
</p>
<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"
type="submit"
data-form-submit
>
Send message
</button>
</form>
</div>
</div>
</div>
</section>
<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="h-px w-full bg-black/20 mb-6"></div>
<div
class="overflow-hidden rounded-[7px] border border-black/10"
>
<iframe
title="Highland Group RDA location"
src="https://www.google.com/maps?q=IV5%207PP&z=11&output=embed"
class="w-full h-[300px] sm:h-[380px] border-0"
loading="lazy"
referrerpolicy="no-referrer-when-downgrade"
></iframe>
</div>
</div>
</section>
<SiteFooter fullBleedOnMobile />
</main>
</div>
</Base>