Initial commit: Ali Alemi storefront (Next.js, Meshkee API integration)
Farsi RTL Next.js 16 storefront for استاد علی عالمی, cloned from the original Meshkee web-builder site and rebuilt with Tailwind 4, live Meshkee API integration (products, store items, portfolios, blog, workshops, comments, cart, customer auth), and standalone production output for the shared-VM deploy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
"use client";
|
||||
|
||||
import { useState, type FormEvent } from "react";
|
||||
import { IconCheck } from "@/components/icons";
|
||||
import { useToast } from "@/components/providers/ToastProvider";
|
||||
import { submitContactForm } from "@/lib/meshkee/contact";
|
||||
|
||||
export function ContactForm() {
|
||||
const [sent, setSent] = useState(false);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const { showToast } = useToast();
|
||||
|
||||
async function handleSubmit(event: FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault();
|
||||
const form = event.currentTarget;
|
||||
const data = new FormData(form);
|
||||
|
||||
setSubmitting(true);
|
||||
try {
|
||||
await submitContactForm({
|
||||
title: "پیام از فرم تماس با ما",
|
||||
name: String(data.get("name") ?? ""),
|
||||
cellNumber: String(data.get("phone") ?? ""),
|
||||
text: String(data.get("message") ?? ""),
|
||||
});
|
||||
showToast("پیام شما با موفقیت ارسال شد.");
|
||||
setSent(true);
|
||||
form.reset();
|
||||
setTimeout(() => setSent(false), 4000);
|
||||
} catch {
|
||||
showToast("ارسال پیام با خطا مواجه شد. لطفاً دوباره تلاش کنید.", "error");
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="grid gap-5 rounded-3xl bg-white p-8 shadow-sm ring-1 ring-line md:grid-cols-2"
|
||||
>
|
||||
{sent && (
|
||||
<div className="flex items-center gap-3 rounded-2xl bg-primary-soft px-5 py-4 md:col-span-2">
|
||||
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-primary text-white">
|
||||
<IconCheck className="h-4 w-4" />
|
||||
</span>
|
||||
<p className="text-sm font-medium text-ink-soft">
|
||||
پیام شما ارسال شد. از اینکه با استاد علی عالمی در ارتباط هستید سپاسگزاریم.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="md:col-span-1">
|
||||
<label htmlFor="name" className="mb-2 block text-sm font-medium text-ink-soft">
|
||||
نام و نام خانوادگی
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
name="name"
|
||||
required
|
||||
type="text"
|
||||
className="w-full rounded-xl border border-line px-4 py-3 text-sm outline-none transition-colors focus:border-primary"
|
||||
placeholder="نام شما"
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-1">
|
||||
<label htmlFor="phone" className="mb-2 block text-sm font-medium text-ink-soft">
|
||||
شماره تماس
|
||||
</label>
|
||||
<input
|
||||
id="phone"
|
||||
name="phone"
|
||||
required
|
||||
type="tel"
|
||||
dir="ltr"
|
||||
className="w-full rounded-xl border border-line px-4 py-3 text-sm outline-none transition-colors focus:border-primary"
|
||||
placeholder="0912 000 0000"
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<label htmlFor="message" className="mb-2 block text-sm font-medium text-ink-soft">
|
||||
پیام شما
|
||||
</label>
|
||||
<textarea
|
||||
id="message"
|
||||
name="message"
|
||||
required
|
||||
rows={5}
|
||||
className="w-full resize-none rounded-xl border border-line px-4 py-3 text-sm outline-none transition-colors focus:border-primary"
|
||||
placeholder="متن پیام..."
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={submitting}
|
||||
className="w-full rounded-full bg-primary px-7 py-3.5 text-sm font-semibold text-white transition-colors hover:bg-primary-dark disabled:opacity-60 md:w-auto"
|
||||
>
|
||||
{submitting ? "در حال ارسال..." : "ارسال پیام"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import Image from "next/image";
|
||||
import { CONTACT } from "@/lib/site";
|
||||
import { IconInstagram, IconLocation, IconPhone } from "@/components/icons";
|
||||
|
||||
const ITEMS = [
|
||||
{ icon: IconLocation, text: CONTACT.address, href: undefined },
|
||||
{ icon: IconPhone, text: CONTACT.phoneLabel, href: `tel:${CONTACT.phone}`, dir: "ltr" as const },
|
||||
{ icon: IconInstagram, text: CONTACT.instagramLabel, href: CONTACT.instagram, dir: "ltr" as const },
|
||||
];
|
||||
|
||||
export function ContactInfo() {
|
||||
return (
|
||||
<section className="py-16 md:py-24">
|
||||
<div className="boxed grid items-center gap-14 md:grid-cols-2">
|
||||
<div className="relative mx-auto w-full max-w-md">
|
||||
<div className="absolute -inset-x-6 -top-6 bottom-10 rounded-[2.5rem] bg-primary-soft" aria-hidden />
|
||||
<Image
|
||||
src="/images/contact-photo.jpg"
|
||||
alt="ارتباط با استاد علی عالمی"
|
||||
width={520}
|
||||
height={600}
|
||||
className="relative w-full rounded-[2.25rem] object-cover shadow-xl"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-primary">راههای ارتباطی</p>
|
||||
<h2 className="mt-2 text-3xl font-bold text-ink">با ما تماس بگیرید</h2>
|
||||
<p className="mt-4 text-base leading-8 text-body">ما هر روز هفته آمادهی شنیدن نظرات شما هستیم.</p>
|
||||
|
||||
<ul className="mt-8 space-y-4">
|
||||
{ITEMS.map(({ icon: Icon, text, href, dir }) => (
|
||||
<li key={text} className="flex items-center gap-4 rounded-2xl bg-primary-soft p-4">
|
||||
<span className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-primary text-white">
|
||||
<Icon className="h-5 w-5" />
|
||||
</span>
|
||||
{href ? (
|
||||
<a href={href} target={href.startsWith("http") ? "_blank" : undefined} rel="noopener noreferrer" dir={dir} className="text-sm font-medium text-ink-soft hover:text-primary">
|
||||
{text}
|
||||
</a>
|
||||
) : (
|
||||
<span className="text-sm font-medium text-ink-soft">{text}</span>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { CUSTOMER_URL } from "@/lib/site";
|
||||
import { IconArrow } from "@/components/icons";
|
||||
|
||||
export function CustomerClubBanner() {
|
||||
return (
|
||||
<section className="relative overflow-hidden bg-gradient-to-l from-secondary to-primary py-14">
|
||||
<div
|
||||
className="pointer-events-none absolute inset-0 opacity-[0.08]"
|
||||
style={{
|
||||
backgroundImage: "radial-gradient(currentColor 1.5px, transparent 1.5px)",
|
||||
backgroundSize: "20px 20px",
|
||||
color: "#ffffff",
|
||||
}}
|
||||
aria-hidden
|
||||
/>
|
||||
<div className="boxed relative flex flex-col items-center justify-between gap-6 text-center md:flex-row md:text-right">
|
||||
<h2 className="text-2xl font-bold text-white md:text-3xl">در باشگاه مشتریان ما عضو شوید</h2>
|
||||
<a
|
||||
href={`${CUSTOMER_URL}/register`}
|
||||
className="flex shrink-0 items-center gap-2 rounded-full bg-white px-7 py-3.5 text-sm font-semibold text-secondary transition-transform hover:scale-105"
|
||||
>
|
||||
عضویت رایگان
|
||||
<IconArrow className="h-4 w-4 -scale-x-100" />
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { CONTACT } from "@/lib/site";
|
||||
import { IconLocation } from "@/components/icons";
|
||||
import { ContactForm } from "./ContactForm";
|
||||
|
||||
export function MessageSection() {
|
||||
const mapsHref = `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(CONTACT.address)}`;
|
||||
|
||||
return (
|
||||
<section className="bg-surface py-16 md:py-24">
|
||||
<div className="boxed">
|
||||
<h2 className="text-center text-3xl font-bold text-ink">به ما پیام بفرستید!</h2>
|
||||
|
||||
<div className="mt-12 grid gap-8 lg:grid-cols-[1.4fr_1fr]">
|
||||
<ContactForm />
|
||||
|
||||
<a
|
||||
href={mapsHref}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group relative flex min-h-[280px] flex-col justify-between overflow-hidden rounded-3xl bg-secondary p-8 text-white transition-colors hover:bg-secondary-dark"
|
||||
>
|
||||
<div
|
||||
className="pointer-events-none absolute inset-0 opacity-[0.08]"
|
||||
style={{
|
||||
backgroundImage: "radial-gradient(currentColor 1.5px, transparent 1.5px)",
|
||||
backgroundSize: "18px 18px",
|
||||
}}
|
||||
aria-hidden
|
||||
/>
|
||||
<span className="relative flex h-12 w-12 items-center justify-center rounded-xl bg-white/10 text-primary">
|
||||
<IconLocation className="h-6 w-6" />
|
||||
</span>
|
||||
<div className="relative">
|
||||
<p className="text-lg font-bold">موقعیت مکانی ما</p>
|
||||
<p className="mt-2 text-sm leading-7 text-white/70">{CONTACT.address}</p>
|
||||
<p className="mt-4 text-xs font-semibold text-primary group-hover:underline">
|
||||
مشاهده در نقشه ←
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user