Initial ASPHP website
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
export default function ContactForm() {
|
||||
const [status, setStatus] = useState("");
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
async function handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
setStatus("");
|
||||
setIsSubmitting(true);
|
||||
|
||||
const form = event.currentTarget;
|
||||
const formData = new FormData(form);
|
||||
const payload = {
|
||||
name: formData.get("name"),
|
||||
cellNumber: formData.get("cellNumber"),
|
||||
title: formData.get("title"),
|
||||
text: formData.get("text")
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/contact", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
if (!response.ok) throw new Error("submit failed");
|
||||
form.reset();
|
||||
setStatus("پیام شما با موفقیت ثبت شد. کارشناسان ما با شما تماس می گیرند.");
|
||||
} catch {
|
||||
setStatus("ارسال پیام با مشکل روبرو شد. لطفا دوباره تلاش کنید یا با شماره های شرکت تماس بگیرید.");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form className="contact-form" onSubmit={handleSubmit}>
|
||||
<label>
|
||||
نام و نام خانوادگی
|
||||
<input name="name" type="text" required />
|
||||
</label>
|
||||
<label>
|
||||
شماره تماس
|
||||
<input name="cellNumber" type="tel" required />
|
||||
</label>
|
||||
<label>
|
||||
موضوع
|
||||
<input name="title" type="text" required />
|
||||
</label>
|
||||
<label>
|
||||
پیام
|
||||
<textarea name="text" rows="5" required />
|
||||
</label>
|
||||
<button type="submit" disabled={isSubmitting}>{isSubmitting ? "در حال ارسال..." : "ارسال درخواست"}</button>
|
||||
{status ? <p className="form-status" role="status">{status}</p> : null}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
function getRemaining(target) {
|
||||
const diff = Math.max(0, target - Date.now());
|
||||
const days = Math.floor(diff / 86400000);
|
||||
const hours = Math.floor((diff % 86400000) / 3600000);
|
||||
const minutes = Math.floor((diff % 3600000) / 60000);
|
||||
const seconds = Math.floor((diff % 60000) / 1000);
|
||||
return { days, hours, minutes, seconds };
|
||||
}
|
||||
|
||||
export default function Countdown() {
|
||||
const target = useMemo(() => Date.now() + 72 * 60 * 60 * 1000, []);
|
||||
const [time, setTime] = useState(() => getRemaining(target));
|
||||
const [isMounted, setIsMounted] = useState(false);
|
||||
const units = [
|
||||
["seconds", "ثانیه", time.seconds],
|
||||
["minutes", "دقیقه", time.minutes],
|
||||
["hours", "ساعت", time.hours],
|
||||
["days", "روز", time.days]
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
setIsMounted(true);
|
||||
const timer = setInterval(() => setTime(getRemaining(target)), 1000);
|
||||
return () => clearInterval(timer);
|
||||
}, [target]);
|
||||
|
||||
return (
|
||||
<div className="sale-countdown" aria-label="زمان باقی مانده فروش ویژه">
|
||||
<span className="sale-countdown-label">پایان فروش ویژه</span>
|
||||
{units.map(([name, label, value]) => {
|
||||
const display = String(value).padStart(2, "0");
|
||||
return (
|
||||
<span className="flip-unit" key={name}>
|
||||
<strong className={`flip-card ${isMounted ? "flip-changing" : ""}`} key={`${name}-${display}`}>{display}</strong>
|
||||
<small>{label}</small>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { CUSTOMER_URL } from "./meshkee";
|
||||
|
||||
function CartIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M3 4h2l2.2 11.2a2 2 0 0 0 2 1.6h7.6a2 2 0 0 0 2-1.5L21 7H6" />
|
||||
<circle cx="10" cy="20" r="1.3" />
|
||||
<circle cx="17" cy="20" r="1.3" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function UserIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<circle cx="12" cy="8" r="4" />
|
||||
<path d="M4 21c1.6-4.2 4.2-6.3 8-6.3s6.4 2.1 8 6.3" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default function HeaderTools() {
|
||||
const [cart, setCart] = useState([]);
|
||||
const [user, setUser] = useState(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const loadCart = () => {
|
||||
try {
|
||||
setCart(JSON.parse(localStorage.getItem("asphp_cart") || "[]"));
|
||||
} catch {
|
||||
setCart([]);
|
||||
}
|
||||
};
|
||||
|
||||
const loadUser = async () => {
|
||||
const token = localStorage.getItem("meshkee_token") || localStorage.getItem("asphp_token");
|
||||
const savedUser = localStorage.getItem("meshkee_user");
|
||||
if (savedUser) {
|
||||
try {
|
||||
setUser(JSON.parse(savedUser));
|
||||
} catch {}
|
||||
}
|
||||
if (!token) return;
|
||||
try {
|
||||
const response = await fetch("/api/auth/me", { headers: { Authorization: `Bearer ${token}` } });
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setUser(data.user || data);
|
||||
}
|
||||
} catch {}
|
||||
};
|
||||
|
||||
loadCart();
|
||||
loadUser();
|
||||
window.addEventListener("asphp-cart-change", loadCart);
|
||||
return () => window.removeEventListener("asphp-cart-change", loadCart);
|
||||
}, []);
|
||||
|
||||
const count = cart.reduce((sum, item) => sum + Number(item.quantity || 1), 0);
|
||||
const displayName = user?.firstName || user?.name || user?.fullName || user?.mobile || user?.cellNumber;
|
||||
const isAdmin = user?.isAdmin || user?.role === "admin" || user?.type === "admin";
|
||||
|
||||
const logout = () => {
|
||||
localStorage.removeItem("meshkee_token");
|
||||
localStorage.removeItem("asphp_token");
|
||||
localStorage.removeItem("meshkee_user");
|
||||
setUser(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="header-tools">
|
||||
{displayName ? (
|
||||
<details className="user-menu">
|
||||
<summary><UserIcon /> {displayName} عزیز</summary>
|
||||
<div>
|
||||
<a href={`${CUSTOMER_URL}/profile`}>پروفایل من</a>
|
||||
{isAdmin ? <a href="https://business.asphp.ir">مدیریت وبسایت</a> : null}
|
||||
<button type="button" onClick={logout}>خروج</button>
|
||||
</div>
|
||||
</details>
|
||||
) : (
|
||||
<a className="login user-login" href={CUSTOMER_URL} aria-label="ورود">
|
||||
<UserIcon />
|
||||
ورود
|
||||
</a>
|
||||
)}
|
||||
|
||||
<details className="cart-menu" open={isOpen} onToggle={(event) => setIsOpen(event.currentTarget.open)}>
|
||||
<summary aria-label="سبد خرید">
|
||||
<CartIcon />
|
||||
{count > 0 ? <span className="cart-badge">{count}</span> : null}
|
||||
</summary>
|
||||
<div className="cart-popup">
|
||||
<h3>سبد خرید</h3>
|
||||
{cart.length ? (
|
||||
<>
|
||||
{cart.map((item) => (
|
||||
<div className="cart-line" key={item.id}>
|
||||
<span>{item.title}</span>
|
||||
<strong>{item.quantity}</strong>
|
||||
</div>
|
||||
))}
|
||||
<a className="cart-continue" href={`${CUSTOMER_URL}/cart`}>ادامه خرید</a>
|
||||
</>
|
||||
) : (
|
||||
<p>سبد خرید شما خالی است.</p>
|
||||
)}
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
export default function StoreActions({ item, businessId }) {
|
||||
const [quantity, setQuantity] = useState(1);
|
||||
const [message, setMessage] = useState("");
|
||||
|
||||
const addToCart = async () => {
|
||||
const token = localStorage.getItem("meshkee_token") || localStorage.getItem("asphp_token");
|
||||
const line = {
|
||||
id: item.id,
|
||||
title: item.title,
|
||||
image: item.image,
|
||||
price: item.price,
|
||||
quantity
|
||||
};
|
||||
|
||||
try {
|
||||
if (token && businessId && !item.id.startsWith("local-")) {
|
||||
await fetch("/api/cart/items", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
||||
body: JSON.stringify({ businessId, storeItemVariantId: item.id, quantity })
|
||||
});
|
||||
}
|
||||
} catch {}
|
||||
|
||||
const cart = JSON.parse(localStorage.getItem("asphp_cart") || "[]");
|
||||
const existing = cart.find((cartItem) => cartItem.id === item.id);
|
||||
if (existing) existing.quantity += quantity;
|
||||
else cart.push(line);
|
||||
localStorage.setItem("asphp_cart", JSON.stringify(cart));
|
||||
window.dispatchEvent(new Event("asphp-cart-change"));
|
||||
setMessage("محصول به سبد خرید اضافه شد.");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="store-actions">
|
||||
<label>
|
||||
تعداد
|
||||
<select value={quantity} onChange={(event) => setQuantity(Number(event.target.value))}>
|
||||
{[1, 2, 3, 4, 5].map((value) => <option key={value} value={value}>{value}</option>)}
|
||||
</select>
|
||||
</label>
|
||||
<button type="button" onClick={addToCart}>افزودن به سبد خرید</button>
|
||||
{message ? <span>{message}</span> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import Image from "next/image";
|
||||
import { PageShell } from "../components";
|
||||
import { pageMetadata } from "../seo";
|
||||
|
||||
export const metadata = pageMetadata({
|
||||
title: "درباره آریا شبکه پیشرو",
|
||||
description: "آشنایی با آریا شبکه پیشرو، تامین کننده تجهیزات سرور HP، قطعات سرور و تجهیزات شبکه با تمرکز بر کیفیت، مشاوره و پشتیبانی.",
|
||||
path: "/about-us",
|
||||
image: "/assets/about-office.jpg"
|
||||
});
|
||||
|
||||
export default function AboutPage() {
|
||||
return (
|
||||
<PageShell>
|
||||
<section className="page-hero">
|
||||
<div className="container">
|
||||
<span>ABOUT ASPHP</span>
|
||||
<h1>درباره آریا شبکه پیشرو</h1>
|
||||
<p>شرکتی دانش بنیان، جوان و خلاق در زمینه تامین تجهیزات شبکه از دو کمپانی HP و CISCO.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section className="section">
|
||||
<div className="container split">
|
||||
<Image src="/assets/about-office.jpg" alt="درباره شرکت آریا شبکه پیشرو" width={560} height={420} priority />
|
||||
<div>
|
||||
<h2>آریا شبکه پیشرو</h2>
|
||||
<p>آریا شبکه پیشرو شرکتی دانش بنیان، جوان و خلاق است. فعالیت مان را از سال ۱۳۹۵ با سرمایه ای اندک ولی با انگیزه سرشار و فکر و نیروی جوان آغاز کردیم.</p>
|
||||
<p>مجموعه ما در زمینه تامین تجهیزات شبکه از دو کمپانی HP و CISCO فعالیت دارد و تمرکز اصلی آن ارائه محصولات با کیفیت، مشاوره پیش از خرید و پشتیبانی پس از فروش است.</p>
|
||||
<div className="stats">
|
||||
<strong>۱۳۹۵</strong>
|
||||
<span>شروع فعالیت</span>
|
||||
<strong>HP</strong>
|
||||
<span>فروش سرور و قطعات</span>
|
||||
<strong>CISCO</strong>
|
||||
<span>تامین تجهیزات شبکه</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="section muted">
|
||||
<div className="container value-grid">
|
||||
<article>
|
||||
<h3>مشتری مداری</h3>
|
||||
<p>مشاوره قبل از خرید، پیش فاکتور متناسب با نیاز کسب و کار و همراهی تا زمان تحویل سفارش.</p>
|
||||
</article>
|
||||
<article>
|
||||
<h3>کیفیت محصول</h3>
|
||||
<p>ارائه تجهیزات با کیفیت و تضمین شده برای شبکه، سرور، رم، هارد، CPU و سوئیچ.</p>
|
||||
</article>
|
||||
<article>
|
||||
<h3>پشتیبانی</h3>
|
||||
<p>پشتیبانی پس از اجرا برای پایداری، دوام و امنیت اطلاعات کسب و کارها.</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default } from "../about-us/page";
|
||||
export { metadata } from "../about-us/page";
|
||||
@@ -0,0 +1,14 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getCurrentUser } from "../../meshkee";
|
||||
|
||||
export async function GET(request) {
|
||||
const token = request.headers.get("authorization")?.replace(/^Bearer\s+/i, "");
|
||||
if (!token) return NextResponse.json({ message: "Missing token" }, { status: 401 });
|
||||
|
||||
try {
|
||||
const data = await getCurrentUser(token);
|
||||
return NextResponse.json(data);
|
||||
} catch {
|
||||
return NextResponse.json({ message: "Could not load user" }, { status: 401 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { addCartItem } from "../../../meshkee";
|
||||
|
||||
export async function POST(request) {
|
||||
const token = request.headers.get("authorization")?.replace(/^Bearer\s+/i, "");
|
||||
if (!token) return NextResponse.json({ message: "Missing token" }, { status: 401 });
|
||||
|
||||
try {
|
||||
const body = await request.json();
|
||||
const data = await addCartItem({
|
||||
businessId: body.businessId,
|
||||
token,
|
||||
storeItemVariantId: body.storeItemVariantId,
|
||||
quantity: body.quantity || 1
|
||||
});
|
||||
return NextResponse.json(data, { status: 201 });
|
||||
} catch {
|
||||
return NextResponse.json({ message: "Could not add cart item" }, { status: 502 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { submitContact } from "../../meshkee";
|
||||
|
||||
export async function POST(request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const data = await submitContact({
|
||||
title: body.title,
|
||||
name: body.name,
|
||||
cellNumber: body.cellNumber,
|
||||
email: body.email,
|
||||
text: body.text
|
||||
});
|
||||
return NextResponse.json(data, { status: 201 });
|
||||
} catch (error) {
|
||||
return NextResponse.json({ message: "Contact submission failed" }, { status: 502 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import Image from "next/image";
|
||||
import { PageShell } from "../../components";
|
||||
import { getBlog } from "../../meshkee";
|
||||
import { blogCanonicalPath, pageMetadata } from "../../seo";
|
||||
|
||||
export async function generateMetadata({ params }) {
|
||||
const { slug } = await params;
|
||||
const blog = await getBlog(slug);
|
||||
return pageMetadata({
|
||||
title: blog.title,
|
||||
description: (blog.abstract || "مقاله آموزشی آریا شبکه پیشرو درباره تجهیزات سرور و شبکه.").slice(0, 160),
|
||||
path: blogCanonicalPath(blog),
|
||||
image: blog.image
|
||||
});
|
||||
}
|
||||
|
||||
export default async function BlogDetailPage({ params }) {
|
||||
const { slug } = await params;
|
||||
const blog = await getBlog(slug);
|
||||
|
||||
return (
|
||||
<PageShell>
|
||||
<article className="section blog-detail">
|
||||
<div className="container blog-detail-inner">
|
||||
<div className="blog-detail-image">
|
||||
<Image src={blog.image} alt={blog.title} fill sizes="(max-width: 900px) 100vw, 900px" />
|
||||
</div>
|
||||
<time>{blog.date}</time>
|
||||
<h1>{blog.title}</h1>
|
||||
<p className="blog-abstract">{blog.abstract}</p>
|
||||
<div className="rich-text" dangerouslySetInnerHTML={{ __html: blog.html || `<p>${blog.abstract}</p>` }} />
|
||||
</div>
|
||||
</article>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { PageShell } from "../components";
|
||||
import { getBlogs } from "../meshkee";
|
||||
import { pageMetadata } from "../seo";
|
||||
|
||||
export const metadata = pageMetadata({
|
||||
title: "مقالات آریا شبکه پیشرو",
|
||||
description: "مقالات آموزشی و راهنمای خرید درباره سرور HP، رم ECC، هارد سرور، تجهیزات شبکه و انتخاب قطعات مناسب کسب و کار.",
|
||||
path: "/blog"
|
||||
});
|
||||
|
||||
export default async function BlogPage() {
|
||||
const { items, fallback } = await getBlogs({ pageSize: 12 });
|
||||
|
||||
return (
|
||||
<PageShell>
|
||||
<section className="page-hero blog-hero">
|
||||
<div className="container">
|
||||
<span>BLOG</span>
|
||||
<h1>مقالات آریا شبکه پیشرو</h1>
|
||||
<p>مقالات از Meshkee خوانده می شود و در صورت نبود داده، محتوای نمونه سایت نمایش داده می شود.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
{fallback ? <p className="api-note">فعلا مقاله ای از API برنگشت؛ نمایش مقالات نمونه فعال است.</p> : null}
|
||||
<div className="blog-grid">
|
||||
{items.map((blog) => (
|
||||
<Link className="blog-card" href={`/blog/${blog.slug}`} key={blog.id}>
|
||||
<div className="blog-image">
|
||||
<Image src={blog.image} alt={blog.title} fill sizes="(max-width: 800px) 100vw, 33vw" />
|
||||
</div>
|
||||
<time>{blog.date}</time>
|
||||
<h2>{blog.title}</h2>
|
||||
<p>{blog.abstract}</p>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { contact, megaCategories, navItems } from "./data";
|
||||
import HeaderTools from "./HeaderTools";
|
||||
|
||||
export function Header() {
|
||||
return (
|
||||
<header className="site-header">
|
||||
<div className="container header-box">
|
||||
<div className="topbar">
|
||||
<div className="topbar-inner">
|
||||
<HeaderTools />
|
||||
<div className="topbar-links">
|
||||
<span className="top-icon" aria-label="تماس">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6.6 10.8c1.5 3 3.8 5.2 6.6 6.6l2.2-2.2c.3-.3.8-.4 1.2-.3 1.3.4 2.6.6 4 .6.7 0 1.2.5 1.2 1.2v3.5c0 .7-.5 1.2-1.2 1.2C10.3 22 2 13.7 2 3.4 2 2.7 2.5 2.2 3.2 2.2h3.5c.7 0 1.2.5 1.2 1.2 0 1.4.2 2.7.6 4 .1.4 0 .9-.3 1.2l-1.6 2.2Z" /></svg>
|
||||
</span>
|
||||
<a href="tel:02177133934">02177133934</a>
|
||||
<span>|</span>
|
||||
<a href="tel:02177132293">02177132293</a>
|
||||
<a className="top-icon" href={contact.instagram} aria-label="Instagram">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="4" y="4" width="16" height="16" rx="5" /><circle cx="12" cy="12" r="3.5" /><circle cx="17" cy="7" r="1" /></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="nav-row">
|
||||
<nav className="main-nav" aria-label="ناوبری اصلی">
|
||||
<details className="category-menu">
|
||||
<summary>
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16" /></svg>
|
||||
دسته بندی ها
|
||||
</summary>
|
||||
<div className="mega-menu">
|
||||
<div className="mega-title">
|
||||
<span>Categories</span>
|
||||
<h3>خرید بر اساس دسته</h3>
|
||||
</div>
|
||||
<div className="mega-tabs">
|
||||
{megaCategories.map((group) => <a href="#" key={group.tab}>{group.tab}</a>)}
|
||||
</div>
|
||||
<div className="mega-columns">
|
||||
{megaCategories.map((group) => (
|
||||
<div key={group.title}>
|
||||
<h4>{group.title}</h4>
|
||||
{group.items.map((item) => <a href="#" key={item}>{item}</a>)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
{navItems.map((item) => (
|
||||
<Link key={item.href} href={item.href}>{item.label}</Link>
|
||||
))}
|
||||
</nav>
|
||||
<details className="search-pop">
|
||||
<summary aria-label="جستجو">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7" /><path d="m16.5 16.5 4 4" /></svg>
|
||||
</summary>
|
||||
<form className="header-search" role="search">
|
||||
<input placeholder="جستجوی سرور، رم، هارد یا CPU" aria-label="جستجو" />
|
||||
</form>
|
||||
</details>
|
||||
<Link href="/" className="brand" aria-label="آریا شبکه پیشرو">
|
||||
<Image src="/assets/logo.png" alt="آریا شبکه پیشرو" width={300} height={52} priority />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export function Footer() {
|
||||
return (
|
||||
<footer className="footer">
|
||||
<div className="container footer-grid">
|
||||
<div>
|
||||
<Image src="/assets/logo.png" alt="شرکت آریا شبکه پیشرو" width={263} height={45} />
|
||||
<p>مارا در شبکه های اجتماعی دنبال کنید.</p>
|
||||
<div className="socials">
|
||||
<a href={contact.telegram}>Telegram</a>
|
||||
<a href={contact.instagram}>Instagram</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3>درباره ما</h3>
|
||||
<p>آریا شبکه پیشرو شرکتی دانش بنیان، جوان و خلاق است. فعالیت مان را از سال ۱۳۹۵ با سرمایه ای اندک ولی با انگیزه سرشار و فکر و نیروی جوان آغاز کردیم. مجموعه ما در زمینه تامین تجهیزات شبکه از دو کمپانی HP و CISCO فعالیت دارد.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3>تماس با ما</h3>
|
||||
{contact.phones.slice(2).map((phone) => <a key={phone} href={`tel:${phone}`}>{phone}</a>)}
|
||||
<p>{contact.address}</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3>صفحات</h3>
|
||||
{navItems.map((item) => <Link key={item.href} href={item.href}>{item.label}</Link>)}
|
||||
<a href="https://www.asphp.co/product-details/6232">خرید سرور اچ پی G9 DL380</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
||||
export function PageShell({ children }) {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main>{children}</main>
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { contact } from "../data";
|
||||
import { PageShell } from "../components";
|
||||
import ContactForm from "../ContactForm";
|
||||
import { pageMetadata } from "../seo";
|
||||
|
||||
export const metadata = pageMetadata({
|
||||
title: "تماس با آریا شبکه پیشرو",
|
||||
description: "راه های تماس با کارشناسان آریا شبکه پیشرو برای مشاوره خرید سرور HP، رم، هارد، CPU و تجهیزات شبکه.",
|
||||
path: "/contact-us"
|
||||
});
|
||||
|
||||
export default function ContactPage() {
|
||||
return (
|
||||
<PageShell>
|
||||
<section className="page-hero contact-hero">
|
||||
<div className="container">
|
||||
<span>CONTACT ASPHP</span>
|
||||
<h1>تماس با آریا شبکه پیشرو</h1>
|
||||
<p>برای مشاوره و خرید در زمینه تجهیزات شبکه با کارشناسان ما در ارتباط باشید.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section className="section">
|
||||
<div className="container contact-grid">
|
||||
<div className="contact-panel">
|
||||
<h2>راه های ارتباطی</h2>
|
||||
<p>{contact.address}</p>
|
||||
{contact.phones.map((phone) => (
|
||||
<a key={phone} href={`tel:${phone.replaceAll(" ", "")}`}>{phone}</a>
|
||||
))}
|
||||
<div className="socials">
|
||||
<a href={contact.instagram}>Instagram</a>
|
||||
<a href={contact.telegram}>Telegram</a>
|
||||
</div>
|
||||
</div>
|
||||
<ContactForm />
|
||||
</div>
|
||||
</section>
|
||||
<section className="map-band">
|
||||
<div className="container">
|
||||
<h2>موقعیت شرکت</h2>
|
||||
<p>عرض جغرافیایی: {contact.latitude} | طول جغرافیایی: {contact.longitude}</p>
|
||||
</div>
|
||||
</section>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default } from "../contact-us/page";
|
||||
export { metadata } from "../contact-us/page";
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
export const navItems = [
|
||||
{ href: "/store", label: "فروشگاه" },
|
||||
{ href: "/blog", label: "مقالات" },
|
||||
{ href: "#special-sale", label: "فروش ویژه" },
|
||||
{ href: "/about-us", label: "درباره ما" },
|
||||
{ href: "/contact-us", label: "تماس با ما" }
|
||||
];
|
||||
|
||||
export const productGroups = ["سرور G8", "سرور G9", "سرور G10", "رم", "CPU", "هارد", "SSD", "سوئیچ Cisco"];
|
||||
|
||||
export const megaCategories = [
|
||||
{
|
||||
tab: "سرور",
|
||||
title: "خرید بر اساس نسل سرور",
|
||||
items: ["سرور G8", "سرور G9", "سرور G10", "سرور DL380", "سرور رکمونت", "کانفیگ اختصاصی"]
|
||||
},
|
||||
{
|
||||
tab: "قطعات",
|
||||
title: "قطعات اصلی سرور",
|
||||
items: ["رم سرور", "هارد SFF", "هارد LFF", "SSD", "CPU", "پاور سرور"]
|
||||
},
|
||||
{
|
||||
tab: "شبکه",
|
||||
title: "تجهیزات شبکه",
|
||||
items: ["سوئیچ Cisco", "رک دیواری", "رک ایستاده", "رید کنترلر", "کابل شبکه", "تجهیزات جانبی"]
|
||||
}
|
||||
];
|
||||
|
||||
export const heroSlides = [
|
||||
"/assets/banner-1.jpg",
|
||||
"/assets/banner-2.jpg",
|
||||
"/assets/banner-3.jpg",
|
||||
"/assets/banner-4.jpg"
|
||||
];
|
||||
|
||||
export const products = [
|
||||
{
|
||||
title: "سرور اچ پی DL380 G9",
|
||||
image: "/assets/product-server-g9.jpg",
|
||||
category: "سرور HP",
|
||||
link: "https://www.asphp.co/product-details/6232"
|
||||
},
|
||||
{
|
||||
title: "سرور اچ پی DL380 G8",
|
||||
image: "/assets/product-server-g8.jpg",
|
||||
category: "سرور HP",
|
||||
link: "https://www.asphp.ir/products/server-g8"
|
||||
},
|
||||
{
|
||||
title: "سی پی یو سرور Intel Xeon Processor E5-2680 V4",
|
||||
image: "/assets/header.jpg",
|
||||
category: "پردازنده مرکزی",
|
||||
link: "https://www.asphp.co/product-details/4928"
|
||||
},
|
||||
{
|
||||
title: "هارد سرور اچ پی 1.2TB 12G SAS 10K 872479-B21",
|
||||
image: "/assets/server-info-1.jpg",
|
||||
category: "هارد سرور",
|
||||
link: "https://www.asphp.co/product-details/6507"
|
||||
}
|
||||
];
|
||||
|
||||
export const services = [
|
||||
{
|
||||
title: "فروش دیگر تجهیزات شبکه",
|
||||
icon: "/assets/service-network.png",
|
||||
body: "باتوجه به اینکه شرکت آریا شبکه پیشرو یک شرکت مادر در زمینه تجهیزات شبکه محسوب می شود، می توانید تجهیزات مرتبط نظیر، رم، CPU و هارد و … را به صورت یکجا و با اعتماد کامل از این شرکت تهیه کنید."
|
||||
},
|
||||
{
|
||||
title: "فروش سوئیچهای Cisco",
|
||||
icon: "/assets/service-cisco.png",
|
||||
body: "آریا شبکه پیشرو به عنوان یکی از معتبرترین شرکت ها در زمینه فروش سوئیچ های سیسکو در صنف تجهیزات شبکه، شناخته شده است. مشتریان ما می توانند با اطمینان این محصول را از ما تهیه نمایند."
|
||||
},
|
||||
{
|
||||
title: "فروش سرورهای HP",
|
||||
icon: "/assets/service-hp.png",
|
||||
body: "یکی از عوامل اولیه تجهیزات شبکه، خرید یک سرور مناسب است، شرکت آریا شبکه پیشرو با ارائه یکی از بهترین برندهای سرور جهان یعنی سرورهای HP خرید شما را تضمین می کند."
|
||||
}
|
||||
];
|
||||
|
||||
export const features = [
|
||||
{
|
||||
icon: "/assets/feature-consult.png",
|
||||
body: "قبل از هر اقدام به خرید با کارشناسان ما تماس بگیرید و از مشاوره ی آنها بهرمند شوید."
|
||||
},
|
||||
{
|
||||
icon: "/assets/feature-order.png",
|
||||
body: "پس از مشاوره قبل از خرید طبق نیاز کسب وکارتان سفارش خود را ثبت کنید و پیش فاکتور دریافت کنید."
|
||||
},
|
||||
{
|
||||
icon: "/assets/feature-call.png",
|
||||
body: "با واحد فروش ما تماس بگیرید و تجهیزات شبکه را با بهترین قیمت و کیفیت تهیه کنید."
|
||||
},
|
||||
{
|
||||
icon: "/assets/feature-payment.png",
|
||||
body: "بعد از خرید پرداخت فاکتور ثبت شده، سفارش شما را وارد مرحله آماده سازی و ارسال خواهد کرد."
|
||||
},
|
||||
{
|
||||
icon: "/assets/feature-delivery.png",
|
||||
body: "سفارشات طبق زمان و مکان تعیین شده به مشتریان تحویل می گردد."
|
||||
},
|
||||
{
|
||||
icon: "/assets/feature-support.png",
|
||||
body: "چرخه ی مشتری مداری ما همیشه برقرار است با پشتیبانی پس از خرید تجهیزات."
|
||||
}
|
||||
];
|
||||
|
||||
export const sourceBanners = [
|
||||
{
|
||||
title: "خرید هارد",
|
||||
href: "https://www.asphp.co/products/hard",
|
||||
image: "/assets/source-frame-hard.jpg",
|
||||
alt: "خرید هارد سرور",
|
||||
variant: "large"
|
||||
},
|
||||
{
|
||||
title: "خرید سرور",
|
||||
href: "https://www.asphp.co/products/server",
|
||||
image: "/assets/source-frame-server.jpg",
|
||||
alt: "خرید سرور"
|
||||
},
|
||||
{
|
||||
title: "آریا شبکه پیشرو",
|
||||
href: "https://www.instagram.com/asphp.co/",
|
||||
image: "/assets/source-frame-aria.jpg",
|
||||
alt: "آریا شبکه پیشرو"
|
||||
}
|
||||
];
|
||||
|
||||
export const projectSteps = [
|
||||
{
|
||||
number: "01",
|
||||
title: "طراحی و تجهیز",
|
||||
body: "برای انجام یک پروژه ی راه اندازی شبکه موفق ابتدا نیاز به طراحی یک طرح کلی و برآورد تجهیزات است. پس با تماس با یک تیم مشاوره خوب ابتدا نقشه راه تهیه کنید و سپس به مرحله اجرا بروید."
|
||||
},
|
||||
{
|
||||
number: "02",
|
||||
title: "اجرا توسط تیم حرفه ای",
|
||||
body: "یک نقشه خوب نیازمند اجرا توسط یک تیم حرفه ای و کارآمد است، نقشه ی حساب شده، تجهیزات عالی و با کیفیت را یک تیم اجرایی حرفه ای کامل می کند."
|
||||
},
|
||||
{
|
||||
number: "03",
|
||||
title: "پشتیبانی بعد از اجرا",
|
||||
body: "یک شبکه و تجهیزات آن همیشه نیازمند یک تیم پشتیبانی منظم و دقیق است تا دوام و پایداری و امنیت اطلاعات یک کسب و کار را تضمین کند."
|
||||
}
|
||||
];
|
||||
|
||||
export const testimonials = [
|
||||
{
|
||||
title: "خدمات پس از فروش",
|
||||
body: "خدمات پس از فروش این فروشگاه واقعاً عالی است. هنگام بروز مشکل در یکی از تجهیزات سرورمان، پشتیبانی فنی بسیار سریع و کارآمد عمل کرد.",
|
||||
name: "وحید رضوی"
|
||||
},
|
||||
{
|
||||
title: "کیفیت عالی محصولات",
|
||||
body: "تجهیزات سروری که اینجا تهیه کردم، نصب و راهاندازی آسانی داشتند. تیم پشتیبانی هم بسیار کمکرسان بودند و در هر مرحله از فرآیند با ما همراهی کردند.",
|
||||
name: "محمدرضا هاشمی"
|
||||
}
|
||||
];
|
||||
|
||||
export const articles = [
|
||||
{
|
||||
title: "سرور (Server) چیست؟ انواع سرور و مزایای آن",
|
||||
date: "۱۴۰۳/۰۸/۲۲",
|
||||
image: "/assets/product-server-g9.jpg",
|
||||
abstract: "نگاهی کوتاه به مفهوم سرور، کاربردهای اصلی آن و نکاتی که هنگام انتخاب سرور برای کسب و کار باید بدانید."
|
||||
},
|
||||
{
|
||||
title: "رم ECC چیست و چه کاربردی دارد؟",
|
||||
date: "۱۴۰۳/۰۷/۱۵",
|
||||
image: "/assets/banner-4.jpg",
|
||||
abstract: "رم ECC خطاهای حافظه را شناسایی و اصلاح می کند و برای پایداری سرورها و پردازش های حساس انتخابی مهم است."
|
||||
},
|
||||
{
|
||||
title: "هارد سرور اچ پی و نکات مهم انتخاب آن",
|
||||
date: "۱۴۰۳/۰۶/۰۴",
|
||||
image: "/assets/banner-2.jpg",
|
||||
abstract: "در انتخاب هارد سرور باید به ظرفیت، سرعت، فرم فکتور، نسل سرور و نوع کاربری ذخیره سازی توجه کرد."
|
||||
},
|
||||
{
|
||||
title: "تفاوت سرورهای HP G9 و G10",
|
||||
date: "۱۴۰۳/۰۵/۱۸",
|
||||
image: "/assets/banner-1.jpg",
|
||||
abstract: "مقایسه ای سریع میان دو نسل محبوب سرورهای HP برای کمک به انتخاب دقیق تر در خرید تجهیزات شبکه."
|
||||
}
|
||||
];
|
||||
|
||||
export const contact = {
|
||||
phones: ["02177133934", "02177132293", "88467222 (021)", "22459734 (021)"],
|
||||
address: "تهران، میدان رسالت خیابان هنگام کوچه شیری پلاک 9 واحد 4",
|
||||
instagram: "https://www.instagram.com/asphp.co/",
|
||||
telegram: "https://t.me/asphpco",
|
||||
latitude: "35.753302",
|
||||
longitude: "51.508703"
|
||||
};
|
||||
+1864
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
import "./globals.css";
|
||||
import { getBranding } from "./meshkee";
|
||||
import { pageMetadata, SITE_ORIGIN } from "./seo";
|
||||
|
||||
export async function generateMetadata() {
|
||||
const branding = await getBranding();
|
||||
return {
|
||||
metadataBase: new URL(SITE_ORIGIN),
|
||||
...pageMetadata({
|
||||
title: "خرید سرور HP و تجهیزات شبکه",
|
||||
path: "/",
|
||||
image: branding.logoUrl
|
||||
}),
|
||||
icons: {
|
||||
icon: branding.faviconUrl
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="fa" dir="rtl">
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
import { articles, products } from "./data.js";
|
||||
|
||||
export const MESHKEE_DOMAIN = "asphp.ir";
|
||||
export const MESHKEE_API = "https://api.meshkee.com/api/v1";
|
||||
export const CUSTOMER_URL = "https://customer.asphp.ir.com";
|
||||
export const DEFAULT_FAVICON = "/assets/favicon.png";
|
||||
|
||||
async function fetchJson(path, options = {}) {
|
||||
const response = await fetch(`${MESHKEE_API}${path}`, {
|
||||
cache: "no-store",
|
||||
...options,
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
...(options.body ? { "Content-Type": "application/json" } : {}),
|
||||
...(options.headers || {})
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Meshkee API ${response.status}: ${path}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
export async function getTenant() {
|
||||
try {
|
||||
return await fetchJson(`/tenants/${MESHKEE_DOMAIN}`);
|
||||
} catch {
|
||||
return { id: "33", domain: MESHKEE_DOMAIN, nameFa: "تجهیزات سرور پارس" };
|
||||
}
|
||||
}
|
||||
|
||||
export async function getBranding() {
|
||||
try {
|
||||
const branding = await fetchJson(`/tenants/${MESHKEE_DOMAIN}/website/favicon`);
|
||||
return {
|
||||
faviconUrl: branding?.faviconUrl || DEFAULT_FAVICON,
|
||||
logoUrl: branding?.logoUrl || "/assets/logo.png",
|
||||
logoDarkUrl: branding?.logoDarkUrl || branding?.logoUrl || "/assets/logo.png",
|
||||
hasDedicatedFavicon: Boolean(branding?.hasDedicatedFavicon)
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
faviconUrl: DEFAULT_FAVICON,
|
||||
logoUrl: "/assets/logo.png",
|
||||
logoDarkUrl: "/assets/logo.png",
|
||||
hasDedicatedFavicon: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStoreItems({ page = 1, pageSize = 12, query = "" } = {}) {
|
||||
try {
|
||||
const search = new URLSearchParams({ page: String(page), pageSize: String(pageSize) });
|
||||
if (query) search.set("name", query);
|
||||
const data = await fetchJson(`/tenants/${MESHKEE_DOMAIN}/store-items?${search}`);
|
||||
const items = (data.items || []).map(normalizeStoreItem);
|
||||
if (items.length) return { ...data, items };
|
||||
} catch {}
|
||||
|
||||
const items = fallbackStoreItems();
|
||||
return { items, total: items.length, page, pageSize, fallback: true };
|
||||
}
|
||||
|
||||
export async function getStoreItem(id) {
|
||||
try {
|
||||
const data = await fetchJson(`/tenants/${MESHKEE_DOMAIN}/store-items/${encodeURIComponent(id)}`);
|
||||
if (data.variant) return normalizeStoreItem(data.variant);
|
||||
} catch {}
|
||||
|
||||
return fallbackStoreItems().find((item) => item.id === id || item.slug === id) || fallbackStoreItems()[0];
|
||||
}
|
||||
|
||||
export async function getBlogs({ page = 1, pageSize = 12 } = {}) {
|
||||
try {
|
||||
const data = await fetchJson(`/tenants/${MESHKEE_DOMAIN}/blogs?page=${page}&pageSize=${pageSize}`);
|
||||
const items = (data.items || []).map(normalizeBlog);
|
||||
if (items.length) return { ...data, items };
|
||||
} catch {}
|
||||
|
||||
const items = articles.map((article, index) => ({
|
||||
id: `local-blog-${index + 1}`,
|
||||
slug: `local-blog-${index + 1}`,
|
||||
title: article.title,
|
||||
date: article.date,
|
||||
image: article.image,
|
||||
abstract: article.abstract,
|
||||
html: `<p>${article.abstract}</p>`
|
||||
}));
|
||||
return { items, total: items.length, page, pageSize, fallback: true };
|
||||
}
|
||||
|
||||
export async function getStaticImageSlots({ pageKey = "home" } = {}) {
|
||||
const fetchSlots = async (filterPageKey) => {
|
||||
const search = filterPageKey ? `?pageKey=${encodeURIComponent(filterPageKey)}` : "";
|
||||
const data = await fetchJson(`/tenants/${MESHKEE_DOMAIN}/website/static-images${search}`);
|
||||
return Array.isArray(data.items) ? data.items.map(normalizeStaticImageSlot) : [];
|
||||
};
|
||||
|
||||
try {
|
||||
const pageSlots = await fetchSlots(pageKey);
|
||||
if (pageSlots.length || !pageKey) return pageSlots;
|
||||
return await fetchSlots("");
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStaticImageSlotMap(pageKey = "home") {
|
||||
const slots = await getStaticImageSlots({ pageKey });
|
||||
return Object.fromEntries(slots.map((slot) => [slot.key, slot]));
|
||||
}
|
||||
|
||||
export function staticSlotImages(slotMap, key, fallbackImages = []) {
|
||||
const slot = slotMap?.[key];
|
||||
const images = slot?.images?.length ? slot.images : fallbackImages;
|
||||
const pickedImages = slot?.kind === "single" ? images.slice(0, 1) : images;
|
||||
|
||||
return pickedImages.map((image, index) => {
|
||||
const normalized = typeof image === "string" ? { url: image } : image;
|
||||
return {
|
||||
url: normalized.url || normalized.image || normalized.imageUrl || normalized.src,
|
||||
href: normalized.linkUrl || normalized.href || normalized.link,
|
||||
titleFa: normalized.titleFa || normalized.nameFa || normalized.title || normalized.alt,
|
||||
titleEn: normalized.titleEn || normalized.nameEn || "",
|
||||
subtext: normalized.subtext || normalized.description || "",
|
||||
alt: normalized.alt || normalized.titleFa || normalized.title || slot?.label || "",
|
||||
width: normalized.width,
|
||||
height: normalized.height,
|
||||
aspectRatio: cssAspectRatio(slot?.aspectRatio || normalized.aspectRatio),
|
||||
sortOrder: normalized.sortOrder ?? index
|
||||
};
|
||||
}).filter((image) => image.url);
|
||||
}
|
||||
|
||||
export async function getBlog(slug) {
|
||||
try {
|
||||
const data = await fetchJson(`/tenants/${MESHKEE_DOMAIN}/blogs/${encodeURIComponent(slug)}`);
|
||||
if (data.blog) return normalizeBlog(data.blog);
|
||||
} catch {}
|
||||
|
||||
const blogs = await getBlogs();
|
||||
return blogs.items.find((blog) => blog.slug === slug || blog.id === slug) || blogs.items[0];
|
||||
}
|
||||
|
||||
export async function submitContact(payload) {
|
||||
return fetchJson(`/tenants/${MESHKEE_DOMAIN}/contact-submissions`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export async function getCurrentUser(token) {
|
||||
return fetchJson("/auth/me", {
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
}
|
||||
|
||||
export async function addCartItem({ businessId, token, storeItemVariantId, quantity }) {
|
||||
return fetchJson(`/businesses/${businessId}/cart/items`, {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
body: JSON.stringify({ storeItemVariantId, quantity })
|
||||
});
|
||||
}
|
||||
|
||||
export function normalizeStoreItem(item) {
|
||||
const product = item.product || {};
|
||||
const images = [
|
||||
...(Array.isArray(item.images) ? item.images : []),
|
||||
...(Array.isArray(item.gallery) ? item.gallery : [])
|
||||
]
|
||||
.map((image) => image.url || image.imageUrl || image)
|
||||
.filter(Boolean);
|
||||
const image =
|
||||
item.imageUrl ||
|
||||
item.titleImageUrl ||
|
||||
item.mainImageUrl ||
|
||||
product.imageUrl ||
|
||||
product.main_image ||
|
||||
images[0] ||
|
||||
"/assets/product-server-g9.jpg";
|
||||
|
||||
return {
|
||||
id: String(item.id || item.variantId || item.storeItemVariantId || item.slug || product.id),
|
||||
slug: String(item.slug || item.id || item.variantId || product.slug || product.id),
|
||||
productId: String(item.productId || product.id || ""),
|
||||
title: item.titleFa || item.nameFa || item.title || product.titleFa || product.nameFa || product.title || "محصول فروشگاه",
|
||||
category: item.categoryName || product.categoryName || "تجهیزات سرور",
|
||||
image,
|
||||
images: images.length ? images : [image],
|
||||
price: item.finalPrice || item.salePrice || item.price || item.amount || null,
|
||||
currency: item.currency || item.priceCurrency || "تومان",
|
||||
description: item.description || item.descriptionHtml || product.description || product.mainTextHtml || "",
|
||||
technicalValues: item.technicalValues || item.technicalData || [],
|
||||
quantity: item.quantity ?? item.stock ?? null,
|
||||
isFestival: Boolean(item.isFestival),
|
||||
raw: item
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeStaticImageSlot(slot) {
|
||||
return {
|
||||
key: slot.key,
|
||||
pageKey: slot.pageKey,
|
||||
kind: slot.kind || "single",
|
||||
label: slot.label || slot.name || slot.key,
|
||||
aspectRatio: cssAspectRatio(slot.aspectRatio),
|
||||
recommendedWidth: slot.recommendedWidth,
|
||||
itemCount: slot.itemCount,
|
||||
images: Array.isArray(slot.images)
|
||||
? slot.images
|
||||
.map((image) => ({
|
||||
id: image.id,
|
||||
url: image.url || image.image || image.imageUrl || image.src,
|
||||
titleFa: image.titleFa || image.nameFa || image.title,
|
||||
titleEn: image.titleEn || image.nameEn || "",
|
||||
subtext: image.subtext || image.description || "",
|
||||
linkUrl: image.linkUrl || image.href || image.link,
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
aspectRatio: cssAspectRatio(image.aspectRatio),
|
||||
sortOrder: image.sortOrder
|
||||
}))
|
||||
.filter((image) => image.url)
|
||||
.sort((a, b) => Number(a.sortOrder || 0) - Number(b.sortOrder || 0))
|
||||
: []
|
||||
};
|
||||
}
|
||||
|
||||
export function cssAspectRatio(value) {
|
||||
if (!value) return undefined;
|
||||
const text = String(value).trim();
|
||||
const comma = text.match(/^(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)$/);
|
||||
if (comma) return `${comma[1]} / ${comma[2]}`;
|
||||
const colon = text.match(/^(\d+(?:\.\d+)?)\s*:\s*(\d+(?:\.\d+)?)$/);
|
||||
if (colon) return `${colon[1]} / ${colon[2]}`;
|
||||
const slash = text.match(/^(\d+(?:\.\d+)?)\s*\/\s*(\d+(?:\.\d+)?)$/);
|
||||
if (slash) return `${slash[1]} / ${slash[2]}`;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function fallbackStoreItems() {
|
||||
return products.map((product, index) => ({
|
||||
id: `local-${index + 1}`,
|
||||
slug: `local-${index + 1}`,
|
||||
productId: `local-product-${index + 1}`,
|
||||
title: product.title,
|
||||
category: product.category,
|
||||
image: product.image,
|
||||
images: [product.image, "/assets/banner-2.jpg", "/assets/banner-3.jpg"],
|
||||
price: null,
|
||||
currency: "تومان",
|
||||
description: "برای قیمت، موجودی و کانفیگ اختصاصی این محصول با کارشناسان فروش تماس بگیرید.",
|
||||
technicalValues: [
|
||||
{ label: "دسته بندی", value: product.category },
|
||||
{ label: "وضعیت قیمت", value: "تماس با فروشگاه" }
|
||||
],
|
||||
quantity: null,
|
||||
isFestival: index === 0,
|
||||
raw: product
|
||||
}));
|
||||
}
|
||||
|
||||
export function normalizeBlog(blog) {
|
||||
return {
|
||||
id: String(blog.id || blog.blogId || blog.slug),
|
||||
slug: String(blog.slug || blog.id || blog.blogId),
|
||||
title: blog.titleFa || blog.title || blog.nameFa || "مقاله",
|
||||
date: blog.publishedAt || blog.createdAt || "",
|
||||
image: blog.titleImageUrl || blog.imageUrl || blog.main_image || blog.thumbnail || "/assets/banner-1.jpg",
|
||||
abstract: blog.abstract || blog.summary || blog.description || "",
|
||||
html: blog.mainTextHtml || blog.bodyHtml || blog.text || blog.content || "",
|
||||
raw: blog
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { MESHKEE_DOMAIN } from "../../meshkee";
|
||||
import { SITE_ORIGIN } from "../../seo";
|
||||
|
||||
export const dynamic = "force-static";
|
||||
|
||||
const generatedAt = new Date().toISOString();
|
||||
|
||||
const staticRoutes = [
|
||||
{ path: "/", priority: 1, changeFrequency: "daily" },
|
||||
{ path: "/store", priority: 0.9, changeFrequency: "daily" },
|
||||
{ path: "/blog", priority: 0.8, changeFrequency: "weekly" },
|
||||
{ path: "/about-us", priority: 0.6, changeFrequency: "monthly" },
|
||||
{ path: "/contact-us", priority: 0.6, changeFrequency: "monthly" }
|
||||
];
|
||||
|
||||
const excludedPathPrefixes = [
|
||||
"/admin",
|
||||
"/auth",
|
||||
"/cart",
|
||||
"/checkout",
|
||||
"/dashboard",
|
||||
"/profile"
|
||||
];
|
||||
|
||||
export async function GET() {
|
||||
const body = {
|
||||
domain: MESHKEE_DOMAIN,
|
||||
origin: SITE_ORIGIN,
|
||||
generatedAt,
|
||||
sitemapIndex: {
|
||||
enabled: true,
|
||||
url: `${SITE_ORIGIN}/sitemap.xml`,
|
||||
children: [
|
||||
`${SITE_ORIGIN}/sitemap-main.xml`,
|
||||
`${SITE_ORIGIN}/sitemap-products.xml`
|
||||
]
|
||||
},
|
||||
staticRoutes: staticRoutes.map((route) => ({
|
||||
...route,
|
||||
url: `${SITE_ORIGIN}${route.path}`
|
||||
})),
|
||||
exclude: excludedPathPrefixes,
|
||||
canonicalTemplates: {
|
||||
product: {
|
||||
path: "/products/{id}/{nameFaSlug}",
|
||||
source: `/tenants/${MESHKEE_DOMAIN}/products/by-id/{id}`
|
||||
},
|
||||
category: {
|
||||
path: "/products/category/{id}/{nameFaSlug}",
|
||||
source: `/tenants/${MESHKEE_DOMAIN}/categories/by-id/{id}`,
|
||||
listSource: `/tenants/${MESHKEE_DOMAIN}/products?categoryId={id}`
|
||||
},
|
||||
blog: {
|
||||
path: "/blog/{id}/{titleSlug}",
|
||||
source: `/tenants/${MESHKEE_DOMAIN}/blogs/by-id/{id}`
|
||||
},
|
||||
portfolio: {
|
||||
path: "/portfolios/{id}/{titleFaSlug}",
|
||||
source: `/tenants/${MESHKEE_DOMAIN}/portfolios/by-id/{id}`
|
||||
}
|
||||
},
|
||||
slugRules: {
|
||||
keepPersianLetters: true,
|
||||
spaces: "-",
|
||||
strip: "punctuation"
|
||||
}
|
||||
};
|
||||
|
||||
return Response.json(body, {
|
||||
headers: {
|
||||
"Cache-Control": "public, max-age=300, stale-while-revalidate=3600"
|
||||
}
|
||||
});
|
||||
}
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
import Image from "next/image";
|
||||
import { articles, contact, features, heroSlides, products, projectSteps, services, sourceBanners, testimonials } from "./data";
|
||||
import { PageShell } from "./components";
|
||||
import Countdown from "./Countdown";
|
||||
import { getStaticImageSlotMap, staticSlotImages } from "./meshkee";
|
||||
|
||||
export default async function Home() {
|
||||
const staticSlots = await getStaticImageSlotMap("home");
|
||||
const sliderImages = staticSlotImages(staticSlots, "slider", heroSlides);
|
||||
const belowSliderBanners = staticSlotImages(staticSlots, "three-banner-below-slider", heroSlides.slice(1, 4)).slice(0, 3);
|
||||
const specialSaleBanner = staticSlotImages(staticSlots, "special-sale-banner", ["/assets/banner-2.jpg"])[0];
|
||||
const serverBanner = staticSlotImages(staticSlots, "server-banner", ["/assets/server-info-1.jpg"])[0];
|
||||
const aboutusBanner = staticSlotImages(staticSlots, "aboutus-banner", ["/assets/server-info-1.jpg"])[0];
|
||||
const threeBanners = staticSlotImages(staticSlots, "three-banner", sourceBanners).slice(0, 3);
|
||||
|
||||
return (
|
||||
<PageShell>
|
||||
<h1 className="sr-only">آریا شبکه پیشرو، فروش تجهیزات سرور HP و شبکه</h1>
|
||||
<section className="hero-section">
|
||||
<div className="container">
|
||||
<div className="hero">
|
||||
<div className="hero-slider" aria-hidden="true">
|
||||
{sliderImages.map((slide, index) => {
|
||||
const content = <Image src={slide.url} alt={slide.alt || ""} fill priority={index === 0} sizes="(max-width: 900px) 92vw, 860px" />;
|
||||
return slide.href ? (
|
||||
<a className={`slide-card slide-${index + 1}`} href={slide.href} key={`${slide.url}-${index}`} style={slotStyle(slide)}>
|
||||
{content}
|
||||
</a>
|
||||
) : (
|
||||
<div className={`slide-card slide-${index + 1}`} key={`${slide.url}-${index}`} style={slotStyle(slide)}>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="banner-row">
|
||||
{belowSliderBanners.map((banner, index) => (
|
||||
<a className="promo-banner" href={banner.href || "tel:02177133934"} key={`${banner.url}-${index}`} style={slotStyle(banner)}>
|
||||
<Image src={banner.url} alt={banner.alt || `بنر فروش تجهیزات سرور ${index + 1}`} fill sizes="(max-width: 900px) 100vw, 33vw" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section" id="special-sale">
|
||||
<div className="container special-sale-frame">
|
||||
<div className="special-heading">
|
||||
<SectionTitle fa="فروش ویژه" en="SPECIAL SALE" align="right" />
|
||||
<Countdown />
|
||||
</div>
|
||||
<div className="product-grid special-grid">
|
||||
<a className="special-banner-card" href={specialSaleBanner.href || "tel:02177133934"} style={slotStyle(specialSaleBanner)}>
|
||||
<Image src={specialSaleBanner.url} alt={specialSaleBanner.alt || "فروش ویژه تجهیزات سرور"} fill sizes="(max-width: 900px) 100vw, 25vw" />
|
||||
</a>
|
||||
{products.map((product) => (
|
||||
<a className="product-card" href={product.link} key={product.title}>
|
||||
<div className="product-image">
|
||||
<Image src={product.image} alt={product.title} fill sizes="(max-width: 800px) 50vw, 25vw" />
|
||||
</div>
|
||||
<span>{product.category}</span>
|
||||
<h3>{product.title}</h3>
|
||||
<strong>تماس با فروشگاه</strong>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section split-section">
|
||||
<div className="container split">
|
||||
<div>
|
||||
<SectionTitle fa="سرور (Server) چیست؟" en="SERVER" align="right" />
|
||||
<p>در ویکی پدیا نیز معنی سرور، سرویس دهنده ذکر شده است. به بیان ساده معنی سرور یا در پاسخ سوال server به چه معناست باید گفت، کامپیوتری همیشه روشن و قدرتمند است که کاربران در زمانهای متفاوت، از آن انتظار پاسخ به درخواستها را دارند.</p>
|
||||
<a className="secondary-btn" href="https://www.asphp.co/article-details/191">درباره سرور بیشتر بدانید</a>
|
||||
</div>
|
||||
<div className="image-pair">
|
||||
<Image src={serverBanner.url} alt={serverBanner.alt || "تجهیزات سرور"} width={Number(serverBanner.width) || 520} height={Number(serverBanner.height) || 390} style={slotStyle(serverBanner)} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section muted">
|
||||
<div className="container">
|
||||
<SectionTitle fa="خدمات آریا شبکه پیشرو" en="SERVICES" />
|
||||
<div className="service-grid">
|
||||
{[...services].reverse().map((service) => (
|
||||
<article className="service-card" key={service.title}>
|
||||
<Image src={service.icon} alt="" width={64} height={64} />
|
||||
<h3>{service.title}</h3>
|
||||
<p>{service.body}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section about-band">
|
||||
<div className="container split">
|
||||
<div>
|
||||
<SectionTitle fa="آریا شبکه پیشرو" en="ARYA SHABAKE PISHRO" align="right" />
|
||||
<h3>شرکتی جوان خلاق، با انگیزه و دانش بنیان</h3>
|
||||
<p>انضباط، مشتری مداری، ارائه محصولات با کیفیت و تضمین شده می باشد. برای مشاوره و خرید در زمینه تجهیزات شبکه با مشاوران ما در آریا شبکه پیشرو تماس بگیرید.</p>
|
||||
<a className="primary-btn" href="tel:02177133934">تماس با ما</a>
|
||||
</div>
|
||||
<div className="image-pair">
|
||||
<Image src={aboutusBanner.url} alt={aboutusBanner.alt || "تجهیزات سرور آریا شبکه پیشرو"} width={Number(aboutusBanner.width) || 560} height={Number(aboutusBanner.height) || 420} style={slotStyle(aboutusBanner)} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section feature-section" id="features">
|
||||
<div className="container feature-showcase">
|
||||
<div className="feature-copy">
|
||||
<SectionTitle fa="ویژگـــی مــــا" en="PROCESS" align="right" />
|
||||
<p>انضباط، مشتری مداری، ارائه محصولات با کیفیت و تضمین شده می باشد.</p>
|
||||
</div>
|
||||
<div className="feature-grid">
|
||||
{features.map((feature) => (
|
||||
<div className="feature" key={feature.body}>
|
||||
<Image src={feature.icon} alt="" width={58} height={58} />
|
||||
<p>{feature.body}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section source-banner-section" id="source-frame">
|
||||
<div className="container source-banner-grid">
|
||||
<a className="source-banner source-banner-large" href={threeBanners[0]?.href || sourceBanners[0].href} style={slotStyle(threeBanners[0])}>
|
||||
<Image src={threeBanners[0]?.url || sourceBanners[0].image} alt={threeBanners[0]?.alt || sourceBanners[0].alt} fill sizes="(max-width: 900px) 100vw, 50vw" />
|
||||
</a>
|
||||
<div className="source-banner-stack">
|
||||
{threeBanners.slice(1).map((banner, index) => (
|
||||
<a className="source-banner" href={banner.href || sourceBanners[index + 1]?.href || "#"} key={`${banner.url}-${index}`} style={slotStyle(banner)}>
|
||||
<Image src={banner.url} alt={banner.alt || sourceBanners[index + 1]?.alt || ""} fill sizes="(max-width: 900px) 100vw, 50vw" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section navy">
|
||||
<div className="container">
|
||||
<SectionTitle fa="طرح یک پروژه" en="PROJECT A DESIGN" light />
|
||||
<div className="steps">
|
||||
{projectSteps.map((step) => (
|
||||
<article key={step.number}>
|
||||
<strong>{step.number}</strong>
|
||||
<h3>{step.title}</h3>
|
||||
<p>{step.body}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section" id="articles">
|
||||
<div className="container">
|
||||
<SectionTitle fa="نظرات مشتریان" en="CUSTOMERS COMMENTS" />
|
||||
<div className="testimonial-grid">
|
||||
{testimonials.map((item) => (
|
||||
<article className="quote" key={item.name}>
|
||||
<h3>{item.title}</h3>
|
||||
<p>{item.body}</p>
|
||||
<strong>{item.name}</strong>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="cta">
|
||||
<div className="container cta-inner">
|
||||
<div className="cta-copy">
|
||||
<span>مشاوره خرید تجهیزات شبکه</span>
|
||||
<p>برای انتخاب سرور، رم، هارد و تجهیزات شبکه با مشاوران آریا شبکه پیشرو تماس بگیرید؛ بهترین راهکارها با مناسب ترین قیمت ها در اختیار شما قرار می گیرد.</p>
|
||||
</div>
|
||||
<a className="cta-phone" href="tel:02177133934" aria-label="تماس با کارشناسان آریا شبکه پیشرو">
|
||||
<span className="cta-phone-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M6.6 10.8c1.5 3 3.8 5.2 6.6 6.6l2.2-2.2c.3-.3.8-.4 1.2-.3 1.3.4 2.6.6 4 .6.7 0 1.2.5 1.2 1.2v3.5c0 .7-.5 1.2-1.2 1.2C10.3 22 2 13.7 2 3.4 2 2.7 2.5 2.2 3.2 2.2h3.5c.7 0 1.2.5 1.2 1.2 0 1.4.2 2.7.6 4 .1.4 0 .9-.3 1.2l-1.6 2.2Z" />
|
||||
</svg>
|
||||
</span>
|
||||
<span>با کارشناسان ما تماس بگیرید</span>
|
||||
<strong>۰۲۱۷۷۱۳۳۹۳۴</strong>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<SectionTitle fa="مقــــالــات" en="ARTICLES" />
|
||||
<div className="article-carousel" aria-label="مقالات">
|
||||
{articles.map((article) => (
|
||||
<article className="article-card" key={article.title}>
|
||||
<div className="article-image">
|
||||
<Image src={article.image} alt={article.title} fill sizes="(max-width: 700px) 78vw, 320px" />
|
||||
</div>
|
||||
<time>{article.date}</time>
|
||||
<h3>{article.title}</h3>
|
||||
<p>{article.abstract}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<a className="telegram-float" href={contact.telegram}>تلگرام</a>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionTitle({ fa, en, light = false, align = "center" }) {
|
||||
return (
|
||||
<div className={`section-title ${light ? "light" : ""} ${align === "right" ? "right" : ""}`}>
|
||||
<span>{en}</span>
|
||||
<h2>{fa}</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function slotStyle(image) {
|
||||
return image?.aspectRatio ? { aspectRatio: image.aspectRatio } : undefined;
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
export const SITE_ORIGIN = "https://www.asphp.ir";
|
||||
export const SITE_NAME = "آریا شبکه پیشرو";
|
||||
export const DEFAULT_DESCRIPTION =
|
||||
"خرید سرور HP، رم سرور، هارد سرور، CPU و تجهیزات شبکه از آریا شبکه پیشرو همراه با مشاوره تخصصی، تضمین کیفیت و خدمات پس از فروش.";
|
||||
|
||||
export function pageMetadata({ title, description = DEFAULT_DESCRIPTION, path = "/", image }) {
|
||||
const canonical = absoluteUrl(path);
|
||||
const metaTitle = title.includes(SITE_NAME) ? title : `${title} | ${SITE_NAME}`;
|
||||
|
||||
return {
|
||||
title: metaTitle,
|
||||
description,
|
||||
alternates: {
|
||||
canonical
|
||||
},
|
||||
openGraph: {
|
||||
title: metaTitle,
|
||||
description,
|
||||
url: canonical,
|
||||
siteName: SITE_NAME,
|
||||
locale: "fa_IR",
|
||||
type: "website",
|
||||
...(image ? { images: [{ url: absoluteUrl(image), alt: title }] } : {})
|
||||
},
|
||||
twitter: {
|
||||
card: image ? "summary_large_image" : "summary",
|
||||
title: metaTitle,
|
||||
description,
|
||||
...(image ? { images: [absoluteUrl(image)] } : {})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function productCanonicalPath(item) {
|
||||
const id = item.productId || item.id;
|
||||
return `/products/${encodeURIComponent(id)}/${slugifyFa(item.title)}`;
|
||||
}
|
||||
|
||||
export function blogCanonicalPath(blog) {
|
||||
return `/blog/${encodeURIComponent(blog.id)}/${slugifyFa(blog.title)}`;
|
||||
}
|
||||
|
||||
export function absoluteUrl(path = "/") {
|
||||
if (/^https?:\/\//.test(path)) return path;
|
||||
return `${SITE_ORIGIN}${path.startsWith("/") ? path : `/${path}`}`;
|
||||
}
|
||||
|
||||
export function slugifyFa(value = "") {
|
||||
return String(value)
|
||||
.trim()
|
||||
.normalize("NFKC")
|
||||
.replace(/[^\p{L}\p{N}\s-]+/gu, "")
|
||||
.replace(/\s+/g, "-")
|
||||
.replace(/-+/g, "-")
|
||||
.replace(/^-|-$/g, "");
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { PageShell } from "../../components";
|
||||
import { CUSTOMER_URL, getStoreItem, getStoreItems, getTenant } from "../../meshkee";
|
||||
import { pageMetadata, productCanonicalPath } from "../../seo";
|
||||
import StoreActions from "../../StoreActions";
|
||||
|
||||
export async function generateMetadata({ params }) {
|
||||
const { id } = await params;
|
||||
const item = await getStoreItem(id);
|
||||
const description = item.description.replace(/<[^>]*>/g, "") || `خرید ${item.title} از آریا شبکه پیشرو همراه با مشاوره تخصصی و اعلام موجودی.`;
|
||||
return pageMetadata({
|
||||
title: item.title,
|
||||
description: description.slice(0, 160),
|
||||
path: productCanonicalPath(item),
|
||||
image: item.image
|
||||
});
|
||||
}
|
||||
|
||||
export default async function StoreDetailPage({ params }) {
|
||||
const { id } = await params;
|
||||
const [{ id: businessId }, item, related] = await Promise.all([
|
||||
getTenant(),
|
||||
getStoreItem(id),
|
||||
getStoreItems({ pageSize: 4 })
|
||||
]);
|
||||
const similar = related.items.filter((relatedItem) => relatedItem.id !== item.id).slice(0, 4);
|
||||
const specs = Array.isArray(item.technicalValues) ? item.technicalValues : [];
|
||||
|
||||
return (
|
||||
<PageShell>
|
||||
<section className="section product-detail">
|
||||
<div className="container detail-grid">
|
||||
<div className="gallery">
|
||||
<a className="gallery-main zoom-frame" href={item.image}>
|
||||
<Image src={item.image} alt={item.title} fill sizes="(max-width: 900px) 100vw, 50vw" />
|
||||
</a>
|
||||
<div className="gallery-thumbs">
|
||||
{item.images.slice(0, 4).map((image) => (
|
||||
<a href={image} key={image}>
|
||||
<Image src={image} alt="" width={92} height={92} />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="detail-copy">
|
||||
<span>{item.category}</span>
|
||||
<h1>{item.title}</h1>
|
||||
<p>{item.description.replace(/<[^>]*>/g, "") || "برای دریافت مشاوره تخصصی و اعلام موجودی با کارشناسان فروش تماس بگیرید."}</p>
|
||||
<strong>{item.price ? `${item.price} ${item.currency}` : "تماس با فروشگاه"}</strong>
|
||||
<StoreActions item={item} businessId={businessId} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="section detail-tabs-section">
|
||||
<div className="container detail-tabs">
|
||||
<details open>
|
||||
<summary>توضیحات</summary>
|
||||
<div dangerouslySetInnerHTML={{ __html: item.description || "<p>توضیحات این محصول از API فروشگاه یا محتوای جایگزین نمایش داده می شود.</p>" }} />
|
||||
</details>
|
||||
<details>
|
||||
<summary>اطلاعات فنی</summary>
|
||||
{specs.length ? (
|
||||
<div className="spec-table">
|
||||
{specs.map((spec, index) => (
|
||||
<p key={`${spec.fieldId || spec.label || index}`}>
|
||||
<span>{spec.label || spec.fieldName || spec.fieldId || `ویژگی ${index + 1}`}</span>
|
||||
<strong>{spec.value || spec.textValue || spec.optionLabel || spec.optionId || "-"}</strong>
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
) : <p>اطلاعات فنی برای این محصول ثبت نشده است.</p>}
|
||||
</details>
|
||||
<details>
|
||||
<summary>نظرات</summary>
|
||||
<div className="login-comment-box">
|
||||
<p>برای ثبت نظر درباره این محصول وارد حساب کاربری شوید.</p>
|
||||
<a href={CUSTOMER_URL}>ورود به حساب کاربری</a>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<div className="section-title"><span>RELATED</span><h2>محصولات مشابه</h2></div>
|
||||
<div className="product-grid store-grid">
|
||||
{similar.map((relatedItem) => (
|
||||
<Link className="product-card" href={`/store/${relatedItem.id}`} key={relatedItem.id}>
|
||||
<div className="product-image">
|
||||
<Image src={relatedItem.image} alt={relatedItem.title} fill sizes="(max-width: 800px) 50vw, 25vw" />
|
||||
</div>
|
||||
<span>{relatedItem.category}</span>
|
||||
<h3>{relatedItem.title}</h3>
|
||||
<strong>{relatedItem.price ? `${relatedItem.price} ${relatedItem.currency}` : "تماس با فروشگاه"}</strong>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { PageShell } from "../components";
|
||||
import { getStoreItems } from "../meshkee";
|
||||
import { pageMetadata } from "../seo";
|
||||
|
||||
export const metadata = pageMetadata({
|
||||
title: "فروشگاه تجهیزات سرور",
|
||||
description: "مشاهده و جستجوی محصولات فروشگاهی آریا شبکه پیشرو شامل سرور HP، رم سرور، هارد سرور، CPU و تجهیزات شبکه.",
|
||||
path: "/store"
|
||||
});
|
||||
|
||||
export default async function StorePage({ searchParams }) {
|
||||
const resolvedSearchParams = await searchParams;
|
||||
const query = resolvedSearchParams?.q || "";
|
||||
const { items, fallback } = await getStoreItems({ pageSize: 16, query });
|
||||
|
||||
return (
|
||||
<PageShell>
|
||||
<section className="page-hero store-hero">
|
||||
<div className="container">
|
||||
<span>STORE ITEMS</span>
|
||||
<h1>فروشگاه تجهیزات سرور</h1>
|
||||
<p>لیست محصولات فروشگاهی از Meshkee خوانده می شود و در صورت نبود داده، محصولات شاخص سایت نمایش داده می شوند.</p>
|
||||
</div>
|
||||
</section>
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<form className="store-toolbar" role="search">
|
||||
<input name="q" defaultValue={query} placeholder="جستجوی سرور، رم، هارد یا CPU" />
|
||||
<button>جستجو</button>
|
||||
</form>
|
||||
{fallback ? <p className="api-note">فعلا آیتمی از API فروشگاه برنگشت؛ نمایش محصولات نمونه فعال است.</p> : null}
|
||||
<div className="product-grid store-grid">
|
||||
{items.map((item) => (
|
||||
<Link className="product-card" href={`/store/${item.id}`} key={item.id}>
|
||||
<div className="product-image">
|
||||
<Image src={item.image} alt={item.title} fill sizes="(max-width: 800px) 50vw, 25vw" />
|
||||
</div>
|
||||
<span>{item.category}</span>
|
||||
<h3>{item.title}</h3>
|
||||
<strong>{item.price ? `${item.price} ${item.currency}` : "تماس با فروشگاه"}</strong>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</PageShell>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user