import { useState } from "react"
import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { z } from "zod"
import { CircleCheckIcon } from "lucide-react"
import { Button } from "./components/ui/button"
import { Input } from "./components/ui/input"
import { Label } from "./components/ui/label"
const kinds = ["Using the site", "Bug report", "Other"]
const schema = z.object({
name: z.string().min(1, "Please enter your name"),
email: z.string().email("Please enter a valid email address"),
kind: z.enum(kinds, { error: "Please choose a type" }),
message: z.string().min(10, "Please write at least 10 characters").max(500, "Please keep it to 500 characters or fewer"),
})
function send(values) {
return new Promise((resolve) => setTimeout(resolve, 1500))
}
function Field({ id, label, error, children }) {
return (
<div className="flex flex-col gap-2">
<Label htmlFor={id}>{label}</Label>
{children}
{error && <p className="text-sm text-destructive">{error}</p>}
</div>
)
}
export default function App() {
const [sent, setSent] = useState(false)
const { register, handleSubmit, watch, reset, formState: { errors, isSubmitting } } = useForm({
resolver: zodResolver(schema),
defaultValues: { name: "", email: "", kind: undefined, message: "" },
})
const message = watch("message")
async function onSubmit(values) {
await send(values)
setSent(true)
}
if (sent) {
return (
<div className="mx-auto my-6 w-full max-w-lg rounded-xl border bg-card p-10 text-center text-card-foreground">
<CircleCheckIcon className="mx-auto size-12 text-emerald-500" />
<p className="mt-4 text-lg font-semibold">Message sent</p>
<p className="mt-2 text-sm text-muted-foreground">
Thank you for contacting us. We'll reply by email within 2 business days.
</p>
{}
<Button variant="outline" className="mt-6" onClick={() => {
reset()
setSent(false)
}}>
Send another (demo)
</Button>
</div >
)
}
return (
<form
onSubmit={handleSubmit(onSubmit)}
className="mx-auto my-6 flex w-full max-w-lg flex-col gap-5 rounded-xl border bg-card p-6 text-card-foreground"
>
<div>
<h1 className="text-lg font-semibold">Contact us</h1>
<p className="mt-1 text-sm text-muted-foreground">Feel free to send us your questions or feedback.</p>
</div>
<Field id="name" label="Name" error={errors.name?.message}>
<Input id="name" placeholder="John Smith" {...register("name")} />
</Field>
<Field id="email" label="Email" error={errors.email?.message}>
<Input id="email" type="email" placeholder="john@example.com" {...register("email")} />
</Field>
<div className="flex flex-col gap-2">
<Label>Type</Label>
<div className="flex flex-wrap gap-2">
{kinds.map((kind) => (
<label
key={kind}
className="cursor-pointer rounded-full border px-3 py-1.5 text-sm has-checked:border-primary has-checked:bg-primary has-checked:text-primary-foreground"
>
<input type="radio" value={kind} className="sr-only" {...register("kind")} />
{kind}
</label>
))}
</div>
{errors.kind && <p className="text-sm text-destructive">{errors.kind.message}</p>}
</div>
<Field id="message" label="Message" error={errors.message?.message}>
<textarea
id="message"
rows={5}
placeholder="Please write at least 10 characters"
className="w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
{...register("message")}
/>
{}
<p className={message.length > 500 ? "text-right text-xs text-destructive" : "text-right text-xs text-muted-foreground"}>
{}
{message.length} / 500
</p>
</Field>
{}
<Button type="submit" disabled={isSubmitting}>
{isSubmitting ? "Sending…" : "Send"}
</Button>
</form>
)
}