Wire live business contact data and refresh homepage/about branding.

Pull landline, cell, and address from Meshkee business-info across footer and contact surfaces, update brand spelling and 14-year messaging, and keep product cards free of placeholder call-to-action pricing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-10-05 00:08:45 +03:30
co-authored by Cursor
parent 3ffa43641d
commit e2468a1ba1
23 changed files with 499 additions and 228 deletions
+6
View File
@@ -20,6 +20,12 @@ Meshkee Website API (`https://api.meshkee.com/api/v1`, docs at
sharing the same display name under different ids (backend data-entry sharing the same display name under different ids (backend data-entry
artifact). The summary merges by name and links to whichever id holds artifact). The summary merges by name and links to whichever id holds
the most products under that name. the most products under that name.
- Business contact data (`getBusinessInfo` /
`getResolvedBusinessContact`) comes from
`GET /tenants/{domain}/website/business-info` — landline/cell
(`phoneNumbers`), addresses, emails, social. Footer, contact page,
and `HomeContactStrip` consume the resolved helper. Empty/failed
fetches keep the previous hardcoded address and phone fallbacks.
## Static image slots (business-managed overrides) ## Static image slots (business-managed overrides)
+14
View File
@@ -31,6 +31,20 @@ Dashboard import catalog: `GET /meshkee/static-image-slots` (`src/app/meshkee/st
If a rendered image size or crop ratio changes, update both the component container (`aspectRatioToCss`) and the matching catalog entry. If a rendered image size or crop ratio changes, update both the component container (`aspectRatioToCss`) and the matching catalog entry.
## Business contact (business-info)
`GET /tenants/aradarisman.com/website/business-info` → `getBusinessInfo` /
`getResolvedBusinessContact` in `src/lib/meshkee.ts`.
| Field | UI |
|---|---|
| `phoneNumbers` (`landline` / `cell`) | Footer, `ContactInfo`, `HomeContactStrip` |
| `addresses[0]` | Footer, `ContactInfo` (map pin is fixed coords on contact page) |
| `emails` | Footer |
| `socialMedia` | Contact page social icons (when set) |
Failed/empty responses keep local address + phone fallbacks so the page never blanks out.
## Other conventions ## Other conventions
- Category tiles from live products (`getCategorySummary`) because the raw `/categories` taxonomy has duplicate display names. - Category tiles from live products (`getCategorySummary`) because the raw `/categories` taxonomy has duplicate display names.
+56 -28
View File
@@ -4,36 +4,54 @@ import Link from "next/link";
import Container from "@/components/Container"; import Container from "@/components/Container";
import PageBanner from "@/components/PageBanner"; import PageBanner from "@/components/PageBanner";
import MissionVision from "@/components/MissionVision"; import MissionVision from "@/components/MissionVision";
import ImportCountries from "@/components/ImportCountries";
import Features from "@/components/Features"; import Features from "@/components/Features";
import { pageMetadata } from "@/lib/seo"; import { pageMetadata } from "@/lib/seo";
import {
getResolvedBusinessContact,
phoneTelHref,
} from "@/lib/meshkee";
export const metadata: Metadata = pageMetadata({ export const metadata: Metadata = pageMetadata({
title: "درباره ما | آراد آریسمان", title: "درباره ما | آراد اَریسمان",
description: description:
"آشنایی با شرکت آراد آریسمان؛ بیش از ۲۳ سال تجربه در تولید و عرضه مکمل‌های غذایی دام، طیور و زنبور عسل.", "آشنایی با شرکت آراد اَریسمان؛ بیش از ۲۳ سال تجربه در تولید و عرضه مکمل‌های غذایی دام، طیور و زنبور عسل.",
path: "/about", path: "/about",
}); });
export default function AboutPage() { export default async function AboutPage() {
const contact = await getResolvedBusinessContact();
const phone = contact.primaryPhone;
return ( return (
<> <>
<PageBanner <PageBanner
title="درباره آراد آریسمان" title="درباره آراد اَریسمان"
subtitle="بیش از دو دهه تجربه در تولید و عرضه مکمل‌های غذایی دام، طیور و زنبور عسل" subtitle="بیش از دو دهه تجربه در تولید و عرضه مکمل‌های غذایی دام، طیور و زنبور عسل"
breadcrumb="درباره ما" breadcrumb="درباره ما"
/> />
<section className="py-16 sm:py-20"> <section className="py-16 sm:py-20">
<Container className="grid grid-cols-1 lg:grid-cols-2 items-center gap-12"> <Container className="grid grid-cols-1 lg:grid-cols-2 items-center gap-12">
<div className="relative aspect-[4/3] rounded-3xl overflow-hidden shadow-xl bg-surface order-2 lg:order-1"> <div className="relative aspect-[4/3] rounded-3xl overflow-hidden shadow-xl bg-secondary order-2 lg:order-1">
<Image <Image
src="/images/category-3.jpg" src="/images/stats-banner.jpg"
alt="محصولات آراد آریسمان" alt=""
fill fill
className="object-contain p-6" className="object-cover scale-110 blur-[6px]"
sizes="(min-width: 1024px) 45vw, 90vw" sizes="(min-width: 1024px) 45vw, 90vw"
aria-hidden
/> />
<div className="absolute inset-0 bg-primary-dark/45" aria-hidden />
<div className="absolute inset-0 flex items-center justify-center p-10">
<Image
src="/images/footer-logo.png"
alt="آراد اَریسمان"
width={220}
height={62}
className="relative z-10 h-auto w-44 sm:w-52 drop-shadow-lg"
priority
/>
</div>
</div> </div>
<div className="order-1 lg:order-2 text-center lg:text-start"> <div className="order-1 lg:order-2 text-center lg:text-start">
@@ -42,7 +60,7 @@ export default function AboutPage() {
تغذیه مطمئن، پرورش موفق تغذیه مطمئن، پرورش موفق
</h2> </h2>
<p className="mt-6 leading-8 text-foreground"> <p className="mt-6 leading-8 text-foreground">
شرکت آراد آریسمان پیشرو در تولید و عرضه افزودنی‌های خوراک، ویتامین‌ها و شرکت آراد اَریسمان پیشرو در تولید و عرضه افزودنی‌های خوراک، ویتامین‌ها و
مکمل‌های غذایی دام و طیور با بیش از ۲۳ سال فعالیت در بازار ایران می‌باشد. مکمل‌های غذایی دام و طیور با بیش از ۲۳ سال فعالیت در بازار ایران می‌باشد.
واردات این شرکت عمدتا از تولیدکنندگان معتبر بین‌المللی غذای دام و طیور از واردات این شرکت عمدتا از تولیدکنندگان معتبر بین‌المللی غذای دام و طیور از
کشورهای انگلستان، فرانسه، ترکیه، هندوستان و چین می‌باشد. کشورهای انگلستان، فرانسه، ترکیه، هندوستان و چین می‌باشد.
@@ -53,18 +71,28 @@ export default function AboutPage() {
نتیجه‌ی این رویکرد، اعتماد پایدار بیش از ۲۳۰ مشتری در سراسر کشور است. نتیجه‌ی این رویکرد، اعتماد پایدار بیش از ۲۳۰ مشتری در سراسر کشور است.
</p> </p>
<div className="mt-8 flex justify-center lg:justify-start gap-10"> <div className="mt-8 flex justify-center lg:justify-start">
<div> <div className="inline-flex items-center gap-4 sm:gap-5">
<div className="text-3xl font-extrabold text-primary">۲۳+</div> <div className="shrink-0 text-center lg:text-start">
<div className="mt-1 text-sm text-foreground">سال تجربه</div> <p className="text-4xl sm:text-5xl font-extrabold tracking-tight leading-none text-primary">
۱۴ سال
</p>
<p
dir="ltr"
className="mt-1.5 font-display text-xs sm:text-sm font-medium tracking-[0.06em] text-primary/55"
>
14 years of experience
</p>
</div> </div>
<div> <div
<div className="text-3xl font-extrabold text-primary">۲۳۰+</div> className="hidden sm:block h-12 w-px shrink-0 bg-primary/20"
<div className="mt-1 text-sm text-foreground">رضایت مشتری</div> aria-hidden
</div> />
<div> <p className="text-sm sm:text-base font-medium leading-7 text-foreground text-start">
<div className="text-3xl font-extrabold text-primary">۵</div> تولید تخصصی محصولات
<div className="mt-1 text-sm text-foreground">کشور واردات</div> <br />
مکمل دام و طیور
</p>
</div> </div>
</div> </div>
</div> </div>
@@ -75,16 +103,14 @@ export default function AboutPage() {
<section className="bg-surface"> <section className="bg-surface">
<Container className="pt-16 sm:pt-20 text-center max-w-xl mx-auto"> <Container className="pt-16 sm:pt-20 text-center max-w-xl mx-auto">
<span className="text-sm font-bold text-accent">چرا آراد آریسمان</span> <span className="text-sm font-bold text-accent">چرا آراد اَریسمان</span>
<h2 className="mt-3 text-3xl sm:text-4xl font-extrabold text-primary"> <h2 className="mt-3 text-3xl sm:text-4xl font-extrabold text-primary">
مزایای مکمل‌های آراد آریسمان مزایای مکمل‌های آراد اَریسمان
</h2> </h2>
</Container> </Container>
<Features /> <Features />
</section> </section>
<ImportCountries />
<section className="py-16 sm:py-20"> <section className="py-16 sm:py-20">
<Container> <Container>
<div className="rounded-3xl bg-primary px-8 py-14 sm:px-16 text-center"> <div className="rounded-3xl bg-primary px-8 py-14 sm:px-16 text-center">
@@ -92,7 +118,7 @@ export default function AboutPage() {
برای مشاوره و خرید محصولات همین حالا با ما در تماس باشید برای مشاوره و خرید محصولات همین حالا با ما در تماس باشید
</h2> </h2>
<p className="mt-4 text-white/80 max-w-xl mx-auto"> <p className="mt-4 text-white/80 max-w-xl mx-auto">
کارشناسان آراد آریسمان آماده پاسخگویی به سوالات شما درباره مکمل‌ها و کارشناسان آراد اَریسمان آماده پاسخگویی به سوالات شما درباره مکمل‌ها و
نحوه مصرف آن‌ها هستند. نحوه مصرف آن‌ها هستند.
</p> </p>
<div className="mt-8 flex flex-wrap justify-center gap-4"> <div className="mt-8 flex flex-wrap justify-center gap-4">
@@ -102,13 +128,15 @@ export default function AboutPage() {
> >
تماس با ما تماس با ما
</Link> </Link>
{phone ? (
<a <a
href="tel:09104067935" href={phoneTelHref(phone.number)}
dir="ltr" dir="ltr"
className="rounded-full border border-white/30 px-7 py-3.5 text-white font-semibold hover:bg-white/10 transition-colors" className="rounded-full border border-white/30 px-7 py-3.5 text-white font-semibold hover:bg-white/10 transition-colors"
> >
09104067935 {phone.number}
</a> </a>
) : null}
</div> </div>
</div> </div>
</Container> </Container>
+2 -2
View File
@@ -31,7 +31,7 @@ export async function generateMetadata({
if (!post) return {}; if (!post) return {};
const title = postTitle(post); const title = postTitle(post);
return pageMetadata({ return pageMetadata({
title: `${title} | آراد آریسمان`, title: `${title} | آراد اَریسمان`,
description: postExcerpt(post, 160), description: postExcerpt(post, 160),
path: blogPath(post), path: blogPath(post),
}); });
@@ -114,7 +114,7 @@ export default async function BlogDetailPage({
<div className="mt-10 rounded-2xl bg-surface p-6 flex flex-wrap items-center justify-between gap-4"> <div className="mt-10 rounded-2xl bg-surface p-6 flex flex-wrap items-center justify-between gap-4">
<div> <div>
<p className="font-bold text-secondary">نویسنده: تیم آراد آریسمان</p> <p className="font-bold text-secondary">نویسنده: تیم آراد اَریسمان</p>
<p className="mt-1 text-sm text-foreground"> <p className="mt-1 text-sm text-foreground">
برای مشاوره تخصصی تغذیه دام و طیور با ما در تماس باشید. برای مشاوره تخصصی تغذیه دام و طیور با ما در تماس باشید.
</p> </p>
+3 -3
View File
@@ -6,9 +6,9 @@ import Pagination from "@/components/Pagination";
import { getPosts } from "@/lib/meshkee"; import { getPosts } from "@/lib/meshkee";
export const metadata = pageMetadata({ export const metadata = pageMetadata({
title: "مقالات | آراد آریسمان", title: "مقالات | آراد اَریسمان",
description: description:
"مقالات آموزشی درباره تغذیه دام، طیور و زنبور عسل از تیم آراد آریسمان.", "مقالات آموزشی درباره تغذیه دام، طیور و زنبور عسل از تیم آراد اَریسمان.",
path: "/blog", path: "/blog",
}); });
@@ -33,7 +33,7 @@ export default async function BlogPage({
return ( return (
<> <>
<PageBanner <PageBanner
title="مقالات آراد آریسمان" title="مقالات آراد اَریسمان"
subtitle="نکات آموزشی و تخصصی درباره تغذیه دام، طیور و زنبور عسل" subtitle="نکات آموزشی و تخصصی درباره تغذیه دام، طیور و زنبور عسل"
breadcrumb="مقالات" breadcrumb="مقالات"
/> />
+47 -12
View File
@@ -3,24 +3,49 @@ import PageBanner from "@/components/PageBanner";
import ContactInfo from "@/components/ContactInfo"; import ContactInfo from "@/components/ContactInfo";
import ContactForm from "@/components/ContactForm"; import ContactForm from "@/components/ContactForm";
import { pageMetadata } from "@/lib/seo"; import { pageMetadata } from "@/lib/seo";
import {
getResolvedBusinessContact,
phoneTelHref,
} from "@/lib/meshkee";
export const metadata = pageMetadata({ export const metadata = pageMetadata({
title: "تماس با ما | آراد آریسمان", title: "تماس با ما | آراد اَریسمان",
description: description:
"راه‌های ارتباطی با شرکت آراد آریسمان؛ آدرس، تلفن تماس و فرم ارسال پیام.", "راه‌های ارتباطی با شرکت آراد اَریسمان؛ آدرس، تلفن تماس و فرم ارسال پیام.",
path: "/contact", path: "/contact",
}); });
const mapQuery = encodeURIComponent( function telegramHref(handle: string): string {
"قم، شهرک صنعتی شکوهیه، خیابان شهید بروجردی" const trimmed = handle.trim();
); if (!trimmed) return "#";
if (trimmed.startsWith("http")) return trimmed;
return `https://t.me/${trimmed.replace(/^@/, "")}`;
}
function whatsappHref(value: string): string {
const trimmed = value.trim();
if (!trimmed) return "#";
if (trimmed.startsWith("http")) return trimmed;
const digits = trimmed
.replace(/[۰-۹]/g, (d) => String("۰۱۲۳۴۵۶۷۸۹".indexOf(d)))
.replace(/[^\d]/g, "");
return digits ? `https://wa.me/${digits}` : "#";
}
const MAP_LAT = 34.79998060691881;
const MAP_LNG = 50.8027208051919;
export default async function ContactPage() {
const contact = await getResolvedBusinessContact();
const telegram = contact.socialMedia.telegram?.trim() || "";
const whatsapp = contact.socialMedia.whatsapp?.trim() || "";
const primaryPhone = contact.primaryPhone;
export default function ContactPage() {
return ( return (
<> <>
<PageBanner <PageBanner
title="تماس با ما" title="تماس با ما"
subtitle="سوالات، پیشنهادات یا درخواست مشاوره خود را برای ما بفرستید؛ کارشناسان آراد آریسمان در اسرع وقت پاسخگوی شما خواهند بود." subtitle="سوالات، پیشنهادات یا درخواست مشاوره خود را برای ما بفرستید؛ کارشناسان آراد اَریسمان در اسرع وقت پاسخگوی شما خواهند بود."
breadcrumb="تماس با ما" breadcrumb="تماس با ما"
/> />
@@ -44,8 +69,8 @@ export default function ContactPage() {
<div className="lg:col-span-2 flex flex-col gap-5"> <div className="lg:col-span-2 flex flex-col gap-5">
<div className="relative flex-1 min-h-[280px] rounded-2xl overflow-hidden border border-black/5 shadow-sm"> <div className="relative flex-1 min-h-[280px] rounded-2xl overflow-hidden border border-black/5 shadow-sm">
<iframe <iframe
title="نقشه آراد آریسمان" title="نقشه آراد اَریسمان"
src={`https://www.google.com/maps?q=${mapQuery}&output=embed`} src={`https://www.google.com/maps?q=${MAP_LAT},${MAP_LNG}&z=17&output=embed`}
className="absolute inset-0 h-full w-full border-0" className="absolute inset-0 h-full w-full border-0"
loading="lazy" loading="lazy"
referrerPolicy="no-referrer-when-downgrade" referrerPolicy="no-referrer-when-downgrade"
@@ -55,8 +80,11 @@ export default function ContactPage() {
<div className="rounded-2xl bg-secondary p-7 text-white"> <div className="rounded-2xl bg-secondary p-7 text-white">
<h3 className="font-bold">ما را در شبکه‌های اجتماعی دنبال کنید</h3> <h3 className="font-bold">ما را در شبکه‌های اجتماعی دنبال کنید</h3>
<div className="mt-5 flex items-center gap-4"> <div className="mt-5 flex items-center gap-4">
{telegram ? (
<a <a
href="#" href={telegramHref(telegram)}
target="_blank"
rel="noopener noreferrer"
aria-label="تلگرام" aria-label="تلگرام"
className="flex h-10 w-10 items-center justify-center rounded-full bg-white/10 hover:bg-accent hover:text-secondary transition-colors" className="flex h-10 w-10 items-center justify-center rounded-full bg-white/10 hover:bg-accent hover:text-secondary transition-colors"
> >
@@ -67,8 +95,12 @@ export default function ContactPage() {
/> />
</svg> </svg>
</a> </a>
) : null}
{whatsapp ? (
<a <a
href="#" href={whatsappHref(whatsapp)}
target="_blank"
rel="noopener noreferrer"
aria-label="واتساپ" aria-label="واتساپ"
className="flex h-10 w-10 items-center justify-center rounded-full bg-white/10 hover:bg-accent hover:text-secondary transition-colors" className="flex h-10 w-10 items-center justify-center rounded-full bg-white/10 hover:bg-accent hover:text-secondary transition-colors"
> >
@@ -79,8 +111,10 @@ export default function ContactPage() {
/> />
</svg> </svg>
</a> </a>
) : null}
{primaryPhone ? (
<a <a
href="tel:09104067935" href={phoneTelHref(primaryPhone.number)}
aria-label="تماس تلفنی" aria-label="تماس تلفنی"
className="flex h-10 w-10 items-center justify-center rounded-full bg-white/10 hover:bg-accent hover:text-secondary transition-colors" className="flex h-10 w-10 items-center justify-center rounded-full bg-white/10 hover:bg-accent hover:text-secondary transition-colors"
> >
@@ -91,6 +125,7 @@ export default function ContactPage() {
/> />
</svg> </svg>
</a> </a>
) : null}
</div> </div>
</div> </div>
</div> </div>
+2 -2
View File
@@ -28,11 +28,11 @@ export async function generateMetadata(): Promise<Metadata> {
return { return {
metadataBase: new URL(SITE_URL), metadataBase: new URL(SITE_URL),
title: { title: {
default: "آراد آریسمان", default: "آراد اَریسمان",
template: "%s", template: "%s",
}, },
description: description:
"شرکت آراد آریسمان، تولید و عرضه مکمل‌ها، ویتامین‌ها و افزودنی‌های خوراک دام و طیور با بیش از ۲۳ سال تجربه.", "شرکت آراد اَریسمان، تولید و عرضه مکمل‌ها، ویتامین‌ها و افزودنی‌های خوراک دام و طیور با بیش از ۲۳ سال تجربه.",
icons: { icons: {
icon: iconUrl, icon: iconUrl,
shortcut: iconUrl, shortcut: iconUrl,
+3 -3
View File
@@ -11,9 +11,9 @@ import { categoryPath, resolveCategorySlotHref } from "@/lib/urls";
import { pageMetadata } from "@/lib/seo"; import { pageMetadata } from "@/lib/seo";
export const metadata: Metadata = pageMetadata({ export const metadata: Metadata = pageMetadata({
title: "آراد آریسمان | صفحه اصلی", title: "آراد اَریسمان | صفحه اصلی",
description: description:
"شرکت آراد آریسمان، تولید و عرضه مکمل‌ها، ویتامین‌ها و افزودنی‌های خوراک دام و طیور با بیش از ۲۳ سال تجربه.", "شرکت آراد اَریسمان، تولید و عرضه مکمل‌ها، ویتامین‌ها و افزودنی‌های خوراک دام و طیور با بیش از ۲۳ سال تجربه.",
path: "/", path: "/",
}); });
@@ -48,7 +48,7 @@ export default async function Home() {
<HomeContactStrip /> <HomeContactStrip />
<CategoryProducts <CategoryProducts
categoryNameEn="Poultry" categoryNameEn="Poultry"
className="py-14 sm:py-16" className="py-14 sm:py-16 bg-[#fbf8f3]"
/> />
<Features /> <Features />
<Stats /> <Stats />
+12 -4
View File
@@ -12,6 +12,8 @@ import {
getCategoryAncestors, getCategoryAncestors,
getProductById, getProductById,
getProducts, getProducts,
getResolvedBusinessContact,
phoneTelHref,
productCatalog, productCatalog,
productImages, productImages,
productPriceLabel, productPriceLabel,
@@ -33,7 +35,7 @@ export async function generateMetadata({
if (!product) return {}; if (!product) return {};
const description = product.summary || stripHtml(product.descriptionHtml, 160); const description = product.summary || stripHtml(product.descriptionHtml, 160);
return pageMetadata({ return pageMetadata({
title: `${product.nameFa} | آراد آریسمان`, title: `${product.nameFa} | آراد اَریسمان`,
description, description,
path: productPath(product), path: productPath(product),
}); });
@@ -58,7 +60,11 @@ export default async function ProductDetailPage({
const catalog = productCatalog(product); const catalog = productCatalog(product);
const shortDesc = product.summary || stripHtml(product.descriptionHtml, 160); const shortDesc = product.summary || stripHtml(product.descriptionHtml, 160);
const allCategories = await getCategories(); const [allCategories, contact] = await Promise.all([
getCategories(),
getResolvedBusinessContact(),
]);
const phone = contact.primaryPhone;
const categoryTrail = product.categoryId const categoryTrail = product.categoryId
? getCategoryAncestors(product.categoryId, allCategories) ? getCategoryAncestors(product.categoryId, allCategories)
: []; : [];
@@ -121,13 +127,15 @@ export default async function ProductDetailPage({
</div> </div>
<div className="mt-6 flex flex-wrap gap-3"> <div className="mt-6 flex flex-wrap gap-3">
{phone ? (
<a <a
href="tel:09104067935" href={phoneTelHref(phone.number)}
dir="ltr" dir="ltr"
className="rounded-full bg-primary px-7 py-3.5 text-white font-semibold hover:bg-primary-dark transition-colors" className="rounded-full bg-primary px-7 py-3.5 text-white font-semibold hover:bg-primary-dark transition-colors"
> >
09104067935 {phone.number}
</a> </a>
) : null}
<Link <Link
href="/contact" href="/contact"
className="rounded-full border border-primary/30 px-7 py-3.5 text-primary font-semibold hover:bg-primary/5 transition-colors" className="rounded-full border border-primary/30 px-7 py-3.5 text-primary font-semibold hover:bg-primary/5 transition-colors"
@@ -30,8 +30,8 @@ export async function generateMetadata({
const category = await getCategoryById(id); const category = await getCategoryById(id);
if (!category) return {}; if (!category) return {};
return pageMetadata({ return pageMetadata({
title: `${category.nameFa} | محصولات آراد آریسمان`, title: `${category.nameFa} | محصولات آراد اَریسمان`,
description: `مشاهده محصولات دسته ${category.nameFa}؛ مکمل‌های غذایی آراد آریسمان.`, description: `مشاهده محصولات دسته ${category.nameFa}؛ مکمل‌های غذایی آراد اَریسمان.`,
path: categoryPath(category), path: categoryPath(category),
}); });
} }
+3 -3
View File
@@ -11,9 +11,9 @@ import { categoryPath } from "@/lib/urls";
import { slugifyFa } from "@/lib/slugify"; import { slugifyFa } from "@/lib/slugify";
export const metadata = pageMetadata({ export const metadata = pageMetadata({
title: "محصولات | آراد آریسمان", title: "محصولات | آراد اَریسمان",
description: description:
"مکمل‌های غذایی، ویتامینه و معدنی دام، طیور و زنبور عسل؛ محصولات آراد آریسمان را مشاهده کنید.", "مکمل‌های غذایی، ویتامینه و معدنی دام، طیور و زنبور عسل؛ محصولات آراد اَریسمان را مشاهده کنید.",
path: "/products", path: "/products",
}); });
@@ -62,7 +62,7 @@ export default async function ProductsPage({
return ( return (
<> <>
<PageBanner <PageBanner
title="محصولات آراد آریسمان" title="محصولات آراد اَریسمان"
subtitle="مکمل‌های غذایی، ویتامینه و معدنی برای دام، طیور و زنبور عسل" subtitle="مکمل‌های غذایی، ویتامینه و معدنی برای دام، طیور و زنبور عسل"
breadcrumb="محصولات" breadcrumb="محصولات"
/> />
+3 -3
View File
@@ -7,7 +7,7 @@ export default async function About() {
const slotImage = slot?.images?.[0]; const slotImage = slot?.images?.[0];
const imageSrc = slotImage?.url || "/images/hero-2.jpg"; const imageSrc = slotImage?.url || "/images/hero-2.jpg";
const imageAlt = slotImage?.titleFa || "محصولات آراد آریسمان"; const imageAlt = slotImage?.titleFa || "محصولات آراد اَریسمان";
const objectPositionClass = slotImage ? "object-center" : "object-left"; const objectPositionClass = slotImage ? "object-center" : "object-left";
const image = ( const image = (
<Image <Image
@@ -38,10 +38,10 @@ export default async function About() {
<div className="order-1 lg:order-2 text-center lg:text-start"> <div className="order-1 lg:order-2 text-center lg:text-start">
<span className="text-sm font-bold text-accent">درباره ما</span> <span className="text-sm font-bold text-accent">درباره ما</span>
<h2 className="mt-3 text-3xl sm:text-4xl font-extrabold text-primary"> <h2 className="mt-3 text-3xl sm:text-4xl font-extrabold text-primary">
آراد آریسمان آراد اَریسمان
</h2> </h2>
<p className="mt-6 leading-8 text-foreground"> <p className="mt-6 leading-8 text-foreground">
شرکت آراد آریسمان پیشرو در تولید و عرضه افزودنی‌های خوراک، ویتامین‌ها و شرکت آراد اَریسمان پیشرو در تولید و عرضه افزودنی‌های خوراک، ویتامین‌ها و
مکمل‌های غذایی دام و طیور با بیش از ۲۳ سال فعالیت در بازار ایران می‌باشد. مکمل‌های غذایی دام و طیور با بیش از ۲۳ سال فعالیت در بازار ایران می‌باشد.
واردات این شرکت عمدتا از تولیدکنندگان معتبر بین‌المللی غذای دام و طیور از واردات این شرکت عمدتا از تولیدکنندگان معتبر بین‌المللی غذای دام و طیور از
کشورهای انگلستان، فرانسه، ترکیه، هندوستان و چین می‌باشد. کشورهای انگلستان، فرانسه، ترکیه، هندوستان و چین می‌باشد.
+53 -38
View File
@@ -1,63 +1,78 @@
const cards = [ import {
{ getResolvedBusinessContact,
title: "آدرس ما", phoneTelHref,
lines: ["قم، شهرک صنعتی شکوهیه، فاز دو،", "خیابان شهید بروجردی، نبش کلاهدوز یک، پلاک ۸۶۶"], type BusinessPhoneNumber,
icon: ( } from "@/lib/meshkee";
const hoursLines = ["شنبه تا پنجشنبه: ۷ الی ۱۸"];
function phoneLabel(phone: BusinessPhoneNumber): string {
if (phone.type === "cell") return "موبایل";
if (phone.type === "landline") return "تلفن ثابت";
return phone.label?.trim() || "تلفن";
}
export default async function ContactInfo() {
const contact = await getResolvedBusinessContact();
return (
<div className="grid grid-cols-1 sm:grid-cols-3 gap-5">
<div className="rounded-2xl border border-black/5 bg-white p-7 shadow-sm">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary/5 text-primary">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path <path
d="M12 22s7-6.3 7-12a7 7 0 1 0-14 0c0 5.7 7 12 7 12Zm0-9a3 3 0 1 1 0-6 3 3 0 0 1 0 6Z" d="M12 22s7-6.3 7-12a7 7 0 1 0-14 0c0 5.7 7 12 7 12Zm0-9a3 3 0 1 1 0-6 3 3 0 0 1 0 6Z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
), </div>
}, <h3 className="mt-4 font-bold text-secondary">آدرس ما</h3>
{ <div className="mt-2 space-y-1 text-sm leading-7 text-foreground">
title: "تلفن تماس", <p>{contact.address}</p>
lines: ["09104067935", "۰۲۵۳۳۳۴۲۶۳۳ ، ۰۲۵۳۳۳۴۲۹۶۷ ، ۰۲۵۳۳۳۴۲۹۸۳"], </div>
icon: ( </div>
<div className="rounded-2xl border border-black/5 bg-white p-7 shadow-sm">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary/5 text-primary">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path <path
d="M6.6 10.8c1.4 2.8 3.8 5.1 6.6 6.6l2.2-2.2c.3-.3.7-.4 1-.2 1.1.4 2.3.6 3.6.6.6 0 1 .4 1 1V20c0 .6-.4 1-1 1C10.6 21 3 13.4 3 4c0-.6.4-1 1-1h3.5c.6 0 1 .4 1 1 0 1.3.2 2.5.6 3.6.1.4 0 .8-.2 1L6.6 10.8z" d="M6.6 10.8c1.4 2.8 3.8 5.1 6.6 6.6l2.2-2.2c.3-.3.7-.4 1-.2 1.1.4 2.3.6 3.6.6.6 0 1 .4 1 1V20c0 .6-.4 1-1 1C10.6 21 3 13.4 3 4c0-.6.4-1 1-1h3.5c.6 0 1 .4 1 1 0 1.3.2 2.5.6 3.6.1.4 0 .8-.2 1L6.6 10.8z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
), </div>
}, <h3 className="mt-4 font-bold text-secondary">تلفن تماس</h3>
{ <div className="mt-2 space-y-1 text-sm leading-7 text-foreground" dir="ltr">
title: "ساعات کاری", {contact.phones.map((p) => (
lines: ["شنبه تا چهارشنبه: ۸ الی ۱۷", "پنجشنبه: ۸ الی ۱۳"], <p key={`${p.type}-${p.number}`} className="text-right">
icon: ( <span className="text-foreground/70">{phoneLabel(p)}: </span>
<a
href={phoneTelHref(p.number)}
className="hover:text-primary transition-colors"
>
{p.number}
</a>
</p>
))}
</div>
</div>
<div className="rounded-2xl border border-black/5 bg-white p-7 shadow-sm">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary/5 text-primary">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path <path
d="M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20Zm1-10.4 4 2.3-1 1.7-5-2.9V6h2v5.6Z" d="M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20Zm1-10.4 4 2.3-1 1.7-5-2.9V6h2v5.6Z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
),
},
];
export default function ContactInfo() {
return (
<div className="grid grid-cols-1 sm:grid-cols-3 gap-5">
{cards.map((c) => (
<div
key={c.title}
className="rounded-2xl border border-black/5 bg-white p-7 shadow-sm"
>
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary/5 text-primary">
{c.icon}
</div> </div>
<h3 className="mt-4 font-bold text-secondary">{c.title}</h3> <h3 className="mt-4 font-bold text-secondary">ساعات کاری</h3>
<div className="mt-2 space-y-1 text-sm leading-7 text-foreground" dir={c.title === "تلفن تماس" ? "ltr" : undefined}> <div className="mt-2 space-y-1 text-sm leading-7 text-foreground">
{c.lines.map((line) => ( {hoursLines.map((line) => (
<p key={line} className={c.title === "تلفن تماس" ? "text-right" : undefined}> <p key={line}>{line}</p>
{line}
</p>
))} ))}
</div> </div>
</div> </div>
))}
</div> </div>
); );
} }
+29 -14
View File
@@ -2,8 +2,7 @@ import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import Container from "./Container"; import Container from "./Container";
import MeshkeeCredit from "./MeshkeeCredit"; import MeshkeeCredit from "./MeshkeeCredit";
import { getResolvedBusinessContact, phoneTelHref } from "@/lib/meshkee";
const phones = ["09104067935", "۰۲۵۳۳۳۴۲۶۳۳", "۰۲۵۳۳۳۴۲۹۶۷", "۰۲۵۳۳۳۴۲۹۸۳"];
const quickLinks = [ const quickLinks = [
{ label: "صفحه اصلی", href: "/" }, { label: "صفحه اصلی", href: "/" },
@@ -13,7 +12,9 @@ const quickLinks = [
{ label: "تماس با ما", href: "/contact" }, { label: "تماس با ما", href: "/contact" },
]; ];
export default function Footer() { export default async function Footer() {
const contact = await getResolvedBusinessContact();
return ( return (
<footer id="footer" className="relative bg-secondary text-white/80 overflow-hidden"> <footer id="footer" className="relative bg-secondary text-white/80 overflow-hidden">
<Image <Image
@@ -27,13 +28,13 @@ export default function Footer() {
<div> <div>
<Image <Image
src="/images/footer-logo.png" src="/images/footer-logo.png"
alt="آراد آریسمان" alt="آراد اَریسمان"
width={140} width={140}
height={40} height={40}
className="h-10 w-auto" className="h-10 w-auto"
/> />
<p className="mt-5 text-sm leading-7 text-white/60"> <p className="mt-5 text-sm leading-7 text-white/60">
شرکت آراد آریسمان، پیشرو در تولید و عرضه افزودنی‌های خوراک، ویتامین‌ها و شرکت آراد اَریسمان، پیشرو در تولید و عرضه افزودنی‌های خوراک، ویتامین‌ها و
مکمل‌های غذایی دام و طیور با بیش از ۲۳ سال تجربه. مکمل‌های غذایی دام و طیور با بیش از ۲۳ سال تجربه.
</p> </p>
</div> </div>
@@ -53,22 +54,36 @@ export default function Footer() {
<div> <div>
<h4 className="text-white font-bold mb-5">آدرس ما</h4> <h4 className="text-white font-bold mb-5">آدرس ما</h4>
<p className="text-sm leading-7 text-white/60"> <p className="text-sm leading-7 text-white/60">{contact.address}</p>
قم، شهرک صنعتی شکوهیه، فاز دو، خیابان شهید بروجردی، نبش کلاهدوز یک، پلاک ۸۶۶ <h4 className="text-white font-bold mt-6 mb-3">ساعات کاری</h4>
</p> <p className="text-sm leading-7 text-white/60">شنبه تا پنجشنبه: ۷ الی ۱۸</p>
</div> </div>
<div> <div>
<h4 className="text-white font-bold mb-5">راه‌های ارتباطی</h4> <h4 className="text-white font-bold mb-5">راه‌های ارتباطی</h4>
<ul className="space-y-3 text-sm"> <ul className="space-y-3 text-sm">
{phones.map((p) => ( {contact.phones.map((p) => (
<li key={p}> <li key={`${p.type}-${p.number}`}>
<a href={`tel:${p}`} dir="ltr" className="hover:text-accent transition-colors block text-right"> <a
{p} href={phoneTelHref(p.number)}
dir="ltr"
className="hover:text-accent transition-colors block text-start"
>
{p.number}
</a>
</li>
))}
{contact.emails.map((email) => (
<li key={email}>
<a
href={`mailto:${email}`}
className="hover:text-accent transition-colors block"
dir="ltr"
>
{email}
</a> </a>
</li> </li>
))} ))}
<li className="text-white/50">۰۲۵۳۳۳۴۲۹۴۲ (فاکس)</li>
</ul> </ul>
</div> </div>
</Container> </Container>
@@ -76,7 +91,7 @@ export default function Footer() {
<div className="footer-bottom"> <div className="footer-bottom">
<Container> <Container>
<div className="footer-bottom-bar"> <div className="footer-bottom-bar">
<div className="footer-bottom-site">آراد آریسمان</div> <div className="footer-bottom-site">آراد اَریسمان</div>
<MeshkeeCredit /> <MeshkeeCredit />
</div> </div>
</Container> </Container>
+6 -2
View File
@@ -1,12 +1,16 @@
import { getTopLevelCategories } from "@/lib/meshkee"; import { getResolvedBusinessContact, getTopLevelCategories } from "@/lib/meshkee";
import { categoryPath } from "@/lib/urls"; import { categoryPath } from "@/lib/urls";
import HeaderNav from "./HeaderNav"; import HeaderNav from "./HeaderNav";
export default async function Header() { export default async function Header() {
const categories = await getTopLevelCategories(); const [categories, contact] = await Promise.all([
getTopLevelCategories(),
getResolvedBusinessContact(),
]);
return ( return (
<HeaderNav <HeaderNav
phoneNumber={contact.primaryPhone?.number ?? ""}
productCategories={categories.map((cat) => ({ productCategories={categories.map((cat) => ({
id: cat.id, id: cat.id,
nameFa: cat.nameFa, nameFa: cat.nameFa,
+9 -3
View File
@@ -22,8 +22,10 @@ const navLinks = [
export default function HeaderNav({ export default function HeaderNav({
productCategories, productCategories,
phoneNumber,
}: { }: {
productCategories: NavCategory[]; productCategories: NavCategory[];
phoneNumber: string;
}) { }) {
const pathname = usePathname(); const pathname = usePathname();
const [scrolledPast, setScrolledPast] = useState(false); const [scrolledPast, setScrolledPast] = useState(false);
@@ -84,8 +86,9 @@ export default function HeaderNav({
<header className="fixed inset-x-0 top-0 z-50"> <header className="fixed inset-x-0 top-0 z-50">
<div className="relative z-[60] bg-secondary text-white"> <div className="relative z-[60] bg-secondary text-white">
<Container className="flex h-11 items-center justify-between text-sm"> <Container className="flex h-11 items-center justify-between text-sm">
{phoneNumber ? (
<a <a
href="tel:09104067935" href={`tel:${phoneNumber.replace(/[^\d+]/g, "")}`}
className="flex items-center gap-2 text-white/90 hover:text-accent transition-colors" className="flex items-center gap-2 text-white/90 hover:text-accent transition-colors"
> >
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" className="shrink-0"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" className="shrink-0">
@@ -94,8 +97,11 @@ export default function HeaderNav({
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
<span dir="ltr">09104067935</span> <span dir="ltr">{phoneNumber}</span>
</a> </a>
) : (
<span />
)}
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<a href="#" aria-label="تلگرام" className="text-white/90 hover:text-accent transition-colors"> <a href="#" aria-label="تلگرام" className="text-white/90 hover:text-accent transition-colors">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none"> <svg width="18" height="18" viewBox="0 0 24 24" fill="none">
@@ -136,7 +142,7 @@ export default function HeaderNav({
<Link href="/" className="shrink-0" onClick={() => setMobileOpen(false)}> <Link href="/" className="shrink-0" onClick={() => setMobileOpen(false)}>
<Image <Image
src={scrolled || mobileOpen ? "/images/logo-wordmark.png" : "/images/footer-logo.png"} src={scrolled || mobileOpen ? "/images/logo-wordmark.png" : "/images/footer-logo.png"}
alt="آراد آریسمان" alt="آراد اَریسمان"
width={140} width={140}
height={40} height={40}
className="h-10 w-auto" className="h-10 w-auto"
+2 -2
View File
@@ -9,7 +9,7 @@ export default function Hero() {
<div className="relative w-full aspect-[4/3] lg:aspect-auto lg:h-[480px] rounded-3xl overflow-hidden shadow-xl"> <div className="relative w-full aspect-[4/3] lg:aspect-auto lg:h-[480px] rounded-3xl overflow-hidden shadow-xl">
<Image <Image
src="/images/hero-1.jpg" src="/images/hero-1.jpg"
alt="محصولات آراد آریسمان در مزرعه گندم" alt="محصولات آراد اَریسمان در مزرعه گندم"
fill fill
priority priority
className="object-cover object-left" className="object-cover object-left"
@@ -39,7 +39,7 @@ export default function Hero() {
<div className="order-1 lg:order-2 text-center lg:text-start"> <div className="order-1 lg:order-2 text-center lg:text-start">
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-extrabold text-primary leading-[1.15]"> <h1 className="text-4xl sm:text-5xl lg:text-6xl font-extrabold text-primary leading-[1.15]">
آراد آریسمان آراد اَریسمان
</h1> </h1>
<p className="mt-5 text-lg sm:text-xl text-foreground max-w-md mx-auto lg:mx-0"> <p className="mt-5 text-lg sm:text-xl text-foreground max-w-md mx-auto lg:mx-0">
قدرت رشد، از دل تغذیه سالم قدرت رشد، از دل تغذیه سالم
+3 -3
View File
@@ -130,7 +130,7 @@ export default function HeroSlider({
<section <section
role="region" role="region"
aria-roledescription="carousel" aria-roledescription="carousel"
aria-label="اسلایدر معرفی آراد آریسمان" aria-label="اسلایدر معرفی آراد اَریسمان"
onMouseEnter={pause} onMouseEnter={pause}
onMouseLeave={resume} onMouseLeave={resume}
className="relative -mt-[125px] h-svh min-h-[520px] w-full overflow-hidden bg-secondary" className="relative -mt-[125px] h-svh min-h-[520px] w-full overflow-hidden bg-secondary"
@@ -149,7 +149,7 @@ export default function HeroSlider({
<Link href={slide.linkUrl} className="absolute inset-0 block" aria-label={slide.title || "مشاهده"}> <Link href={slide.linkUrl} className="absolute inset-0 block" aria-label={slide.title || "مشاهده"}>
<Image <Image
src={slide.image} src={slide.image}
alt={slide.title || "آراد آریسمان"} alt={slide.title || "آراد اَریسمان"}
fill fill
priority={i === 0} priority={i === 0}
className="object-cover" className="object-cover"
@@ -159,7 +159,7 @@ export default function HeroSlider({
) : ( ) : (
<Image <Image
src={slide.image} src={slide.image}
alt={slide.title || "آراد آریسمان"} alt={slide.title || "آراد اَریسمان"}
fill fill
priority={i === 0} priority={i === 0}
className="object-cover" className="object-cover"
+10 -4
View File
@@ -1,10 +1,14 @@
import Link from "next/link"; import Link from "next/link";
import Container from "./Container"; import Container from "./Container";
import ScrollReveal from "./ScrollReveal"; import ScrollReveal from "./ScrollReveal";
import { getResolvedBusinessContact, phoneTelHref } from "@/lib/meshkee";
export default async function HomeContactStrip() {
const contact = await getResolvedBusinessContact();
const phone = contact.primaryPhone;
export default function HomeContactStrip() {
return ( return (
<section className="bg-[#f1e7d9] text-secondary"> <section className="bg-[#f7f1e8] text-secondary">
<Container> <Container>
<ScrollReveal> <ScrollReveal>
<div className="flex flex-col gap-6 py-10 sm:flex-row sm:items-center sm:justify-between sm:py-12"> <div className="flex flex-col gap-6 py-10 sm:flex-row sm:items-center sm:justify-between sm:py-12">
@@ -16,8 +20,9 @@ export default function HomeContactStrip() {
</div> </div>
<div className="flex flex-wrap items-center gap-4 sm:gap-5 shrink-0"> <div className="flex flex-wrap items-center gap-4 sm:gap-5 shrink-0">
{phone ? (
<a <a
href="tel:09104067935" href={phoneTelHref(phone.number)}
className="inline-flex items-center gap-2 rounded-full border border-primary/20 bg-white/40 px-5 py-3 text-[15px] font-medium text-primary hover:bg-white/70 transition-colors" className="inline-flex items-center gap-2 rounded-full border border-primary/20 bg-white/40 px-5 py-3 text-[15px] font-medium text-primary hover:bg-white/70 transition-colors"
> >
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden> <svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden>
@@ -26,8 +31,9 @@ export default function HomeContactStrip() {
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
<span dir="ltr">09104067935</span> <span dir="ltr">{phone.number}</span>
</a> </a>
) : null}
<Link <Link
href="/contact" href="/contact"
className="inline-flex items-center justify-center rounded-full bg-primary px-7 py-3 text-[15px] font-bold text-white hover:bg-primary-dark transition-colors" className="inline-flex items-center justify-center rounded-full bg-primary px-7 py-3 text-[15px] font-bold text-white hover:bg-primary-dark transition-colors"
+1 -1
View File
@@ -18,7 +18,7 @@ export default function ImportCountries() {
واردات از تولیدکنندگان معتبر جهانی واردات از تولیدکنندگان معتبر جهانی
</h2> </h2>
<p className="mt-4 text-foreground"> <p className="mt-4 text-foreground">
آراد آریسمان محصولات خود را عمدتا از تولیدکنندگان معتبر بین‌المللی آراد اَریسمان محصولات خود را عمدتا از تولیدکنندگان معتبر بین‌المللی
این کشورها وارد می‌کند این کشورها وارد می‌کند
</p> </p>
</div> </div>
+4
View File
@@ -52,9 +52,13 @@ export default function ProductCard({ product }: { product: Product }) {
<p className="mt-2 text-sm text-foreground line-clamp-2">{excerpt}</p> <p className="mt-2 text-sm text-foreground line-clamp-2">{excerpt}</p>
)} )}
<div className="mt-3 flex items-center justify-between"> <div className="mt-3 flex items-center justify-between">
{product.store?.minPrice ? (
<span className="text-sm font-bold text-primary"> <span className="text-sm font-bold text-primary">
{productPriceLabel(product)} {productPriceLabel(product)}
</span> </span>
) : (
<span />
)}
<span className="flex h-8 w-8 items-center justify-center rounded-full bg-primary/5 text-primary rotate-180 group-hover:bg-primary group-hover:text-white transition-colors"> <span className="flex h-8 w-8 items-center justify-center rounded-full bg-primary/5 text-primary rotate-180 group-hover:bg-primary group-hover:text-white transition-colors">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"> <svg width="14" height="14" viewBox="0 0 24 24" fill="none">
<path <path
+24 -13
View File
@@ -1,14 +1,9 @@
import Image from "next/image"; import Image from "next/image";
import Container from "./Container"; import Container from "./Container";
const stats = [
{ value: "۲۳۰+", label: "رضایت مشتری" },
{ value: "۲۳", label: "سال تجربه" },
];
export default function Stats() { export default function Stats() {
return ( return (
<section className="relative py-20 sm:py-28"> <section className="relative py-20 sm:py-28 overflow-hidden">
<Image <Image
src="/images/stats-banner.jpg" src="/images/stats-banner.jpg"
alt="" alt=""
@@ -18,15 +13,31 @@ export default function Stats() {
/> />
<div className="absolute inset-0 bg-primary-dark/80" /> <div className="absolute inset-0 bg-primary-dark/80" />
<Container className="relative"> <Container className="relative">
<div className="grid grid-cols-2 max-w-xl mx-auto text-center gap-8"> <div className="flex justify-center text-white">
{stats.map((s) => ( <div className="inline-flex items-center gap-4 sm:gap-5">
<div key={s.label}> <div className="shrink-0 text-center">
<div className="text-4xl sm:text-5xl font-extrabold text-white"> <p className="text-5xl sm:text-7xl md:text-8xl font-extrabold tracking-tight leading-none">
{s.value} ۱۴ سال
</p>
<p
dir="ltr"
className="mt-2 font-display text-xs sm:text-sm font-medium tracking-[0.06em] text-white/75"
>
14 years of experience
</p>
</div> </div>
<div className="mt-3 text-white/80">{s.label}</div>
<div
className="hidden sm:block h-14 sm:h-16 w-px shrink-0 bg-white/35"
aria-hidden
/>
<p className="text-base sm:text-xl md:text-2xl font-medium leading-8 sm:leading-10 text-white/90 text-start">
تولید تخصصی محصولات
<br />
مکمل دام و طیور
</p>
</div> </div>
))}
</div> </div>
</Container> </Container>
</section> </section>
+119
View File
@@ -49,6 +49,125 @@ export async function getWebsiteFavicon(): Promise<WebsiteFavicon> {
} }
} }
/** Public business profile: about, phones, addresses, social (dashboard Business Data). */
export type BusinessPhoneNumber = {
label?: string;
type: "landline" | "cell" | string;
number: string;
};
export type BusinessAddress = {
id: string;
name: string | null;
province: string | null;
city: string | null;
address: string | null;
postalCode: string | null;
landline: string | null;
};
export type BusinessSocialMedia = {
whatsapp?: string;
telegram?: string;
instagram?: string;
linkedin?: string;
youtube?: string;
aparat?: string;
};
export type BusinessInfo = {
id: string;
name: string;
nameFa: string;
about: string;
vision: string;
logoUrl: string | null;
logoDarkUrl: string | null;
faviconUrl: string | null;
emails: string[];
phoneNumbers: BusinessPhoneNumber[];
socialMedia: BusinessSocialMedia;
addresses: BusinessAddress[];
schema?: {
enabled: boolean;
type: string;
jsonLd?: Record<string, unknown> | null;
};
};
const FALLBACK_ADDRESS =
"قم، شهرک صنعتی شکوهیه، فاز دو، خیابان شهید بروجردی، نبش کلاهدوز یک، پلاک ۸۶۶";
const FALLBACK_PHONES: BusinessPhoneNumber[] = [
{ type: "cell", number: "09128540059", label: "Cell" },
{ type: "landline", number: "02533342633", label: "Landline" },
];
export async function getBusinessInfo(): Promise<BusinessInfo | null> {
try {
return await meshkeeFetch<BusinessInfo>(
`/tenants/${TENANT_DOMAIN}/website/business-info`,
300
);
} catch {
return null;
}
}
/** Build a single-line Persian address from a Meshkee address object. */
export function formatBusinessAddress(addr: BusinessAddress | null | undefined): string {
if (!addr) return "";
const parts: string[] = [];
if (addr.province?.trim()) parts.push(addr.province.trim());
if (addr.city?.trim() && addr.city.trim() !== addr.province?.trim()) {
parts.push(addr.city.trim());
}
if (addr.address?.trim()) parts.push(addr.address.trim());
return parts.join("، ");
}
/** ASCII digits for `tel:` / `mailto:` hrefs (accepts Persian digits). */
export function phoneTelHref(number: string): string {
const ascii = number
.replace(/[۰-۹]/g, (d) => String("۰۱۲۳۴۵۶۷۸۹".indexOf(d)))
.replace(/[^\d+]/g, "");
return ascii ? `tel:${ascii}` : "#";
}
export type ResolvedBusinessContact = {
address: string;
phones: BusinessPhoneNumber[];
cells: BusinessPhoneNumber[];
landlines: BusinessPhoneNumber[];
primaryPhone: BusinessPhoneNumber | null;
emails: string[];
socialMedia: BusinessSocialMedia;
};
/** Contact fields for UI — API values when present, stable local fallbacks otherwise. */
export async function getResolvedBusinessContact(): Promise<ResolvedBusinessContact> {
const info = await getBusinessInfo();
const phones =
info?.phoneNumbers?.filter((p) => p.number?.trim()) ?? [];
const resolvedPhones = phones.length > 0 ? phones : FALLBACK_PHONES;
const cells = resolvedPhones.filter((p) => p.type === "cell");
const landlines = resolvedPhones.filter((p) => p.type === "landline");
const primaryPhone = cells[0] ?? landlines[0] ?? resolvedPhones[0] ?? null;
const formatted =
formatBusinessAddress(info?.addresses?.[0]) || FALLBACK_ADDRESS;
return {
address: formatted,
phones: resolvedPhones,
cells,
landlines,
primaryPhone,
emails: info?.emails?.filter(Boolean) ?? [],
socialMedia: info?.socialMedia ?? {},
};
}
/** /**
* Business-managed image overrides (hero category icons, category tiles, * Business-managed image overrides (hero category icons, category tiles,
* feature icons, about photo, brand logos). Each slot has a stable `key`; when a * feature icons, about photo, brand logos). Each slot has a stable `key`; when a