Add Meshkee product catalog navigation and details
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
00126e5777
commit
34ce4f6925
@@ -0,0 +1,126 @@
|
||||
"use client";
|
||||
|
||||
import { ChevronDown, ChevronLeft } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
categoryPath,
|
||||
fetchProductCategories,
|
||||
getLevelTwoProductCategories,
|
||||
type ProductCategory,
|
||||
} from "../lib/meshkee";
|
||||
|
||||
type ActivePage = "home" | "products" | "about" | "contact";
|
||||
|
||||
const navigationItems: {
|
||||
label: string;
|
||||
href: string;
|
||||
key?: ActivePage;
|
||||
}[] = [
|
||||
{ label: "صفحه اصلی", href: "/", key: "home" },
|
||||
{ label: "خدمات", href: "/#services" },
|
||||
{ label: "اخبار", href: "/#news" },
|
||||
{ label: "درباره ما", href: "/about-us", key: "about" },
|
||||
{ label: "تماس با ما", href: "/contact-us", key: "contact" },
|
||||
];
|
||||
|
||||
export default function HeaderNavigation({
|
||||
active,
|
||||
mobileOpen,
|
||||
onNavigate,
|
||||
}: {
|
||||
active?: ActivePage;
|
||||
mobileOpen: boolean;
|
||||
onNavigate?: () => void;
|
||||
}) {
|
||||
const [categories, setCategories] = useState<ProductCategory[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
|
||||
fetchProductCategories(controller.signal)
|
||||
.then(setCategories)
|
||||
.catch(() => {
|
||||
// Keep the all-products link when categories cannot be loaded.
|
||||
});
|
||||
|
||||
return () => controller.abort();
|
||||
}, []);
|
||||
|
||||
const levelTwoCategories = useMemo(
|
||||
() => getLevelTwoProductCategories(categories),
|
||||
[categories],
|
||||
);
|
||||
|
||||
const renderCategoryLinks = () => (
|
||||
<>
|
||||
<Link href="/products" onClick={onNavigate}>
|
||||
همه ماشینآلات
|
||||
<small>ALL PRODUCTS</small>
|
||||
</Link>
|
||||
{levelTwoCategories.map((category) => (
|
||||
<Link
|
||||
href={categoryPath(category)}
|
||||
key={category.id}
|
||||
onClick={onNavigate}
|
||||
>
|
||||
{category.nameFa || category.name}
|
||||
<small dir="ltr">{category.name}</small>
|
||||
</Link>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className="desktop-nav" aria-label="ناوبری اصلی">
|
||||
<Link className={active === "home" ? "active" : ""} href="/">
|
||||
صفحه اصلی
|
||||
</Link>
|
||||
<div className="product-menu">
|
||||
<Link
|
||||
className={active === "products" ? "active" : ""}
|
||||
href="/products"
|
||||
>
|
||||
محصولات
|
||||
<ChevronDown size={14} />
|
||||
</Link>
|
||||
<div className="product-menu__dropdown">
|
||||
<div>{renderCategoryLinks()}</div>
|
||||
</div>
|
||||
</div>
|
||||
{navigationItems.slice(1).map((item) => (
|
||||
<Link
|
||||
className={item.key === active ? "active" : ""}
|
||||
href={item.href}
|
||||
key={item.label}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{mobileOpen && (
|
||||
<nav className="mobile-nav" aria-label="ناوبری موبایل">
|
||||
<Link href="/" onClick={onNavigate}>
|
||||
صفحه اصلی
|
||||
<ChevronLeft size={17} />
|
||||
</Link>
|
||||
<details className="mobile-product-menu">
|
||||
<summary>
|
||||
محصولات
|
||||
<ChevronDown size={17} />
|
||||
</summary>
|
||||
<div>{renderCategoryLinks()}</div>
|
||||
</details>
|
||||
{navigationItems.slice(1).map((item) => (
|
||||
<Link href={item.href} key={item.label} onClick={onNavigate}>
|
||||
{item.label}
|
||||
<ChevronLeft size={17} />
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
import {
|
||||
ArrowUpLeft,
|
||||
Camera,
|
||||
ChevronLeft,
|
||||
CircleUserRound,
|
||||
Link as LinkIcon,
|
||||
Mail,
|
||||
@@ -17,22 +16,14 @@ import {
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import HeaderNavigation from "./HeaderNavigation";
|
||||
|
||||
const assetBase = "/assets";
|
||||
|
||||
const navItems = [
|
||||
{ label: "صفحه اصلی", href: "/" },
|
||||
{ label: "محصولات", href: "/#products" },
|
||||
{ label: "خدمات", href: "/#services" },
|
||||
{ label: "اخبار", href: "/#news" },
|
||||
{ label: "درباره ما", href: "/about-us", key: "about" },
|
||||
{ label: "تماس با ما", href: "/contact-us", key: "contact" },
|
||||
];
|
||||
|
||||
export function InteriorHeader({
|
||||
active,
|
||||
}: {
|
||||
active?: "about" | "contact";
|
||||
active?: "products" | "about" | "contact";
|
||||
}) {
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
|
||||
@@ -62,17 +53,11 @@ export function InteriorHeader({
|
||||
<Link className="logo" href="/" aria-label="سامان اندیش">
|
||||
<img src={`${assetBase}/KN83Drb6oI.png`} alt="سامان اندیش" />
|
||||
</Link>
|
||||
<nav className="desktop-nav" aria-label="ناوبری اصلی">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
className={item.key === active ? "active" : ""}
|
||||
href={item.href}
|
||||
key={item.label}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
<HeaderNavigation
|
||||
active={active}
|
||||
mobileOpen={mobileOpen}
|
||||
onNavigate={() => setMobileOpen(false)}
|
||||
/>
|
||||
<div className="header__actions">
|
||||
<Link className="icon-button" href="/#smart-search" aria-label="جستجو">
|
||||
<Search size={22} />
|
||||
@@ -93,20 +78,6 @@ export function InteriorHeader({
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{mobileOpen && (
|
||||
<nav className="mobile-nav">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
href={item.href}
|
||||
key={item.label}
|
||||
onClick={() => setMobileOpen(false)}
|
||||
>
|
||||
{item.label}
|
||||
<ChevronLeft size={17} />
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
)}
|
||||
</header>
|
||||
</>
|
||||
);
|
||||
@@ -138,7 +109,7 @@ export function InteriorFooter() {
|
||||
<div className="footer__links">
|
||||
<h3>دسترسی سریع</h3>
|
||||
<Link href="/">صفحه اصلی</Link>
|
||||
<Link href="/#products">ماشینآلات</Link>
|
||||
<Link href="/products">ماشینآلات</Link>
|
||||
<Link href="/about-us">درباره ما</Link>
|
||||
<Link href="/contact-us">تماس با ما</Link>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { ArrowUpLeft, Phone } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { productPath, type Product } from "../lib/meshkee";
|
||||
|
||||
export default function ProductCard({ product }: { product: Product }) {
|
||||
const href = productPath(product);
|
||||
const title = product.nameFa || product.title;
|
||||
const category =
|
||||
product.categoryNameFa || product.categoryName || "ماشینآلات صنعتی";
|
||||
const image =
|
||||
product.thumbnailUrl ||
|
||||
product.thumbnail ||
|
||||
product.image ||
|
||||
"/assets/product-quadding.jpg";
|
||||
|
||||
return (
|
||||
<article className="product-card">
|
||||
<Link className="product-card__image" href={href}>
|
||||
<img src={image} alt={title} />
|
||||
<span>ماشینآلات</span>
|
||||
<i>
|
||||
<ArrowUpLeft />
|
||||
</i>
|
||||
</Link>
|
||||
<div className="product-card__body">
|
||||
<small>{category}</small>
|
||||
<h3>
|
||||
<Link href={href}>{title}</Link>
|
||||
</h3>
|
||||
<p dir="ltr">{product.title}</p>
|
||||
<div>
|
||||
<code>{product.categoryName || "SAMAN ANDISH"}</code>
|
||||
<a href="tel:+982188530256">
|
||||
استعلام قیمت
|
||||
<Phone size={15} />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user