Add about/contact/installments pages, fix category slot bugs, wire live branding
- Enlarge category carousel thumbnails (90px -> 104-140px responsive) and
confirm the slider/categories/banner static-image slots are all reading
correctly from the CMS (verified live, not a wiring bug)
- Fix installments page dropping "هندزفری و هدفون": it's a level-3 category,
not level-2, so level2.find() silently missed it - use findNode() over the
whole tree instead
- Extend gen-category-placeholders.mjs to generate a placeholder for every
category at every level (was level-2 only), needed for the fix above
- Add /about, /contact, /installments pages
- About: real copy transcribed from the live moalem.shop theme, real
product photos from the API, text-only brand strip (no scraped logos)
- Contact: ContactForm.tsx posts to the real POST /contact-submissions,
branch cards with key-less Google Maps embeds
- Installments: flat brand-colored hero (no photo/gradient), category
highlights reusing the same categories slot images as the homepage
- Replace the placeholder header/footer phone number with the real one, add
real branch addresses and Instagram/Telegram links (src/data/site.ts),
pulled from the live theme's footer
- Wire logo/favicon to GET /website/favicon (getFavicon() in api.ts) instead
of the static bundled files, so a dashboard upload now shows automatically;
layout.tsx metadata is now generateMetadata() to support this. The tenant's
uploaded logo/favicon currently belongs to a different business - the
wiring is correct and will self-correct once that's fixed in the dashboard
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
09a409b3e7
commit
db7d00e473
@@ -0,0 +1,88 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { submitContactForm } from "@/lib/api";
|
||||
|
||||
type Status = "idle" | "submitting" | "sent" | "error";
|
||||
|
||||
// Posts straight to POST /tenants/moalem.shop/contact-submissions — a real,
|
||||
// documented endpoint, no bearer required.
|
||||
export default function ContactForm() {
|
||||
const [status, setStatus] = useState<Status>("idle");
|
||||
|
||||
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
const form = new FormData(e.currentTarget);
|
||||
const name = String(form.get("name") || "").trim();
|
||||
const title = String(form.get("title") || "").trim();
|
||||
const text = String(form.get("text") || "").trim();
|
||||
const email = String(form.get("email") || "").trim();
|
||||
const cellNumber = String(form.get("cellNumber") || "").trim();
|
||||
if (!name || !title || !text) return;
|
||||
|
||||
setStatus("submitting");
|
||||
try {
|
||||
await submitContactForm({
|
||||
name,
|
||||
title,
|
||||
text,
|
||||
email: email || undefined,
|
||||
cellNumber: cellNumber || undefined,
|
||||
});
|
||||
setStatus("sent");
|
||||
e.currentTarget.reset();
|
||||
} catch {
|
||||
setStatus("error");
|
||||
}
|
||||
}
|
||||
|
||||
if (status === "sent") {
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-3 rounded-2xl border border-line bg-surface p-10 text-center">
|
||||
<p className="text-[15px] font-bold text-ink">پیام شما با موفقیت ارسال شد.</p>
|
||||
<p className="text-[13px] text-body">همکاران ما در اسرع وقت با شما تماس میگیرند.</p>
|
||||
<button
|
||||
onClick={() => setStatus("idle")}
|
||||
className="mt-2 rounded-lg border border-line px-4 py-2 text-[13px] text-ink-soft transition hover:border-brand hover:text-brand"
|
||||
>
|
||||
ارسال پیام دیگر
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const input =
|
||||
"h-12 w-full rounded-xl border border-line bg-white px-4 text-sm outline-none transition focus:border-brand";
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<input name="name" required placeholder="نام و نام خانوادگی" className={input} />
|
||||
<input name="cellNumber" placeholder="شماره موبایل" inputMode="tel" className={input} />
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
<input name="email" type="email" placeholder="ایمیل (اختیاری)" className={input} />
|
||||
<input name="title" required placeholder="موضوع پیام" className={input} />
|
||||
</div>
|
||||
<textarea
|
||||
name="text"
|
||||
required
|
||||
rows={5}
|
||||
placeholder="متن پیام شما…"
|
||||
className="w-full rounded-xl border border-line bg-white p-4 text-sm outline-none transition focus:border-brand"
|
||||
/>
|
||||
|
||||
{status === "error" && (
|
||||
<p className="text-[12.5px] text-brand">ارسال پیام ناموفق بود. لطفاً دوباره تلاش کنید.</p>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={status === "submitting"}
|
||||
className="flex h-12 w-full items-center justify-center rounded-xl bg-brand text-sm font-bold text-white transition hover:bg-brand-dark disabled:cursor-not-allowed disabled:opacity-50 sm:w-auto sm:px-10"
|
||||
>
|
||||
{status === "submitting" ? "در حال ارسال…" : "ارسال پیام"}
|
||||
</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user