Files
wagtailcms-frontend/blocks/form/LeadForm.tsx
T
Zhengen b5fe5e13ab feat: add products/cases pages and lead-capture form block
- types: Product/CaseStudy page types, CaseStudyBlockValue, FormBlockValue
- services: product.service.ts, case.service.ts
- app/products, app/cases: list + detail pages
- blocks: CaseStudyCard, LeadForm (submits to /api/v1/custom/leads/)
- Header nav: add 产品/案例 links
2026-08-06 16:04:43 +08:00

109 lines
3.3 KiB
TypeScript

"use client";
import { useState } from "react";
import type { FormBlockValue } from "@/types/wagtail";
const API_BASE_URL =
process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000";
export default function LeadForm({ value }: { value: FormBlockValue }) {
const [values, setValues] = useState<Record<string, string>>({});
const [status, setStatus] = useState<"idle" | "submitting" | "done" | "error">(
"idle",
);
const form = value.form;
if (!form) {
return null;
}
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setStatus("submitting");
try {
const res = await fetch(`${API_BASE_URL}/api/v1/custom/leads/`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
form_id: form!.id,
data: values,
source_url: typeof window !== "undefined" ? window.location.href : "",
}),
});
if (!res.ok) throw new Error("submit failed");
setStatus("done");
} catch {
setStatus("error");
}
}
if (status === "done") {
return (
<div className="mx-auto max-w-xl px-6 py-12 text-center text-slate-700">
{form.success_message}
</div>
);
}
return (
<form
onSubmit={handleSubmit}
className="mx-auto max-w-xl space-y-4 px-6 py-12"
>
<h3 className="text-2xl font-semibold text-slate-900">{form.name}</h3>
{form.fields.map((field) => (
<div key={field.field_key}>
<label className="mb-1 block text-sm font-medium text-slate-700">
{field.label}
{field.required && <span className="text-red-500">*</span>}
</label>
{field.field_type === "textarea" ? (
<textarea
required={field.required}
className="w-full rounded-lg border border-slate-200 px-4 py-2"
rows={4}
onChange={(e) =>
setValues((v) => ({ ...v, [field.field_key]: e.target.value }))
}
/>
) : field.field_type === "select" ? (
<select
required={field.required}
className="w-full rounded-lg border border-slate-200 px-4 py-2"
onChange={(e) =>
setValues((v) => ({ ...v, [field.field_key]: e.target.value }))
}
>
<option value="">请选择</option>
{field.choices.map((choice) => (
<option key={choice} value={choice}>
{choice}
</option>
))}
</select>
) : (
<input
type={field.field_type}
required={field.required}
className="w-full rounded-lg border border-slate-200 px-4 py-2"
onChange={(e) =>
setValues((v) => ({ ...v, [field.field_key]: e.target.value }))
}
/>
)}
</div>
))}
<button
type="submit"
disabled={status === "submitting"}
className="w-full rounded-full bg-primary px-6 py-3 font-medium text-white disabled:opacity-50"
>
{status === "submitting" ? "提交中..." : form.submit_button_text}
</button>
{status === "error" && (
<p className="text-sm text-red-500">提交失败,请稍后重试。</p>
)}
</form>
);
}