Files
gitlab-instance-0a899031_cn…/app/[locale]/meetups/host/page.tsx
2026-06-06 04:29:46 -05:00

227 lines
8.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState } from "react";
import { Link, useTranslation } from "@/i18n/navigation";
import { cities } from "@/app/data/cities";
import { apiFetch } from "@/app/lib/api-client";
const CITY_OPTIONS = cities.map((c) => ({ value: c.name, label: `${c.icon} ${c.name}` }));
const CITY_BY_NAME = new Map(cities.map((city) => [city.name, city]));
export default function HostMeetupPage() {
const { t } = useTranslation("hostMeetup");
const [submitted, setSubmitted] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [form, setForm] = useState({
city: "",
date: "",
time: "19:00",
venue: "",
address: "",
description: "",
maxAttendees: "20",
});
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const city = CITY_BY_NAME.get(form.city);
setSubmitting(true);
try {
await apiFetch("/api/meetups", {
method: "POST",
body: JSON.stringify({
city: form.city,
emoji: city?.icon || "📍",
date: form.date,
time: form.time,
venue: form.venue,
address: form.address,
description: form.description,
rsvpCount: 1,
gradientFrom: city?.gradientFrom || "#ff4d4f",
gradientTo: city?.gradientTo || "#ff7a45",
attendees: [],
isUpcoming: true,
status: "pending",
}),
});
setSubmitted(true);
} finally {
setSubmitting(false);
}
};
if (submitted) {
return (
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
<main className="max-w-[600px] mx-auto px-4 sm:px-6 py-12 sm:py-20">
<div className="bg-white dark:bg-gray-900 rounded-2xl shadow-lg border border-gray-100 dark:border-gray-800 p-8 sm:p-12 text-center">
<span className="text-6xl mb-6 block">🎉</span>
<h2 className="text-xl sm:text-2xl font-bold text-gray-900 dark:text-gray-100 mb-3">
{t("successTitle")}
</h2>
<p className="text-gray-500 dark:text-gray-400 mb-8">
{t("successDesc")}
</p>
<div className="flex flex-col sm:flex-row gap-3 justify-center">
<Link
href="/meetups/host"
onClick={() => setSubmitted(false)}
className="px-6 py-3 rounded-xl border border-gray-200 dark:border-gray-700 text-gray-700 dark:text-gray-300 font-medium hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
>
{t("createAnother")}
</Link>
<Link
href="/meetups"
className="px-6 py-3 rounded-xl bg-[#ff4d4f] text-white font-medium hover:bg-[#ff7a45] transition-colors"
>
{t("viewMeetups")}
</Link>
</div>
</div>
</main>
</div>
);
}
return (
<div className="min-h-screen bg-[#fafafa] dark:bg-gray-950">
<main className="max-w-[640px] mx-auto px-4 sm:px-6 py-8 sm:py-12">
<header className="mb-8">
<Link
href="/meetups"
className="inline-flex items-center gap-1.5 text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 mb-4"
>
{t("backToMeetups")}
</Link>
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900 dark:text-gray-100 flex items-center gap-2">
<span className="w-1 h-8 bg-[#ff4d4f] rounded-full" />
{t("title")}
</h1>
<p className="text-gray-500 dark:text-gray-400 mt-1 text-sm sm:text-base">
{t("subtitle")}
</p>
</header>
<form
onSubmit={handleSubmit}
className="bg-white dark:bg-gray-900 rounded-2xl shadow-sm border border-gray-100 dark:border-gray-800 p-6 sm:p-8 space-y-6"
>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
{t("city")} *
</label>
<select
required
value={form.city}
onChange={(e) => setForm((f) => ({ ...f, city: e.target.value }))}
className="form-input w-full border border-gray-200 dark:border-gray-700 rounded-xl px-4 py-3 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
>
<option value="">{t("selectCity")}</option>
{CITY_OPTIONS.map((c) => (
<option key={c.value} value={c.value}>
{c.label}
</option>
))}
</select>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
{t("date")} *
</label>
<input
type="date"
required
value={form.date}
onChange={(e) => setForm((f) => ({ ...f, date: e.target.value }))}
className="form-input w-full border border-gray-200 dark:border-gray-700 rounded-xl px-4 py-3 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
{t("time")} *
</label>
<input
type="time"
required
value={form.time}
onChange={(e) => setForm((f) => ({ ...f, time: e.target.value }))}
className="form-input w-full border border-gray-200 dark:border-gray-700 rounded-xl px-4 py-3 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
{t("venue")} *
</label>
<input
type="text"
required
placeholder={t("venuePlaceholder")}
value={form.venue}
onChange={(e) => setForm((f) => ({ ...f, venue: e.target.value }))}
className="form-input w-full border border-gray-200 dark:border-gray-700 rounded-xl px-4 py-3 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-400"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
{t("address")} *
</label>
<input
type="text"
required
placeholder={t("addressPlaceholder")}
value={form.address}
onChange={(e) => setForm((f) => ({ ...f, address: e.target.value }))}
className="form-input w-full border border-gray-200 dark:border-gray-700 rounded-xl px-4 py-3 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-400"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
{t("description")}
</label>
<textarea
rows={4}
placeholder={t("descriptionPlaceholder")}
value={form.description}
onChange={(e) => setForm((f) => ({ ...f, description: e.target.value }))}
className="form-input w-full border border-gray-200 dark:border-gray-700 rounded-xl px-4 py-3 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-400 resize-none"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
{t("maxAttendees")}
</label>
<input
type="number"
min={2}
max={100}
value={form.maxAttendees}
onChange={(e) => setForm((f) => ({ ...f, maxAttendees: e.target.value }))}
className="form-input w-full border border-gray-200 dark:border-gray-700 rounded-xl px-4 py-3 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
/>
</div>
<p className="text-xs text-gray-400 dark:text-gray-500">
PocketBase meetups pending
</p>
<button
type="submit"
disabled={submitting}
className="w-full py-3.5 rounded-xl bg-[#ff4d4f] text-white font-semibold hover:bg-[#ff7a45] transition-colors"
>
{submitting ? "提交中..." : t("submit")}
</button>
</form>
</main>
</div>
);
}