Initial ASPHP website
@@ -0,0 +1,7 @@
|
||||
.next/
|
||||
node_modules/
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
*.log
|
||||
.DS_Store
|
||||
@@ -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";
|
||||
@@ -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"
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
# ASPHP Co Website Context
|
||||
|
||||
## Source Prompt
|
||||
|
||||
Create a Persian RTL Next.js website for a server parts store from `asphp.ir`. Use banners, images, logo, fonts, icon feel, and color palette from the main website. Clone only the homepage, about us, and contact us pages. Keep the design flat.
|
||||
|
||||
## Source Site Data
|
||||
|
||||
- Brand: شرکت آریا شبکه پیشرو
|
||||
- Domain: `https://www.asphp.ir`
|
||||
- Meshkee backend domain: `asphp.ir`
|
||||
- Meshkee API base: `https://api.meshkee.com/api/v1`
|
||||
- Customer login/profile target from backend prompt: `https://customer.asphp.ir.com`
|
||||
- Business: فروش تجهیزات سرور HP، رم سرور، هارد سرور، CPU، سوئیچ Cisco و تجهیزات شبکه
|
||||
- Language/direction: Persian, `fa`, `rtl`
|
||||
- Primary color: `#0097D9`
|
||||
- Secondary/tertiary color: `#883F3F`
|
||||
- Header background: `#17214B`
|
||||
- Main font: Iran Yekan from Meshkee CDN, downloaded into `public/assets`
|
||||
- Latin/number font: Montserrat from Meshkee CDN, downloaded into `public/assets`
|
||||
- Contact phones from source: `02177133934`, `02177132293`, `88467222 (021)`, `22459734 (021)`
|
||||
- Address: تهران، میدان رسالت خیابان هنگام کوچه شیری پلاک 9 واحد 4
|
||||
- Socials: `https://www.instagram.com/asphp.co/`, `https://t.me/asphpco`
|
||||
|
||||
## Local Implementation
|
||||
|
||||
- Framework: Next.js App Router
|
||||
- Routes:
|
||||
- `/`
|
||||
- `/about-us`
|
||||
- `/contact-us`
|
||||
- `/store`
|
||||
- `/store/[id]`
|
||||
- `/blog`
|
||||
- `/blog/[slug]`
|
||||
- `/aboutus` alias
|
||||
- `/contactus` alias
|
||||
- Shared content lives in `app/data.js`
|
||||
- Meshkee backend helpers live in `app/meshkee.js`.
|
||||
- Header login/user/cart behavior lives in `app/HeaderTools.js`.
|
||||
- Store add-to-cart controls live in `app/StoreActions.js`.
|
||||
- Contact submission UI lives in `app/ContactForm.js` and posts through `app/api/contact/route.js`.
|
||||
- Shared layout components live in `app/components.js`
|
||||
- Global design tokens and responsive CSS live in `app/globals.css`
|
||||
- Downloaded source HTML is saved under `.source/` for future AI/system reference.
|
||||
- Downloaded visual assets and fonts are saved under `public/assets/`.
|
||||
- Dependencies are locked in `package-lock.json`; Next is pinned to `16.3.4` after npm reported a security warning for `15.5.2`.
|
||||
- `npm install --ignore-scripts --no-audit --no-fund` completed. `npm run build` starts Next but is killed by this runner with exit code `137` immediately after config load; dev server also reports ready and exits in this environment.
|
||||
|
||||
## Notes For Future Prompts
|
||||
|
||||
- Keep Persian text RTL and avoid converting source phrasing to English.
|
||||
- Preserve the ASPHP palette unless the user explicitly asks for a redesign.
|
||||
- Prefer adding or editing structured arrays in `app/data.js` for content changes.
|
||||
- Update this file whenever future prompts change site goals, content sources, routes, brand rules, or design constraints.
|
||||
|
||||
## 2026-09-03 Layout Update
|
||||
|
||||
- Header is container-width and boxed, with the top contact bar, logo/nav, and product search input inside the same header box.
|
||||
- Homepage search was moved from below the hero into the header.
|
||||
- Homepage slider is container-width rather than full-bleed.
|
||||
- Slider uses animated transitions and keeps adjacent previous/next banner panels visible around the active slide.
|
||||
- Slider text overlay was removed so only banner artwork appears.
|
||||
- Slider card/box styling was removed; banners now slide as flat images inside the container area.
|
||||
- Three promo banners were added below the slider; each is fixed at `200px` height on desktop.
|
||||
- `preview-server.mjs` mirrors these layout changes for the lightweight runnable preview.
|
||||
|
||||
## 2026-09-03 Header And Slider Refinement
|
||||
|
||||
- Slider images now have rounded corners, with the active slide enlarged.
|
||||
- Site typography was reduced and header navigation uses lighter font weight.
|
||||
- Header search uses an icon-only search button.
|
||||
- Logo was moved to the visual left side of the header.
|
||||
- Navigation now uses a `دسته بندی ها` mega menu, removes `صفحه اصلی` and `محصولات`, and adds `فروش ویژه`.
|
||||
|
||||
## 2026-09-03 Special Sale Frame
|
||||
|
||||
- Product images are now square (`1:1`) and cover their frame.
|
||||
- The homepage product section is now a `فروش ویژه` frame with a minimal light blue background.
|
||||
- A banner card was added as the first card in the special sale grid.
|
||||
|
||||
## 2026-09-03 Mega Menu And Header Search
|
||||
|
||||
- `دسته بندی ها` is now a large tabbed-style mega menu panel with tabs and grouped columns.
|
||||
- `فروش ویژه` appears directly after `دسته بندی ها` in the header.
|
||||
- Header search is now an icon-only control; clicking it opens the search input.
|
||||
- Header top bar uses only phone and Instagram icons instead of descriptive labels.
|
||||
- Iran Yekan light was downloaded and set as the base font weight.
|
||||
|
||||
## 2026-09-03 Slider Simplification
|
||||
|
||||
- Category menu label now has a small icon plus a minimal light-blue border.
|
||||
- Slider no longer shows previous/next slides; the active slide fills the container width.
|
||||
- Slider keeps looping through slides with a fade transition.
|
||||
|
||||
## 2026-09-03 Section Title And Card Density
|
||||
|
||||
- Section titles now use a branded pill, gradient accent line, and small dot detail.
|
||||
- Special sale card spacing and frame padding were reduced so product cards appear larger and denser.
|
||||
|
||||
## 2026-09-03 Special Sale Countdown
|
||||
|
||||
- Added a rolling 72-hour countdown to the `فروش ویژه` frame.
|
||||
- Next source uses `app/Countdown.js`; `preview-server.mjs` mirrors the same countdown with inline JavaScript for the local preview.
|
||||
|
||||
## 2026-09-03 Special Sale Band Refinement
|
||||
|
||||
- The special sale light-blue background now spans the full page width while keeping content inside the main container.
|
||||
- The special sale title is aligned to the right and the countdown is aligned to the left.
|
||||
- The section title dot was moved beside the Persian title and now pulses slowly.
|
||||
- Special sale card spacing was tightened again.
|
||||
|
||||
## 2026-09-03 Flip Clock Countdown
|
||||
|
||||
- The special sale countdown now uses compact flip-clock number cards for days, hours, minutes, and seconds.
|
||||
- The preview server countdown markup and inline timer script were updated to match the React component.
|
||||
|
||||
## 2026-09-03 Sale Frame Spacing
|
||||
|
||||
- The special sale band now uses reduced vertical padding with outside top/bottom margin.
|
||||
- Added blue top and bottom rules to the full-width sale frame.
|
||||
- Flip-clock numbers now use the primary ASPHP blue instead of the dark red accent.
|
||||
- Product card gaps in the sale frame were tightened further.
|
||||
|
||||
## 2026-09-03 Articles Carousel
|
||||
|
||||
- Articles are now structured with image, date, title, and abstract fields in `app/data.js`.
|
||||
- Homepage articles render as a horizontal carousel with square (`1:1`) cover images.
|
||||
- `preview-server.mjs` mirrors the carousel article cards for the local static preview.
|
||||
|
||||
## 2026-09-05 Services Order
|
||||
|
||||
- Services section card order is rendered in reverse visual order on the homepage and preview server.
|
||||
- Corrected the `articles` anchor so it targets the articles carousel section.
|
||||
|
||||
## 2026-09-05 Feature Frame Redesign
|
||||
|
||||
- Downloaded the six original feature icons from the ASPHP source theme into `public/assets/feature-*.png`.
|
||||
- Feature data now stores icon/body objects instead of plain strings.
|
||||
- The `ویژگی ما` frame now uses a source-inspired split layout with a title/copy panel and a six-item icon grid.
|
||||
|
||||
## 2026-09-05 Original Banner Frame
|
||||
|
||||
- Downloaded the original three-image `infoitems2` banner frame assets into `public/assets/source-frame-*.jpg`.
|
||||
- Added a homepage frame after `ویژگی ما` with one large hard-drive banner and two stacked right-side banners, matching the original site composition.
|
||||
- `preview-server.mjs` mirrors the frame for local preview.
|
||||
- Updated the homepage about-band image to use the same server-room image and image-over-blue-backing treatment.
|
||||
|
||||
## 2026-09-05 Contact CTA Row
|
||||
|
||||
- Reworked the homepage contact row into a light-blue branded call-to-action panel with consultation copy, a phone icon, and a prominent phone number.
|
||||
- `preview-server.mjs` mirrors the CTA row for local preview.
|
||||
- Later refined into a flat full-width row with top/bottom blue rules and corrected the phone number typography to use the Persian site font without letter spacing.
|
||||
|
||||
## 2026-09-05 Feature Icon Normalization
|
||||
|
||||
- Normalized visual sizing for original feature icons by scaling wider source PNGs inside the same fixed icon box.
|
||||
|
||||
## 2026-09-05 Countdown Order
|
||||
|
||||
- Reversed the special sale countdown unit order in both the React component and preview server.
|
||||
|
||||
## 2026-09-05 Meshkee Backend Integration
|
||||
|
||||
- Added `app/meshkee.js` as the central Meshkee Website API helper for tenant, favicon/logo, store items, blogs, contact submissions, authenticated user lookup, and cart item submission.
|
||||
- Added Meshkee static image slot helpers for `/tenants/asphp.ir/website/static-images`, including key lookup, list/single handling, linkUrl mapping, and CSS-safe aspect ratio parsing.
|
||||
- Added store list/detail pages connected to `/tenants/asphp.ir/store-items`, with local product fallbacks because the live API currently returns an empty item list.
|
||||
- Store detail pages include square lightbox-style gallery links, hover zoom, quantity selector, add-to-cart behavior, description/technical/comments tabs, a login prompt for comments, and similar products.
|
||||
- Added blog list/detail pages connected to `/tenants/asphp.ir/blogs`, with local article fallbacks because the live API currently returns an empty blog list.
|
||||
- Updated metadata favicon handling to read Meshkee favicon/logo data first and fall back to `/assets/favicon.png`.
|
||||
- Header now includes customer login/user dropdown behavior and a cart icon with localStorage count/popup support.
|
||||
- Contact page form now submits through the Meshkee contact submissions API via a local route handler, resets on success, and shows status text.
|
||||
- `preview-server.mjs` includes static preview equivalents for `/store`, `/store/:id`, `/blog`, and `/blog/:slug`.
|
||||
- `next.config.mjs` allows Meshkee API/storage/template image hosts for backend-provided images.
|
||||
- Homepage image placeholders now read static image slots by key: `slider`, `three-banner-below-slider`, `special-sale-banner`, `server-banner`, `aboutus-banner`, and `three-banner`; if the API is empty or fails, keep the current local placeholder. When image size or aspect ratio changes, update the static image reader usage so `slot.aspectRatio` stays respected.
|
||||
|
||||
## 2026-09-06 SEO And Sitemap Config
|
||||
|
||||
- Do not create or ship `public/sitemap.xml` or `public/robots.txt`; production nginx proxies both to Meshkee.
|
||||
- Added `/meshkee/sitemap-config.json` as the sitemap config endpoint with static public routes and Meshkee canonical templates for products, categories, blogs, and portfolios.
|
||||
- Public page metadata now uses unique titles/descriptions, canonical URLs, and Open Graph/Twitter metadata through `app/seo.js`.
|
||||
- Product detail metadata prefers CMS product fields and canonicalizes to `/products/{id}/{nameFaSlug}`; blog detail metadata canonicalizes to `/blog/{id}/{titleSlug}`.
|
||||
- The homepage now has exactly one hidden H1 while section titles remain H2/H3.
|
||||
|
||||
## 2026-09-05 CTA Position
|
||||
|
||||
- The contact CTA row was moved back to its lower homepage position after the project section and before the articles carousel.
|
||||
- `preview-server.mjs` mirrors the same homepage section order.
|
||||
|
||||
## 2026-09-05 Image Pair And Banner Ratio
|
||||
|
||||
- Updated the split server-info image pair so the main image uses a 3:2 visual ratio.
|
||||
- Moved the secondary blue image below the main image instead of overlaying it.
|
||||
- Made the stacked original banner-frame images taller with a 3:2 ratio.
|
||||
- Replaced the dark secondary server-info image with the clearer HP G9 product image.
|
||||
- Restyled the blue rectangle as a backing layer behind the secondary product image instead of a separate visible block.
|
||||
- Removed the secondary white product image from the server-info pair and moved the blue accent rectangle upward behind the main image.
|
||||
|
||||
## 2026-09-03 Header Flattening
|
||||
|
||||
- Top contact strip uses its own light-blue boxed background.
|
||||
- Logo/navigation row uses a separate white boxed card.
|
||||
- Top contact strip and navigation row are visually attached with shared edges and complementary rounded corners.
|
||||
- Login button now uses Iran Yekan instead of the Latin/number font.
|
||||
|
||||
## 2026-09-05 Header Tools Position
|
||||
|
||||
- Moved the login/user and cart tools into the topbar's former login-button position.
|
||||
- Removed the duplicate header tools from the main nav row and reversed their order so login appears before cart.
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
const nextConfig = {
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{ protocol: "https", hostname: "api.meshkee.com" },
|
||||
{ protocol: "https", hostname: "cdn.meshkee.com" },
|
||||
{ protocol: "https", hostname: "meshkee-storage.sas.amin.parminstorage.ir" },
|
||||
{ protocol: "https", hostname: "meshkee-templates.sas.amin.parminstorage.ir" }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
@@ -0,0 +1,904 @@
|
||||
{
|
||||
"name": "asphp-co",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "asphp-co",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"next": "16.3.4",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.11.3",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/colour": {
|
||||
"version": "1.1.0",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.35.4",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz",
|
||||
"integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-freebsd-wasm32": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz",
|
||||
"integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"dependencies": {
|
||||
"@img/sharp-wasm32": "0.35.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.3.3",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz",
|
||||
"integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz",
|
||||
"integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz",
|
||||
"integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz",
|
||||
"integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-riscv64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz",
|
||||
"integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz",
|
||||
"integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz",
|
||||
"integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz",
|
||||
"integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz",
|
||||
"integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz",
|
||||
"integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz",
|
||||
"integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-ppc64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz",
|
||||
"integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-riscv64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz",
|
||||
"integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-riscv64": "1.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz",
|
||||
"integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz",
|
||||
"integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz",
|
||||
"integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz",
|
||||
"integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.35.4",
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.11.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-webcontainers-wasm32": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz",
|
||||
"integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@img/sharp-wasm32": "0.35.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-arm64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz",
|
||||
"integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz",
|
||||
"integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz",
|
||||
"integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "16.3.4",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "16.3.4",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.4.tgz",
|
||||
"integrity": "sha512-2dpiSyl2Jw/NrBPaU2MAKGSa+2MR82pJIn4Sm5Rjr+gxAeuh0z158Su3Z2O8zn7UNNq+ej4bToed6RcRN/Lydg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.4.tgz",
|
||||
"integrity": "sha512-+t+U8HZT+fApePCS5h89CSH3datz29MkzyfCn+6fpsZBG/oiEOhINcb9rtkv6sdpToLGFn2e6146NzaKCXkqrA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.4.tgz",
|
||||
"integrity": "sha512-mx03GNs1ocQA5JQ4FxDMmIsNkdrZh8cuezKCrId28e5/gIPU/l7Kcy2+vmCCzdjnnmXJy+iOAu+7K0QppO6Urg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.4.tgz",
|
||||
"integrity": "sha512-YIhGY6fSMfha52bnVxnzc9zaVBzJg+cqQTOD8tXIBSx4fuv0pVMxQTE0PaS59YhnMOiYiG09IMwxJAf/CFm/Dw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.4.tgz",
|
||||
"integrity": "sha512-+eaaX6axpDb0yF1GCpiERe6njplvdC+nks/fKfcHu3XPGRrald8P3/X7yv7QLdjA51knnxwl9pxdIJsg+w1L+Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.4.tgz",
|
||||
"integrity": "sha512-0jcXW7Xs/uzICrmgV3MhDYDeRy++1CqnpDIerlPIqYO4bhzB4WNbX/aRnQclustsAyTkFKB0z6rbcjmNg5tR8A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.4.tgz",
|
||||
"integrity": "sha512-vvBzwu1pYQCp92maZCFCIw/XgOTMR5tur9GjakwIo2cmwRTMKajRZZDS9+e4KsUZWKu1E007WUeAFXRRjZeuzw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.23",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.11.20",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"baseline-browser-mapping": "dist/cli.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001810",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/client-only": {
|
||||
"version": "0.0.1",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.18",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "16.3.4",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@next/env": "16.3.4",
|
||||
"@swc/helpers": "0.5.23",
|
||||
"baseline-browser-mapping": "^2.9.19",
|
||||
"caniuse-lite": "^1.0.30001579",
|
||||
"postcss": "8.5.23",
|
||||
"styled-jsx": "5.1.6"
|
||||
},
|
||||
"bin": {
|
||||
"next": "dist/bin/next"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@next/swc-darwin-arm64": "16.3.4",
|
||||
"@next/swc-darwin-x64": "16.3.4",
|
||||
"@next/swc-linux-arm64-gnu": "16.3.4",
|
||||
"@next/swc-linux-arm64-musl": "16.3.4",
|
||||
"@next/swc-linux-x64-gnu": "16.3.4",
|
||||
"@next/swc-linux-x64-musl": "16.3.4",
|
||||
"@next/swc-win32-arm64-msvc": "16.3.4",
|
||||
"@next/swc-win32-x64-msvc": "16.3.4",
|
||||
"sharp": "^0.35.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"babel-plugin-react-compiler": "*",
|
||||
"react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
|
||||
"react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
|
||||
"sass": "^1.3.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
},
|
||||
"@playwright/test": {
|
||||
"optional": true
|
||||
},
|
||||
"babel-plugin-react-compiler": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.23",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.16",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.1.0",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.26.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.8.5",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.35.4",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.1.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
"semver": "^7.8.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.35.4",
|
||||
"@img/sharp-darwin-x64": "0.35.4",
|
||||
"@img/sharp-freebsd-wasm32": "0.35.4",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.3.3",
|
||||
"@img/sharp-libvips-darwin-x64": "1.3.3",
|
||||
"@img/sharp-libvips-linux-arm": "1.3.3",
|
||||
"@img/sharp-libvips-linux-arm64": "1.3.3",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.3.3",
|
||||
"@img/sharp-libvips-linux-riscv64": "1.3.3",
|
||||
"@img/sharp-libvips-linux-s390x": "1.3.3",
|
||||
"@img/sharp-libvips-linux-x64": "1.3.3",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.3.3",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.3.3",
|
||||
"@img/sharp-linux-arm": "0.35.4",
|
||||
"@img/sharp-linux-arm64": "0.35.4",
|
||||
"@img/sharp-linux-ppc64": "0.35.4",
|
||||
"@img/sharp-linux-riscv64": "0.35.4",
|
||||
"@img/sharp-linux-s390x": "0.35.4",
|
||||
"@img/sharp-linux-x64": "0.35.4",
|
||||
"@img/sharp-linuxmusl-arm64": "0.35.4",
|
||||
"@img/sharp-linuxmusl-x64": "0.35.4",
|
||||
"@img/sharp-webcontainers-wasm32": "0.35.4",
|
||||
"@img/sharp-win32-arm64": "0.35.4",
|
||||
"@img/sharp-win32-ia32": "0.35.4",
|
||||
"@img/sharp-win32-x64": "0.35.4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/styled-jsx": {
|
||||
"version": "5.1.6",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"client-only": "0.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@babel/core": {
|
||||
"optional": true
|
||||
},
|
||||
"babel-plugin-macros": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"license": "0BSD"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "asphp-co",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "16.3.4",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
import { createReadStream } from "node:fs";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { createServer } from "node:http";
|
||||
import { extname, join, normalize } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import {
|
||||
articles,
|
||||
contact,
|
||||
features,
|
||||
heroSlides,
|
||||
megaCategories,
|
||||
products,
|
||||
projectSteps,
|
||||
services,
|
||||
sourceBanners
|
||||
} from "./app/data.js";
|
||||
import { getStaticImageSlotMap, staticSlotImages } from "./app/meshkee.js";
|
||||
|
||||
const root = fileURLToPath(new URL(".", import.meta.url));
|
||||
const hostname = process.env.HOSTNAME || "127.0.0.1";
|
||||
const port = Number(process.env.PORT || 3000);
|
||||
const css = await readFile(join(root, "app/globals.css"), "utf8");
|
||||
const customerUrl = "https://customer.asphp.ir.com";
|
||||
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);
|
||||
|
||||
const mime = {
|
||||
".css": "text/css; charset=utf-8",
|
||||
".jpg": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".png": "image/png",
|
||||
".ttf": "font/ttf"
|
||||
};
|
||||
|
||||
function layout(title, body) {
|
||||
return `<!doctype html>
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>${title}</title>
|
||||
<meta name="description" content="خرید سرور HP، رم سرور، هارد سرور، CPU و تجهیزات شبکه از آریا شبکه پیشرو همراه با مشاوره تخصصی.">
|
||||
<link rel="canonical" href="https://www.asphp.ir/">
|
||||
<style>${css}</style>
|
||||
</head>
|
||||
<body>
|
||||
${header()}
|
||||
<main>${body}</main>
|
||||
${footer()}
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
function header() {
|
||||
const mega = `<div class="mega-title"><span>Categories</span><h3>خرید بر اساس دسته</h3></div><div class="mega-tabs">${megaCategories.map((group) => `<a href="#">${group.tab}</a>`).join("")}</div><div class="mega-columns">${megaCategories.map((group) => `<div><h4>${group.title}</h4>${group.items.map((item) => `<a href="#">${item}</a>`).join("")}</div>`).join("")}</div>`;
|
||||
const phoneIcon = `<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>`;
|
||||
const instagramIcon = `<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>`;
|
||||
const searchIcon = `<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>`;
|
||||
const cartIcon = `<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>`;
|
||||
const userIcon = `<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>`;
|
||||
return `<header class="site-header">
|
||||
<div class="container header-box">
|
||||
<div class="topbar"><div class="topbar-inner">
|
||||
<div class="header-tools"><a class="login user-login" href="${customerUrl}">${userIcon}ورود</a><details class="cart-menu"><summary aria-label="سبد خرید">${cartIcon}<span class="cart-badge">0</span></summary><div class="cart-popup"><h3>سبد خرید</h3><p>سبد خرید شما خالی است.</p></div></details></div>
|
||||
<div class="topbar-links"><span class="top-icon" aria-label="تماس">${phoneIcon}</span><a href="tel:02177133934">02177133934</a><span>|</span><a href="tel:02177132293">02177132293</a><a class="top-icon" href="${contact.instagram}" aria-label="Instagram">${instagramIcon}</a></div>
|
||||
</div></div>
|
||||
<div class="nav-row">
|
||||
<nav class="main-nav"><details class="category-menu"><summary><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16M4 12h16M4 18h16" /></svg>دسته بندی ها</summary><div class="mega-menu">${mega}</div></details><a href="/store">فروشگاه</a><a href="/blog">مقالات</a><a href="/#special-sale">فروش ویژه</a><a href="/about-us">درباره ما</a><a href="/contact-us">تماس با ما</a></nav>
|
||||
<details class="search-pop"><summary aria-label="جستجو">${searchIcon}</summary><form class="header-search" role="search"><input placeholder="جستجوی سرور، رم، هارد یا CPU" aria-label="جستجو"></form></details>
|
||||
<a class="brand" href="/"><img src="/assets/logo.png" alt="آریا شبکه پیشرو" width="300" height="52"></a>
|
||||
</div>
|
||||
</div>
|
||||
</header>`;
|
||||
}
|
||||
|
||||
function footer() {
|
||||
return `<footer class="footer"><div class="container footer-grid">
|
||||
<div><img src="/assets/logo.png" alt="شرکت آریا شبکه پیشرو" width="263" height="45"><p>مارا در شبکه های اجتماعی دنبال کنید.</p><div class="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><a href="tel:88467222">88467222 (021)</a><a href="tel:22459734">22459734 (021)</a><p>${contact.address}</p></div>
|
||||
<div><h3>صفحات</h3><a href="/">صفحه اصلی</a><a href="/about-us">درباره ما</a><a href="/contact-us">تماس با ما</a><a href="https://www.asphp.co/product-details/6232">خرید سرور اچ پی G9 DL380</a></div>
|
||||
</div></footer>`;
|
||||
}
|
||||
|
||||
function sectionTitle(fa, en, extra = "") {
|
||||
return `<div class="section-title ${extra}"><span>${en}</span><h2>${fa}</h2></div>`;
|
||||
}
|
||||
|
||||
function countdownMarkup() {
|
||||
const unit = (name, label, value) => `<span class="flip-unit"><strong class="flip-card" data-unit="${name}" data-value="${value}">${value}</strong><small>${label}</small></span>`;
|
||||
return `<div class="sale-countdown" data-countdown="sale" aria-label="زمان باقی مانده فروش ویژه"><span class="sale-countdown-label">پایان فروش ویژه</span>${unit("seconds", "ثانیه", "00")}${unit("minutes", "دقیقه", "00")}${unit("hours", "ساعت", "00")}${unit("days", "روز", "03")}</div>`;
|
||||
}
|
||||
|
||||
function countdownScript() {
|
||||
return `<script>
|
||||
(() => {
|
||||
const countdown = document.querySelector('[data-countdown="sale"]');
|
||||
if (!countdown) return;
|
||||
const target = Date.now() + 72 * 60 * 60 * 1000;
|
||||
const set = (name, value) => {
|
||||
const el = countdown.querySelector('[data-unit="' + name + '"]');
|
||||
const display = String(value).padStart(2, '0');
|
||||
if (el && el.dataset.value !== display) {
|
||||
el.dataset.value = display;
|
||||
el.textContent = display;
|
||||
el.classList.remove('flip-changing');
|
||||
void el.offsetWidth;
|
||||
el.classList.add('flip-changing');
|
||||
}
|
||||
};
|
||||
const tick = () => {
|
||||
const diff = Math.max(0, target - Date.now());
|
||||
set('days', Math.floor(diff / 86400000));
|
||||
set('hours', Math.floor((diff % 86400000) / 3600000));
|
||||
set('minutes', Math.floor((diff % 3600000) / 60000));
|
||||
set('seconds', Math.floor((diff % 60000) / 1000));
|
||||
};
|
||||
tick();
|
||||
setInterval(tick, 1000);
|
||||
})();
|
||||
</script>`;
|
||||
}
|
||||
|
||||
function slotStyle(image) {
|
||||
return image?.aspectRatio ? ` style="aspect-ratio:${image.aspectRatio}"` : "";
|
||||
}
|
||||
|
||||
function linkedImageTag(item, className, fallbackHref, sizesAlt = "") {
|
||||
const href = item?.href || fallbackHref || "#";
|
||||
const alt = item?.alt || sizesAlt;
|
||||
return `<a class="${className}" href="${href}"${slotStyle(item)}><img src="${item.url}" alt="${alt}"></a>`;
|
||||
}
|
||||
|
||||
function home() {
|
||||
return layout("شرکت آریا شبکه پیشرو | فروش تجهیزات سرور HP", `
|
||||
<h1 class="sr-only">آریا شبکه پیشرو، فروش تجهیزات سرور HP و شبکه</h1><section class="hero-section"><div class="container"><div class="hero"><div class="hero-slider" aria-hidden="true">${sliderImages.map((slide, index) => slide.href ? `<a class="slide-card slide-${index + 1}" href="${slide.href}"${slotStyle(slide)}><img src="${slide.url}" alt="${slide.alt || ""}"></a>` : `<div class="slide-card slide-${index + 1}"${slotStyle(slide)}><img src="${slide.url}" alt="${slide.alt || ""}"></div>`).join("")}</div></div><div class="banner-row">${belowSliderBanners.map((banner, index) => linkedImageTag(banner, "promo-banner", "tel:02177133934", `بنر فروش تجهیزات سرور ${index + 1}`)).join("")}</div></div></section>
|
||||
<section class="section" id="special-sale"><div class="container special-sale-frame"><div class="special-heading">${sectionTitle("فروش ویژه", "SPECIAL SALE", "right")}${countdownMarkup()}</div><div class="product-grid special-grid">${linkedImageTag(specialSaleBanner, "special-banner-card", "tel:02177133934", "فروش ویژه تجهیزات سرور")}${products.map((p) => `<a class="product-card" href="${p.link}"><div class="product-image"><img src="${p.image}" alt="${p.title}"></div><span>${p.category}</span><h3>${p.title}</h3><strong>تماس با فروشگاه</strong></a>`).join("")}</div></div></section>
|
||||
<section class="section split-section"><div class="container split"><div>${sectionTitle("سرور (Server) چیست؟", "SERVER", "right")}<p>در ویکی پدیا نیز معنی سرور، سرویس دهنده ذکر شده است. به بیان ساده معنی سرور یا در پاسخ سوال server به چه معناست باید گفت، کامپیوتری همیشه روشن و قدرتمند است که کاربران در زمانهای متفاوت، از آن انتظار پاسخ به درخواستها را دارند.</p><a class="secondary-btn" href="https://www.asphp.co/article-details/191">درباره سرور بیشتر بدانید</a></div><div class="image-pair"><img src="${serverBanner.url}" alt="${serverBanner.alt || "تجهیزات سرور"}"${slotStyle(serverBanner)}></div></div></section>
|
||||
<section class="section muted"><div class="container">${sectionTitle("خدمات آریا شبکه پیشرو", "SERVICES")}<div class="service-grid">${[...services].reverse().map((s) => `<article class="service-card"><img src="${s.icon}" alt="" width="64" height="64"><h3>${s.title}</h3><p>${s.body}</p></article>`).join("")}</div></div></section>
|
||||
<section class="section about-band"><div class="container split"><div>${sectionTitle("آریا شبکه پیشرو", "ARYA SHABAKE PISHRO", "right")}<h3>شرکتی جوان خلاق، با انگیزه و دانش بنیان</h3><p>انضباط، مشتری مداری، ارائه محصولات با کیفیت و تضمین شده می باشد. برای مشاوره و خرید در زمینه تجهیزات شبکه با مشاوران ما در آریا شبکه پیشرو تماس بگیرید.</p><a class="primary-btn" href="tel:02177133934">تماس با ما</a></div><div class="image-pair"><img src="${aboutusBanner.url}" alt="${aboutusBanner.alt || "تجهیزات سرور آریا شبکه پیشرو"}"${slotStyle(aboutusBanner)}></div></div></section>
|
||||
<section class="section feature-section" id="features"><div class="container feature-showcase"><div class="feature-copy">${sectionTitle("ویژگـــی مــــا", "PROCESS", "right")}<p>انضباط، مشتری مداری، ارائه محصولات با کیفیت و تضمین شده می باشد.</p></div><div class="feature-grid">${features.map((f) => `<div class="feature"><img src="${f.icon}" alt="" width="58" height="58"><p>${f.body}</p></div>`).join("")}</div></div></section>
|
||||
<section class="section source-banner-section" id="source-frame"><div class="container source-banner-grid">${linkedImageTag(threeBanners[0], "source-banner source-banner-large", sourceBanners[0].href, sourceBanners[0].alt)}<div class="source-banner-stack">${threeBanners.slice(1).map((b, index) => linkedImageTag(b, "source-banner", sourceBanners[index + 1]?.href, sourceBanners[index + 1]?.alt)).join("")}</div></div></section>
|
||||
<section class="section navy"><div class="container">${sectionTitle("طرح یک پروژه", "PROJECT A DESIGN", "light")}<div class="steps">${projectSteps.map((s) => `<article><strong>${s.number}</strong><h3>${s.title}</h3><p>${s.body}</p></article>`).join("")}</div></div></section>
|
||||
<section class="cta"><div class="container cta-inner"><div class="cta-copy"><span>مشاوره خرید تجهیزات شبکه</span><p>برای انتخاب سرور، رم، هارد و تجهیزات شبکه با مشاوران آریا شبکه پیشرو تماس بگیرید؛ بهترین راهکارها با مناسب ترین قیمت ها در اختیار شما قرار می گیرد.</p></div><a class="cta-phone" href="tel:02177133934" aria-label="تماس با کارشناسان آریا شبکه پیشرو"><span class="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 class="section" id="articles"><div class="container">${sectionTitle("مقــــالــات", "ARTICLES")}<div class="article-carousel" aria-label="مقالات">${articles.map((a) => `<article class="article-card"><div class="article-image"><img src="${a.image}" alt="${a.title}"></div><time>${a.date}</time><h3>${a.title}</h3><p>${a.abstract}</p></article>`).join("")}</div></div></section><a class="telegram-float" href="${contact.telegram}">تلگرام</a>${countdownScript()}`);
|
||||
}
|
||||
|
||||
function sitemapConfig() {
|
||||
return {
|
||||
domain: "asphp.ir",
|
||||
origin: "https://www.asphp.ir",
|
||||
generatedAt: new Date().toISOString(),
|
||||
sitemapIndex: {
|
||||
enabled: true,
|
||||
url: "https://www.asphp.ir/sitemap.xml",
|
||||
children: [
|
||||
"https://www.asphp.ir/sitemap-main.xml",
|
||||
"https://www.asphp.ir/sitemap-products.xml"
|
||||
]
|
||||
},
|
||||
staticRoutes: [
|
||||
{ path: "/", url: "https://www.asphp.ir/", priority: 1, changeFrequency: "daily" },
|
||||
{ path: "/store", url: "https://www.asphp.ir/store", priority: 0.9, changeFrequency: "daily" },
|
||||
{ path: "/blog", url: "https://www.asphp.ir/blog", priority: 0.8, changeFrequency: "weekly" },
|
||||
{ path: "/about-us", url: "https://www.asphp.ir/about-us", priority: 0.6, changeFrequency: "monthly" },
|
||||
{ path: "/contact-us", url: "https://www.asphp.ir/contact-us", priority: 0.6, changeFrequency: "monthly" }
|
||||
],
|
||||
exclude: ["/admin", "/auth", "/cart", "/checkout", "/dashboard", "/profile"],
|
||||
canonicalTemplates: {
|
||||
product: { path: "/products/{id}/{nameFaSlug}", source: "/tenants/asphp.ir/products/by-id/{id}" },
|
||||
category: { path: "/products/category/{id}/{nameFaSlug}", source: "/tenants/asphp.ir/categories/by-id/{id}", listSource: "/tenants/asphp.ir/products?categoryId={id}" },
|
||||
blog: { path: "/blog/{id}/{titleSlug}", source: "/tenants/asphp.ir/blogs/by-id/{id}" },
|
||||
portfolio: { path: "/portfolios/{id}/{titleFaSlug}", source: "/tenants/asphp.ir/portfolios/by-id/{id}" }
|
||||
},
|
||||
slugRules: { keepPersianLetters: true, spaces: "-", strip: "punctuation" }
|
||||
};
|
||||
}
|
||||
|
||||
function about() {
|
||||
return layout("درباره آریا شبکه پیشرو", `<section class="page-hero"><div class="container"><span>ABOUT ASPHP</span><h1>درباره آریا شبکه پیشرو</h1><p>شرکتی دانش بنیان، جوان و خلاق در زمینه تامین تجهیزات شبکه از دو کمپانی HP و CISCO.</p></div></section><section class="section"><div class="container split"><img src="/assets/about-office.jpg" alt="درباره شرکت آریا شبکه پیشرو"><div><h2>آریا شبکه پیشرو</h2><p>آریا شبکه پیشرو شرکتی دانش بنیان، جوان و خلاق است. فعالیت مان را از سال ۱۳۹۵ با سرمایه ای اندک ولی با انگیزه سرشار و فکر و نیروی جوان آغاز کردیم.</p><p>مجموعه ما در زمینه تامین تجهیزات شبکه از دو کمپانی HP و CISCO فعالیت دارد و تمرکز اصلی آن ارائه محصولات با کیفیت، مشاوره پیش از خرید و پشتیبانی پس از فروش است.</p></div></div></section>`);
|
||||
}
|
||||
|
||||
function contactPage() {
|
||||
return layout("تماس با آریا شبکه پیشرو", `<section class="page-hero contact-hero"><div class="container"><span>CONTACT ASPHP</span><h1>تماس با آریا شبکه پیشرو</h1><p>برای مشاوره و خرید در زمینه تجهیزات شبکه با کارشناسان ما در ارتباط باشید.</p></div></section><section class="section"><div class="container contact-grid"><div class="contact-panel"><h2>راه های ارتباطی</h2><p>${contact.address}</p>${contact.phones.map((p) => `<a href="tel:${p.replaceAll(" ", "")}">${p}</a>`).join("")}<div class="socials"><a href="${contact.instagram}">Instagram</a><a href="${contact.telegram}">Telegram</a></div></div><form class="contact-form"><label>نام و نام خانوادگی<input name="name" type="text"></label><label>شماره تماس<input name="cellNumber" type="tel"></label><label>موضوع<input name="title" type="text"></label><label>پیام<textarea name="text" rows="5"></textarea></label><button type="button">ارسال درخواست</button></form></div></section><section class="map-band"><div class="container"><h2>موقعیت شرکت</h2><p>عرض جغرافیایی: ${contact.latitude} | طول جغرافیایی: ${contact.longitude}</p></div></section>`);
|
||||
}
|
||||
|
||||
function fallbackStoreItems() {
|
||||
return products.map((p, index) => ({ ...p, id: `local-${index + 1}`, description: "برای قیمت، موجودی و کانفیگ اختصاصی این محصول با کارشناسان فروش تماس بگیرید." }));
|
||||
}
|
||||
|
||||
function storePage() {
|
||||
const cards = fallbackStoreItems().map((p) => `<a class="product-card" href="/store/${p.id}"><div class="product-image"><img src="${p.image}" alt="${p.title}"></div><span>${p.category}</span><h3>${p.title}</h3><strong>تماس با فروشگاه</strong></a>`).join("");
|
||||
return layout("فروشگاه تجهیزات سرور", `<section class="page-hero store-hero"><div class="container"><span>STORE ITEMS</span><h1>فروشگاه تجهیزات سرور</h1><p>این صفحه به API Store Items متصل شده و در پیش نمایش از محصولات نمونه استفاده می کند.</p></div></section><section class="section"><div class="container"><form class="store-toolbar"><input name="q" placeholder="جستجوی سرور، رم، هارد یا CPU"><button>جستجو</button></form><p class="api-note">فعلا آیتمی از API فروشگاه برنگشت؛ نمایش محصولات نمونه فعال است.</p><div class="product-grid store-grid">${cards}</div></div></section>`);
|
||||
}
|
||||
|
||||
function storeDetail(id) {
|
||||
const item = fallbackStoreItems().find((p) => p.id === id) || fallbackStoreItems()[0];
|
||||
const related = fallbackStoreItems().filter((p) => p.id !== item.id).map((p) => `<a class="product-card" href="/store/${p.id}"><div class="product-image"><img src="${p.image}" alt="${p.title}"></div><span>${p.category}</span><h3>${p.title}</h3><strong>تماس با فروشگاه</strong></a>`).join("");
|
||||
return layout(item.title, `<section class="section product-detail"><div class="container detail-grid"><div class="gallery"><a class="gallery-main zoom-frame" href="${item.image}"><img src="${item.image}" alt="${item.title}"></a><div class="gallery-thumbs"><a href="${item.image}"><img src="${item.image}" alt=""></a><a href="/assets/banner-2.jpg"><img src="/assets/banner-2.jpg" alt=""></a><a href="/assets/banner-3.jpg"><img src="/assets/banner-3.jpg" alt=""></a></div></div><div class="detail-copy"><span>${item.category}</span><h1>${item.title}</h1><p>${item.description}</p><strong>تماس با فروشگاه</strong><div class="store-actions"><label>تعداد<select><option>1</option><option>2</option><option>3</option></select></label><button type="button">افزودن به سبد خرید</button></div></div></div></section><section class="section detail-tabs-section"><div class="container detail-tabs"><details open><summary>توضیحات</summary><div><p>${item.description}</p></div></details><details><summary>اطلاعات فنی</summary><div class="spec-table"><p><span>دسته بندی</span><strong>${item.category}</strong></p><p><span>وضعیت قیمت</span><strong>تماس با فروشگاه</strong></p></div></details><details><summary>نظرات</summary><div class="login-comment-box"><p>برای ثبت نظر درباره این محصول وارد حساب کاربری شوید.</p><a href="${customerUrl}">ورود به حساب کاربری</a></div></details></div></section><section class="section"><div class="container">${sectionTitle("محصولات مشابه", "RELATED")}<div class="product-grid store-grid">${related}</div></div></section>`);
|
||||
}
|
||||
|
||||
function blogPage() {
|
||||
const cards = articles.map((a, index) => `<a class="blog-card" href="/blog/local-blog-${index + 1}"><div class="blog-image"><img src="${a.image}" alt="${a.title}"></div><time>${a.date}</time><h2>${a.title}</h2><p>${a.abstract}</p></a>`).join("");
|
||||
return layout("مقالات آریا شبکه پیشرو", `<section class="page-hero blog-hero"><div class="container"><span>BLOG</span><h1>مقالات آریا شبکه پیشرو</h1><p>این صفحه به API Blog متصل شده و در پیش نمایش از مقالات نمونه استفاده می کند.</p></div></section><section class="section"><div class="container"><p class="api-note">فعلا مقاله ای از API برنگشت؛ نمایش مقالات نمونه فعال است.</p><div class="blog-grid">${cards}</div></div></section>`);
|
||||
}
|
||||
|
||||
function blogDetail(slug) {
|
||||
const index = Math.max(0, Number(slug.replace("local-blog-", "")) - 1);
|
||||
const article = articles[index] || articles[0];
|
||||
return layout(article.title, `<article class="section blog-detail"><div class="container blog-detail-inner"><div class="blog-detail-image"><img src="${article.image}" alt="${article.title}"></div><time>${article.date}</time><h1>${article.title}</h1><p class="blog-abstract">${article.abstract}</p><div class="rich-text"><p>${article.abstract}</p></div></div></article>`);
|
||||
}
|
||||
|
||||
createServer((req, res) => {
|
||||
const url = new URL(req.url || "/", `http://${hostname}:${port}`);
|
||||
|
||||
if (url.pathname.startsWith("/assets/")) {
|
||||
const relative = normalize(url.pathname.replace(/^\/assets\//, ""));
|
||||
const file = join(root, "public/assets", relative);
|
||||
res.setHeader("Content-Type", mime[extname(file)] || "application/octet-stream");
|
||||
createReadStream(file).on("error", () => {
|
||||
res.writeHead(404);
|
||||
res.end("Not found");
|
||||
}).pipe(res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.pathname === "/meshkee/sitemap-config.json") {
|
||||
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
||||
res.end(JSON.stringify(sitemapConfig(), null, 2));
|
||||
} else if (url.pathname === "/sitemap.xml" || url.pathname === "/robots.txt") {
|
||||
res.writeHead(404);
|
||||
res.end("Not served by this preview. Production nginx proxies this to Meshkee.");
|
||||
} else {
|
||||
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||
if (url.pathname === "/" || url.pathname === "/index.html") res.end(home());
|
||||
else if (url.pathname === "/about-us" || url.pathname === "/aboutus") res.end(about());
|
||||
else if (url.pathname === "/contact-us" || url.pathname === "/contactus") res.end(contactPage());
|
||||
else if (url.pathname === "/store") res.end(storePage());
|
||||
else if (url.pathname.startsWith("/store/")) res.end(storeDetail(url.pathname.split("/").pop()));
|
||||
else if (url.pathname === "/blog") res.end(blogPage());
|
||||
else if (url.pathname.startsWith("/blog/")) res.end(blogDetail(url.pathname.split("/").pop()));
|
||||
else {
|
||||
res.writeHead(404);
|
||||
res.end(layout("یافت نشد", `<section class="page-hero"><div class="container"><h1>404</h1><p>صفحه مورد نظر پیدا نشد.</p></div></section>`));
|
||||
}
|
||||
}
|
||||
}).listen(port, hostname, () => {
|
||||
console.log(`ASPHP preview ready: http://${hostname}:${port}`);
|
||||
});
|
||||
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 156 KiB |
|
After Width: | Height: | Size: 51 KiB |
@@ -0,0 +1,15 @@
|
||||
import { createServer } from "node:http";
|
||||
import next from "next";
|
||||
|
||||
const hostname = process.env.HOSTNAME || "127.0.0.1";
|
||||
const port = Number(process.env.PORT || 3000);
|
||||
const app = next({ dev: true, hostname, port });
|
||||
const handle = app.getRequestHandler();
|
||||
|
||||
await app.prepare();
|
||||
|
||||
createServer((req, res) => {
|
||||
handle(req, res);
|
||||
}).listen(port, hostname, () => {
|
||||
console.log(`Ready on http://${hostname}:${port}`);
|
||||
});
|
||||