fix(legal): show legal pages in site language with bidirectional toggle
Wire legal pages to LanguageContext, pass locale on footer/booking links, translate layout chrome, and restore SSR content when switching back to English.
This commit is contained in:
@@ -1,17 +1,74 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import Link from 'next/link';
|
||||
import { ArrowLeftIcon } from '@heroicons/react/24/outline';
|
||||
import { useLanguage } from '@/context/LanguageContext';
|
||||
import { legalPagesApi } from '@/lib/api';
|
||||
|
||||
interface LegalPageLayoutProps {
|
||||
slug: string;
|
||||
initialLocale: 'en' | 'es';
|
||||
title: string;
|
||||
content: string;
|
||||
lastUpdated?: string;
|
||||
}
|
||||
|
||||
export default function LegalPageLayout({ title, content, lastUpdated }: LegalPageLayoutProps) {
|
||||
function extractLastUpdated(contentMarkdown: string, updatedAt?: string): string | undefined {
|
||||
const match = contentMarkdown?.match(/Last updated:\s*(.+)/i);
|
||||
return match ? match[1].trim() : updatedAt;
|
||||
}
|
||||
|
||||
export default function LegalPageLayout({
|
||||
slug,
|
||||
initialLocale,
|
||||
title: initialTitle,
|
||||
content: initialContent,
|
||||
lastUpdated: initialLastUpdated,
|
||||
}: LegalPageLayoutProps) {
|
||||
const { locale, t } = useLanguage();
|
||||
const [title, setTitle] = useState(initialTitle);
|
||||
const [content, setContent] = useState(initialContent);
|
||||
const [lastUpdated, setLastUpdated] = useState(initialLastUpdated);
|
||||
const [loadedLocale, setLoadedLocale] = useState(initialLocale);
|
||||
|
||||
useEffect(() => {
|
||||
if (locale === loadedLocale) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Returning to the server-rendered language: restore SSR content without a fetch
|
||||
if (locale === initialLocale) {
|
||||
setTitle(initialTitle);
|
||||
setContent(initialContent);
|
||||
setLastUpdated(initialLastUpdated);
|
||||
setLoadedLocale(initialLocale);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
legalPagesApi
|
||||
.getBySlug(slug, locale)
|
||||
.then(({ page }) => {
|
||||
if (cancelled || !page) {
|
||||
return;
|
||||
}
|
||||
setTitle(page.title);
|
||||
setContent(page.contentMarkdown);
|
||||
setLastUpdated(extractLastUpdated(page.contentMarkdown, page.updatedAt));
|
||||
setLoadedLocale(locale);
|
||||
})
|
||||
.catch(() => {
|
||||
// Keep the server-rendered content if the re-fetch fails
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [locale, loadedLocale, initialLocale, slug, initialTitle, initialContent, initialLastUpdated]);
|
||||
|
||||
return (
|
||||
<div className="section-padding">
|
||||
<div className="container-page max-w-4xl">
|
||||
@@ -21,7 +78,7 @@ export default function LegalPageLayout({ title, content, lastUpdated }: LegalPa
|
||||
className="inline-flex items-center text-gray-600 hover:text-primary-dark transition-colors mb-8"
|
||||
>
|
||||
<ArrowLeftIcon className="w-4 h-4 mr-2" />
|
||||
Back to Home
|
||||
{t('legalPage.backToHome')}
|
||||
</Link>
|
||||
|
||||
{/* Title */}
|
||||
@@ -31,7 +88,7 @@ export default function LegalPageLayout({ title, content, lastUpdated }: LegalPa
|
||||
</h1>
|
||||
{lastUpdated && lastUpdated !== '[Insert Date]' && (
|
||||
<p className="text-sm text-gray-500">
|
||||
Last updated: {lastUpdated}
|
||||
{t('legalPage.lastUpdated', { date: lastUpdated })}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -182,7 +239,7 @@ export default function LegalPageLayout({ title, content, lastUpdated }: LegalPa
|
||||
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
||||
className="text-gray-500 hover:text-primary-dark transition-colors text-sm"
|
||||
>
|
||||
Back to top
|
||||
{t('legalPage.backToTop')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user