From 2a835d9875ce7f2d8bef3faf3df6c4e24f7eea75 Mon Sep 17 00:00:00 2001 From: Tero Halla-aho Date: Thu, 27 Nov 2025 11:21:46 +0200 Subject: [PATCH] Add About and Pricing pages with footer links --- AGENTS.md | 6 ++++ PROGRESS.md | 1 + app/about/page.tsx | 58 +++++++++++++++++++++++++++++++++++ app/components/SiteFooter.tsx | 24 +++++++++++++++ app/layout.tsx | 14 ++------- app/page.tsx | 45 --------------------------- app/pricing/page.tsx | 58 +++++++++++++++++++++++++++++++++++ lib/i18n.ts | 34 ++++++++++++++++++++ 8 files changed, 183 insertions(+), 57 deletions(-) create mode 100644 AGENTS.md create mode 100644 app/about/page.tsx create mode 100644 app/components/SiteFooter.tsx create mode 100644 app/pricing/page.tsx diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..604cd53 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,6 @@ +# Agent kickoff notes + +- Assume full access to network, shell commands, and all files under the current working directory. +- On session start, open `PROGRESS.md` to refresh project status. +- After that, scan the repo structure (e.g., list key dirs/files) to regain context before continuing work. +- After finishing each new feature, create a git commit with a sensible message and update documentation to reflect the changes. diff --git a/PROGRESS.md b/PROGRESS.md index c5e0d70..e1dcdc0 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -61,6 +61,7 @@ - Listing creation amenities UI improved with toggle cards and EV button group. - Mermaid docs fixed: all sequence diagrams declare their participants and avoid “->” inside message text; the listing creation diagram message was rewritten to prevent parse errors. Use mermaid.live or browser console to debug future syntax issues (errors flag the offending line/column). - New amenities added: kitchen, dishwasher, washing machine, barbecue; API/UI/i18n updated and seeds randomized to populate missing prices/amenities. Prisma migration `20250210_more_amenities` applied to shared DB; registry pull secret added to k8s Deployment to avoid image pull errors in prod. +- Added About and Pricing pages (FI/EN), moved highlights/runtime config to About, and linked footer navigation. To resume: 1) If desired, render diagrams locally: PlantUML in `docs/plantuml`, draw.io in `docs/drawio`. diff --git a/app/about/page.tsx b/app/about/page.tsx new file mode 100644 index 0000000..613e4dd --- /dev/null +++ b/app/about/page.tsx @@ -0,0 +1,58 @@ +'use client'; + +import Link from 'next/link'; +import { useI18n } from '../components/I18nProvider'; + +const highlights = [ + { keyTitle: 'highlightQualityTitle', keyBody: 'highlightQualityBody' }, + { keyTitle: 'highlightLocalTitle', keyBody: 'highlightLocalBody' }, + { keyTitle: 'highlightApiTitle', keyBody: 'highlightApiBody' }, +]; + +export default function AboutPage() { + const { t } = useI18n(); + const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'; + const apiBase = process.env.NEXT_PUBLIC_API_BASE || 'http://localhost:3000/api'; + const appEnv = process.env.APP_ENV || 'local'; + const appVersion = process.env.NEXT_PUBLIC_VERSION || 'dev'; + + return ( +
+
+
+ {t('homeCrumb')} / {t('aboutTitle')} +
+

{t('aboutTitle')}

+

{t('aboutLead')}

+
+ +
+ {highlights.map((item) => ( +
+

{t(item.keyTitle as any)}

+

{t(item.keyBody as any)}

+
+ ))} +
+ +
+

{t('runtimeConfigTitle')}

+

{t('runtimeConfigLead')}

