dev #19
@@ -1370,7 +1370,7 @@ export default function BookingPage() {
|
||||
>
|
||||
{t('booking.form.termsAgreePart1')}
|
||||
<Link
|
||||
href="/legal/terms-policy"
|
||||
href={`/legal/terms-policy${locale === 'es' ? '?locale=es' : ''}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-secondary-blue hover:text-brand-navy underline"
|
||||
@@ -1379,7 +1379,7 @@ export default function BookingPage() {
|
||||
</Link>
|
||||
{t('booking.form.termsAgreePart2')}
|
||||
<Link
|
||||
href="/legal/privacy-policy"
|
||||
href={`/legal/privacy-policy${locale === 'es' ? '?locale=es' : ''}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-secondary-blue hover:text-brand-navy underline"
|
||||
|
||||
@@ -68,6 +68,8 @@ export default async function LegalPage({ params, searchParams }: PageProps) {
|
||||
|
||||
return (
|
||||
<LegalPageLayout
|
||||
slug={resolvedParams.slug}
|
||||
initialLocale={locale}
|
||||
title={legalPage.title}
|
||||
content={legalPage.content}
|
||||
lastUpdated={legalPage.lastUpdated}
|
||||
|
||||
@@ -108,7 +108,7 @@ export default function Footer() {
|
||||
{legalLinks.map((link) => (
|
||||
<Link
|
||||
key={link.slug}
|
||||
href={`/legal/${link.slug}`}
|
||||
href={`/legal/${link.slug}${locale === 'es' ? '?locale=es' : ''}`}
|
||||
className="hover:opacity-70 transition-colors text-sm"
|
||||
style={{ color: '#002F44' }}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -322,6 +322,11 @@
|
||||
"refund": "Refund Policy"
|
||||
}
|
||||
},
|
||||
"legalPage": {
|
||||
"backToHome": "Back to Home",
|
||||
"lastUpdated": "Last updated: {date}",
|
||||
"backToTop": "Back to top"
|
||||
},
|
||||
"linktree": {
|
||||
"tagline": "Language Exchange Community",
|
||||
"nextEvent": "Next Event",
|
||||
|
||||
@@ -322,6 +322,11 @@
|
||||
"refund": "Política de Reembolso"
|
||||
}
|
||||
},
|
||||
"legalPage": {
|
||||
"backToHome": "Volver al inicio",
|
||||
"lastUpdated": "Última actualización: {date}",
|
||||
"backToTop": "Volver arriba"
|
||||
},
|
||||
"linktree": {
|
||||
"tagline": "Comunidad de Intercambio de Idiomas",
|
||||
"nextEvent": "Próximo Evento",
|
||||
|
||||
Reference in New Issue
Block a user