Files
hope-events/frontend/src/app/layout.tsx
T
joshuaandClaude Sonnet 5 bdae0c6b08 Fix org name not showing in tab title, homepage, and backend pages
The browser tab title, homepage "Welcome to..." heading, and the
backend status/API docs pages all had the "Cross Code" default
hardcoded instead of reading the configured org_name setting.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 21:09:35 +02:00

87 lines
3.0 KiB
TypeScript

import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { AuthProvider } from "@/contexts/AuthContext";
import { SiteSettingsProvider } from "@/contexts/SiteSettingsContext";
import type { SiteSettings } from "@/contexts/SiteSettingsContext";
import { ToastProvider } from "@/components/shared/ToastProvider";
import { BannerBar } from "@/components/shared/BannerBar";
import { SetupGuard } from "@/components/shared/SetupGuard";
import HelpFab from "@/components/shared/HelpFab";
import { API_BASE, resolveToApiOrigin } from "@/lib/api";
import { buildThemeCssVars } from "@/lib/colorScale";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
const appName = process.env.NEXT_PUBLIC_APP_NAME || 'Cross Code';
/**
* Server-side settings fetch, shared by generateMetadata() and RootLayout()
* below. Both call this with the identical URL + options so Next's per-request
* fetch memoization dedupes them into a single network call. Degrades to `{}`
* (static defaults everywhere downstream) if the backend is unreachable at
* request time — intentional, not a bug: branding just isn't critical enough
* to fail the whole page render over.
*/
async function getServerSettings(): Promise<SiteSettings> {
try {
const res = await fetch(`${API_BASE}/api/settings`, { next: { revalidate: 60 } });
if (!res.ok) return {};
return await res.json();
} catch {
return {};
}
}
export async function generateMetadata(): Promise<Metadata> {
const settings = await getServerSettings();
const faviconUrl = settings.favicon_url ? resolveToApiOrigin(settings.favicon_url) : null;
const displayName = settings.org_name || appName;
return {
title: displayName,
description: `Manage and register for events with ${displayName}`,
icons: {
icon: faviconUrl || "/favicon.ico",
},
};
}
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const settings = await getServerSettings();
const cssVars = buildThemeCssVars({
primary: settings.primary_color || settings.accent_color,
secondary: settings.secondary_color,
accent: settings.primary_color ? settings.accent_color : undefined,
});
return (
<html lang="en">
<body>
{cssVars && (
<style id="brand-theme" dangerouslySetInnerHTML={{ __html: `:root{${cssVars}}` }} />
)}
<SiteSettingsProvider initialSettings={settings}>
<AuthProvider>
<ToastProvider>
<SetupGuard />
<BannerBar />
{children}
<HelpFab />
</ToastProvider>
</AuthProvider>
</SiteSettingsProvider>
</body>
</html>
);
}