Anybody ever add a contact form with the astropaper theme? #363
tylermanning
started this conversation in
General
Replies: 1 comment 1 reply
-
Hello @tylermanning , My blog uses Astro-paper, and I created a contact form in React with fields such as name, email, subject, message, and token (from Google reCAPTCHA). I send my form to my API, which processes the form data to send it to me by email (line: import { useRef, useState } from "react";
import api from "@utils/api";
import ReCaptchaV3 from "@components/integration/reCaptchaV3";
export default function ContactForm() {
const captchaRef = useRef(null);
const [isDisabled, setIsDisabled] = useState(false);
const [formState, setFormState] = useState({
name: "",
email: "",
subject: "",
message: "",
token: "",
});
const [errors, setErrors] = useState({
name: "",
email: "",
subject: "",
message: "",
token: "",
});
const [success, setSuccess] = useState({
message: "",
});
function errorsCopy(value: string, name: string, errorMessage: string) {
const errorsCopy: { [index: string]: any } = { ...errors };
if (value === "") {
errorsCopy[name] = errorMessage;
} else {
errorsCopy[name] = "";
}
return errorsCopy as {
name: string;
email: string;
subject: string;
message: string;
token: string;
};
}
const handleChange = (e: { target: { name: string; value: string } }) => {
const { name, value } = e.target;
setFormState({ ...formState, [name]: value });
setSuccess({ message: "" });
setErrors(errorsCopy(value, name, `${name} est vide`));
};
const handleSubmit = async (e: { preventDefault: () => void }) => {
setIsDisabled(true);
e.preventDefault();
const emailFormat = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
if (!emailFormat.test(formState.email)) {
setErrors(
errorsCopy(
formState.email,
"email",
`Invalid email address format.`
)
);
return;
}
if (formState.message.length > 500) {
setErrors(errorsCopy(formState.message, "message", `Message too long`));
return;
}
if (captchaRef === null) {
setErrors(errorsCopy(formState.token, "token", `Invalid recaptcha`));
return;
}
formState["token"] = window.grecaptcha.getResponse();
if (formState["token"].length === 0) {
setErrors({
name: "",
email: "",
subject: "",
message: "",
token: "Please validate the captcha.",
});
return;
}
const result = await api.postContactForm(formState);
if (result?.data.accepted?.length > 0) {
setFormState({
name: "",
email: "",
subject: "",
message: "",
token: "",
});
setErrors({
name: "",
email: "",
subject: "",
message: "",
token: "",
});
setSuccess({
message: "Your message has been successfully sent.",
});
} else {
setErrors({
name: "",
email: "",
subject: "",
message: "",
token: result.data,
});
}
setIsDisabled(false);
};
return (
<div className="form-contact mx-auto my-10 w-full max-w-xs">
<form
className="bg-bglight dark:bg-bgdark text-bgdark dark:text-bglight mb-4 rounded px-8 pb-8 pt-6 shadow-md dark:shadow-inner"
onSubmit={handleSubmit}
>
<div className="mb-4">
<label className="mb-2 block text-sm font-bold" htmlFor="name">
Name:
<input
className="w-full rounded border px-3 py-2"
type="text"
name="name"
value={formState.name}
onChange={handleChange}
required
/>
</label>
</div>
<div className="mb-6">
<label className="mb-2 block text-sm font-bold" htmlFor="email">
Email:
<input
className="w-full rounded border px-3 py-2"
type="email"
name="email"
value={formState.email}
onChange={handleChange}
required
/>
</label>
</div>
<div className="mb-6">
<label className="mb-2 block text-sm font-bold" htmlFor="email">
Subject:
<input
className="w-full rounded border px-3 py-2"
type="text"
name="subject"
value={formState.subject}
onChange={handleChange}
required
/>
</label>
</div>
<div className="mb-6">
<label className="mb-2 block text-sm font-bold" htmlFor="message">
Message:
<textarea
className="w-full rounded border px-3 py-2"
name="message"
value={formState.message}
onChange={handleChange}
required
/>
</label>
</div>
<div className="mb-6">
<ReCaptchaV3 />
</div>
<div className="mb-6 flex justify-center">
<button
className="rounded px-3 py-2 outline-offset-2 focus-visible:outline-double lg:text-xl"
type="submit"
disabled={isDisabled}
>
{isDisabled ? "Sending in progress..." : "Contact me"}
</button>
</div>
<div className="mb-6">
{Object.keys(errors).map((error: string, index) => {
const key = error as keyof typeof errors;
return (
<span key={index} className="error">
{errors[key] !== "" ? errors[key] : ""}
</span>
);
})}
{success.message !== "" && (
<span className="success">{success.message}</span>
)}
</div>
</form>
</div>
);
} And the code for the component reCaptchaV3: declare global {
interface Window {
grecaptcha: any;
}
}
export default function ReCaptchaV3() {
const imported = document.createElement("script");
imported.src = "https://www.google.com/recaptcha/api.js";
document.head.appendChild(imported);
return (
<>
<div
className="g-recaptcha"
data-sitekey={`${import.meta.env.PUBLIC_GOOGLE_RECAPTCHA_SITE_KEY}`}
style={{
transform: "scale(0.85)",
WebkitTransform: "scale(0.85)",
transformOrigin: "0 0",
WebkitTransformOrigin: "0 0",
}}
></div>
</>
);
} |
Beta Was this translation helpful? Give feedback.
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
I tried adding a contact form with some basic validation but it quickly spiraled and got messy. Wondering if anyone added a basic contact form like this: https://codepen.io/surjithctly/pen/LYxNPEm
Beta Was this translation helpful? Give feedback.
All reactions