From 4e58794c50aa2e06a5d832d057016e4326a4cd25 Mon Sep 17 00:00:00 2001 From: Chris Dumas Date: Fri, 22 May 2026 15:14:35 +0000 Subject: [PATCH] Build public home, contact, and content pages --- src/app/[slug]/page.tsx | 108 ++++++++++++ src/app/contact/page.tsx | 92 +++++++++++ src/app/globals.scss | 263 ++++++++++++++++++++++++++++- src/app/page.tsx | 284 +++++++++++++++++++++----------- src/components/SiteFooter.tsx | 14 +- src/components/SiteHeader.tsx | 21 +-- src/lib/site.ts | 220 ++++++++++++++++++++++++- tests/e2e/contact.spec.ts | 13 ++ tests/e2e/content-pages.spec.ts | 11 ++ tests/e2e/home.spec.ts | 18 +- tests/e2e/responsive.spec.ts | 7 +- 11 files changed, 911 insertions(+), 140 deletions(-) create mode 100644 src/app/[slug]/page.tsx create mode 100644 src/app/contact/page.tsx create mode 100644 tests/e2e/contact.spec.ts create mode 100644 tests/e2e/content-pages.spec.ts diff --git a/src/app/[slug]/page.tsx b/src/app/[slug]/page.tsx new file mode 100644 index 0000000..8c6752d --- /dev/null +++ b/src/app/[slug]/page.tsx @@ -0,0 +1,108 @@ +import type { Metadata } from 'next'; +import Link from 'next/link'; +import { notFound } from 'next/navigation'; +import { Section } from '@/components/Section'; +import { contentPages, getContentPage, site } from '@/lib/site'; + +type ContentPageProps = { + params: Promise<{ + slug: string; + }>; +}; + +export function generateStaticParams() { + return contentPages.map((page) => ({ slug: page.slug })); +} + +export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise { + const { slug } = await params; + const page = getContentPage(slug); + + if (!page) { + return { + title: site.name, + description: site.description, + }; + } + + return { + title: page.seoTitle, + description: page.seoDescription, + }; +} + +export default async function ContentPage({ params }: ContentPageProps) { + const { slug } = await params; + const page = getContentPage(slug); + + if (!page) { + notFound(); + } + + return ( + <> +
+

Content page

+

{page.title}

+

{page.intro}

+
+ +
+
+
+ {page.sections.map((section) => ( +
+

{section.title}

+ {section.paragraphs.map((paragraph) => ( +

{paragraph}

+ ))} + {section.bullets ? ( +
    + {section.bullets.map((bullet) => ( +
  • {bullet}
  • + ))} +
+ ) : null} +
+ ))} +
+
+ + +
+ + ); +} diff --git a/src/app/contact/page.tsx b/src/app/contact/page.tsx new file mode 100644 index 0000000..8484c13 --- /dev/null +++ b/src/app/contact/page.tsx @@ -0,0 +1,92 @@ +import type { Metadata } from 'next'; +import Link from 'next/link'; +import { Section } from '@/components/Section'; +import { site } from '@/lib/site'; + +export const metadata: Metadata = { + title: `Contact | ${site.name}`, + description: 'Get in touch about a holiday stay, a booking question, or a property enquiry.', +}; + +const reasons = [ + 'Ask about a property before the booking engine is live', + 'Request more details about a location or house rules', + 'Raise a special requirement or accessibility question', +]; + +export default function ContactPage() { + return ( + <> +
+

Contact

+

Talk to the team before you book

+

+ The contact page gives guests a direct path to the business with the core information they need in one place. +

+
+ +
+
+
+ + + +