1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2026-06-05 19:15:30 -04:00

refactor: migrate contact form to TanStack Form

- Replace manual state management with @tanstack/react-form
- Add proper Field/FieldLabel components for accessibility
- Simplify server action (remove useActionState signature)
- Remove use-debounce dependency
- Update PGP key links and minor styling tweaks

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-29 22:08:54 -05:00
parent 013311a618
commit bcc595e141
7 changed files with 266 additions and 180 deletions
+20 -20
View File
@@ -6,49 +6,49 @@ import { ContactSchema } from "@/lib/schemas/contact";
import siteConfig from "@/lib/config/site";
import { checkBotId } from "botid/server";
// Schema and type now imported from shared validation module
export type ContactState = {
export type ContactResult = {
success: boolean;
message: string;
errors?: Record<string, string[]>;
};
export const send = async (state: ContactState, payload: FormData): Promise<ContactState> => {
export const sendContactForm = async (formData: FormData): Promise<ContactResult> => {
// TODO: remove after debugging why automated spam entries are causing 500 errors
console.debug("[server/contact] received payload:", payload);
console.debug("[server/contact] received payload:", formData);
// BotID server-side verification
const verification = await checkBotId();
if (verification.isBot) {
console.warn("[server/contact] botid verification failed:", verification);
throw new Error("Bot check failed");
return {
success: false,
message: "Verification failed. Please try again.",
};
}
const parsed = ContactSchema.safeParse(Object.fromEntries(formData));
if (!parsed.success) {
return {
success: false,
message: "Please make sure all fields are filled in correctly.",
errors: parsed.error.flatten().fieldErrors,
};
}
try {
const data = ContactSchema.safeParse(Object.fromEntries(payload));
if (!data.success) {
return {
success: false,
message: "Please make sure all fields are filled in.",
errors: data.error.flatten().fieldErrors,
};
}
if (env.RESEND_FROM_EMAIL === "onboarding@resend.dev") {
// https://resend.com/docs/api-reference/emails/send-email
console.warn("[server/contact] 'RESEND_FROM_EMAIL' is not set, falling back to onboarding@resend.dev.");
}
// send email
const resend = new Resend(env.RESEND_API_KEY);
await resend.emails.send({
from: `${data.data.name} <${env.RESEND_FROM_EMAIL || "onboarding@resend.dev"}>`,
replyTo: `${data.data.name} <${data.data.email}>`,
from: `${parsed.data.name} <${env.RESEND_FROM_EMAIL || "onboarding@resend.dev"}>`,
replyTo: `${parsed.data.name} <${parsed.data.email}>`,
to: [env.RESEND_TO_EMAIL],
subject: `[${siteConfig.name}] Contact Form Submission`,
text: data.data.message,
text: parsed.data.message,
});
return { success: true, message: "Thanks! You should hear from me soon." };