Add SEO metadata and fix mobile horizontal overflow.

Prevent homepage layout shift on small screens by constraining the hero aspect box and service/product carousels.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-09 17:44:15 +03:30
co-authored by Cursor
parent 701ec5851a
commit ccf8817b26
37 changed files with 692 additions and 134 deletions
+7 -3
View File
@@ -1,14 +1,18 @@
import type { Metadata } from "next";
import Image from "next/image"; import Image from "next/image";
import { Footer } from "@/components/Footer"; import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero } from "@/components/PageShared"; import { PageHero } from "@/components/PageShared";
import { aboutPage } from "@/data/company"; import { aboutPage } from "@/data/company";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "درباره ما | رسام تجارت هواران", title: "درباره ما",
description: description:
"درباره شرکت رسام تجارت هواران؛ طراحی، تامین و تولید تجهیزات تهویه مطبوع و سیستم‌های مدیریت دود", "درباره شرکت رسام تجارت هواران؛ طراحی، تامین و تولید تجهیزات تهویه مطبوع و سیستم‌های مدیریت دود",
}; path: "/about",
image: aboutPage.image,
});
export default function AboutUsPage() { export default function AboutUsPage() {
return ( return (
+29 -3
View File
@@ -18,11 +18,37 @@ export async function generateMetadata({ params }: PageProps) {
const { slug } = await params; const { slug } = await params;
const { item } = await getBlog(slug); const { item } = await getBlog(slug);
if (!item) { if (!item) {
return { title: "مقاله | رسام تجارت هواران" }; return {
title: "مقاله",
robots: { index: false, follow: true },
};
} }
const description = item.excerpt || item.title;
const image = item.image || "/images/brand/logo.png";
return { return {
title: `${item.title} | رسام تجارت هواران`, title: item.title,
description: item.excerpt || item.title, description,
alternates: {
canonical: `/articles/${item.slug}`,
},
openGraph: {
type: "article",
locale: "fa_IR",
url: `/articles/${item.slug}`,
siteName: "رسام تجارت هواران",
title: item.title,
description,
images: [{ url: image, alt: item.title }],
publishedTime: item.date || undefined,
},
twitter: {
card: "summary_large_image",
title: item.title,
description,
images: [image],
},
}; };
} }
+6 -3
View File
@@ -4,11 +4,14 @@ import { PageHero } from "@/components/PageShared";
import { ArticlesGrid } from "@/components/ArticlesGrid"; import { ArticlesGrid } from "@/components/ArticlesGrid";
import { Pagination } from "@/components/Pagination"; import { Pagination } from "@/components/Pagination";
import { DEFAULT_LIST_PAGE_SIZE, listBlogs } from "@/lib/meshkee"; import { DEFAULT_LIST_PAGE_SIZE, listBlogs } from "@/lib/meshkee";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "مقالات | رسام تجارت هواران", title: "مقالات",
description: "مقالات و دانشنامه تخصصی شرکت رسام تجارت هواران", description: "مقالات و دانشنامه تخصصی شرکت رسام تجارت هواران",
}; path: "/articles",
});
type PageProps = { type PageProps = {
searchParams: Promise<{ page?: string }>; searchParams: Promise<{ page?: string }>;
+6 -3
View File
@@ -3,12 +3,15 @@ import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero } from "@/components/PageShared"; import { PageHero } from "@/components/PageShared";
import { catalogPage } from "@/data/company"; import { catalogPage } from "@/data/company";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "کاتالوگ | رسام تجارت هواران", title: "کاتالوگ",
description: description:
"دانلود کاتالوگ عمومی شرکت رسام تجارت هواران؛ محصولات و فعالیت‌ها", "دانلود کاتالوگ عمومی شرکت رسام تجارت هواران؛ محصولات و فعالیت‌ها",
}; path: "/catalog",
});
export default function CatalogPage() { export default function CatalogPage() {
return ( return (
+7 -4
View File
@@ -1,10 +1,13 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { ContactUsView } from "@/components/ContactUsView"; import { ContactUsView } from "@/components/ContactUsView";
import { pageMetadata } from "@/lib/seo";
export const metadata: Metadata = { export const metadata: Metadata = pageMetadata({
title: "تماس باما | هواران", title: "تماس با ما",
description: "تماس با رسام تجارت هواران؛ آدرس، تلفن، ایمیل و فرم پیام", description:
}; "تماس با رسام تجارت هواران؛ آدرس دفتر و کارخانه، تلفن، ایمیل و فرم پیام",
path: "/contact",
});
export default function ContactUsPage() { export default function ContactUsPage() {
return <ContactUsView />; return <ContactUsView />;
+7 -5
View File
@@ -3,12 +3,14 @@ import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero } from "@/components/PageShared"; import { PageHero } from "@/components/PageShared";
import { documentsCerts, documentsSatisfaction } from "@/data/documents"; import { documentsCerts, documentsSatisfaction } from "@/data/documents";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "مدارک | رسام تجارت هواران", title: "مدارک",
description: description: "گواهینامه‌ها و رضایتمندی‌های مشتریان رسام تجارت هواران",
"گواهینامه‌ها و رضایتمندی‌های مشتریان رسام تجارت هواران", path: "/documents",
}; });
export default function DocumentsPage() { export default function DocumentsPage() {
return ( return (
+80
View File
@@ -165,6 +165,86 @@ img {
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05); box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
} }
.meshkee-credit {
position: relative;
}
.meshkee-credit__frame {
position: absolute;
inset: 0;
pointer-events: none;
}
.meshkee-credit__line {
position: absolute;
background: #c8c8c8;
transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
/* Draw clockwise starting from the left edge */
.meshkee-credit__line--l {
top: 0;
left: 0;
width: 1px;
height: 100%;
transform: scaleY(0);
transform-origin: top;
transition-delay: 0s;
}
.meshkee-credit__line--b {
bottom: 0;
left: 0;
width: 100%;
height: 1px;
transform: scaleX(0);
transform-origin: left;
transition-delay: 0.14s;
}
.meshkee-credit__line--r {
top: 0;
right: 0;
width: 1px;
height: 100%;
transform: scaleY(0);
transform-origin: bottom;
transition-delay: 0.28s;
}
.meshkee-credit__line--t {
top: 0;
left: 0;
width: 100%;
height: 1px;
transform: scaleX(0);
transform-origin: right;
transition-delay: 0.42s;
}
.meshkee-credit:hover .meshkee-credit__line--l,
.meshkee-credit:hover .meshkee-credit__line--r {
transform: scaleY(1);
}
.meshkee-credit:hover .meshkee-credit__line--t,
.meshkee-credit:hover .meshkee-credit__line--b {
transform: scaleX(1);
}
.meshkee-credit:not(:hover) .meshkee-credit__line--l {
transition-delay: 0.42s;
}
.meshkee-credit:not(:hover) .meshkee-credit__line--b {
transition-delay: 0.28s;
}
.meshkee-credit:not(:hover) .meshkee-credit__line--r {
transition-delay: 0.14s;
}
.meshkee-credit:not(:hover) .meshkee-credit__line--t {
transition-delay: 0s;
}
.round-box { .round-box {
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
+6 -3
View File
@@ -2,12 +2,15 @@ import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero } from "@/components/PageShared"; import { PageHero } from "@/components/PageShared";
import { imsPolicy } from "@/data/company"; import { imsPolicy } from "@/data/company";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "خط مشی سیستم مدیریت یکپارچه | رسام تجارت هواران", title: "خط مشی سیستم مدیریت یکپارچه",
description: description:
"خط مشی سیستم مدیریت یکپارچه (IMS) شرکت رسام تجارت هواران بر اساس استانداردهای ISO 9001، ISO 14001 و ISO 45001", "خط مشی سیستم مدیریت یکپارچه (IMS) شرکت رسام تجارت هواران بر اساس استانداردهای ISO 9001، ISO 14001 و ISO 45001",
}; path: "/ims-policy",
});
export default function ImsPolicyPage() { export default function ImsPolicyPage() {
return ( return (
+82 -6
View File
@@ -1,14 +1,84 @@
import type { Metadata } from "next"; import type { Metadata, Viewport } from "next";
import { LatinDigitsEnhancer } from "@/components/LatinDigits"; import { LatinDigitsEnhancer } from "@/components/LatinDigits";
import {
DEFAULT_DESCRIPTION,
DEFAULT_OG_IMAGE,
SITE_KEYWORDS,
SITE_NAME,
SITE_NAME_EN,
SITE_URL,
organizationJsonLd,
websiteJsonLd,
} from "@/lib/seo";
import "./globals.css"; import "./globals.css";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "صفحه اصلی | رسام تجارت هواران", metadataBase: new URL(SITE_URL),
description: title: {
"شرکت رسام تجارت هواران در حوزه طراحی و تولید تجهیزاتی نظیر چیلر، فن، هواساز، فن کویل و غیره و همچنین توان و تجربه در امور بازرگانی اعضای مجموعه خود آماده همکاری و ارائه خدمات در این زمینه است.", default: `${SITE_NAME} | طراحی و تولید تجهیزات تهویه مطبوع`,
icons: { template: `%s | ${SITE_NAME}`,
icon: "/favicon.png",
}, },
description: DEFAULT_DESCRIPTION,
applicationName: SITE_NAME,
authors: [{ name: SITE_NAME, url: SITE_URL }],
creator: SITE_NAME,
publisher: SITE_NAME,
keywords: SITE_KEYWORDS,
category: "business",
icons: {
icon: [
{ url: "/favicon.ico", sizes: "any" },
{ url: "/favicon.png", type: "image/png", sizes: "512x512" },
{ url: "/images/brand/logo.png", type: "image/png" },
],
apple: [{ url: "/favicon.png", type: "image/png", sizes: "512x512" }],
shortcut: ["/favicon.ico"],
},
openGraph: {
type: "website",
locale: "fa_IR",
url: SITE_URL,
siteName: SITE_NAME,
title: `${SITE_NAME} | ${SITE_NAME_EN}`,
description: DEFAULT_DESCRIPTION,
images: [
{
url: DEFAULT_OG_IMAGE,
alt: SITE_NAME,
},
],
},
twitter: {
card: "summary_large_image",
title: `${SITE_NAME} | ${SITE_NAME_EN}`,
description: DEFAULT_DESCRIPTION,
images: [DEFAULT_OG_IMAGE],
},
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
"max-image-preview": "large",
"max-snippet": -1,
"max-video-preview": -1,
},
},
alternates: {
canonical: "/",
},
formatDetection: {
telephone: true,
email: true,
address: true,
},
};
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
themeColor: "#002776",
}; };
export default function RootLayout({ export default function RootLayout({
@@ -16,9 +86,15 @@ export default function RootLayout({
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
const jsonLd = [organizationJsonLd(), websiteJsonLd()];
return ( return (
<html lang="fa" dir="rtl"> <html lang="fa" dir="rtl">
<body className="min-h-full antialiased"> <body className="min-h-full antialiased">
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{children} {children}
<LatinDigitsEnhancer /> <LatinDigitsEnhancer />
</body> </body>
+30
View File
@@ -0,0 +1,30 @@
import type { MetadataRoute } from "next";
import { SITE_NAME, SITE_NAME_EN, DEFAULT_DESCRIPTION } from "@/lib/seo";
export default function manifest(): MetadataRoute.Manifest {
return {
name: SITE_NAME,
short_name: SITE_NAME_EN,
description: DEFAULT_DESCRIPTION,
start_url: "/",
display: "standalone",
background_color: "#ffffff",
theme_color: "#002776",
lang: "fa",
dir: "rtl",
icons: [
{
src: "/favicon.png",
sizes: "512x512",
type: "image/png",
purpose: "any",
},
{
src: "/images/brand/logo.png",
sizes: "512x512",
type: "image/png",
purpose: "any",
},
],
};
}
+35
View File
@@ -0,0 +1,35 @@
import type { Metadata } from "next";
import Link from "next/link";
import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header";
export const metadata: Metadata = {
title: "صفحه پیدا نشد",
robots: {
index: false,
follow: true,
},
};
export default function NotFound() {
return (
<>
<Header />
<main className="section">
<div className="container-page py-20 text-center">
<p className="mb-2 text-sm tracking-[2px] text-[#9a9a9a]">404</p>
<h1 className="mb-4 text-2xl font-bold text-[#232323] sm:text-[32px]">
صفحه مورد نظر پیدا نشد
</h1>
<p className="mb-8 text-sm leading-8 text-[#666]">
ممکن است آدرس تغییر کرده باشد یا صفحه حذف شده باشد.
</p>
<Link href="/" className="btn-more">
بازگشت به صفحه اصلی
</Link>
</div>
</main>
<Footer />
</>
);
}
+13
View File
@@ -1,3 +1,4 @@
import type { Metadata } from "next";
import { About } from "@/components/About"; import { About } from "@/components/About";
import { Articles } from "@/components/Articles"; import { Articles } from "@/components/Articles";
import { Clients } from "@/components/Clients"; import { Clients } from "@/components/Clients";
@@ -10,6 +11,18 @@ import { Products } from "@/components/Products";
import { Services } from "@/components/Services"; import { Services } from "@/components/Services";
import { Solutions } from "@/components/Solutions"; import { Solutions } from "@/components/Solutions";
import { SpecialProducts } from "@/components/SpecialProducts"; import { SpecialProducts } from "@/components/SpecialProducts";
import { DEFAULT_DESCRIPTION, pageMetadata } from "@/lib/seo";
export const metadata: Metadata = {
...pageMetadata({
title: "صفحه اصلی",
description: DEFAULT_DESCRIPTION,
path: "/",
}),
title: {
absolute: "رسام تجارت هواران | طراحی و تولید تجهیزات تهویه مطبوع",
},
};
export default function Home() { export default function Home() {
return ( return (
+29 -5
View File
@@ -18,13 +18,37 @@ export async function generateMetadata({ params }: PageProps) {
const { slug } = await params; const { slug } = await params;
const { item } = await getPortfolio(slug); const { item } = await getPortfolio(slug);
if (!item) { if (!item) {
return { title: "پروژه | رسام تجارت هواران" }; return {
title: "پروژه",
robots: { index: false, follow: true },
};
} }
const description =
item.description || `نمونه کار ${item.title} — شرکت رسام تجارت هواران`;
const image = item.coverImage || item.image || "/images/brand/logo.png";
return { return {
title: `${item.title} | رسام تجارت هواران`, title: item.title,
description: description,
item.description || alternates: {
`نمونه کار ${item.title} — شرکت رسام تجارت هواران`, canonical: `/portfolios/${item.slug}`,
},
openGraph: {
type: "website",
locale: "fa_IR",
url: `/portfolios/${item.slug}`,
siteName: "رسام تجارت هواران",
title: item.title,
description,
images: [{ url: image, alt: item.title }],
},
twitter: {
card: "summary_large_image",
title: item.title,
description,
images: [image],
},
}; };
} }
+6 -3
View File
@@ -4,11 +4,14 @@ import { PageHero } from "@/components/PageShared";
import { PortfolioGrid } from "@/components/PortfolioGrid"; import { PortfolioGrid } from "@/components/PortfolioGrid";
import { Pagination } from "@/components/Pagination"; import { Pagination } from "@/components/Pagination";
import { DEFAULT_LIST_PAGE_SIZE, listPortfolios } from "@/lib/meshkee"; import { DEFAULT_LIST_PAGE_SIZE, listPortfolios } from "@/lib/meshkee";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "پروژه ها | رسام تجارت هواران", title: "پروژه ها",
description: "پروژه ها و نمونه کار های شرکت رسام تجارت هواران", description: "پروژه ها و نمونه کار های شرکت رسام تجارت هواران",
}; path: "/portfolios",
});
type PageProps = { type PageProps = {
searchParams: Promise<{ page?: string }>; searchParams: Promise<{ page?: string }>;
+7 -3
View File
@@ -4,11 +4,15 @@ import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero, SpecList } from "@/components/PageShared"; import { PageHero, SpecList } from "@/components/PageShared";
import { ahuAdvantages, ahuFaq, ahuIntro } from "@/data/ahu"; import { ahuAdvantages, ahuFaq, ahuIntro } from "@/data/ahu";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "هواساز | رسام تجارت هواران", title: "هواساز",
description: "خرید انواع هواساز با قیمت مناسب و گارانتی معتبر", description: "خرید انواع هواساز با قیمت مناسب و گارانتی معتبر",
}; path: "/products/air-conditioner",
image: "/images/products/ahu.png",
});
export default function AirConditionerPage() { export default function AirConditionerPage() {
return ( return (
+8 -4
View File
@@ -4,11 +4,15 @@ import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero, SpecList } from "@/components/PageShared"; import { PageHero, SpecList } from "@/components/PageShared";
import { chillerBlocks, chillerFaq, chillerIntro } from "@/data/chiller"; import { chillerBlocks, chillerFaq, chillerIntro } from "@/data/chiller";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "چیلر | رسام تجارت هواران", title: "چیلر",
description: "خرید انواع چیلر با قیمت مناسب و کیفیت بالا | رسام تجارت هواران", description: "خرید انواع چیلر با قیمت مناسب و کیفیت بالا",
}; path: "/products/chiller",
image: "/images/pages/chiller/air-cooled.png",
});
export default function ChillerPage() { export default function ChillerPage() {
return ( return (
+8 -5
View File
@@ -4,12 +4,15 @@ import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero } from "@/components/PageShared"; import { PageHero } from "@/components/PageShared";
import { f300Blocks, f300Intro } from "@/data/f300"; import { f300Blocks, f300Intro } from "@/data/f300";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "فن تخلیه دود F300 | رسام تجارت هواران", title: "فن تخلیه دود F300",
description: description: "فن تخلیه دود F300 با بهترین قیمت و کیفیت مناسب",
"فن تخلیه دود f300 با بهترین قیمت و کیفیت مناسب | رسام تجارت هواران", path: "/products/f300",
}; image: "/images/pages/f300/hero.jpg",
});
export default function F300Page() { export default function F300Page() {
return ( return (
+8 -4
View File
@@ -4,11 +4,15 @@ import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero, SpecList } from "@/components/PageShared"; import { PageHero, SpecList } from "@/components/PageShared";
import { fancoilBlocks, fancoilFaq, fancoilIntro } from "@/data/fancoil"; import { fancoilBlocks, fancoilFaq, fancoilIntro } from "@/data/fancoil";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "فن کویل | رسام تجارت هواران", title: "فن کویل",
description: "خرید انواع فن کویل با کیفیت مناسب | رسام تجارت هواران", description: "خرید انواع فن کویل با کیفیت مناسب",
}; path: "/products/fancoil",
image: "/images/products/fancoil.png",
});
export default function FanCoilPage() { export default function FanCoilPage() {
return ( return (
+7 -3
View File
@@ -1,3 +1,4 @@
import type { Metadata } from "next";
import Image from "next/image"; import Image from "next/image";
import { Accordion } from "@/components/Accordion"; import { Accordion } from "@/components/Accordion";
import { Footer } from "@/components/Footer"; import { Footer } from "@/components/Footer";
@@ -9,11 +10,14 @@ import {
havaranFanCentrifugal, havaranFanCentrifugal,
havaranFanFaq, havaranFanFaq,
} from "@/data/havaranFan"; } from "@/data/havaranFan";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "فن هواران | رسام تجارت هواران", title: "فن هواران",
description: "خرید انواع فن های هواران با بهترین کیفیت", description: "خرید انواع فن های هواران با بهترین کیفیت",
}; path: "/products/havaran-fan",
image: "/images/products/havaran-fan.png",
});
export default function HavaranFanPage() { export default function HavaranFanPage() {
const axialBlocks = havaranFanBlocks.filter((b) => b.group === "axial"); const axialBlocks = havaranFanBlocks.filter((b) => b.group === "axial");
+6 -3
View File
@@ -2,12 +2,15 @@ import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { CatalogCard, PageHero } from "@/components/PageShared"; import { CatalogCard, PageHero } from "@/components/PageShared";
import { productsList } from "@/data/pages"; import { productsList } from "@/data/pages";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "محصولات | رسام تجارت هواران", title: "محصولات",
description: description:
"خرید انواع فن تخلیه دود F300، فن هواران، چیلر، فن کویل و هواساز از رسام تجارت هواران", "خرید انواع فن تخلیه دود F300، فن هواران، چیلر، فن کویل و هواساز از رسام تجارت هواران",
}; path: "/products",
});
export default function ProductsPage() { export default function ProductsPage() {
return ( return (
+16
View File
@@ -0,0 +1,16 @@
import type { MetadataRoute } from "next";
import { SITE_URL } from "@/lib/seo";
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: "*",
allow: "/",
disallow: ["/api/", "/blogs", "/projects", "/article-details/", "/portfolio-details/"],
},
],
sitemap: `${SITE_URL}/sitemap.xml`,
host: SITE_URL,
};
}
+63
View File
@@ -0,0 +1,63 @@
import type { MetadataRoute } from "next";
import { articlesFallback } from "@/data/articles";
import { productsList, solutionsList } from "@/data/pages";
import { portfoliosFallback } from "@/data/portfolios";
import { SITE_URL } from "@/lib/seo";
const staticRoutes: Array<{
path: string;
changeFrequency: MetadataRoute.Sitemap[number]["changeFrequency"];
priority: number;
}> = [
{ path: "/", changeFrequency: "weekly", priority: 1 },
{ path: "/products", changeFrequency: "weekly", priority: 0.9 },
{ path: "/solutions", changeFrequency: "weekly", priority: 0.9 },
{ path: "/portfolios", changeFrequency: "weekly", priority: 0.8 },
{ path: "/articles", changeFrequency: "weekly", priority: 0.8 },
{ path: "/documents", changeFrequency: "monthly", priority: 0.7 },
{ path: "/catalog", changeFrequency: "monthly", priority: 0.7 },
{ path: "/about", changeFrequency: "monthly", priority: 0.7 },
{ path: "/contact", changeFrequency: "monthly", priority: 0.7 },
{ path: "/ims-policy", changeFrequency: "yearly", priority: 0.5 },
];
export default function sitemap(): MetadataRoute.Sitemap {
const lastModified = new Date();
const pages = staticRoutes.map((route) => ({
url: `${SITE_URL}${route.path === "/" ? "" : route.path}`,
lastModified,
changeFrequency: route.changeFrequency,
priority: route.priority,
}));
const products = productsList.map((item) => ({
url: `${SITE_URL}${item.href}`,
lastModified,
changeFrequency: "monthly" as const,
priority: 0.8,
}));
const solutions = solutionsList.map((item) => ({
url: `${SITE_URL}${item.href}`,
lastModified,
changeFrequency: "monthly" as const,
priority: 0.75,
}));
const articles = articlesFallback.map((item) => ({
url: `${SITE_URL}/articles/${item.slug}`,
lastModified: item.date ? new Date(item.date) : lastModified,
changeFrequency: "monthly" as const,
priority: 0.6,
}));
const portfolios = portfoliosFallback.map((item) => ({
url: `${SITE_URL}/portfolios/${item.slug}`,
lastModified,
changeFrequency: "monthly" as const,
priority: 0.6,
}));
return [...pages, ...products, ...solutions, ...articles, ...portfolios];
}
@@ -1,10 +1,14 @@
import { Footer } from "@/components/Footer"; import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero, UpdatingNotice } from "@/components/PageShared"; import { PageHero, UpdatingNotice } from "@/components/PageShared";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "تاسیسات ساختمانی | هواران", title: "تاسیسات ساختمانی",
}; description: "خدمات و تجهیزات تاسیسات ساختمانی رسام تجارت هواران",
path: "/solutions/building-installations",
});
export default function BuildingInstallationsPage() { export default function BuildingInstallationsPage() {
return ( return (
@@ -3,11 +3,15 @@ import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero } from "@/components/PageShared"; import { PageHero } from "@/components/PageShared";
import { smokeBlocks, smokeIntro } from "@/data/smoke"; import { smokeBlocks, smokeIntro } from "@/data/smoke";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "مدیریت دود ساختمان | هواران", title: "مدیریت دود ساختمان",
description: "سیستم های مدیریت دود ساختمان | رسام تجارت هواران", description: "طراحی، تامین و اجرای سیستمهای مدیریت دود ساختمان",
}; path: "/solutions/building-smoke-management",
image: "/images/pages/smoke/hero.png",
});
export default function SmokeManagementPage() { export default function SmokeManagementPage() {
return ( return (
+7 -3
View File
@@ -1,10 +1,14 @@
import { Footer } from "@/components/Footer"; import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero, UpdatingNotice } from "@/components/PageShared"; import { PageHero, UpdatingNotice } from "@/components/PageShared";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "تهویه مطبوع | هواران", title: "تهویه مطبوع",
}; description: "سیستم‌های تهویه مطبوع برای فضاهای مسکونی، اداری و تجاری",
path: "/solutions/hvac",
});
export default function HvacSolutionPage() { export default function HvacSolutionPage() {
return ( return (
+6 -3
View File
@@ -2,12 +2,15 @@ import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { CatalogCard, PageHero } from "@/components/PageShared"; import { CatalogCard, PageHero } from "@/components/PageShared";
import { solutionsList } from "@/data/pages"; import { solutionsList } from "@/data/pages";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "راهکارها | رسام تجارت هواران", title: "راهکارها",
description: description:
"راهکارهای مدیریت دود ساختمان، تهویه فضاهای مختلف، تهویه مطبوع، تونل و مترو و تاسیسات ساختمانی", "راهکارهای مدیریت دود ساختمان، تهویه فضاهای مختلف، تهویه مطبوع، تونل و مترو و تاسیسات ساختمانی",
}; path: "/solutions",
});
export default function SolutionsPage() { export default function SolutionsPage() {
return ( return (
@@ -3,11 +3,15 @@ import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero } from "@/components/PageShared"; import { PageHero } from "@/components/PageShared";
import { tunnelBlocks, tunnelIntro } from "@/data/tunnel"; import { tunnelBlocks, tunnelIntro } from "@/data/tunnel";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "تونل و مترو | هواران", title: "تونل و مترو",
description: "راهکارهای تهویه تونل و مترو | رسام تجارت هواران", description: "راهکارهای تهویه تونل و مترو",
}; path: "/solutions/tunnels-and-subways",
image: "/images/pages/f300/axial-jet.png",
});
export default function TunnelsPage() { export default function TunnelsPage() {
return ( return (
@@ -1,10 +1,14 @@
import { Footer } from "@/components/Footer"; import { Footer } from "@/components/Footer";
import { Header } from "@/components/Header"; import { Header } from "@/components/Header";
import { PageHero, UpdatingNotice } from "@/components/PageShared"; import { PageHero, UpdatingNotice } from "@/components/PageShared";
import type { Metadata } from "next";
import { pageMetadata } from "@/lib/seo";
export const metadata = { export const metadata: Metadata = pageMetadata({
title: "تهویه فضاهای مختلف | هواران", title: "تهویه فضاهای مختلف",
}; description: "راهکارهای تهویه برای فضاهای گوناگون ساختمانی و صنعتی",
path: "/solutions/ventilation-spaces",
});
export default function VentilationSpacesPage() { export default function VentilationSpacesPage() {
return ( return (
+2 -1
View File
@@ -152,7 +152,8 @@ export function ContactUsView() {
<p key={phone}> <p key={phone}>
<a <a
href={`tel:${phone.replace(/\D/g, "")}`} href={`tel:${phone.replace(/\D/g, "")}`}
className="hover:text-brand-navy" className="latin-digits hover:text-brand-navy"
dir="ltr"
> >
{phone} {phone}
</a> </a>
+37 -28
View File
@@ -1,11 +1,9 @@
"use client";
import Image from "next/image"; import Image from "next/image";
import { footerLinks, site } from "@/data/home"; import { footerLinks, site } from "@/data/home";
export function Footer() { export function Footer() {
return ( return (
<footer id="contact" className="bg-[#f7f7f7] text-[#333]"> <footer id="contact" className="border-t-4 border-brand-navy bg-white text-[#333]">
<div className="container-page grid gap-8 py-12 md:grid-cols-2 lg:grid-cols-4"> <div className="container-page grid gap-8 py-12 md:grid-cols-2 lg:grid-cols-4">
<div> <div>
<Image <Image
@@ -21,7 +19,9 @@ export function Footer() {
<p>{site.factory}</p> <p>{site.factory}</p>
<p>{site.factoryAddress}</p> <p>{site.factoryAddress}</p>
{site.phones.map((phone) => ( {site.phones.map((phone) => (
<p key={phone}>{phone}</p> <p key={phone} className="latin-digits" dir="ltr">
{phone}
</p>
))} ))}
</div> </div>
</div> </div>
@@ -54,7 +54,7 @@ export function Footer() {
<div> <div>
<h4 className="mb-4 text-sm font-bold">همراه ما باشید</h4> <h4 className="mb-4 text-sm font-bold">همراه ما باشید</h4>
<ul className="mb-5 space-y-2 text-sm text-[#666]"> <ul className="space-y-2 text-sm text-[#666]">
{footerLinks.pages.map((item) => ( {footerLinks.pages.map((item) => (
<li key={item.label}> <li key={item.label}>
<a href={item.href} className="hover:text-brand"> <a href={item.href} className="hover:text-brand">
@@ -63,32 +63,41 @@ export function Footer() {
</li> </li>
))} ))}
</ul> </ul>
<form
className="space-y-2"
onSubmit={(e) => {
e.preventDefault();
}}
>
<input
type="text"
placeholder="نام خانوادگی"
className="w-full border border-[#ddd] bg-white px-3 py-2 text-sm outline-none"
/>
<input
type="tel"
placeholder="شماره همراه"
className="w-full border border-[#ddd] bg-white px-3 py-2 text-sm outline-none"
/>
<button type="submit" className="btn-more w-full">
عضویت
</button>
</form>
</div> </div>
</div> </div>
<div className="border-t border-[#e5e5e5] bg-white"> <div className="border-t border-[#e5e5e5] bg-[#f7f7f7]">
<div className="container-page py-4 text-center text-xs text-[#999]"> <div className="container-page flex flex-col items-center justify-between gap-3 py-4 text-xs text-[#999] sm:flex-row">
© {new Date().getFullYear()} {site.name} <div>
© {new Date().getFullYear()} {site.name}
</div>
<a
href="https://meshkee.com"
target="_blank"
rel="noopener noreferrer"
dir="ltr"
className="meshkee-credit group inline-flex items-center gap-2.5 px-2.5 py-1.5 text-[#999] transition-colors duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] hover:text-[#555]"
>
<span className="meshkee-credit__frame" aria-hidden>
<span className="meshkee-credit__line meshkee-credit__line--l" />
<span className="meshkee-credit__line meshkee-credit__line--t" />
<span className="meshkee-credit__line meshkee-credit__line--r" />
<span className="meshkee-credit__line meshkee-credit__line--b" />
</span>
<Image
src="/images/brand/MeshkeeLogo.png"
alt="Meshkee"
width={36}
height={32}
className="relative h-8 w-auto shrink-0 object-contain opacity-50 transition-opacity duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:opacity-100"
/>
<span className="relative flex flex-col leading-snug">
<span className="text-[11px]">Designed And Developed By</span>
<span className="text-[13px] font-semibold text-[#666] transition-colors duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] group-hover:text-[#333]">
Meshkee E-Commerece Team
</span>
</span>
</a>
</div> </div>
</div> </div>
</footer> </footer>
+2 -2
View File
@@ -19,8 +19,8 @@ export function Hero() {
<div className="container-page"> <div className="container-page">
<div className="grid items-stretch gap-3 md:grid-cols-12 md:gap-4"> <div className="grid items-stretch gap-3 md:grid-cols-12 md:gap-4">
{/* In RTL: order-1 sits on the right (main slider), order-2 on the left (side banners) */} {/* In RTL: order-1 sits on the right (main slider), order-2 on the left (side banners) */}
<div className="order-1 h-full md:col-span-8"> <div className="order-1 min-w-0 md:col-span-8 md:h-full">
<div className="round-box card-shadow relative aspect-[800/474] h-full min-h-[280px] overflow-hidden bg-[#f3f3f3] md:aspect-auto"> <div className="round-box card-shadow relative aspect-[800/474] w-full overflow-hidden bg-[#f3f3f3] md:aspect-auto md:h-full md:min-h-[280px]">
{heroSlides.map((slide, i) => ( {heroSlides.map((slide, i) => (
<a <a
key={`${slide.title}-${i}`} key={`${slide.title}-${i}`}
+2 -2
View File
@@ -19,7 +19,7 @@ export function Products() {
<p>HAVARAN Products</p> <p>HAVARAN Products</p>
</div> </div>
<div className="relative"> <div className="relative min-w-0">
<button <button
type="button" type="button"
aria-label="قبلی" aria-label="قبلی"
@@ -39,7 +39,7 @@ export function Products() {
<div <div
ref={scroller} ref={scroller}
className="flex gap-4 overflow-x-auto px-1 pb-2 scrollbar-none" className="flex max-w-full gap-4 overflow-x-auto px-1 pb-2 scrollbar-none"
style={{ scrollbarWidth: "none" }} style={{ scrollbarWidth: "none" }}
> >
{productCategories.map((item) => ( {productCategories.map((item) => (
+4 -4
View File
@@ -16,9 +16,9 @@ export function Services() {
<p>Havaran Services</p> <p>Havaran Services</p>
</div> </div>
<div className="grid gap-6 lg:grid-cols-12"> <div className="grid min-w-0 gap-6 lg:grid-cols-12">
<div className="lg:col-span-3"> <div className="min-w-0 lg:col-span-3">
<div className="flex flex-row gap-2 overflow-x-auto lg:flex-col lg:overflow-visible"> <div className="flex max-w-full flex-row gap-2 overflow-x-auto lg:flex-col lg:overflow-visible">
{services.map((item) => { {services.map((item) => {
const isActive = item.id === active; const isActive = item.id === active;
return ( return (
@@ -39,7 +39,7 @@ export function Services() {
</div> </div>
</div> </div>
<div className="grid overflow-hidden border border-[#eee] bg-white lg:col-span-9 md:grid-cols-2"> <div className="grid min-w-0 overflow-hidden border border-[#eee] bg-white lg:col-span-9 md:grid-cols-2">
<div className="relative min-h-[280px] bg-[#f7f7f7]"> <div className="relative min-h-[280px] bg-[#f7f7f7]">
<Image <Image
key={current.image} key={current.image}
+8 -13
View File
@@ -19,7 +19,7 @@ export function SpecialProducts() {
<p>Special Products</p> <p>Special Products</p>
</div> </div>
<div className="relative"> <div className="relative min-w-0">
<button <button
type="button" type="button"
aria-label="قبلی" aria-label="قبلی"
@@ -39,7 +39,7 @@ export function SpecialProducts() {
<div <div
ref={scroller} ref={scroller}
className="flex items-stretch gap-4 overflow-x-auto px-1 pb-2" className="flex max-w-full items-stretch gap-4 overflow-x-auto px-1 pb-2"
style={{ scrollbarWidth: "none" }} style={{ scrollbarWidth: "none" }}
> >
{specialProducts.map((item) => ( {specialProducts.map((item) => (
@@ -60,23 +60,18 @@ export function SpecialProducts() {
<h3 className="mb-1 line-clamp-2 h-12 text-sm font-bold leading-6 text-[#232323]"> <h3 className="mb-1 line-clamp-2 h-12 text-sm font-bold leading-6 text-[#232323]">
{item.title} {item.title}
</h3> </h3>
<p className="mb-3 text-xs text-[#999]">{item.code}</p> <p className="mb-4 text-xs text-[#999]">{item.code}</p>
<p className="mb-4 text-sm font-bold text-[#232323]"> <a href="tel:02146144267" className="btn-cart mt-auto">
{item.price} <span className="font-medium">تومان</span>
</p>
<button type="button" className="btn-cart mt-auto">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<path <path
d="M6 6h15l-1.5 9h-12z" d="M8.5 3.5h3l1 5-2 1.5a12 12 0 0 0 5.5 5.5l1.5-2 5 1v3c0 1.1-.9 2-2 2A16 16 0 0 1 4.5 5.5c0-1.1.9-2 2-2Z"
stroke="currentColor" stroke="currentColor"
strokeWidth="1.6" strokeWidth="1.6"
strokeLinejoin="round"
/> />
<circle cx="9" cy="20" r="1.3" fill="currentColor" />
<circle cx="18" cy="20" r="1.3" fill="currentColor" />
<path d="M6 6 5 3H2" stroke="currentColor" strokeWidth="1.6" />
</svg> </svg>
افزودن به سبد خرید تماس بگیرید
</button> </a>
</div> </div>
</article> </article>
))} ))}
-4
View File
@@ -293,13 +293,9 @@ export const footerLinks = {
], ],
pages: [ pages: [
{ label: "صفحه اصلی", href: "/" }, { label: "صفحه اصلی", href: "/" },
{ label: "دانشنامه", href: "/articles" },
{ label: "پروژه ها", href: "/portfolios" }, { label: "پروژه ها", href: "/portfolios" },
{ label: "درباره ما", href: "/about" }, { label: "درباره ما", href: "/about" },
{ label: "تماس با ما", href: "/contact" }, { label: "تماس با ما", href: "/contact" },
{ label: "کاتالوگ", href: "/catalog" }, { label: "کاتالوگ", href: "/catalog" },
{ label: "راهنمای خرید", href: "#" },
{ label: "سوالات متداول", href: "#" },
{ label: "قوانین سایت", href: "#" },
], ],
}; };
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

+128
View File
@@ -0,0 +1,128 @@
import type { Metadata } from "next";
import { site } from "@/data/home";
export const SITE_URL =
process.env.NEXT_PUBLIC_SITE_URL ?? "https://www.havaran.com";
export const SITE_NAME = site.name;
export const SITE_NAME_EN = site.nameEn;
export const DEFAULT_DESCRIPTION = site.description;
export const SITE_KEYWORDS = [
"هواران",
"رسام تجارت هواران",
"HAVARAN",
"فن تخلیه دود",
"F300",
"چیلر",
"فن کویل",
"هواساز",
"تهویه مطبوع",
"مدیریت دود",
"جت فن",
"تاسیسات ساختمانی",
];
export const DEFAULT_OG_IMAGE = "/images/brand/logo.png";
export function absoluteUrl(path = "/"): string {
const normalized = path.startsWith("/") ? path : `/${path}`;
return `${SITE_URL}${normalized === "/" ? "" : normalized}`;
}
type PageMetaInput = {
title: string;
description: string;
path: string;
image?: string;
noIndex?: boolean;
};
export function pageMetadata({
title,
description,
path,
image = DEFAULT_OG_IMAGE,
noIndex = false,
}: PageMetaInput): Metadata {
const url = absoluteUrl(path);
return {
title,
description,
alternates: {
canonical: path,
},
openGraph: {
type: "website",
locale: "fa_IR",
url,
siteName: SITE_NAME,
title: `${title} | ${SITE_NAME}`,
description,
images: [
{
url: image,
alt: title,
},
],
},
twitter: {
card: "summary_large_image",
title: `${title} | ${SITE_NAME}`,
description,
images: [image],
},
robots: noIndex
? { index: false, follow: false }
: { index: true, follow: true },
};
}
export function organizationJsonLd() {
return {
"@context": "https://schema.org",
"@type": "Organization",
name: SITE_NAME,
alternateName: SITE_NAME_EN,
url: SITE_URL,
logo: absoluteUrl("/images/brand/logo.png"),
email: site.email,
telephone: site.phones.map((phone) => phone.replace(/\s/g, "")),
address: {
"@type": "PostalAddress",
addressCountry: "IR",
addressLocality: "تهران",
streetAddress: site.office,
},
contactPoint: [
{
"@type": "ContactPoint",
telephone: "+982146144267",
contactType: "customer service",
areaServed: "IR",
availableLanguage: ["fa", "en"],
},
],
};
}
export function websiteJsonLd() {
return {
"@context": "https://schema.org",
"@type": "WebSite",
name: SITE_NAME,
alternateName: SITE_NAME_EN,
url: SITE_URL,
inLanguage: "fa-IR",
publisher: {
"@type": "Organization",
name: SITE_NAME,
logo: {
"@type": "ImageObject",
url: absoluteUrl("/images/brand/logo.png"),
},
},
};
}