feat(web): complete MVP0 public layout, banner, and SEO baseline
This commit is contained in:
13
apps/web/src/app/[locale]/about/page.tsx
Normal file
13
apps/web/src/app/[locale]/about/page.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { getTranslations } from "next-intl/server"
|
||||
|
||||
export default async function AboutPage() {
|
||||
const t = await getTranslations("About")
|
||||
|
||||
return (
|
||||
<section className="mx-auto w-full max-w-6xl space-y-4 px-6 py-16">
|
||||
<p className="text-sm uppercase tracking-[0.2em] text-neutral-500">{t("badge")}</p>
|
||||
<h1 className="text-4xl font-semibold tracking-tight">{t("title")}</h1>
|
||||
<p className="max-w-3xl text-neutral-600">{t("description")}</p>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
13
apps/web/src/app/[locale]/contact/page.tsx
Normal file
13
apps/web/src/app/[locale]/contact/page.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { getTranslations } from "next-intl/server"
|
||||
|
||||
export default async function ContactPage() {
|
||||
const t = await getTranslations("Contact")
|
||||
|
||||
return (
|
||||
<section className="mx-auto w-full max-w-6xl space-y-4 px-6 py-16">
|
||||
<p className="text-sm uppercase tracking-[0.2em] text-neutral-500">{t("badge")}</p>
|
||||
<h1 className="text-4xl font-semibold tracking-tight">{t("title")}</h1>
|
||||
<p className="max-w-3xl text-neutral-600">{t("description")}</p>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
import { getPublicHeaderBanner } from "@cms/db"
|
||||
import { notFound } from "next/navigation"
|
||||
import { hasLocale, NextIntlClientProvider } from "next-intl"
|
||||
import { getTranslations } from "next-intl/server"
|
||||
import type { ReactNode } from "react"
|
||||
|
||||
import { PublicHeaderBanner } from "@/components/public-header-banner"
|
||||
import { PublicSiteFooter } from "@/components/public-site-footer"
|
||||
import { PublicSiteHeader } from "@/components/public-site-header"
|
||||
import { routing } from "@/i18n/routing"
|
||||
import { Providers } from "../providers"
|
||||
|
||||
@@ -12,6 +17,28 @@ type LocaleLayoutProps = {
|
||||
}>
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: LocaleLayoutProps) {
|
||||
const { locale } = await params
|
||||
|
||||
if (!hasLocale(routing.locales, locale)) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const t = await getTranslations({
|
||||
locale,
|
||||
namespace: "Seo",
|
||||
})
|
||||
|
||||
return {
|
||||
title: t("title"),
|
||||
description: t("description"),
|
||||
openGraph: {
|
||||
title: t("title"),
|
||||
description: t("description"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default async function LocaleLayout({ children, params }: LocaleLayoutProps) {
|
||||
const { locale } = await params
|
||||
|
||||
@@ -19,9 +46,16 @@ export default async function LocaleLayout({ children, params }: LocaleLayoutPro
|
||||
notFound()
|
||||
}
|
||||
|
||||
const banner = await getPublicHeaderBanner()
|
||||
|
||||
return (
|
||||
<NextIntlClientProvider locale={locale}>
|
||||
<Providers>{children}</Providers>
|
||||
<Providers>
|
||||
<PublicHeaderBanner banner={banner} />
|
||||
<PublicSiteHeader />
|
||||
<main>{children}</main>
|
||||
<PublicSiteFooter />
|
||||
</Providers>
|
||||
</NextIntlClientProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,20 +2,15 @@ import { listPosts } from "@cms/db"
|
||||
import { Button } from "@cms/ui/button"
|
||||
import { getTranslations } from "next-intl/server"
|
||||
|
||||
import { LanguageSwitcher } from "@/components/language-switcher"
|
||||
|
||||
export const dynamic = "force-dynamic"
|
||||
|
||||
export default async function HomePage() {
|
||||
const [posts, t] = await Promise.all([listPosts(), getTranslations("Home")])
|
||||
|
||||
return (
|
||||
<main className="mx-auto flex min-h-screen w-full max-w-3xl flex-col gap-6 px-6 py-16">
|
||||
<section className="mx-auto flex w-full max-w-6xl flex-col gap-6 px-6 py-16">
|
||||
<header className="space-y-3">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<p className="text-sm uppercase tracking-[0.2em] text-neutral-500">{t("badge")}</p>
|
||||
<LanguageSwitcher />
|
||||
</div>
|
||||
<p className="text-sm uppercase tracking-[0.2em] text-neutral-500">{t("badge")}</p>
|
||||
<h1 className="text-4xl font-semibold tracking-tight">{t("title")}</h1>
|
||||
<p className="text-neutral-600">{t("description")}</p>
|
||||
</header>
|
||||
@@ -36,6 +31,6 @@ export default async function HomePage() {
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user