Place CMS meta tag HTML in the document head.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
b4d6b30243
commit
9bf26705da
+1
-1
@@ -65,7 +65,7 @@ Homepage images: `GET /tenants/{domain}/website/static-images?pageKey=home`. Cat
|
|||||||
|
|
||||||
`GET /tenants/{domain}/website/business-info`
|
`GET /tenants/{domain}/website/business-info`
|
||||||
|
|
||||||
- `metaTags`: `[{ name, content }]` emitted once from root `generateMetadata` as `other`. Empty → omit.
|
- `metaTags`: `[{ html }]` — each full `<meta …>` tag is placed in the root `<head>`. Empty → omit.
|
||||||
- `trustBadges`: `[{ kind: "enamad" | "samandehi", embedHtml }]` rendered in the footer with `dangerouslySetInnerHTML`. Empty → omit. Do not hardcode eNamad or Samandehi placeholders.
|
- `trustBadges`: `[{ kind: "enamad" | "samandehi", embedHtml }]` rendered in the footer with `dangerouslySetInnerHTML`. Empty → omit. Do not hardcode eNamad or Samandehi placeholders.
|
||||||
|
|
||||||
## Local fallbacks
|
## Local fallbacks
|
||||||
|
|||||||
+13
-12
@@ -6,7 +6,7 @@ import { Header } from "@/components/layout/Header";
|
|||||||
import { Footer } from "@/components/layout/Footer";
|
import { Footer } from "@/components/layout/Footer";
|
||||||
import { getNavCategories } from "@/lib/categories";
|
import { getNavCategories } from "@/lib/categories";
|
||||||
import { getLogoSrc } from "@/lib/media";
|
import { getLogoSrc } from "@/lib/media";
|
||||||
import { getBusinessInfo, metaTagsToOther } from "@/lib/meshkee";
|
import { getBusinessInfo, parseMetaTagHtml } from "@/lib/meshkee";
|
||||||
|
|
||||||
const vazir = Vazirmatn({
|
const vazir = Vazirmatn({
|
||||||
subsets: ["arabic", "latin"],
|
subsets: ["arabic", "latin"],
|
||||||
@@ -15,17 +15,11 @@ const vazir = Vazirmatn({
|
|||||||
weight: ["300", "400", "500", "600", "700", "800"],
|
weight: ["300", "400", "500", "600", "700", "800"],
|
||||||
});
|
});
|
||||||
|
|
||||||
export async function generateMetadata(): Promise<Metadata> {
|
export const metadata: Metadata = {
|
||||||
const businessInfo = await getBusinessInfo();
|
title: "داروخانه دکتر ارجمند | صفحه اصلی",
|
||||||
const other = metaTagsToOther(businessInfo?.metaTags);
|
description:
|
||||||
|
"خرید آنلاین دارو، مکمل، محصولات بهداشتی و آرایشی از داروخانه دکتر ارجمند در قم، سالاریه.",
|
||||||
return {
|
};
|
||||||
title: "داروخانه دکتر ارجمند | صفحه اصلی",
|
|
||||||
description:
|
|
||||||
"خرید آنلاین دارو، مکمل، محصولات بهداشتی و آرایشی از داروخانه دکتر ارجمند در قم، سالاریه.",
|
|
||||||
...(other ? { other } : {}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
||||||
const [logoSrc, categories, businessInfo] = await Promise.all([
|
const [logoSrc, categories, businessInfo] = await Promise.all([
|
||||||
@@ -34,9 +28,16 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
|||||||
getBusinessInfo(),
|
getBusinessInfo(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const cmsMetaTags = (businessInfo?.metaTags ?? []).flatMap((tag, index) => {
|
||||||
|
const attrs = parseMetaTagHtml(tag.html ?? "");
|
||||||
|
if (!attrs) return [];
|
||||||
|
return [<meta key={`cms-meta-${index}`} {...attrs} />];
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="fa" dir="rtl" className={vazir.variable}>
|
<html lang="fa" dir="rtl" className={vazir.variable}>
|
||||||
<head>
|
<head>
|
||||||
|
{cmsMetaTags}
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
||||||
<link
|
<link
|
||||||
|
|||||||
+17
-12
@@ -27,11 +27,26 @@ export async function getBusinessId() {
|
|||||||
return tenant?.id != null ? String(tenant.id) : "";
|
return tenant?.id != null ? String(tenant.id) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Full sanitized `<meta …>` HTML from business-info. */
|
||||||
export type BusinessMetaTag = {
|
export type BusinessMetaTag = {
|
||||||
name: string;
|
html: string;
|
||||||
content: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Parse a single `<meta …>` tag into React attribute props for the document head. */
|
||||||
|
export function parseMetaTagHtml(html: string): Record<string, string> | null {
|
||||||
|
const match = html.trim().match(/^<meta\b([^>]*)\/?\s*>$/i);
|
||||||
|
if (!match) return null;
|
||||||
|
const attrs: Record<string, string> = {};
|
||||||
|
const attrRe = /([:\w.-]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
|
||||||
|
let found: RegExpExecArray | null;
|
||||||
|
while ((found = attrRe.exec(match[1]))) {
|
||||||
|
const key = found[1].toLowerCase();
|
||||||
|
if (key === "/") continue;
|
||||||
|
attrs[key] = found[2] ?? found[3] ?? found[4] ?? "";
|
||||||
|
}
|
||||||
|
return Object.keys(attrs).length > 0 ? attrs : null;
|
||||||
|
}
|
||||||
|
|
||||||
export type BusinessTrustBadge = {
|
export type BusinessTrustBadge = {
|
||||||
kind: "enamad" | "samandehi";
|
kind: "enamad" | "samandehi";
|
||||||
embedHtml: string;
|
embedHtml: string;
|
||||||
@@ -45,13 +60,3 @@ export type BusinessInfo = {
|
|||||||
export async function getBusinessInfo() {
|
export async function getBusinessInfo() {
|
||||||
return meshkeeGet<BusinessInfo | null>(tenantPath("/website/business-info"), null);
|
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;
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user