Load site meta tags and trust badges from Meshkee business info.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-10-03 18:23:38 +03:30
co-authored by Cursor
parent cd88e0ce46
commit b4d6b30243
6 changed files with 74 additions and 28 deletions
+7
View File
@@ -61,6 +61,13 @@ Homepage images: `GET /tenants/{domain}/website/static-images?pageKey=home`. Cat
`url` → img, `linkUrl` → wrap in a link, empty/fail → placeholder. Use `slot.aspectRatio`. If a slot’s size/ratio changes in CSS, update `STATIC_IMAGE_SLOTS` in `lib/static-images.ts` in the same change.
## Tags & badges
`GET /tenants/{domain}/website/business-info`
- `metaTags`: `[{ name, content }]` emitted once from root `generateMetadata` as `other`. Empty → omit.
- `trustBadges`: `[{ kind: "enamad" | "samandehi", embedHtml }]` rendered in the footer with `dangerouslySetInnerHTML`. Empty → omit. Do not hardcode eNamad or Samandehi placeholders.
## Local fallbacks
- Slider: `src/assets/images/slides`, else `/placeholders/slider-*.svg`
+18 -7
View File
@@ -6,6 +6,7 @@ import { Header } from "@/components/layout/Header";
import { Footer } from "@/components/layout/Footer";
import { getNavCategories } from "@/lib/categories";
import { getLogoSrc } from "@/lib/media";
import { getBusinessInfo, metaTagsToOther } from "@/lib/meshkee";
const vazir = Vazirmatn({
subsets: ["arabic", "latin"],
@@ -14,14 +15,24 @@ const vazir = Vazirmatn({
weight: ["300", "400", "500", "600", "700", "800"],
});
export const metadata: Metadata = {
title: "داروخانه دکتر ارجمند | صفحه اصلی",
description:
"خرید آنلاین دارو، مکمل، محصولات بهداشتی و آرایشی از داروخانه دکتر ارجمند در قم، سالاریه.",
};
export async function generateMetadata(): Promise<Metadata> {
const businessInfo = await getBusinessInfo();
const other = metaTagsToOther(businessInfo?.metaTags);
return {
title: "داروخانه دکتر ارجمند | صفحه اصلی",
description:
"خرید آنلاین دارو، مکمل، محصولات بهداشتی و آرایشی از داروخانه دکتر ارجمند در قم، سالاریه.",
...(other ? { other } : {}),
};
}
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const [logoSrc, categories] = await Promise.all([Promise.resolve(getLogoSrc()), getNavCategories()]);
const [logoSrc, categories, businessInfo] = await Promise.all([
Promise.resolve(getLogoSrc()),
getNavCategories(),
getBusinessInfo(),
]);
return (
<html lang="fa" dir="rtl" className={vazir.variable}>
@@ -37,7 +48,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
<TopBar />
<Header logoSrc={logoSrc} categories={categories} />
<main className="pb-12">{children}</main>
<Footer logoSrc={logoSrc} />
<Footer logoSrc={logoSrc} trustBadges={businessInfo?.trustBadges} />
</body>
</html>
);
+20 -9
View File
@@ -3,8 +3,16 @@ import { Instagram, MapPin, Phone, Send } from "lucide-react";
import { Container } from "@/components/Container";
import { Placeholder } from "@/components/Placeholder";
import { footerLinks, site } from "@/lib/data";
import type { BusinessTrustBadge } from "@/lib/meshkee";
export function Footer({ logoSrc }: { logoSrc: string }) {
export function Footer({
logoSrc,
trustBadges = [],
}: {
logoSrc: string;
trustBadges?: BusinessTrustBadge[];
}) {
const badges = trustBadges.filter((badge) => badge.embedHtml?.trim());
return (
<footer id="contact" className="mt-6 bg-navy-deep text-white">
<Container className="grid gap-10 py-12 sm:grid-cols-2 lg:grid-cols-4">
@@ -60,14 +68,17 @@ export function Footer({ logoSrc }: { logoSrc: string }) {
<Phone className="size-4" />
{site.phone}
</a>
<div className="mt-5 flex gap-3">
<span className="overflow-hidden rounded-lg bg-white">
<Placeholder src="/placeholders/enamad.svg" className="size-16 object-cover" />
</span>
<span className="overflow-hidden rounded-lg bg-white">
<Placeholder src="/placeholders/samandehi.svg" className="size-16 object-cover" />
</span>
</div>
{badges.length > 0 ? (
<div className="mt-5 flex flex-wrap items-center gap-3">
{badges.map((badge) => (
<div
key={badge.kind}
className="trust-badge overflow-hidden rounded-lg bg-white p-1 [&_img]:block [&_img]:h-16 [&_img]:w-auto"
dangerouslySetInnerHTML={{ __html: badge.embedHtml }}
/>
))}
</div>
) : null}
</div>
</Container>
<div className="border-t border-white/10">
+29
View File
@@ -26,3 +26,32 @@ export async function getBusinessId() {
const tenant = await getTenant();
return tenant?.id != null ? String(tenant.id) : "";
}
export type BusinessMetaTag = {
name: string;
content: string;
};
export type BusinessTrustBadge = {
kind: "enamad" | "samandehi";
embedHtml: string;
};
export type BusinessInfo = {
metaTags?: BusinessMetaTag[];
trustBadges?: BusinessTrustBadge[];
};
export async function getBusinessInfo() {
return meshkeeGet<BusinessInfo | null>(tenantPath("/website/business-info"), null);
}
export function metaTagsToOther(tags: BusinessMetaTag[] | undefined) {
const other: Record<string, string> = {};
for (const tag of tags ?? []) {
const name = tag.name?.trim();
const content = tag.content?.trim();
if (name && content) other[name] = content;
}
return Object.keys(other).length > 0 ? other : undefined;
}
-6
View File
@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="90" height="90" viewBox="0 0 90 90">
<rect width="90" height="90" fill="#F3F7FF"/>
<rect x="34.1" y="36.9" width="21.9" height="16.2" rx="6.0" fill="none" stroke="#2261FE" stroke-width="2.0" opacity="0.35"/>
<circle cx="45.0" cy="46.0" r="2.9" fill="none" stroke="#2261FE" stroke-width="2.0" opacity="0.35"/>
<circle cx="38.9" cy="41.4" r="1.0" fill="#2261FE" opacity="0.28"/>
</svg>

Before

Width:  |  Height:  |  Size: 439 B

-6
View File
@@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="90" height="90" viewBox="0 0 90 90">
<rect width="90" height="90" fill="#F3F7FF"/>
<rect x="34.1" y="36.9" width="21.9" height="16.2" rx="6.0" fill="none" stroke="#2261FE" stroke-width="2.0" opacity="0.35"/>
<circle cx="45.0" cy="46.0" r="2.9" fill="none" stroke="#2261FE" stroke-width="2.0" opacity="0.35"/>
<circle cx="38.9" cy="41.4" r="1.0" fill="#2261FE" opacity="0.28"/>
</svg>

Before

Width:  |  Height:  |  Size: 439 B