Treat invalid calendar URLs as missing availability

This commit is contained in:
Tero Halla-aho 2025-12-07 00:29:38 +02:00
parent 53bd324fa6
commit 1fa0b4d300
2 changed files with 40 additions and 10 deletions

View file

@ -138,6 +138,15 @@ export async function GET(req: Request) {
); );
const translation = pickTranslation(listing.translations, locale); const translation = pickTranslation(listing.translations, locale);
const fallback = listing.translations[0]; const fallback = listing.translations[0];
const validCalendarUrls = (listing.calendarUrls ?? []).filter((url) => {
if (!url) return false;
try {
const parsed = new URL(url);
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
} catch {
return false;
}
});
return { return {
id: listing.id, id: listing.id,
title: translation?.title ?? fallback?.title ?? 'Listing', title: translation?.title ?? fallback?.title ?? 'Listing',
@ -173,7 +182,7 @@ export async function GET(req: Request) {
priceWeekendEuros: listing.priceWeekendEuros, priceWeekendEuros: listing.priceWeekendEuros,
coverImage: resolveImageUrl(listing.images.find((img) => img.isCover) ?? listing.images[0] ?? { id: '', url: null, size: null }), coverImage: resolveImageUrl(listing.images.find((img) => img.isCover) ?? listing.images[0] ?? { id: '', url: null, size: null }),
isSample, isSample,
hasCalendar: Boolean(listing.calendarUrls?.length), hasCalendar: Boolean(validCalendarUrls.length),
availableForDates: availabilityFilterActive ? Boolean(availabilityMap.get(listing.id)) : undefined, availableForDates: availabilityFilterActive ? Boolean(availabilityMap.get(listing.id)) : undefined,
}; };
}); });

View file

@ -52,7 +52,15 @@ export default async function ListingPage({ params }: ListingPageProps) {
const { listing, title, description, teaser, locale: translationLocale } = translation; const { listing, title, description, teaser, locale: translationLocale } = translation;
const isSample = listing.isSample || listing.contactEmail === 'host@lomavuokraus.fi' || SAMPLE_LISTING_SLUGS.includes(params.slug); const isSample = listing.isSample || listing.contactEmail === 'host@lomavuokraus.fi' || SAMPLE_LISTING_SLUGS.includes(params.slug);
const calendarUrls = listing.calendarUrls ?? []; const calendarUrls = (listing.calendarUrls ?? []).filter((url) => {
if (!url) return false;
try {
const parsed = new URL(url);
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
} catch {
return false;
}
});
const hasCalendar = calendarUrls.length > 0; const hasCalendar = calendarUrls.length > 0;
const availabilityFrom = new Date(); const availabilityFrom = new Date();
availabilityFrom.setUTCHours(0, 0, 0, 0); availabilityFrom.setUTCHours(0, 0, 0, 0);
@ -143,14 +151,27 @@ export default async function ListingPage({ params }: ListingPageProps) {
)} )}
</div> </div>
<div className="panel" style={{ padding: 12 }}> <div className="panel" style={{ padding: 12 }}>
{hasCalendar ? ( <div style={{ position: 'relative' }}>
<AvailabilityCalendar blockedDates={blockedDates} months={1} disabled={!listing.calendarUrls?.length} /> <AvailabilityCalendar blockedDates={blockedDates} months={1} disabled={!hasCalendar} />
) : ( {!hasCalendar ? (
<div style={{ display: 'grid', gap: 8 }}> <div
<div style={{ fontWeight: 700 }}>{t('availabilityTitle')}</div> style={{
<p style={{ color: '#cbd5e1', margin: 0 }}>{t('availabilityMissing')}</p> position: 'absolute',
</div> inset: 0,
)} display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: '#cbd5e1',
fontWeight: 600,
textAlign: 'center',
background: 'linear-gradient(135deg, rgba(15,23,42,0.55), rgba(15,23,42,0.65))',
borderRadius: 12,
}}
>
{t('availabilityMissing')}
</div>
) : null}
</div>
</div> </div>
</div> </div>
)} )}