mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
Re-applies the original SEO work from PR #47 that was reverted in PR #54 because its opengraph-image.tsx crashed `next build` during static prerender of /opengraph-image. Satori (next/og's renderer) errored with: Error: Expected <div> to have explicit "display: flex", "display: contents", or "display: none" if it has more than one child node. Failed to load dynamic font for ✓ . Status: 400 Root cause in the OG image JSX: - Several <div>s held text + a sibling element (e.g. "Run your WhatsApp business from <span>one inbox.</span>") without an explicit display value. Satori requires it whenever there is more than one child. - The ✓ Unicode glyph isn't covered by the default font Satori loads, so next/og tried to download a dynamic font and failed. Fix: - Every <div> with more than one child now has display: flex. - Multi-span headline sits in a flex-wrap row of two <span>s. - ✓ replaced with plain text dot separators in the feature row. Verified end-to-end: `npm run build` now completes with 27/27 pages prerendered, /opengraph-image among them. All the other SEO-pass content is identical to the originally merged-and-reverted work: metadataBase + title template, OpenGraph + Twitter Cards defaults, JSON-LD (WebSite + Organization + SoftwareApplication + FAQPage), sitemap.ts, robots.ts, noindex on auth + dashboard route-group layouts, shared faq-data + site-config modules. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
112 lines
2.6 KiB
TypeScript
112 lines
2.6 KiB
TypeScript
import type { Metadata, Viewport } from "next";
|
|
import { Inter } from "next/font/google";
|
|
import { Toaster } from "sonner";
|
|
import "./globals.css";
|
|
import {
|
|
OG_IMAGE_ALT,
|
|
SITE_DESCRIPTION,
|
|
SITE_KEYWORDS,
|
|
SITE_NAME,
|
|
SITE_TAGLINE,
|
|
SITE_URL,
|
|
} from "@/lib/seo/site-config";
|
|
|
|
const inter = Inter({
|
|
variable: "--font-sans",
|
|
subsets: ["latin"],
|
|
});
|
|
|
|
// Consolidated site-wide metadata. Page-specific metadata (landing,
|
|
// auth pages, etc.) override the narrower fields (title, description,
|
|
// robots); defaults defined here are inherited everywhere else so we
|
|
// never ship a page without OpenGraph / Twitter coverage.
|
|
export const metadata: Metadata = {
|
|
metadataBase: new URL(SITE_URL),
|
|
title: {
|
|
default: `${SITE_NAME} — ${SITE_TAGLINE}`,
|
|
template: `%s — ${SITE_NAME}`,
|
|
},
|
|
description: SITE_DESCRIPTION,
|
|
keywords: SITE_KEYWORDS,
|
|
applicationName: SITE_NAME,
|
|
authors: [{ name: SITE_NAME, url: SITE_URL }],
|
|
creator: SITE_NAME,
|
|
publisher: SITE_NAME,
|
|
category: "Business Software",
|
|
alternates: {
|
|
canonical: "/",
|
|
},
|
|
openGraph: {
|
|
type: "website",
|
|
siteName: SITE_NAME,
|
|
title: `${SITE_NAME} — ${SITE_TAGLINE}`,
|
|
description: SITE_DESCRIPTION,
|
|
url: SITE_URL,
|
|
locale: "en_US",
|
|
images: [
|
|
{
|
|
url: "/opengraph-image",
|
|
width: 1200,
|
|
height: 630,
|
|
alt: OG_IMAGE_ALT,
|
|
},
|
|
],
|
|
},
|
|
twitter: {
|
|
card: "summary_large_image",
|
|
title: `${SITE_NAME} — ${SITE_TAGLINE}`,
|
|
description: SITE_DESCRIPTION,
|
|
images: ["/opengraph-image"],
|
|
},
|
|
robots: {
|
|
index: true,
|
|
follow: true,
|
|
googleBot: {
|
|
index: true,
|
|
follow: true,
|
|
"max-image-preview": "large",
|
|
"max-snippet": -1,
|
|
},
|
|
},
|
|
icons: {
|
|
icon: [{ url: "/icon" }],
|
|
shortcut: ["/favicon.ico"],
|
|
},
|
|
formatDetection: {
|
|
email: false,
|
|
address: false,
|
|
telephone: false,
|
|
},
|
|
};
|
|
|
|
// Dark theme color for the mobile browser chrome.
|
|
export const viewport: Viewport = {
|
|
themeColor: "#020617",
|
|
colorScheme: "dark",
|
|
};
|
|
|
|
export default function RootLayout({
|
|
children,
|
|
}: Readonly<{
|
|
children: React.ReactNode;
|
|
}>) {
|
|
return (
|
|
<html lang="en" className={`${inter.variable} h-full antialiased`}>
|
|
<body className="min-h-full bg-slate-950 text-white font-sans">
|
|
{children}
|
|
<Toaster
|
|
theme="dark"
|
|
position="top-right"
|
|
toastOptions={{
|
|
style: {
|
|
background: "rgb(30 41 59)",
|
|
border: "1px solid rgb(51 65 85)",
|
|
color: "white",
|
|
},
|
|
}}
|
|
/>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|