diff --git a/public/videos/promo.mp4 b/public/videos/promo.mp4 new file mode 100644 index 0000000..37bf4f1 Binary files /dev/null and b/public/videos/promo.mp4 differ diff --git a/src/app/page.tsx b/src/app/page.tsx index 245e111..8a1a7dc 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -9,6 +9,7 @@ import FeaturesBar from "@/components/home/FeaturesBar"; import BlackCollection from "@/components/home/BlackCollection"; import AboutSection from "@/components/home/AboutSection"; import SiteFooter from "@/components/home/SiteFooter"; +import SubscribeModal from "@/components/home/SubscribeModal"; import { mapHomeSlots } from "@/lib/map-home-slots"; import { fetchPublicWebsiteSlots, @@ -50,6 +51,7 @@ export default async function HomePage() { return (
+
diff --git a/src/components/home/SubscribeModal.tsx b/src/components/home/SubscribeModal.tsx new file mode 100644 index 0000000..bc2ad8c --- /dev/null +++ b/src/components/home/SubscribeModal.tsx @@ -0,0 +1,255 @@ +"use client"; + +import { FormEvent, useCallback, useEffect, useId, useState } from "react"; +import { createPortal } from "react-dom"; +import { API_URL } from "@/lib/public-api"; + +const HIDDEN_KEY = "mana-subscribe-hidden"; +const LEGACY_DONE_KEY = "mana-subscribe-done"; +const PROMO_VIDEO = "/videos/promo.mp4"; + +function isHiddenForever() { + try { + return ( + window.localStorage.getItem(HIDDEN_KEY) === "1" || + window.localStorage.getItem(LEGACY_DONE_KEY) === "1" + ); + } catch { + return false; + } +} + +function rememberHidden() { + try { + window.localStorage.setItem(HIDDEN_KEY, "1"); + } catch { + /* storage unavailable */ + } +} + +const PERSIAN_DIGITS = "۰۱۲۳۴۵۶۷۸۹"; +const ARABIC_DIGITS = "٠١٢٣٤٥٦٧٨٩"; + +function normalizeCell(value: string) { + return value + .trim() + .replace(/[۰-۹]/g, (digit) => String(PERSIAN_DIGITS.indexOf(digit))) + .replace(/[٠-٩]/g, (digit) => String(ARABIC_DIGITS.indexOf(digit))) + .replace(/[^\d]/g, ""); +} + +function apiErrorMessage(body: unknown) { + if (body && typeof body === "object" && "message" in body) { + const message = (body as { message: unknown }).message; + if (Array.isArray(message) && message.length) return String(message[0]); + if (typeof message === "string" && message.trim()) return message; + } + return "ثبت شماره ناموفق بود. دوباره تلاش کنید."; +} + +export default function SubscribeModal() { + const titleId = useId(); + const [mounted, setMounted] = useState(false); + const [open, setOpen] = useState(false); + const [phone, setPhone] = useState(""); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); + const [done, setDone] = useState(false); + + useEffect(() => { + setMounted(true); + if (isHiddenForever()) return; + const timer = window.setTimeout(() => setOpen(true), 600); + return () => window.clearTimeout(timer); + }, []); + + const dismiss = useCallback(() => { + setOpen(false); + }, []); + + const decline = useCallback(() => { + rememberHidden(); + setOpen(false); + }, []); + + useEffect(() => { + if (!open) return; + const previous = document.body.style.overflow; + document.body.style.overflow = "hidden"; + const onKey = (event: KeyboardEvent) => { + if (event.key === "Escape") dismiss(); + }; + window.addEventListener("keydown", onKey); + return () => { + document.body.style.overflow = previous; + window.removeEventListener("keydown", onKey); + }; + }, [open, dismiss]); + + async function handleSubmit(event: FormEvent) { + event.preventDefault(); + const cellNumber = normalizeCell(phone); + if (!/^09\d{9}$/.test(cellNumber)) { + setError("شماره موبایل را با ۰۹ و ۱۱ رقم وارد کنید."); + return; + } + + setSaving(true); + setError(null); + try { + const res = await fetch(`${API_URL}/public/subscribe`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ cellNumber }), + }); + const body: unknown = await res.json().catch(() => null); + if (!res.ok) { + setError(apiErrorMessage(body)); + return; + } + rememberHidden(); + setDone(true); + } catch { + setError("ارتباط با سرور برقرار نشد. دوباره تلاش کنید."); + } finally { + setSaving(false); + } + } + + if (!mounted || !open) return null; + + return createPortal( +
+ + + {done ? ( +
+

+ You are on the list +

+

+ شماره شما ثبت شد +

+

+ از این پس خبر کالکشن‌های تازه و فروش ویژه مانا را برای همین + شماره می‌فرستیم. +

+ +
+ ) : ( +
+

+ Join Mana +

+

+ به جمع مانا بپیوندید +

+

+ شماره موبایل‌تان را ثبت کنید تا از کالکشن‌های تازه، فروش ویژه و + خبرهای مانا باخبر شوید. +

+ + + + {error ? ( +

+ {error} +

+ ) : null} + + + +
+ )} +
+
+ , + document.body, + ); +}