"use client"; import { use, useState, type FormEvent } from "react"; import { Link, useLocale } from "@/i18n/navigation"; import { apiFetch } from "@/app/lib/api-client"; import { citySlug, findCityBySlug } from "@/app/lib/city-slugs"; interface CityVolunteerPageProps { params: Promise<{ slug: string }>; } interface VolunteerForm { applicantName: string; email: string; wechat: string; phone: string; currentCity: string; availability: string; localExperience: string; motivation: string; links: string; roles: string[]; } const ROLE_OPTIONS = { zh: ["城市资料维护", "新成员 CoffeeChat", "地陪路线协助", "Citywalk/徒步带队", "同城活动协助", "线上社群答疑"], en: ["City data updates", "New member CoffeeChat", "Local guide routes", "Citywalk / hiking host", "City event support", "Online community support"], }; const copy = { zh: { notFound: "城市不存在", back: "返回首页", kicker: "城市志愿者招募", titleSuffix: "城市志愿者申请", subtitle: "帮助维护城市资料、迎新 CoffeeChat、组织同城活动或带 Citywalk。提交后会进入审核状态。", status: "提交后状态:待审核", applicantName: "姓名 / 昵称", applicantNamePlaceholder: "方便社区联系你的称呼", email: "邮箱", wechat: "微信", phone: "手机", currentCity: "当前所在城市", availability: "可投入时间", availabilityPlaceholder: "例如:每周 2 小时 / 周末可带活动", roles: "想参与的志愿者方向", localExperience: "你和这个城市的关系", localExperiencePlaceholder: "例如:已在这里居住 1 年,熟悉咖啡馆、短租和徒步路线。", motivation: "申请说明", motivationPlaceholder: "你希望如何帮助这个城市的新成员?", links: "补充资料链接", linksPlaceholder: "可选:个人主页、社群资料、作品链接等", contactHint: "邮箱、微信、手机至少填写一项。", roleHint: "至少选择一个志愿者方向。", submit: "提交申请", submitting: "提交中...", successTitle: "申请已提交", successDesc: "当前状态:待审核。审核通过后,社区会把你加入该城市的志愿者协作名单。", submitAnother: "再提交一份", viewCity: "返回城市列表", }, en: { notFound: "City not found", back: "Back home", kicker: "City volunteer recruiting", titleSuffix: "Volunteer application", subtitle: "Help maintain city data, welcome new members, support local events, or host citywalks. Applications enter pending review after submission.", status: "After submission: pending review", applicantName: "Name / nickname", applicantNamePlaceholder: "How the community should contact you", email: "Email", wechat: "WeChat", phone: "Phone", currentCity: "Current city", availability: "Availability", availabilityPlaceholder: "e.g. 2 hours weekly / weekends for events", roles: "Volunteer areas", localExperience: "Your connection to this city", localExperiencePlaceholder: "e.g. I have lived here for one year and know cafes, short rentals, and hiking routes.", motivation: "Application note", motivationPlaceholder: "How would you help new members in this city?", links: "Supporting links", linksPlaceholder: "Optional: profile, community record, portfolio links", contactHint: "Please provide at least one contact: email, WeChat, or phone.", roleHint: "Please select at least one volunteer area.", submit: "Submit application", submitting: "Submitting...", successTitle: "Application submitted", successDesc: "Current status: pending review. Once approved, the community will add you to this city's volunteer roster.", submitAnother: "Submit another", viewCity: "Back to cities", }, }; function getCopy(locale: string) { return locale === "zh" ? copy.zh : copy.en; } const initialForm: VolunteerForm = { applicantName: "", email: "", wechat: "", phone: "", currentCity: "", availability: "", localExperience: "", motivation: "", links: "", roles: [], }; export default function CityVolunteerPage({ params }: CityVolunteerPageProps) { const { slug } = use(params); const city = findCityBySlug(slug); const locale = useLocale(); const c = getCopy(locale); const roleOptions = locale === "zh" ? ROLE_OPTIONS.zh : ROLE_OPTIONS.en; const [form, setForm] = useState(initialForm); const [submitted, setSubmitted] = useState(false); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); if (!city) { return (

{c.notFound}

{c.back}
); } const slugToSubmit = citySlug(city); const setField = (field: K, value: VolunteerForm[K]) => { setForm((current) => ({ ...current, [field]: value })); }; const toggleRole = (role: string) => { setForm((current) => { const selected = new Set(current.roles); if (selected.has(role)) selected.delete(role); else selected.add(role); return { ...current, roles: Array.from(selected) }; }); }; const handleSubmit = async (event: FormEvent) => { event.preventDefault(); setError(null); if (!form.email.trim() && !form.wechat.trim() && !form.phone.trim()) { setError(c.contactHint); return; } if (form.roles.length === 0) { setError(c.roleHint); return; } setSubmitting(true); try { await apiFetch(`/api/cities/${slugToSubmit}/volunteer-applications`, { method: "POST", body: JSON.stringify({ citySlug: slugToSubmit, cityName: city.name, ...form, }), }); setSubmitted(true); } catch (err) { setError(err instanceof Error ? err.message : "Submit failed"); } finally { setSubmitting(false); } }; if (submitted) { return (
OK

{city.name}

{c.successTitle}

{c.successDesc}

{c.viewCity}
); } return (
{c.back}

{c.kicker}

{city.name} {c.titleSuffix}

{c.subtitle}

{c.status}
setField("applicantName", value)} /> setField("currentCity", value)} />
setField("email", value)} /> setField("wechat", value)} /> setField("phone", value)} />
setField("availability", value)} />
{c.roles} *
{roleOptions.map((role) => { const checked = form.roles.includes(role); return ( ); })}