feat: full site build — Project/Melody schema (Option A), admin CRUD, public sections, uploads, email+SMTP, internal analytics, legal pages, docs
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { submitContactMessage } from "@/app/[locale]/contact/actions";
|
||||
|
||||
type ContactFormProps = {
|
||||
labels: {
|
||||
name: string;
|
||||
email: string;
|
||||
message: string;
|
||||
submit: string;
|
||||
sending: string;
|
||||
success: string;
|
||||
savedWarning: string;
|
||||
invalid: string;
|
||||
rateLimit: string;
|
||||
};
|
||||
};
|
||||
|
||||
export default function ContactForm({ labels }: ContactFormProps) {
|
||||
const [status, setStatus] = useState<{ type: "success" | "warning" | "error"; message: string } | null>(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
setStatus(null);
|
||||
setIsSubmitting(true);
|
||||
const form = event.currentTarget;
|
||||
const result = await submitContactMessage(Object.fromEntries(new FormData(form).entries()));
|
||||
setIsSubmitting(false);
|
||||
if (result.error) {
|
||||
setStatus({ type: "error", message: result.error === "rate-limit" ? labels.rateLimit : labels.invalid });
|
||||
return;
|
||||
}
|
||||
form.reset();
|
||||
setStatus({ type: result.warning ? "warning" : "success", message: result.warning ? labels.savedWarning : labels.success });
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="card contact-form space-y-4" noValidate>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<label className="space-y-2"><span>{labels.name}</span><input name="name" required maxLength={100} autoComplete="name" /></label>
|
||||
<label className="space-y-2"><span>{labels.email}</span><input name="email" type="email" required maxLength={254} autoComplete="email" /></label>
|
||||
</div>
|
||||
<label className="space-y-2"><span>{labels.message}</span><textarea name="message" required minLength={10} maxLength={5000} rows={6} /></label>
|
||||
<label aria-hidden="true" className="absolute -left-[9999px] h-px w-px overflow-hidden"><span>Website</span><input name="website" tabIndex={-1} autoComplete="off" /></label>
|
||||
<button type="submit" disabled={isSubmitting}>{isSubmitting ? labels.sending : labels.submit}</button>
|
||||
{status ? <p role={status.type === "error" ? "alert" : "status"} className={status.type === "error" ? "text-sm text-destructive" : "text-sm text-muted-foreground"}>{status.message}</p> : null}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user