119 lines
5.3 KiB
TypeScript
119 lines
5.3 KiB
TypeScript
import type { Metadata } from 'next';
|
|
import Link from 'next/link';
|
|
import { notFound } from 'next/navigation';
|
|
import { cookies, headers } from 'next/headers';
|
|
import { getListingBySlug, DEFAULT_LOCALE } from '../../../lib/listings';
|
|
import { resolveLocale, t as translate } from '../../../lib/i18n';
|
|
|
|
type ListingPageProps = {
|
|
params: { slug: string };
|
|
};
|
|
|
|
const amenityIcons: Record<string, string> = {
|
|
sauna: '🧖',
|
|
fireplace: '🔥',
|
|
wifi: '📶',
|
|
pets: '🐾',
|
|
lake: '🌊',
|
|
ac: '❄️',
|
|
ev: '⚡',
|
|
};
|
|
|
|
export async function generateMetadata({ params }: ListingPageProps): Promise<Metadata> {
|
|
const translation = await getListingBySlug({ slug: params.slug, locale: DEFAULT_LOCALE });
|
|
|
|
return {
|
|
title: translation ? `${translation.title} | Lomavuokraus.fi` : `${params.slug} | Lomavuokraus.fi`,
|
|
description: translation?.teaser ?? translation?.description?.slice(0, 140),
|
|
};
|
|
}
|
|
|
|
export default async function ListingPage({ params }: ListingPageProps) {
|
|
const cookieStore = cookies();
|
|
const locale = resolveLocale({ cookieLocale: cookieStore.get('locale')?.value, acceptLanguage: headers().get('accept-language') });
|
|
const t = (key: any, vars?: Record<string, string | number>) => translate(locale, key as any, vars);
|
|
|
|
const translation = await getListingBySlug({ slug: params.slug, locale: locale ?? DEFAULT_LOCALE });
|
|
|
|
if (!translation) {
|
|
notFound();
|
|
}
|
|
|
|
const { listing, title, description, teaser, locale: translationLocale } = translation;
|
|
|
|
return (
|
|
<main className="listing-shell">
|
|
<div className="breadcrumb">
|
|
<Link href="/">{t('homeCrumb')}</Link> / <span>{params.slug}</span>
|
|
</div>
|
|
<div className="panel">
|
|
<h1>{title}</h1>
|
|
<p style={{ marginTop: 8 }}>{teaser ?? description}</p>
|
|
<div style={{ marginTop: 12 }}>
|
|
<strong>{t('listingAddress')}:</strong> {listing.streetAddress ? `${listing.streetAddress}, ` : ''}
|
|
{listing.city}, {listing.region}, {listing.country}
|
|
</div>
|
|
{listing.addressNote ? (
|
|
<div style={{ marginTop: 4, color: '#cbd5e1' }}>
|
|
<em>{listing.addressNote}</em>
|
|
</div>
|
|
) : null}
|
|
<div style={{ marginTop: 12 }}>
|
|
<strong>{t('listingCapacity')}:</strong> {t('capacityGuests', { count: listing.maxGuests })} - {t('capacityBedrooms', { count: listing.bedrooms })} -{' '}
|
|
{t('capacityBeds', { count: listing.beds })} - {t('capacityBathrooms', { count: listing.bathrooms })}
|
|
</div>
|
|
<div style={{ marginTop: 12 }}>
|
|
<strong>{t('listingAmenities')}:</strong>
|
|
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 6 }}>
|
|
{listing.hasSauna ? <span className="badge">{amenityIcons.sauna} {t('amenitySauna')}</span> : null}
|
|
{listing.hasFireplace ? <span className="badge">{amenityIcons.fireplace} {t('amenityFireplace')}</span> : null}
|
|
{listing.hasWifi ? <span className="badge">{amenityIcons.wifi} {t('amenityWifi')}</span> : null}
|
|
{listing.petsAllowed ? <span className="badge">{amenityIcons.pets} {t('amenityPets')}</span> : null}
|
|
{listing.byTheLake ? <span className="badge">{amenityIcons.lake} {t('amenityLake')}</span> : null}
|
|
{listing.hasAirConditioning ? <span className="badge">{amenityIcons.ac} {t('amenityAirConditioning')}</span> : null}
|
|
{listing.evCharging === 'FREE' ? <span className="badge">{amenityIcons.ev} {t('amenityEvFree')}</span> : null}
|
|
{listing.evCharging === 'PAID' ? <span className="badge">{amenityIcons.ev} {t('amenityEvPaid')}</span> : null}
|
|
{!(
|
|
listing.hasSauna ||
|
|
listing.hasFireplace ||
|
|
listing.hasWifi ||
|
|
listing.petsAllowed ||
|
|
listing.byTheLake ||
|
|
listing.hasAirConditioning ||
|
|
listing.evCharging !== 'NONE'
|
|
)
|
|
? <span className="badge">-</span>
|
|
: null}
|
|
</div>
|
|
</div>
|
|
<div style={{ marginTop: 8 }}>
|
|
<strong>{t('listingContact')}:</strong> {listing.contactName} - {listing.contactEmail}
|
|
{listing.contactPhone ? ` - ${listing.contactPhone}` : ''}
|
|
{listing.externalUrl ? (
|
|
<>
|
|
{' - '}
|
|
<a href={listing.externalUrl} target="_blank" rel="noreferrer">
|
|
{t('listingMoreInfo')}
|
|
</a>
|
|
</>
|
|
) : null}
|
|
</div>
|
|
{listing.images.length > 0 ? (
|
|
<div style={{ marginTop: 16, display: 'grid', gap: 8, gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))' }}>
|
|
{listing.images.map((img) => (
|
|
<figure key={img.id} style={{ border: '1px solid #ddd', borderRadius: 8, overflow: 'hidden', background: '#fafafa' }}>
|
|
<img src={img.url} alt={img.altText ?? title} style={{ width: '100%', height: '180px', objectFit: 'cover' }} />
|
|
{img.altText ? (
|
|
<figcaption style={{ padding: '8px 12px', fontSize: 14, color: '#444' }}>{img.altText}</figcaption>
|
|
) : null}
|
|
</figure>
|
|
))}
|
|
</div>
|
|
) : null}
|
|
<div style={{ marginTop: 16, fontSize: 14, color: '#666' }}>
|
|
{t('localeLabel')}: <code>{translationLocale}</code>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|