Files
einfach-produktiv/app/layout.tsx
T
Marco 028a1fc4ec Add active-product-count-driven automation
- 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.
2026-07-21 20:39:22 +00:00

75 lines
2.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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>
);
}