028a1fc4ec
- Products gain `active`/`spotlight*`/`updatedAt` on the base Product type (folded in from the now-removed separate SpotlightProduct type) so shop grid, spotlight, and related-products can each filter `.active` from the same already-fetched list — cart/checkout/order-confirmation/product- detail pages keep resolving any product regardless of active status. - getSpotlightProduct() now derives from getProducts() instead of its own Payload query: with exactly 1 active product, that one IS the spotlight (overriding any `spotlight` flag elsewhere); otherwise same most-recently-updated tie-break as before, just computed client-side. - ProductGrid drops the already-dead SHOP_GRID_EXCLUDE_IDS list in favor of the same `active` filter, with an empty-state message if 0 active. - RelatedProducts gates on >=2 active products regardless of cart contents or how many display slots would otherwise resolve. - Navbar's "Shop" link becomes an anchor to the homepage spotlight section (id="spotlight") instead of a real /shop navigation whenever exactly 1 product is active — passed down from the now-async root layout, which fetches the catalog once for this decision.
75 lines
2.0 KiB
TypeScript
75 lines
2.0 KiB
TypeScript
import type { Metadata } from "next";
|
||
import { Inter, Playfair_Display, Caveat, Lora } from "next/font/google";
|
||
import "./globals.css";
|
||
import { Navbar } from "./components/Navbar";
|
||
import { CartFlyProvider } from "./components/CartFly";
|
||
import { getProducts } from "./lib/payload";
|
||
|
||
const inter = Inter({
|
||
variable: "--font-inter",
|
||
subsets: ["latin"],
|
||
weight: ["400", "500", "600", "700"],
|
||
});
|
||
|
||
const playfair = Playfair_Display({
|
||
variable: "--font-playfair",
|
||
subsets: ["latin"],
|
||
weight: ["600", "700", "800"],
|
||
});
|
||
|
||
const caveat = Caveat({
|
||
variable: "--font-caveat",
|
||
subsets: ["latin"],
|
||
weight: ["700"],
|
||
});
|
||
|
||
const lora = Lora({
|
||
variable: "--font-lora",
|
||
subsets: ["latin"],
|
||
weight: ["400", "600"],
|
||
});
|
||
|
||
export const metadata: Metadata = {
|
||
metadataBase: new URL("https://einfach-produktiv.mk360.de"),
|
||
title: {
|
||
default: "einfach produktiv. – Werkzeuge und Impulse für einen leichteren Alltag",
|
||
template: "%s | einfach produktiv.",
|
||
},
|
||
description: "Werkzeuge, Impulse und ein Blog für mehr Klarheit im Alltag.",
|
||
openGraph: {
|
||
siteName: "einfach produktiv.",
|
||
locale: "de_DE",
|
||
type: "website",
|
||
},
|
||
twitter: {
|
||
card: "summary_large_image",
|
||
},
|
||
};
|
||
|
||
export default async function RootLayout({
|
||
children,
|
||
}: Readonly<{
|
||
children: React.ReactNode;
|
||
}>) {
|
||
// Same 60s-ISR-cached call every other page already makes — reused here
|
||
// just to know whether Navbar's "Shop" link should behave as an anchor
|
||
// to the homepage spotlight instead of a real /shop navigation (see
|
||
// Navbar.tsx/ProductSpotlight.tsx).
|
||
const products = await getProducts();
|
||
const singleActiveProduct = products.filter((p) => p.active).length === 1;
|
||
|
||
return (
|
||
<html
|
||
lang="de"
|
||
className={`${inter.variable} ${playfair.variable} ${caveat.variable} ${lora.variable} h-full antialiased scroll-smooth`}
|
||
>
|
||
<body className="min-h-full flex flex-col">
|
||
<CartFlyProvider>
|
||
<Navbar singleActiveProduct={singleActiveProduct} />
|
||
{children}
|
||
</CartFlyProvider>
|
||
</body>
|
||
</html>
|
||
);
|
||
}
|