This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
// The root layout: the document, the brand layer and the app shell (spec §10.2).
|
||||
//
|
||||
// The brand's `--brand-*` declarations are written as a style attribute on <html> (src/ui/document.tsx),
|
||||
// which is the layer a tenant brand overrides at runtime without any stylesheet being rebuilt.
|
||||
|
||||
import { appBrand, publicUrlOf } from "@app/config"
|
||||
import { ACCOUNT_PATH } from "@app/modules/account/contract"
|
||||
import type { Metadata, Viewport } from "next"
|
||||
import type { ReactNode } from "react"
|
||||
import { pageContext } from "../src/server/page"
|
||||
import { BoundaryMessages } from "../src/ui/boundary"
|
||||
import { brandMetadata, Document } from "../src/ui/document"
|
||||
import { LanguageSwitch, ThemeToggle } from "../src/ui/shell-controls"
|
||||
import { SignOut } from "../src/ui/sign-out"
|
||||
import "./globals.css"
|
||||
|
||||
/**
|
||||
* The app's own name and share image. `metadataBase` makes the image's address absolute, as a link preview needs, from
|
||||
* the public URL the deployment states; it is read when the page is asked for, never at build (one image, every environment).
|
||||
*/
|
||||
export function generateMetadata(): Metadata {
|
||||
return {
|
||||
...brandMetadata(appBrand),
|
||||
metadataBase: new URL(publicUrlOf()),
|
||||
}
|
||||
}
|
||||
|
||||
export const viewport: Viewport = { width: "device-width", initialScale: 1 }
|
||||
|
||||
export default async function RootLayout({ children }: { children: ReactNode }): Promise<ReactNode> {
|
||||
const { i18n, signedIn, csrf, theme, observe } = await pageContext()
|
||||
const message = (key: string): string => i18n.message(key)
|
||||
return (
|
||||
<Document
|
||||
brand={appBrand}
|
||||
locale={i18n.locale}
|
||||
theme={theme}
|
||||
signedIn={signedIn}
|
||||
{...(csrf ? { csrf } : {})}
|
||||
{...(observe ? { observe } : {})}
|
||||
signIn={message("app.signIn")}
|
||||
signOut={
|
||||
<>
|
||||
<a href={ACCOUNT_PATH}>{message("account.link")}</a>
|
||||
<SignOut label={message("app.signOut")} />
|
||||
</>
|
||||
}
|
||||
controls={
|
||||
<>
|
||||
<LanguageSwitch
|
||||
current={i18n.locale}
|
||||
signedIn={signedIn}
|
||||
label={message("shell.language")}
|
||||
options={[
|
||||
{ value: "en", label: message("shell.languageEn") },
|
||||
{ value: "lt", label: message("shell.languageLt") },
|
||||
]}
|
||||
failedTemplate={message("app.failed")}
|
||||
/>
|
||||
<ThemeToggle
|
||||
initial={theme}
|
||||
label={message("shell.theme")}
|
||||
darkLabel={message("shell.themeDark")}
|
||||
lightLabel={message("shell.themeLight")}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<BoundaryMessages
|
||||
strings={{
|
||||
title: message("boundary.errorTitle"),
|
||||
body: message("boundary.errorBody"),
|
||||
code: message("boundary.errorCode"),
|
||||
retry: message("boundary.retry"),
|
||||
home: message("boundary.home"),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</BoundaryMessages>
|
||||
</Document>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user