dev #19
@@ -1370,7 +1370,7 @@ export default function BookingPage() {
|
|||||||
>
|
>
|
||||||
{t('booking.form.termsAgreePart1')}
|
{t('booking.form.termsAgreePart1')}
|
||||||
<Link
|
<Link
|
||||||
href="/legal/terms-policy"
|
href={`/legal/terms-policy${locale === 'es' ? '?locale=es' : ''}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-secondary-blue hover:text-brand-navy underline"
|
className="text-secondary-blue hover:text-brand-navy underline"
|
||||||
@@ -1379,7 +1379,7 @@ export default function BookingPage() {
|
|||||||
</Link>
|
</Link>
|
||||||
{t('booking.form.termsAgreePart2')}
|
{t('booking.form.termsAgreePart2')}
|
||||||
<Link
|
<Link
|
||||||
href="/legal/privacy-policy"
|
href={`/legal/privacy-policy${locale === 'es' ? '?locale=es' : ''}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="text-secondary-blue hover:text-brand-navy underline"
|
className="text-secondary-blue hover:text-brand-navy underline"
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ export default async function LegalPage({ params, searchParams }: PageProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<LegalPageLayout
|
<LegalPageLayout
|
||||||
|
slug={resolvedParams.slug}
|
||||||
|
initialLocale={locale}
|
||||||
title={legalPage.title}
|
title={legalPage.title}
|
||||||
content={legalPage.content}
|
content={legalPage.content}
|
||||||
lastUpdated={legalPage.lastUpdated}
|
lastUpdated={legalPage.lastUpdated}
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export default function Footer() {
|
|||||||
{legalLinks.map((link) => (
|
{legalLinks.map((link) => (
|
||||||
<Link
|
<Link
|
||||||
key={link.slug}
|
key={link.slug}
|
||||||
href={`/legal/${link.slug}`}
|
href={`/legal/${link.slug}${locale === 'es' ? '?locale=es' : ''}`}
|
||||||
className="hover:opacity-70 transition-colors text-sm"
|
className="hover:opacity-70 transition-colors text-sm"
|
||||||
style={{ color: '#002F44' }}
|
style={{ color: '#002F44' }}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,17 +1,74 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import remarkGfm from 'remark-gfm';
|
import remarkGfm from 'remark-gfm';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { ArrowLeftIcon } from '@heroicons/react/24/outline';
|
import { ArrowLeftIcon } from '@heroicons/react/24/outline';
|
||||||
|
import { useLanguage } from '@/context/LanguageContext';
|
||||||
|
import { legalPagesApi } from '@/lib/api';
|
||||||
|
|
||||||
interface LegalPageLayoutProps {
|
interface LegalPageLayoutProps {
|
||||||
|
slug: string;
|
||||||
|
initialLocale: 'en' | 'es';
|
||||||
title: string;
|
title: string;
|
||||||
content: string;
|
content: string;
|
||||||
lastUpdated?: 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 (
|
return (
|
||||||
<div className="section-padding">
|
<div className="section-padding">
|
||||||
<div className="container-page max-w-4xl">
|
<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"
|
className="inline-flex items-center text-gray-600 hover:text-primary-dark transition-colors mb-8"
|
||||||
>
|
>
|
||||||
<ArrowLeftIcon className="w-4 h-4 mr-2" />
|
<ArrowLeftIcon className="w-4 h-4 mr-2" />
|
||||||
Back to Home
|
{t('legalPage.backToHome')}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Title */}
|
{/* Title */}
|
||||||
@@ -31,7 +88,7 @@ export default function LegalPageLayout({ title, content, lastUpdated }: LegalPa
|
|||||||
</h1>
|
</h1>
|
||||||
{lastUpdated && lastUpdated !== '[Insert Date]' && (
|
{lastUpdated && lastUpdated !== '[Insert Date]' && (
|
||||||
<p className="text-sm text-gray-500">
|
<p className="text-sm text-gray-500">
|
||||||
Last updated: {lastUpdated}
|
{t('legalPage.lastUpdated', { date: lastUpdated })}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -182,7 +239,7 @@ export default function LegalPageLayout({ title, content, lastUpdated }: LegalPa
|
|||||||
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
|
||||||
className="text-gray-500 hover:text-primary-dark transition-colors text-sm"
|
className="text-gray-500 hover:text-primary-dark transition-colors text-sm"
|
||||||
>
|
>
|
||||||
Back to top
|
{t('legalPage.backToTop')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -322,6 +322,11 @@
|
|||||||
"refund": "Refund Policy"
|
"refund": "Refund Policy"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"legalPage": {
|
||||||
|
"backToHome": "Back to Home",
|
||||||
|
"lastUpdated": "Last updated: {date}",
|
||||||
|
"backToTop": "Back to top"
|
||||||
|
},
|
||||||
"linktree": {
|
"linktree": {
|
||||||
"tagline": "Language Exchange Community",
|
"tagline": "Language Exchange Community",
|
||||||
"nextEvent": "Next Event",
|
"nextEvent": "Next Event",
|
||||||
|
|||||||
@@ -322,6 +322,11 @@
|
|||||||
"refund": "Política de Reembolso"
|
"refund": "Política de Reembolso"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"legalPage": {
|
||||||
|
"backToHome": "Volver al inicio",
|
||||||
|
"lastUpdated": "Última actualización: {date}",
|
||||||
|
"backToTop": "Volver arriba"
|
||||||
|
},
|
||||||
"linktree": {
|
"linktree": {
|
||||||
"tagline": "Comunidad de Intercambio de Idiomas",
|
"tagline": "Comunidad de Intercambio de Idiomas",
|
||||||
"nextEvent": "Próximo Evento",
|
"nextEvent": "Próximo Evento",
|
||||||
|
|||||||
Reference in New Issue
Block a user