Marketing site (bargevasat.ir) + admin-editable store links + subdomain split
- New standalone Next.js marketing site under site/ (static export, SEO): landing, download/install guide (Bazaar/Myket/iOS-PWA/web), FAQ (JSON-LD), privacy, terms, support, /admin link editor. fa RTL, sitemap/robots/manifest. - Backend: SiteLinksService (JSON-file persisted) + GET /api/site/links (public) + POST /api/admin/site/links (X-Admin-Token). ADMIN_TOKEN + Site__DataDir via env. - compose: hokm-site service (:1520) + hokm_data volume for links JSON. - CI deploy job builds + deploys the site container. - deploy/SUBDOMAIN_SPLIT.md: nginx blocks, cert reissue, DNS, ENV split. - Exclude site/ from root tsc + web docker context. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { fetchLinks, FALLBACK_LINKS, type SiteLinks } from "@/lib/links";
|
||||
import { API_URL } from "@/lib/site";
|
||||
|
||||
type Field = { key: keyof SiteLinks; label: string; type: "text" | "bool" };
|
||||
|
||||
const FIELDS: Field[] = [
|
||||
{ key: "bazaarUrl", label: "لینک کافهبازار", type: "text" },
|
||||
{ key: "bazaarEnabled", label: "نمایش دکمهٔ کافهبازار", type: "bool" },
|
||||
{ key: "myketUrl", label: "لینک مایکت", type: "text" },
|
||||
{ key: "myketEnabled", label: "نمایش دکمهٔ مایکت", type: "bool" },
|
||||
{ key: "directApkUrl", label: "لینک دانلود مستقیم APK", type: "text" },
|
||||
{ key: "directApkEnabled", label: "نمایش دانلود مستقیم", type: "bool" },
|
||||
{ key: "webPlayUrl", label: "آدرس بازی (وب)", type: "text" },
|
||||
{ key: "iosPwaEnabled", label: "نمایش نصب iOS/PWA", type: "bool" },
|
||||
{ key: "instagram", label: "اینستاگرام", type: "text" },
|
||||
{ key: "telegram", label: "تلگرام", type: "text" },
|
||||
{ key: "supportEmail", label: "ایمیل پشتیبانی", type: "text" },
|
||||
{ key: "supportPhone", label: "تلفن پشتیبانی", type: "text" },
|
||||
{ key: "appVersion", label: "نسخهٔ اپ", type: "text" },
|
||||
];
|
||||
|
||||
export default function AdminPage() {
|
||||
const [token, setToken] = useState("");
|
||||
const [authed, setAuthed] = useState(false);
|
||||
const [links, setLinks] = useState<SiteLinks>(FALLBACK_LINKS);
|
||||
const [msg, setMsg] = useState<string | null>(null);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
async function login() {
|
||||
setBusy(true);
|
||||
setMsg(null);
|
||||
const l = await fetchLinks();
|
||||
setLinks(l);
|
||||
setAuthed(true);
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
async function save() {
|
||||
setBusy(true);
|
||||
setMsg(null);
|
||||
try {
|
||||
const res = await fetch(`${API_URL}/api/admin/site/links`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", "X-Admin-Token": token },
|
||||
body: JSON.stringify(links),
|
||||
});
|
||||
if (res.status === 401) {
|
||||
setMsg("توکن نامعتبر است.");
|
||||
} else if (!res.ok) {
|
||||
setMsg("خطا در ذخیره.");
|
||||
} else {
|
||||
setLinks(await res.json());
|
||||
setMsg("ذخیره شد ✓");
|
||||
}
|
||||
} catch {
|
||||
setMsg("سرور در دسترس نیست.");
|
||||
}
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
function set<K extends keyof SiteLinks>(k: K, v: SiteLinks[K]) {
|
||||
setLinks((p) => ({ ...p, [k]: v }));
|
||||
}
|
||||
|
||||
if (!authed) {
|
||||
return (
|
||||
<section className="mx-auto max-w-md px-4 py-20">
|
||||
<h1 className="text-2xl font-black gold-text">ورود مدیریت</h1>
|
||||
<p className="mt-2 text-sm text-cream/60">توکن مدیریت را وارد کن.</p>
|
||||
<input
|
||||
type="password"
|
||||
value={token}
|
||||
onChange={(e) => setToken(e.target.value)}
|
||||
placeholder="ADMIN_TOKEN"
|
||||
className="mt-5 w-full rounded-xl bg-navy-800 px-4 py-3 text-cream outline-none ring-1 ring-gold/20 focus:ring-gold/50"
|
||||
/>
|
||||
<button
|
||||
onClick={login}
|
||||
disabled={!token || busy}
|
||||
className="mt-4 w-full rounded-xl btn-gold px-4 py-3 disabled:opacity-50"
|
||||
>
|
||||
ورود
|
||||
</button>
|
||||
<p className="mt-3 text-xs text-cream/45">
|
||||
توکن همان مقدار ADMIN_TOKEN در فایل محیطی سرور است. ذخیره هنگام «ثبت» اعتبارسنجی میشود.
|
||||
</p>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="mx-auto max-w-2xl px-4 py-14">
|
||||
<h1 className="text-2xl font-black gold-text">مدیریت لینکها</h1>
|
||||
<p className="mt-2 text-sm text-cream/60">لینکهای کافهبازار، مایکت، شبکههای اجتماعی و پشتیبانی را اینجا تنظیم کن.</p>
|
||||
|
||||
<div className="mt-8 space-y-4">
|
||||
{FIELDS.map((f) =>
|
||||
f.type === "bool" ? (
|
||||
<label key={f.key} className="glass flex items-center justify-between rounded-xl px-4 py-3">
|
||||
<span>{f.label}</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={Boolean(links[f.key])}
|
||||
onChange={(e) => set(f.key, e.target.checked as never)}
|
||||
className="h-5 w-5 accent-[#d4af37]"
|
||||
/>
|
||||
</label>
|
||||
) : (
|
||||
<div key={f.key}>
|
||||
<label className="mb-1 block text-sm text-cream/70">{f.label}</label>
|
||||
<input
|
||||
dir="ltr"
|
||||
value={String(links[f.key] ?? "")}
|
||||
onChange={(e) => set(f.key, e.target.value as never)}
|
||||
className="w-full rounded-xl bg-navy-800 px-4 py-2.5 text-cream outline-none ring-1 ring-gold/15 focus:ring-gold/50"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex items-center gap-3">
|
||||
<button onClick={save} disabled={busy} className="rounded-xl btn-gold px-6 py-3 disabled:opacity-50">
|
||||
ثبت تغییرات
|
||||
</button>
|
||||
{msg && <span className="text-sm text-cream/80">{msg}</span>}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import type { Metadata } from "next";
|
||||
import { PageShell } from "@/components/PageShell";
|
||||
import { DownloadButtons } from "@/components/DownloadButtons";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "دانلود و نصب",
|
||||
description:
|
||||
"برگ وسط را روی اندروید (کافهبازار، مایکت)، آیفون (نصب وب/PWA) یا مستقیماً در مرورگر اجرا کن. راهنمای گامبهگام نصب.",
|
||||
alternates: { canonical: "/download" },
|
||||
};
|
||||
|
||||
function Steps({ items }: { items: string[] }) {
|
||||
return (
|
||||
<ol className="space-y-3">
|
||||
{items.map((s, i) => (
|
||||
<li key={i} className="flex gap-3">
|
||||
<span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full btn-gold text-sm font-black">
|
||||
{i + 1}
|
||||
</span>
|
||||
<span className="pt-0.5 text-cream/80">{s}</span>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
);
|
||||
}
|
||||
|
||||
export default function DownloadPage() {
|
||||
return (
|
||||
<PageShell title="دانلود و نصب" subtitle="هر طور که دوست داری بازی کن — روی گوشی نصب کن یا مستقیم در مرورگر اجرا کن.">
|
||||
<div className="mb-8">
|
||||
<DownloadButtons variant="full" />
|
||||
</div>
|
||||
|
||||
{/* Web */}
|
||||
<div className="glass rounded-2xl p-6">
|
||||
<h2 className="text-xl font-bold text-cream">🌐 بازی در مرورگر (بدون نصب)</h2>
|
||||
<p className="mt-2 text-cream/70">
|
||||
سریعترین راه: کافی است آدرس بازی را در مرورگر باز کنی و وارد شوی. هیچ نصبی لازم نیست و روی هر دستگاهی کار میکند.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Android */}
|
||||
<div id="android" className="glass rounded-2xl p-6">
|
||||
<h2 className="text-xl font-bold text-cream">🤖 اندروید</h2>
|
||||
<p className="mt-2 mb-4 text-cream/70">از کافهبازار یا مایکت نصب کن، یا اپ وب را به صفحهٔ اصلی اضافه کن:</p>
|
||||
<Steps
|
||||
items={[
|
||||
"آدرس بازی را در مرورگر کروم باز کن.",
|
||||
"روی منوی سهنقطهٔ بالا-راست بزن.",
|
||||
"گزینهٔ «افزودن به صفحهٔ اصلی / Install app» را انتخاب کن.",
|
||||
"آیکن برگ وسط مثل یک اپ روی گوشیات مینشیند.",
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* iOS */}
|
||||
<div id="ios" className="glass rounded-2xl p-6">
|
||||
<h2 className="text-xl font-bold text-cream">🍏 آیفون و آیپد (iOS)</h2>
|
||||
<p className="mt-2 mb-4 text-cream/70">
|
||||
روی iOS بازی را بهصورت وباپ (PWA) نصب کن — درست مثل یک اپ واقعی، با آیکن روی صفحهٔ اصلی:
|
||||
</p>
|
||||
<Steps
|
||||
items={[
|
||||
"آدرس بازی را در مرورگر Safari باز کن.",
|
||||
"روی دکمهٔ «اشتراکگذاری» (مربع با فلش رو به بالا) بزن.",
|
||||
"کمی پایین برو و «Add to Home Screen / افزودن به صفحهٔ اصلی» را انتخاب کن.",
|
||||
"روی «Add» بزن — آیکن برگ وسط روی صفحهٔ اصلی اضافه میشود و تمامصفحه اجرا میشود.",
|
||||
]}
|
||||
/>
|
||||
<p className="mt-4 text-sm text-cream/55">
|
||||
نکته: روی آیفون حتماً از مرورگر Safari استفاده کن؛ افزودن به صفحهٔ اصلی فقط در Safari کار میکند.
|
||||
</p>
|
||||
</div>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { Metadata } from "next";
|
||||
import { PageShell } from "@/components/PageShell";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "سوالهای متداول",
|
||||
description: "پاسخ پرسشهای رایج دربارهٔ بازی حکم آنلاین برگ وسط — رایگان بودن، نصب، بازی با دوستان و سکهها.",
|
||||
alternates: { canonical: "/faq" },
|
||||
};
|
||||
|
||||
const FAQ = [
|
||||
{ q: "بازی رایگان است؟", a: "بله، برگ وسط کاملاً رایگان است. میتوانی همهٔ بخشها را بدون پرداخت بازی کنی. خرید سکه فقط اختیاری است." },
|
||||
{ q: "چطور با دوستانم بازی کنم؟", a: "یک اتاق خصوصی بساز، کد اتاق را برای دوستانت بفرست و همتیمی و حریفهایت را انتخاب کن." },
|
||||
{ q: "اینترنت لازم دارم؟", a: "برای بازی آنلاین بله، اما بخش «بازی با کامپیوتر» کاملاً آفلاین کار میکند." },
|
||||
{ q: "روی آیفون نصب میشود؟", a: "بله، روی iOS از طریق Safari بازی را به صفحهٔ اصلی اضافه کن (PWA). راهنمای کامل در صفحهٔ دانلود هست." },
|
||||
{ q: "سکهها به چه درد میخورند؟", a: "با سکه در لیگهای بالاتر بازی میکنی و آیتمهای ظاهری مثل آواتار، طرح کارت و عنوان میخری." },
|
||||
{ q: "اگر وسط بازی قطع شوم چه میشود؟", a: "بازیات زنده میماند و میتوانی برگردی و ادامه دهی." },
|
||||
{ q: "کُت (کوت) یعنی چه؟", a: "اگر تیم حاکم همهٔ ۷ دست را ببرد، حریف «کُت» میشود و امتیاز و جایزهٔ بیشتری میگیری." },
|
||||
];
|
||||
|
||||
export default function FaqPage() {
|
||||
const jsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "FAQPage",
|
||||
mainEntity: FAQ.map((f) => ({
|
||||
"@type": "Question",
|
||||
name: f.q,
|
||||
acceptedAnswer: { "@type": "Answer", text: f.a },
|
||||
})),
|
||||
};
|
||||
return (
|
||||
<PageShell title="سوالهای متداول">
|
||||
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
|
||||
<div className="space-y-3">
|
||||
{FAQ.map((f) => (
|
||||
<details key={f.q} className="glass group rounded-2xl p-5">
|
||||
<summary className="cursor-pointer list-none text-lg font-bold text-cream marker:hidden">
|
||||
{f.q}
|
||||
</summary>
|
||||
<p className="mt-3 text-cream/70">{f.a}</p>
|
||||
</details>
|
||||
))}
|
||||
</div>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
@import "tailwindcss";
|
||||
@import "@fontsource-variable/vazirmatn";
|
||||
|
||||
:root {
|
||||
--navy-950: #070b18;
|
||||
--navy-900: #0b1226;
|
||||
--navy-800: #111a33;
|
||||
--gold: #d4af37;
|
||||
--gold-soft: #e7c873;
|
||||
--teal: #2dd4bf;
|
||||
--cream: #f5efe0;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-navy-950: var(--navy-950);
|
||||
--color-navy-900: var(--navy-900);
|
||||
--color-navy-800: var(--navy-800);
|
||||
--color-gold: var(--gold);
|
||||
--color-gold-soft: var(--gold-soft);
|
||||
--color-teal: var(--teal);
|
||||
--color-cream: var(--cream);
|
||||
--font-sans: "Vazirmatn Variable", ui-sans-serif, system-ui, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: radial-gradient(120% 120% at 50% 0%, #0e1730 0%, var(--navy-950) 60%);
|
||||
color: var(--cream);
|
||||
font-family: "Vazirmatn Variable", ui-sans-serif, system-ui, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
[dir="rtl"] {
|
||||
font-family: "Vazirmatn Variable", ui-sans-serif, system-ui, sans-serif;
|
||||
}
|
||||
|
||||
/* Utility helpers */
|
||||
.gold-text {
|
||||
background: linear-gradient(180deg, var(--gold-soft), var(--gold));
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.glass {
|
||||
background: rgba(17, 26, 51, 0.55);
|
||||
border: 1px solid rgba(212, 175, 55, 0.18);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.btn-gold {
|
||||
background: linear-gradient(180deg, var(--gold-soft), var(--gold));
|
||||
color: #1a1206;
|
||||
font-weight: 800;
|
||||
}
|
||||
.btn-gold:hover {
|
||||
filter: brightness(1.06);
|
||||
}
|
||||
|
||||
.felt {
|
||||
background:
|
||||
radial-gradient(120% 120% at 50% 0%, rgba(45, 212, 191, 0.08), transparent 60%),
|
||||
radial-gradient(80% 80% at 80% 90%, rgba(212, 175, 55, 0.06), transparent 60%);
|
||||
}
|
||||
|
||||
.card-pattern {
|
||||
background-image:
|
||||
linear-gradient(45deg, rgba(212, 175, 55, 0.04) 25%, transparent 25%),
|
||||
linear-gradient(-45deg, rgba(212, 175, 55, 0.04) 25%, transparent 25%);
|
||||
background-size: 22px 22px;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import type { Metadata, Viewport } from "next";
|
||||
import "./globals.css";
|
||||
import { BRAND, SITE_URL } from "@/lib/site";
|
||||
import { Nav } from "@/components/Nav";
|
||||
import { Footer } from "@/components/Footer";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL(SITE_URL),
|
||||
title: {
|
||||
default: `${BRAND.nameFa} | بازی حکم آنلاین رایگان`,
|
||||
template: `%s | ${BRAND.nameFa}`,
|
||||
},
|
||||
description: BRAND.descFa,
|
||||
keywords: [
|
||||
"حکم",
|
||||
"بازی حکم",
|
||||
"حکم آنلاین",
|
||||
"بازی ورق ایرانی",
|
||||
"برگ وسط",
|
||||
"بازی کارتی آنلاین",
|
||||
"حکم با دوستان",
|
||||
"Hokm",
|
||||
"Barg-e Vasat",
|
||||
],
|
||||
applicationName: BRAND.nameFa,
|
||||
authors: [{ name: BRAND.nameFa }],
|
||||
alternates: { canonical: "/" },
|
||||
openGraph: {
|
||||
type: "website",
|
||||
locale: "fa_IR",
|
||||
url: SITE_URL,
|
||||
siteName: BRAND.nameFa,
|
||||
title: `${BRAND.nameFa} | بازی حکم آنلاین رایگان`,
|
||||
description: BRAND.descFa,
|
||||
images: [{ url: "/og.png", width: 1200, height: 630, alt: BRAND.nameFa }],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: `${BRAND.nameFa} | بازی حکم آنلاین`,
|
||||
description: BRAND.descFa,
|
||||
images: ["/og.png"],
|
||||
},
|
||||
icons: { icon: "/icon.svg", apple: "/icon.svg" },
|
||||
manifest: "/manifest.webmanifest",
|
||||
};
|
||||
|
||||
export const viewport: Viewport = {
|
||||
themeColor: "#070b18",
|
||||
width: "device-width",
|
||||
initialScale: 1,
|
||||
};
|
||||
|
||||
const jsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "VideoGame",
|
||||
name: "برگ وسط",
|
||||
alternateName: "Barg-e Vasat",
|
||||
description: BRAND.descFa,
|
||||
url: SITE_URL,
|
||||
applicationCategory: "GameApplication",
|
||||
genre: "بازی کارتی",
|
||||
operatingSystem: "Android, iOS, Web",
|
||||
inLanguage: "fa-IR",
|
||||
offers: { "@type": "Offer", price: "0", priceCurrency: "IRR" },
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="fa" dir="rtl">
|
||||
<body>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
||||
/>
|
||||
<Nav />
|
||||
<main>{children}</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { MetadataRoute } from "next";
|
||||
|
||||
export const dynamic = "force-static";
|
||||
|
||||
export default function manifest(): MetadataRoute.Manifest {
|
||||
return {
|
||||
name: "برگ وسط — بازی حکم آنلاین",
|
||||
short_name: "برگ وسط",
|
||||
description: "بازی حکم ایرانی آنلاین با دوستان و هوش مصنوعی.",
|
||||
start_url: "/",
|
||||
display: "standalone",
|
||||
background_color: "#070b18",
|
||||
theme_color: "#070b18",
|
||||
dir: "rtl",
|
||||
lang: "fa",
|
||||
icons: [
|
||||
{ src: "/icon.svg", sizes: "any", type: "image/svg+xml", purpose: "any" },
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import {
|
||||
Users, Bot, Trophy, Gift, MessageCircle, Globe, ShieldCheck, Zap, Crown, Star,
|
||||
} from "lucide-react";
|
||||
import { DownloadButtons } from "@/components/DownloadButtons";
|
||||
import { BRAND } from "@/lib/site";
|
||||
|
||||
const FEATURES = [
|
||||
{ icon: Users, title: "حکم ۴ نفره آنلاین", desc: "با بازیکنهای واقعی از سراسر ایران، دونفره و تیمی بازی کن." },
|
||||
{ icon: Bot, title: "بازی با هوش مصنوعی", desc: "آفلاین و بدون اینترنت، با رباتهای هوشمند تمرین کن." },
|
||||
{ icon: Trophy, title: "لیگ و رتبهبندی", desc: "از لیگ مبتدی تا استاد بالا برو و در جدول قهرمانان بدرخش." },
|
||||
{ icon: Gift, title: "جایزههای روزانه", desc: "هر روز سکه بگیر، دستاورد باز کن و جوایز ویژه ببر." },
|
||||
{ icon: MessageCircle, title: "چت و شکلک", desc: "سر میز با همتیمی و حریف کلکل کن؛ استیکرهای فارسی." },
|
||||
{ icon: Globe, title: "همهجا در دسترس", desc: "اندروید، آیفون و مرورگر — پیشرفتت همهجا همگام میشود." },
|
||||
];
|
||||
|
||||
const STEPS = [
|
||||
{ n: "۱", title: "وارد شو", desc: "با شماره موبایل ثبتنام کن — سریع و رایگان." },
|
||||
{ n: "۲", title: "میز انتخاب کن", desc: "بازی سریع آنلاین، اتاق خصوصی با دوستان، یا بازی با کامپیوتر." },
|
||||
{ n: "۳", title: "حکم بزن و ببر", desc: "حاکم شو، خال حکم را انتخاب کن و حریف را کُت کن!" },
|
||||
];
|
||||
|
||||
const STATS = [
|
||||
{ icon: Zap, label: "بازی سریع", value: "زیر ۱۵ ثانیه شروع" },
|
||||
{ icon: ShieldCheck, label: "بدون تقلب", value: "سرور منصف و امن" },
|
||||
{ icon: Crown, label: "کاملاً رایگان", value: "بدون اجبار خرید" },
|
||||
];
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
{/* Hero */}
|
||||
<section className="felt card-pattern">
|
||||
<div className="mx-auto max-w-6xl px-4 py-16 text-center sm:py-24">
|
||||
<span className="inline-flex items-center gap-1.5 rounded-full glass px-3 py-1 text-xs text-gold-soft">
|
||||
<Star size={13} /> بازی حکمِ ایرانی، حرفهایتر از همیشه
|
||||
</span>
|
||||
<h1 className="mx-auto mt-6 max-w-3xl text-4xl font-black leading-tight sm:text-6xl">
|
||||
<span className="gold-text">{BRAND.nameFa}</span>
|
||||
<br />
|
||||
بازی حکم آنلاین با دوستان
|
||||
</h1>
|
||||
<p className="mx-auto mt-5 max-w-2xl text-base leading-8 text-cream/70 sm:text-lg">
|
||||
{BRAND.descFa}
|
||||
</p>
|
||||
<div className="mt-9 flex justify-center">
|
||||
<DownloadButtons variant="hero" />
|
||||
</div>
|
||||
|
||||
<div className="mx-auto mt-12 grid max-w-3xl gap-3 sm:grid-cols-3">
|
||||
{STATS.map((s) => (
|
||||
<div key={s.label} className="glass rounded-2xl px-4 py-4">
|
||||
<s.icon className="mx-auto text-teal" size={22} />
|
||||
<div className="mt-2 text-sm font-bold text-cream">{s.label}</div>
|
||||
<div className="text-xs text-cream/55">{s.value}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Features */}
|
||||
<section id="features" className="mx-auto max-w-6xl px-4 py-16">
|
||||
<h2 className="text-center text-3xl font-black sm:text-4xl">
|
||||
چرا <span className="gold-text">برگ وسط</span>؟
|
||||
</h2>
|
||||
<p className="mx-auto mt-3 max-w-xl text-center text-cream/60">
|
||||
همهٔ چیزی که یک بازی حکم بینقص لازم دارد، در یک اپ.
|
||||
</p>
|
||||
<div className="mt-10 grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{FEATURES.map((f) => (
|
||||
<div key={f.title} className="glass rounded-2xl p-6 transition hover:border-gold/40">
|
||||
<f.icon className="text-gold" size={28} />
|
||||
<h3 className="mt-4 text-lg font-bold text-cream">{f.title}</h3>
|
||||
<p className="mt-2 text-sm leading-7 text-cream/65">{f.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* How to play */}
|
||||
<section className="mx-auto max-w-6xl px-4 py-16">
|
||||
<div className="glass rounded-3xl p-8 sm:p-12">
|
||||
<h2 className="text-center text-3xl font-black sm:text-4xl">در ۳ قدم شروع کن</h2>
|
||||
<div className="mt-10 grid gap-6 sm:grid-cols-3">
|
||||
{STEPS.map((s) => (
|
||||
<div key={s.n} className="text-center">
|
||||
<div className="mx-auto flex h-14 w-14 items-center justify-center rounded-full btn-gold text-2xl font-black">
|
||||
{s.n}
|
||||
</div>
|
||||
<h3 className="mt-4 text-lg font-bold text-cream">{s.title}</h3>
|
||||
<p className="mt-2 text-sm leading-7 text-cream/65">{s.desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Final CTA */}
|
||||
<section className="mx-auto max-w-4xl px-4 py-16 text-center">
|
||||
<h2 className="text-3xl font-black sm:text-4xl">
|
||||
همین حالا <span className="gold-text">حکم</span> را شروع کن
|
||||
</h2>
|
||||
<p className="mx-auto mt-3 max-w-lg text-cream/65">
|
||||
رایگان روی مرورگر بازی کن یا اپ را روی گوشیات نصب کن.
|
||||
</p>
|
||||
<div className="mt-8 flex justify-center">
|
||||
<DownloadButtons variant="full" />
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import type { Metadata } from "next";
|
||||
import { PageShell } from "@/components/PageShell";
|
||||
import { BRAND } from "@/lib/site";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "حریم خصوصی",
|
||||
description: "سیاست حریم خصوصی برگ وسط: چه دادههایی جمعآوری میشود و چگونه از آن محافظت میکنیم.",
|
||||
alternates: { canonical: "/privacy" },
|
||||
};
|
||||
|
||||
export default function PrivacyPage() {
|
||||
return (
|
||||
<PageShell title="سیاست حریم خصوصی" subtitle="آخرین بهروزرسانی: ۱۴۰۴">
|
||||
<p>
|
||||
برگ وسط ({BRAND.nameEn}) به حریم خصوصی شما احترام میگذارد. این سند توضیح میدهد که چه اطلاعاتی جمعآوری
|
||||
میشود و چگونه استفاده میشود.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-bold text-cream">۱. اطلاعاتی که جمعآوری میکنیم</h2>
|
||||
<ul className="list-disc space-y-2 pr-5">
|
||||
<li>شمارهٔ موبایل برای ورود و احراز هویت.</li>
|
||||
<li>اطلاعات نمایه که خودتان وارد میکنید (نام نمایشی، آواتار، تنظیمات).</li>
|
||||
<li>دادههای بازی مانند امتیاز، رتبه، سکه و دستاوردها.</li>
|
||||
<li>اطلاعات فنی پایه برای پایداری سرویس (مانند نوع دستگاه و خطاها).</li>
|
||||
</ul>
|
||||
|
||||
<h2 className="text-xl font-bold text-cream">۲. استفاده از اطلاعات</h2>
|
||||
<p>
|
||||
از اطلاعات فقط برای ارائهٔ سرویس بازی، ذخیرهٔ پیشرفت شما، جلوگیری از تقلب و بهبود تجربهٔ کاربری استفاده
|
||||
میکنیم. اطلاعات شما را به اشخاص ثالث نمیفروشیم.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-bold text-cream">۳. پرداختها</h2>
|
||||
<p>
|
||||
خریدهای درونبرنامهای از طریق درگاههای معتبر (زرینپال) و فروشگاهها (کافهبازار، مایکت) انجام میشود و
|
||||
اطلاعات کارت بانکی شما نزد ما ذخیره نمیشود.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-bold text-cream">۴. امنیت</h2>
|
||||
<p>برای محافظت از دادهها از رمزنگاری و سرورهای امن استفاده میکنیم.</p>
|
||||
|
||||
<h2 className="text-xl font-bold text-cream">۵. حذف حساب</h2>
|
||||
<p>
|
||||
برای حذف حساب و دادههای مرتبط، از طریق ایمیل {BRAND.email} با ما تماس بگیرید.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-bold text-cream">۶. تماس</h2>
|
||||
<p>برای هر پرسشی دربارهٔ حریم خصوصی به {BRAND.email} ایمیل بزنید.</p>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { MetadataRoute } from "next";
|
||||
import { SITE_URL } from "@/lib/site";
|
||||
|
||||
export const dynamic = "force-static";
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
rules: { userAgent: "*", allow: "/", disallow: "/admin" },
|
||||
sitemap: `${SITE_URL}/sitemap.xml`,
|
||||
host: SITE_URL,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { MetadataRoute } from "next";
|
||||
import { SITE_URL } from "@/lib/site";
|
||||
|
||||
export const dynamic = "force-static";
|
||||
|
||||
export default function sitemap(): MetadataRoute.Sitemap {
|
||||
const routes = ["", "/download", "/faq", "/support", "/privacy", "/terms"];
|
||||
return routes.map((r) => ({
|
||||
url: `${SITE_URL}${r}`,
|
||||
changeFrequency: r === "" ? "weekly" : "monthly",
|
||||
priority: r === "" ? 1 : 0.7,
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { Metadata } from "next";
|
||||
import { PageShell } from "@/components/PageShell";
|
||||
import { SupportContact } from "@/components/SupportContact";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "پشتیبانی",
|
||||
description: "از تیم پشتیبانی برگ وسط کمک بگیرید — ایمیل، تلگرام و اینستاگرام.",
|
||||
alternates: { canonical: "/support" },
|
||||
};
|
||||
|
||||
export default function SupportPage() {
|
||||
return (
|
||||
<PageShell title="پشتیبانی" subtitle="سوالی داری یا مشکلی پیش آمده؟ ما اینجاییم.">
|
||||
<SupportContact />
|
||||
<p className="text-sm text-cream/55">
|
||||
پیش از تماس، نگاهی به{" "}
|
||||
<a href="/faq" className="text-gold-soft underline">
|
||||
سوالهای متداول
|
||||
</a>{" "}
|
||||
بینداز — شاید جوابت همانجا باشد.
|
||||
</p>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import type { Metadata } from "next";
|
||||
import { PageShell } from "@/components/PageShell";
|
||||
import { BRAND } from "@/lib/site";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "قوانین و مقررات",
|
||||
description: "قوانین و شرایط استفاده از بازی حکم آنلاین برگ وسط.",
|
||||
alternates: { canonical: "/terms" },
|
||||
};
|
||||
|
||||
export default function TermsPage() {
|
||||
return (
|
||||
<PageShell title="قوانین و مقررات" subtitle="آخرین بهروزرسانی: ۱۴۰۴">
|
||||
<p>با استفاده از برگ وسط، شرایط زیر را میپذیرید.</p>
|
||||
|
||||
<h2 className="text-xl font-bold text-cream">۱. استفادهٔ مجاز</h2>
|
||||
<p>
|
||||
استفاده از تقلب، رباتهای غیرمجاز، سوءاستفاده از باگها یا هرگونه رفتار مخل بازی ممنوع است و میتواند به
|
||||
مسدودسازی حساب منجر شود.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-bold text-cream">۲. حساب کاربری</h2>
|
||||
<p>مسئولیت حفظ امنیت حساب و فعالیتهای انجامشده با آن بر عهدهٔ شماست.</p>
|
||||
|
||||
<h2 className="text-xl font-bold text-cream">۳. سکه و خریدها</h2>
|
||||
<p>
|
||||
سکهها و آیتمهای مجازی ارزش واقعی پولی ندارند و قابل بازگشت به وجه نقد نیستند. خریدهای درونبرنامهای پس از
|
||||
انجام، طبق قوانین فروشگاه مربوطه قابل بازگشتاند.
|
||||
</p>
|
||||
|
||||
<h2 className="text-xl font-bold text-cream">۴. رفتار سر میز</h2>
|
||||
<p>توهین، آزار و محتوای نامناسب در چت ممنوع است.</p>
|
||||
|
||||
<h2 className="text-xl font-bold text-cream">۵. تغییرات</h2>
|
||||
<p>ممکن است این قوانین بهمرور بهروزرسانی شوند. ادامهٔ استفاده بهمنزلهٔ پذیرش نسخهٔ جدید است.</p>
|
||||
|
||||
<h2 className="text-xl font-bold text-cream">۶. تماس</h2>
|
||||
<p>برای سوالها به {BRAND.email} ایمیل بزنید.</p>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user