adding a webform

This commit is contained in:
Kismet Hasanaj
2026-05-03 01:39:28 +02:00
parent e3d7e938cc
commit fa7be4c426
4 changed files with 164 additions and 24 deletions
+1
View File
@@ -0,0 +1 @@
NEXT_PUBLIC_WEB3FORMS_ACCESS_KEY=YOUR_WEB3FORMS_ACCESS_KEY
+3
View File
@@ -32,5 +32,8 @@ node_modules/
.next/ .next/
out/ out/
tsconfig.tsbuildinfo tsconfig.tsbuildinfo
# ---> Local environment config
.env.local
# ---> Local backups # ---> Local backups
+14
View File
@@ -82,6 +82,20 @@ Clone the Gitea repo:
git clone http://10.10.10.11:3000/kismet.hasanaj/novarix.uk.git /var/www/novarix.uk git clone http://10.10.10.11:3000/kismet.hasanaj/novarix.uk.git /var/www/novarix.uk
``` ```
If you want the built-in contact form to send mail through Web3Forms, create a
local environment file before building:
```bash
cd /var/www/novarix.uk
cp .env.example .env.local
```
Then edit `.env.local` and set:
```bash
NEXT_PUBLIC_WEB3FORMS_ACCESS_KEY=your_web3forms_access_key
```
Install the website dependencies and build the static site: Install the website dependencies and build the static site:
```bash ```bash
+146 -24
View File
@@ -30,6 +30,8 @@ import { openCookieBanner } from "@/components/CookieBanner";
// Type for the mouse-pointer position used to move the background glow. // Type for the mouse-pointer position used to move the background glow.
type PointerState = { x: number; y: number }; type PointerState = { x: number; y: number };
const WEB3FORMS_ENDPOINT = "https://api.web3forms.com/submit";
export default function HomePage() { export default function HomePage() {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// STATE // STATE
@@ -39,6 +41,11 @@ export default function HomePage() {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const [pointer, setPointer] = useState<PointerState>({ x: 50, y: 22 }); const [pointer, setPointer] = useState<PointerState>({ x: 50, y: 22 });
const [copiedEmail, setCopiedEmail] = useState(false); const [copiedEmail, setCopiedEmail] = useState(false);
const [formStatus, setFormStatus] = useState<
"idle" | "submitting" | "success" | "error"
>("idle");
const [formMessage, setFormMessage] = useState("");
const web3FormsKey = process.env.NEXT_PUBLIC_WEB3FORMS_ACCESS_KEY;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// BACKGROUND POSITION // BACKGROUND POSITION
@@ -65,6 +72,63 @@ export default function HomePage() {
} }
} }
async function handleContactSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
if (!web3FormsKey) {
setFormStatus("error");
setFormMessage(
"Contact form is not configured yet. Please use the copy email button for now."
);
return;
}
const form = event.currentTarget;
const formData = new FormData(form);
formData.append("access_key", web3FormsKey);
formData.append("subject", "Novarix website enquiry");
formData.append("from_name", "Novarix website");
formData.append("replyto", String(formData.get("email") ?? ""));
const payload = Object.fromEntries(formData);
setFormStatus("submitting");
setFormMessage("Sending message...");
try {
const response = await fetch(WEB3FORMS_ENDPOINT, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(payload),
});
const result = (await response.json()) as {
success?: boolean;
message?: string;
};
if (response.ok && result.success) {
form.reset();
setFormStatus("success");
setFormMessage("Message sent. We will get back to you within one working day.");
return;
}
setFormStatus("error");
setFormMessage(
result.message || "The message could not be sent. Please try again."
);
} catch {
setFormStatus("error");
setFormMessage(
"The message could not be sent right now. Please try again or copy the email address instead."
);
}
}
return ( return (
<main <main
id="top" id="top"
@@ -366,31 +430,89 @@ export default function HomePage() {
{site.contact.description} {site.contact.description}
</p> </p>
{/* Email button + small response-time note */} <form className="mt-8 space-y-4" onSubmit={handleContactSubmit}>
<div className="mt-8 flex flex-wrap items-center gap-3"> <input
<a type="checkbox"
href={`mailto:${site.contact.email}`} name="botcheck"
className="group inline-flex h-12 items-center gap-2 rounded-xl bg-[var(--button-bg)] px-5 text-sm font-semibold text-[var(--button-fg)] shadow-sm transition-all hover:-translate-y-0.5 hover:bg-[var(--button-hover)] hover:shadow-md" className="hidden"
> tabIndex={-1}
{site.contact.email} autoComplete="off"
<span />
aria-hidden="true"
className="transition-transform group-hover:translate-x-0.5" <div className="grid gap-4 sm:grid-cols-2">
> <label className="block">
<span className="mb-2 block text-sm font-medium text-[var(--text)]">
Name
</span>
<input
type="text"
name="name"
required
className="h-12 w-full rounded-xl border border-[var(--border-strong)] bg-[var(--surface-strong)] px-4 text-sm text-[var(--text)] outline-none transition-colors placeholder:text-[var(--text-soft)] focus:border-[var(--accent)]"
placeholder="Your name"
/>
</label>
<label className="block">
<span className="mb-2 block text-sm font-medium text-[var(--text)]">
Email
</span>
<input
type="email"
name="email"
required
className="h-12 w-full rounded-xl border border-[var(--border-strong)] bg-[var(--surface-strong)] px-4 text-sm text-[var(--text)] outline-none transition-colors placeholder:text-[var(--text-soft)] focus:border-[var(--accent)]"
placeholder="you@example.com"
/>
</label>
</div>
<label className="block">
<span className="mb-2 block text-sm font-medium text-[var(--text)]">
Message
</span> </span>
</a> <textarea
<button name="message"
type="button" required
onClick={copyContactEmail} rows={6}
className="inline-flex h-12 items-center rounded-xl border border-[var(--border-strong)] bg-[var(--surface)] px-5 text-sm font-semibold text-[var(--text)] backdrop-blur transition-all hover:-translate-y-0.5 hover:border-[var(--accent)] hover:text-[var(--accent)]" className="w-full rounded-xl border border-[var(--border-strong)] bg-[var(--surface-strong)] px-4 py-3 text-sm text-[var(--text)] outline-none transition-colors placeholder:text-[var(--text-soft)] focus:border-[var(--accent)]"
> placeholder="Tell us a little about what you need."
{copiedEmail ? "Copied" : "Copy email"} />
</button> </label>
<span className="text-sm text-[var(--text-soft)]">
{site.contact.note} <div className="flex flex-wrap items-center gap-3">
</span> <button
</div> type="submit"
disabled={formStatus === "submitting"}
className="inline-flex h-12 items-center rounded-xl bg-[var(--button-bg)] px-5 text-sm font-semibold text-[var(--button-fg)] shadow-sm transition-all hover:-translate-y-0.5 hover:bg-[var(--button-hover)] hover:shadow-md disabled:translate-y-0 disabled:opacity-60 disabled:shadow-none"
>
{formStatus === "submitting" ? "Sending..." : "Send message"}
</button>
<button
type="button"
onClick={copyContactEmail}
className="inline-flex h-12 items-center rounded-xl border border-[var(--border-strong)] bg-[var(--surface)] px-5 text-sm font-semibold text-[var(--text)] backdrop-blur transition-all hover:-translate-y-0.5 hover:border-[var(--accent)] hover:text-[var(--accent)]"
>
{copiedEmail ? "Copied" : "Copy email"}
</button>
<span className="text-sm text-[var(--text-soft)]">
{site.contact.note}
</span>
</div>
{formMessage && (
<p
className={`text-sm ${
formStatus === "error"
? "text-red-500"
: formStatus === "success"
? "text-emerald-500"
: "text-[var(--text-soft)]"
}`}
>
{formMessage}
</p>
)}
</form>
</div> </div>
</div> </div>
</section> </section>