+
+ + {t('runtimeAppEnv')} {appEnv} + + + {t('runtimeSiteUrl')} {siteUrl} + + + {t('runtimeApiBase')} {apiBase} + + + Version {appVersion} + +
+
+
+ ); +} diff --git a/app/components/SiteFooter.tsx b/app/components/SiteFooter.tsx new file mode 100644 index 0000000..3339a86 --- /dev/null +++ b/app/components/SiteFooter.tsx @@ -0,0 +1,24 @@ +'use client'; + +import Link from 'next/link'; +import { useI18n } from './I18nProvider'; + +export default function SiteFooter() { + const { t } = useI18n(); + const version = process.env.NEXT_PUBLIC_VERSION || 'dev'; + + return ( + + ); +} diff --git a/app/layout.tsx b/app/layout.tsx index a11f01d..ff11f12 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from 'next'; import './globals.css'; import NavBar from './components/NavBar'; import { I18nProvider } from './components/I18nProvider'; +import SiteFooter from './components/SiteFooter'; export const metadata: Metadata = { title: 'Lomavuokraus.fi', @@ -13,24 +14,13 @@ export default function RootLayout({ }: { children: React.ReactNode; }) { - const version = process.env.NEXT_PUBLIC_VERSION || 'dev'; - return ( {children} - + diff --git a/app/page.tsx b/app/page.tsx index 115fb60..3f00ce0 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -18,27 +18,8 @@ type LatestListing = { export const dynamic = 'force-dynamic'; -const highlights = [ - { - keyTitle: 'highlightQualityTitle', - keyBody: 'highlightQualityBody', - }, - { - keyTitle: 'highlightLocalTitle', - keyBody: 'highlightLocalBody', - }, - { - keyTitle: 'highlightApiTitle', - keyBody: 'highlightApiBody', - }, -]; - export default function HomePage() { const { t } = useI18n(); - const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'; - const apiBase = process.env.NEXT_PUBLIC_API_BASE || 'http://localhost:3000/api'; - const appEnv = process.env.APP_ENV || 'local'; - const appVersion = process.env.NEXT_PUBLIC_VERSION || 'dev'; const [latest, setLatest] = useState([]); const [activeIndex, setActiveIndex] = useState(0); const [loadingLatest, setLoadingLatest] = useState(false); @@ -91,32 +72,6 @@ export default function HomePage() { -
- {highlights.map((item) => ( -
-

{t(item.keyTitle as any)}

-

{t(item.keyBody as any)}

-
- ))} -
-

{t('runtimeConfigTitle')}

-
- - {t('runtimeAppEnv')} {appEnv} - - - {t('runtimeSiteUrl')} {siteUrl} - - - {t('runtimeApiBase')} {apiBase} - - - Version {appVersion} - -
-
-
-
diff --git a/app/pricing/page.tsx b/app/pricing/page.tsx new file mode 100644 index 0000000..9fdc270 --- /dev/null +++ b/app/pricing/page.tsx @@ -0,0 +1,58 @@ +'use client'; + +import Link from 'next/link'; +import { useI18n } from '../components/I18nProvider'; + +const pricing = [ + { + keyTitle: 'pricingMonthly', + price: '10€', + interval: 'pricingPerMonth', + keyBody: 'pricingMonthlyBody', + }, + { + keyTitle: 'pricingAnnual', + price: '100€', + interval: 'pricingPerYear', + keyBody: 'pricingAnnualBody', + }, +]; + +export default function PricingPage() { + const { t } = useI18n(); + + return ( +
+
+
+ {t('homeCrumb')} / {t('pricingTitle')} +
+

{t('pricingTitle')}

+

{t('pricingLead')}

+
+ +
+ {pricing.map((item) => ( +
+
+

+ {t(item.keyTitle as any)} +

+
+
{item.price}
+
{t(item.interval as any)}
+
+
+

{t(item.keyBody as any)}

+

{t('pricingPerListing')}

+
+ ))} +
+ +
+

{t('pricingNotesTitle')}

+

{t('pricingNotesBody')}

+
+
+ ); +} diff --git a/lib/i18n.ts b/lib/i18n.ts index 7064c5b..b0e5aeb 100644 --- a/lib/i18n.ts +++ b/lib/i18n.ts @@ -27,9 +27,26 @@ const allMessages = { highlightApiTitle: 'API-friendly', highlightApiBody: 'Structured data so you can surface listings wherever you need them.', runtimeConfigTitle: 'Runtime configuration', + runtimeConfigLead: 'Build-time and runtime values used by the service.', runtimeAppEnv: 'APP_ENV', runtimeSiteUrl: 'NEXT_PUBLIC_SITE_URL', runtimeApiBase: 'NEXT_PUBLIC_API_BASE', + aboutTitle: 'About lomavuokraus.fi', + aboutLead: 'A focused marketplace for Finnish holiday rentals with fast browsing, clear moderation, and a simple host experience.', + pricingTitle: 'Pricing', + pricingLead: 'Straightforward pricing for hosts with no surprises.', + pricingMonthly: 'Monthly', + pricingAnnual: 'Annual', + pricingPerMonth: 'per month', + pricingPerYear: 'per year (save 20%)', + pricingMonthlyBody: 'Start with a monthly plan at 10€ per listing.', + pricingAnnualBody: 'Pay annually for 100€ per listing and keep your costs predictable.', + pricingPerListing: 'Pricing is per active listing. Cancel anytime.', + pricingNotesTitle: 'Notes', + pricingNotesBody: 'We keep pricing simple while we build out hosting tools, messaging, and integrations. All current features are included.', + footerAbout: 'About', + footerPricing: 'Pricing', + footerPrivacy: 'Privacy & cookies', loginTitle: 'Login', emailLabel: 'Email', passwordLabel: 'Password', @@ -244,9 +261,26 @@ const allMessages = { highlightApiTitle: 'API-ystävällinen', highlightApiBody: 'Strukturoitu data, jotta löydät kohteet kaikissa kanavissa.', runtimeConfigTitle: 'Ajoaikainen konfiguraatio', + runtimeConfigLead: 'Ajo- ja rakennusaikaiset arvot, joita palvelu käyttää.', runtimeAppEnv: 'APP_ENV', runtimeSiteUrl: 'NEXT_PUBLIC_SITE_URL', runtimeApiBase: 'NEXT_PUBLIC_API_BASE', + aboutTitle: 'Tietoja lomavuokraus.fi:stä', + aboutLead: 'Keskittynyt suomalainen loma-asuntopalvelu: nopea selaus, selkeä moderointi ja mutkaton kokemus vuokraajalle.', + pricingTitle: 'Hinnasto', + pricingLead: 'Selkeä hinnoittelu vuokraajille ilman yllätyksiä.', + pricingMonthly: 'Kuukausi', + pricingAnnual: 'Vuosimaksu', + pricingPerMonth: 'kuukaudessa', + pricingPerYear: 'vuodessa (20 % säästö)', + pricingMonthlyBody: 'Aloita kuukausihinnalla 10 € per kohde.', + pricingAnnualBody: 'Maksa vuodessa 100 € per kohde ja pidä kulut ennustettavina.', + pricingPerListing: 'Hinta on per aktiivinen kohde. Voit perua milloin vain.', + pricingNotesTitle: 'Huomiot', + pricingNotesBody: 'Pidämme hinnoittelun yksinkertaisena samalla kun rakennamme uusia isäntätyökaluja, viestejä ja integraatioita. Kaikki nykyiset ominaisuudet sisältyvät.', + footerAbout: 'Tietoa', + footerPricing: 'Hinnasto', + footerPrivacy: 'Tietosuoja ja evästeet', loginTitle: 'Kirjaudu sisään', emailLabel: 'Sähköposti', passwordLabel: 'Salasana',