feat(website): Next.js 16 marketing website with RTL/Farsi
Marketing website for Meezi platform: - Server-side rendered pages: home, demo, blog, pricing - RTL/Farsi layout with Vazirmatn font - SEO metadata and Open Graph tags - proxy.ts for Next.js 16 middleware convention - MEEZI_API_URL internal Docker network routing Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { CheckCircle2, AlertCircle, Send } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface FormState {
|
||||
name: string;
|
||||
business: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
branches: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export function DemoForm() {
|
||||
const t = useTranslations("demo");
|
||||
const [form, setForm] = useState<FormState>({
|
||||
name: "",
|
||||
business: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
branches: "1",
|
||||
message: "",
|
||||
});
|
||||
const [status, setStatus] = useState<"idle" | "submitting" | "success" | "error">("idle");
|
||||
|
||||
const handleChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>
|
||||
) => setForm((prev) => ({ ...prev, [e.target.name]: e.target.value }));
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setStatus("submitting");
|
||||
try {
|
||||
const res = await fetch("/api/demo", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(form),
|
||||
});
|
||||
if (!res.ok) throw new Error("failed");
|
||||
setStatus("success");
|
||||
} catch {
|
||||
setStatus("error");
|
||||
}
|
||||
};
|
||||
|
||||
const inputCls =
|
||||
"w-full rounded-xl border border-gray-200 bg-white px-4 py-3 text-sm text-gray-900 placeholder-gray-400 outline-none transition-all focus:border-brand-400 focus:ring-2 focus:ring-brand-100 disabled:opacity-50";
|
||||
|
||||
if (status === "success") {
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-4 rounded-2xl border border-green-100 bg-green-50 px-8 py-12 text-center">
|
||||
<CheckCircle2 className="h-14 w-14 text-green-500" />
|
||||
<h3 className="text-xl font-bold text-gray-900">{t("successTitle")}</h3>
|
||||
<p className="text-gray-500">{t("successDesc")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
{/* Name */}
|
||||
<div>
|
||||
<label className="mb-1.5 block text-sm font-medium text-gray-700">
|
||||
{t("nameLabel")} <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<input
|
||||
name="name"
|
||||
required
|
||||
value={form.name}
|
||||
onChange={handleChange}
|
||||
placeholder={t("namePlaceholder")}
|
||||
className={inputCls}
|
||||
disabled={status === "submitting"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Business */}
|
||||
<div>
|
||||
<label className="mb-1.5 block text-sm font-medium text-gray-700">
|
||||
{t("businessLabel")} <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<input
|
||||
name="business"
|
||||
required
|
||||
value={form.business}
|
||||
onChange={handleChange}
|
||||
placeholder={t("businessPlaceholder")}
|
||||
className={inputCls}
|
||||
disabled={status === "submitting"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Phone */}
|
||||
<div>
|
||||
<label className="mb-1.5 block text-sm font-medium text-gray-700">
|
||||
{t("phoneLabel")} <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<input
|
||||
name="phone"
|
||||
type="tel"
|
||||
required
|
||||
value={form.phone}
|
||||
onChange={handleChange}
|
||||
placeholder={t("phonePlaceholder")}
|
||||
className={inputCls}
|
||||
disabled={status === "submitting"}
|
||||
dir="ltr"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
<div>
|
||||
<label className="mb-1.5 block text-sm font-medium text-gray-700">
|
||||
{t("emailLabel")}
|
||||
</label>
|
||||
<input
|
||||
name="email"
|
||||
type="email"
|
||||
value={form.email}
|
||||
onChange={handleChange}
|
||||
placeholder={t("emailPlaceholder")}
|
||||
className={inputCls}
|
||||
disabled={status === "submitting"}
|
||||
dir="ltr"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Branches */}
|
||||
<div>
|
||||
<label className="mb-1.5 block text-sm font-medium text-gray-700">
|
||||
{t("branchLabel")} <span className="text-red-400">*</span>
|
||||
</label>
|
||||
<select
|
||||
name="branches"
|
||||
required
|
||||
value={form.branches}
|
||||
onChange={handleChange}
|
||||
className={inputCls}
|
||||
disabled={status === "submitting"}
|
||||
>
|
||||
<option value="1">{t("branch1")}</option>
|
||||
<option value="2-3">{t("branch2")}</option>
|
||||
<option value="4-10">{t("branch3")}</option>
|
||||
<option value="10+">{t("branch4")}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Message */}
|
||||
<div>
|
||||
<label className="mb-1.5 block text-sm font-medium text-gray-700">
|
||||
{t("messageLabel")}
|
||||
</label>
|
||||
<textarea
|
||||
name="message"
|
||||
rows={4}
|
||||
value={form.message}
|
||||
onChange={handleChange}
|
||||
placeholder={t("messagePlaceholder")}
|
||||
className={cn(inputCls, "resize-none")}
|
||||
disabled={status === "submitting"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{status === "error" && (
|
||||
<div className="flex items-center gap-2 rounded-xl border border-red-100 bg-red-50 px-4 py-3 text-sm text-red-600">
|
||||
<AlertCircle className="h-4 w-4 shrink-0" />
|
||||
{t("errorDesc")}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={status === "submitting"}
|
||||
className="flex w-full items-center justify-center gap-2 rounded-xl bg-brand-700 py-3.5 text-sm font-semibold text-white shadow-lg shadow-brand-700/25 transition-all hover:bg-brand-800 disabled:opacity-70"
|
||||
>
|
||||
{status === "submitting" ? (
|
||||
<>
|
||||
<span className="h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
|
||||
{t("submitting")}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Send className="h-4 w-4" />
|
||||
{t("submit")}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user