From cb92a17f1d54087575b61583275feaef827de8c3 Mon Sep 17 00:00:00 2001 From: Tero Halla-aho Date: Wed, 17 Dec 2025 13:40:47 +0200 Subject: [PATCH] Add on-site EV charging amenity --- PROGRESS.md | 2 +- app/admin/pending/page.tsx | 16 ++++++++++++++- app/api/admin/pending/route.ts | 11 +++++++++- app/api/listings/[id]/route.ts | 10 +++++++++- app/api/listings/route.ts | 9 ++++++++- app/listings/[slug]/page.tsx | 4 +++- app/listings/edit/[id]/page.tsx | 20 ++++++++++++++++++- app/listings/new/page.tsx | 20 ++++++++++++++++++- app/listings/page.tsx | 8 ++++++-- lib/i18n.ts | 12 ++++++++--- .../migration.sql | 3 +++ prisma/schema.prisma | 1 + prisma/seed.js | 15 +++++++++++--- 13 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 prisma/migrations/20251217_ev_charging_on_site/migration.sql diff --git a/PROGRESS.md b/PROGRESS.md index a4dc936..f8d47f2 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -86,4 +86,4 @@ - Netdata installed on k3s node (`node1.lomavuokraus.fi:8443`) and DB host (`db1.lomavuokraus.fi:8443`) behind self-signed TLS + basic auth; DB Netdata includes Postgres metrics via dedicated `netdata` role. - Footer now includes a minimal cookie usage statement (essential cookies only; site requires acceptance). - Forgejo deployment scaffolding added: Docker Compose + runner config guidance and Apache vhost for git.halla-aho.net, plus CI workflow placeholder under `.forgejo/workflows/`. -- Amenities: added wheelchair accessibility flag and clarified EV charging amenity wording to mean on-site charging. +- Amenities: added separate EV charging flags (on-site vs nearby) plus wheelchair accessibility, including browse filters and admin approvals view badges. diff --git a/app/admin/pending/page.tsx b/app/admin/pending/page.tsx index bfb3e8d..168e4a0 100644 --- a/app/admin/pending/page.tsx +++ b/app/admin/pending/page.tsx @@ -4,7 +4,16 @@ import { useEffect, useState } from 'react'; import { useI18n } from '../../components/I18nProvider'; type PendingUser = { id: string; email: string; status: string; emailVerifiedAt: string | null; approvedAt: string | null; role: string }; -type PendingListing = { id: string; status: string; createdAt: string; owner: { email: string }; translations: { title: string; slug: string; locale: string }[] }; +type PendingListing = { + id: string; + status: string; + createdAt: string; + owner: { email: string }; + translations: { title: string; slug: string; locale: string }[]; + evChargingAvailable: boolean; + evChargingOnSite: boolean; + wheelchairAccessible: boolean; +}; export default function PendingAdminPage() { const { t } = useI18n(); @@ -150,6 +159,11 @@ export default function PendingAdminPage() {
{l.translations[0]?.title ?? 'Listing'} — owner: {l.owner.email}
+
+ {l.evChargingOnSite ? {t('amenityEvOnSite')} : null} + {l.evChargingAvailable && !l.evChargingOnSite ? {t('amenityEvNearby')} : null} + {l.wheelchairAccessible ? {t('amenityWheelchairAccessible')} : null} +
{t('slugsLabel')}: {l.translations.map((t) => `${t.slug} (${t.locale})`).join(', ')}
diff --git a/app/api/admin/pending/route.ts b/app/api/admin/pending/route.ts index 5a9248f..9b7b666 100644 --- a/app/api/admin/pending/route.ts +++ b/app/api/admin/pending/route.ts @@ -28,7 +28,16 @@ export async function GET(req: Request) { wantsListings ? prisma.listing.findMany({ where: { status: ListingStatus.PENDING, removedAt: null }, - select: { id: true, status: true, createdAt: true, owner: { select: { email: true } }, translations: { select: { title: true, slug: true, locale: true } } }, + select: { + id: true, + status: true, + createdAt: true, + evChargingAvailable: true, + evChargingOnSite: true, + wheelchairAccessible: true, + owner: { select: { email: true } }, + translations: { select: { title: true, slug: true, locale: true } }, + }, orderBy: { createdAt: 'asc' }, take: 50, }) diff --git a/app/api/listings/[id]/route.ts b/app/api/listings/[id]/route.ts index 77b0963..30d1be1 100644 --- a/app/api/listings/[id]/route.ts +++ b/app/api/listings/[id]/route.ts @@ -183,6 +183,13 @@ export async function PUT(req: Request, { params }: { params: { id: string } }) } } + const incomingEvChargingOnSite = + body.evChargingOnSite === undefined ? existing.evChargingOnSite : Boolean(body.evChargingOnSite); + const incomingEvChargingAvailable = + body.evChargingAvailable === undefined ? existing.evChargingAvailable : Boolean(body.evChargingAvailable); + const evChargingAvailable = incomingEvChargingOnSite ? true : incomingEvChargingAvailable; + const evChargingOnSite = evChargingAvailable ? incomingEvChargingOnSite : false; + const updateData: any = { status, approvedAt: status === ListingStatus.PUBLISHED ? existing.approvedAt ?? new Date() : null, @@ -211,7 +218,8 @@ export async function PUT(req: Request, { params }: { params: { id: string } }) hasMicrowave: body.hasMicrowave === undefined ? existing.hasMicrowave : Boolean(body.hasMicrowave), hasFreeParking: body.hasFreeParking === undefined ? existing.hasFreeParking : Boolean(body.hasFreeParking), hasSkiPass: body.hasSkiPass === undefined ? existing.hasSkiPass : Boolean(body.hasSkiPass), - evChargingAvailable: body.evChargingAvailable === undefined ? existing.evChargingAvailable : Boolean(body.evChargingAvailable), + evChargingAvailable, + evChargingOnSite, wheelchairAccessible: body.wheelchairAccessible === undefined ? existing.wheelchairAccessible : Boolean(body.wheelchairAccessible), priceWeekdayEuros, diff --git a/app/api/listings/route.ts b/app/api/listings/route.ts index 2daf91d..63ccfc7 100644 --- a/app/api/listings/route.ts +++ b/app/api/listings/route.ts @@ -58,6 +58,8 @@ export async function GET(req: Request) { const region = searchParams.get('region')?.trim(); const evChargingParam = searchParams.get('evCharging'); const evCharging = evChargingParam === 'true' ? true : evChargingParam === 'false' ? false : null; + const evChargingOnSiteParam = searchParams.get('evChargingOnSite'); + const evChargingOnSite = evChargingOnSiteParam === 'true' ? true : evChargingOnSiteParam === 'false' ? false : null; const startDateParam = searchParams.get('availableStart'); const endDateParam = searchParams.get('availableEnd'); const startDate = startDateParam ? new Date(startDateParam) : null; @@ -82,6 +84,7 @@ export async function GET(req: Request) { if (amenityFilters.includes('parking')) amenityWhere.hasFreeParking = true; if (amenityFilters.includes('skipass')) amenityWhere.hasSkiPass = true; if (amenityFilters.includes('accessible')) amenityWhere.wheelchairAccessible = true; + if (amenityFilters.includes('ev-onsite')) amenityWhere.evChargingOnSite = true; const where: Prisma.ListingWhereInput = { status: ListingStatus.PUBLISHED, @@ -89,6 +92,7 @@ export async function GET(req: Request) { city: city ? { contains: city, mode: 'insensitive' } : undefined, region: region ? { contains: region, mode: 'insensitive' } : undefined, evChargingAvailable: evCharging ?? undefined, + evChargingOnSite: evChargingOnSite ?? undefined, ...amenityWhere, translations: q ? { @@ -175,6 +179,7 @@ export async function GET(req: Request) { hasFreeParking: listing.hasFreeParking, hasSkiPass: listing.hasSkiPass, evChargingAvailable: listing.evChargingAvailable, + evChargingOnSite: listing.evChargingOnSite, wheelchairAccessible: listing.wheelchairAccessible, maxGuests: listing.maxGuests, bedrooms: listing.bedrooms, @@ -333,7 +338,8 @@ export async function POST(req: Request) { const autoApprove = !saveDraft && (process.env.AUTO_APPROVE_LISTINGS === 'true' || auth.role === 'ADMIN'); const status = saveDraft ? ListingStatus.DRAFT : autoApprove ? ListingStatus.PUBLISHED : ListingStatus.PENDING; const isSample = (contactEmail || '').toLowerCase() === SAMPLE_EMAIL; - const evChargingAvailable = Boolean(body.evChargingAvailable); + const evChargingOnSite = Boolean(body.evChargingOnSite); + const evChargingAvailable = Boolean(body.evChargingAvailable) || evChargingOnSite; const wheelchairAccessible = Boolean(body.wheelchairAccessible); const listing = await prisma.listing.create({ @@ -367,6 +373,7 @@ export async function POST(req: Request) { hasFreeParking: Boolean(body.hasFreeParking), hasSkiPass: Boolean(body.hasSkiPass), evChargingAvailable, + evChargingOnSite, wheelchairAccessible, priceWeekdayEuros, priceWeekendEuros, diff --git a/app/listings/[slug]/page.tsx b/app/listings/[slug]/page.tsx index b928b04..4b9032b 100644 --- a/app/listings/[slug]/page.tsx +++ b/app/listings/[slug]/page.tsx @@ -22,6 +22,7 @@ const amenityIcons: Record = { lake: '🌊', ac: '❄️', ev: '⚡', + evOnSite: '🔌', kitchen: '🍽️', dishwasher: '🧼', washer: '🧺', @@ -92,7 +93,8 @@ export default async function ListingPage({ params }: ListingPageProps) { listing.petsAllowed ? { icon: amenityIcons.pets, label: t('amenityPets') } : null, listing.byTheLake ? { icon: amenityIcons.lake, label: t('amenityLake') } : null, listing.hasAirConditioning ? { icon: amenityIcons.ac, label: t('amenityAirConditioning') } : null, - listing.evChargingAvailable ? { icon: amenityIcons.ev, label: t('amenityEvAvailable') } : null, + listing.evChargingOnSite ? { icon: amenityIcons.evOnSite, label: t('amenityEvOnSite') } : null, + listing.evChargingAvailable && !listing.evChargingOnSite ? { icon: amenityIcons.ev, label: t('amenityEvNearby') } : null, listing.wheelchairAccessible ? { icon: amenityIcons.accessible, label: t('amenityWheelchairAccessible') } : null, listing.hasSkiPass ? { icon: amenityIcons.ski, label: t('amenitySkiPass') } : null, listing.hasKitchen ? { icon: amenityIcons.kitchen, label: t('amenityKitchen') } : null, diff --git a/app/listings/edit/[id]/page.tsx b/app/listings/edit/[id]/page.tsx index 528e27b..5c90ade 100644 --- a/app/listings/edit/[id]/page.tsx +++ b/app/listings/edit/[id]/page.tsx @@ -59,6 +59,7 @@ export default function EditListingPage({ params }: { params: { id: string } }) const [hasFreeParking, setHasFreeParking] = useState(false); const [hasSkiPass, setHasSkiPass] = useState(false); const [evChargingAvailable, setEvChargingAvailable] = useState(false); + const [evChargingOnSite, setEvChargingOnSite] = useState(false); const [wheelchairAccessible, setWheelchairAccessible] = useState(false); const [calendarUrls, setCalendarUrls] = useState(''); const [selectedImages, setSelectedImages] = useState([]); @@ -139,6 +140,7 @@ export default function EditListingPage({ params }: { params: { id: string } }) setHasFreeParking(listing.hasFreeParking); setHasSkiPass(listing.hasSkiPass); setEvChargingAvailable(listing.evChargingAvailable); + setEvChargingOnSite(Boolean(listing.evChargingOnSite)); setWheelchairAccessible(Boolean(listing.wheelchairAccessible)); setCalendarUrls((listing.calendarUrls || []).join('\n')); if (listing.images?.length) { @@ -257,6 +259,20 @@ export default function EditListingPage({ params }: { params: { id: string } }) } } + function toggleEvChargingNearby(next: boolean) { + setEvChargingAvailable(next); + if (!next) { + setEvChargingOnSite(false); + } + } + + function toggleEvChargingOnSite(next: boolean) { + setEvChargingOnSite(next); + if (next) { + setEvChargingAvailable(true); + } + } + const amenityOptions = [ { key: 'sauna', label: t('amenitySauna'), icon: '🧖', checked: hasSauna, toggle: setHasSauna }, { key: 'fireplace', label: t('amenityFireplace'), icon: '🔥', checked: hasFireplace, toggle: setHasFireplace }, @@ -271,7 +287,8 @@ export default function EditListingPage({ params }: { params: { id: string } }) { key: 'microwave', label: t('amenityMicrowave'), icon: '🍲', checked: hasMicrowave, toggle: setHasMicrowave }, { key: 'parking', label: t('amenityFreeParking'), icon: '🅿️', checked: hasFreeParking, toggle: setHasFreeParking }, { key: 'ski', label: t('amenitySkiPass'), icon: '⛷️', checked: hasSkiPass, toggle: setHasSkiPass }, - { key: 'ev', label: t('amenityEvAvailable'), icon: '⚡', checked: evChargingAvailable, toggle: setEvChargingAvailable }, + { key: 'ev', label: t('amenityEvNearby'), icon: '⚡', checked: evChargingAvailable, toggle: toggleEvChargingNearby }, + { key: 'ev-onsite', label: t('amenityEvOnSite'), icon: '🔌', checked: evChargingOnSite, toggle: toggleEvChargingOnSite }, { key: 'accessible', label: t('amenityWheelchairAccessible'), icon: '♿', checked: wheelchairAccessible, toggle: setWheelchairAccessible }, ]; @@ -444,6 +461,7 @@ export default function EditListingPage({ params }: { params: { id: string } }) hasFreeParking, hasSkiPass, evChargingAvailable, + evChargingOnSite, wheelchairAccessible, coverImageIndex, images: selectedImages.length ? parseImages() : undefined, diff --git a/app/listings/new/page.tsx b/app/listings/new/page.tsx index 4323fb8..5aab2f7 100644 --- a/app/listings/new/page.tsx +++ b/app/listings/new/page.tsx @@ -55,6 +55,7 @@ export default function NewListingPage() { const [hasFreeParking, setHasFreeParking] = useState(false); const [hasSkiPass, setHasSkiPass] = useState(false); const [evChargingAvailable, setEvChargingAvailable] = useState(false); + const [evChargingOnSite, setEvChargingOnSite] = useState(false); const [wheelchairAccessible, setWheelchairAccessible] = useState(false); const [calendarUrls, setCalendarUrls] = useState(''); const [selectedImages, setSelectedImages] = useState([]); @@ -124,6 +125,20 @@ export default function NewListingPage() { } } + function toggleEvChargingNearby(next: boolean) { + setEvChargingAvailable(next); + if (!next) { + setEvChargingOnSite(false); + } + } + + function toggleEvChargingOnSite(next: boolean) { + setEvChargingOnSite(next); + if (next) { + setEvChargingAvailable(true); + } + } + const amenityOptions = [ { key: 'sauna', label: t('amenitySauna'), icon: '🧖', checked: hasSauna, toggle: setHasSauna }, { key: 'fireplace', label: t('amenityFireplace'), icon: '🔥', checked: hasFireplace, toggle: setHasFireplace }, @@ -138,7 +153,8 @@ export default function NewListingPage() { { key: 'microwave', label: t('amenityMicrowave'), icon: '🍲', checked: hasMicrowave, toggle: setHasMicrowave }, { key: 'parking', label: t('amenityFreeParking'), icon: '🅿️', checked: hasFreeParking, toggle: setHasFreeParking }, { key: 'ski', label: t('amenitySkiPass'), icon: '⛷️', checked: hasSkiPass, toggle: setHasSkiPass }, - { key: 'ev', label: t('amenityEvAvailable'), icon: '⚡', checked: evChargingAvailable, toggle: setEvChargingAvailable }, + { key: 'ev', label: t('amenityEvNearby'), icon: '⚡', checked: evChargingAvailable, toggle: toggleEvChargingNearby }, + { key: 'ev-onsite', label: t('amenityEvOnSite'), icon: '🔌', checked: evChargingOnSite, toggle: toggleEvChargingOnSite }, { key: 'accessible', label: t('amenityWheelchairAccessible'), icon: '♿', checked: wheelchairAccessible, toggle: setWheelchairAccessible }, ]; @@ -376,6 +392,7 @@ export default function NewListingPage() { hasFreeParking, hasSkiPass, evChargingAvailable, + evChargingOnSite, wheelchairAccessible, coverImageIndex, images: parseImages(), @@ -418,6 +435,7 @@ export default function NewListingPage() { setHasFreeParking(false); setHasSkiPass(false); setEvChargingAvailable(false); + setEvChargingOnSite(false); setWheelchairAccessible(false); setRegion(''); setCity(''); diff --git a/app/listings/page.tsx b/app/listings/page.tsx index f5bec3d..e82908e 100644 --- a/app/listings/page.tsx +++ b/app/listings/page.tsx @@ -31,6 +31,7 @@ type ListingResult = { hasMicrowave: boolean; hasFreeParking: boolean; evChargingAvailable: boolean; + evChargingOnSite: boolean; wheelchairAccessible: boolean; hasSkiPass: boolean; maxGuests: number; @@ -94,6 +95,7 @@ const amenityIcons: Record = { accessible: '♿', ski: '⛷️', ev: '⚡', + evOnSite: '🔌', }; function ListingsMap({ @@ -226,7 +228,8 @@ export default function ListingsIndexPage() { { key: 'parking', label: t('amenityFreeParking'), icon: amenityIcons.parking }, { key: 'accessible', label: t('amenityWheelchairAccessible'), icon: amenityIcons.accessible }, { key: 'skipass', label: t('amenitySkiPass'), icon: amenityIcons.ski }, - { key: 'ev', label: t('amenityEvAvailable'), icon: amenityIcons.ev }, + { key: 'ev', label: t('amenityEvNearby'), icon: amenityIcons.ev }, + { key: 'ev-onsite', label: t('amenityEvOnSite'), icon: amenityIcons.evOnSite }, ]; async function fetchListings() { @@ -479,7 +482,8 @@ export default function ListingsIndexPage() { {startDate && endDate && l.availableForDates ? ( {t('availableForDates')} ) : null} - {l.evChargingAvailable ? {t('amenityEvAvailable')} : null} + {l.evChargingOnSite ? {t('amenityEvOnSite')} : null} + {l.evChargingAvailable && !l.evChargingOnSite ? {t('amenityEvNearby')} : null} {l.wheelchairAccessible ? {t('amenityWheelchairAccessible')} : null} {l.hasSkiPass ? {t('amenitySkiPass')} : null} {l.hasAirConditioning ? {t('amenityAirConditioning')} : null} diff --git a/lib/i18n.ts b/lib/i18n.ts index 3c65b75..8446ead 100644 --- a/lib/i18n.ts +++ b/lib/i18n.ts @@ -251,7 +251,9 @@ const baseMessages = { amenityBarbecue: 'Barbecue grill', amenityMicrowave: 'Microwave', amenityFreeParking: 'Free parking', - amenityEvAvailable: 'EV charging', + amenityEvAvailable: 'EV charging nearby', + amenityEvNearby: 'EV charging nearby', + amenityEvOnSite: 'EV charging on-site', amenityWheelchairAccessible: 'Wheelchair accessible', amenitySkiPass: 'Ski pass included', evChargingLabel: 'EV charging', @@ -568,7 +570,9 @@ const baseMessages = { amenityBarbecue: 'Grilli', amenityMicrowave: 'Mikroaaltouuni', amenityFreeParking: 'Maksuton pysäköinti', - amenityEvAvailable: 'Sähköauton lataus', + amenityEvAvailable: 'Sähköauton lataus lähellä', + amenityEvNearby: 'Sähköauton lataus lähellä', + amenityEvOnSite: 'Sähköauton lataus kohteessa', amenityWheelchairAccessible: 'Esteetön / pyörätuolilla', amenitySkiPass: 'Hissilippu sisältyy', evChargingLabel: 'Sähköauton lataus', @@ -720,7 +724,9 @@ const svMessages: Record = { priceNotSet: 'Ej angivet', listingPrices: 'Priser', capacityUnknown: 'Kapacitet ej angiven', - amenityEvAvailable: 'EV-laddning', + amenityEvAvailable: 'EV-laddning i närheten', + amenityEvNearby: 'EV-laddning i närheten', + amenityEvOnSite: 'EV-laddning på plats', amenityWheelchairAccessible: 'Rullstolsanpassat', amenitySkiPass: 'Liftkort ingår', amenityMicrowave: 'Mikrovågsugn', diff --git a/prisma/migrations/20251217_ev_charging_on_site/migration.sql b/prisma/migrations/20251217_ev_charging_on_site/migration.sql new file mode 100644 index 0000000..204c55c --- /dev/null +++ b/prisma/migrations/20251217_ev_charging_on_site/migration.sql @@ -0,0 +1,3 @@ +-- Add on-site EV charging amenity (in addition to nearby charging) +ALTER TABLE "Listing" ADD COLUMN IF NOT EXISTS "evChargingOnSite" BOOLEAN NOT NULL DEFAULT false; + diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 832ee5e..44913ba 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -90,6 +90,7 @@ model Listing { hasFreeParking Boolean @default(false) hasSkiPass Boolean @default(false) evChargingAvailable Boolean @default(false) + evChargingOnSite Boolean @default(false) wheelchairAccessible Boolean @default(false) calendarUrls String[] @db.Text @default([]) priceWeekdayEuros Int? diff --git a/prisma/seed.js b/prisma/seed.js index 9e3ed23..fec685b 100644 --- a/prisma/seed.js +++ b/prisma/seed.js @@ -301,6 +301,7 @@ async function main() { petsAllowed: false, byTheLake: true, evChargingAvailable: true, + evChargingOnSite: true, hasSkiPass: true, priceWeekdayEuros: 189, priceWeekendEuros: 215, @@ -389,6 +390,7 @@ async function main() { petsAllowed: true, byTheLake: true, evChargingAvailable: true, + evChargingOnSite: true, hasSkiPass: true, priceWeekdayEuros: 245, priceWeekendEuros: 275, @@ -432,6 +434,7 @@ async function main() { petsAllowed: false, byTheLake: true, evChargingAvailable: true, + evChargingOnSite: true, hasSkiPass: true, priceWeekdayEuros: 129, priceWeekendEuros: 149, @@ -518,6 +521,7 @@ async function main() { petsAllowed: false, byTheLake: false, evChargingAvailable: true, + evChargingOnSite: true, priceWeekdayEuros: 105, priceWeekendEuros: 120, cover: { @@ -582,6 +586,8 @@ async function main() { const randBool = (p = 0.5) => Math.random() < p; listings = listings.map((item) => { const weekdayPrice = item.priceWeekdayEuros ?? Math.round(Math.random() * (220 - 90) + 90); + const evChargingOnSite = item.evChargingOnSite ?? randBool(0.15); + const evChargingAvailable = item.evChargingAvailable ?? evChargingOnSite ?? randBool(0.4); return { ...item, priceWeekdayEuros: weekdayPrice, @@ -592,7 +598,8 @@ async function main() { hasBarbecue: item.hasBarbecue ?? randBool(0.5), hasMicrowave: item.hasMicrowave ?? randBool(0.7), hasFreeParking: item.hasFreeParking ?? randBool(0.6), - evChargingAvailable: item.evChargingAvailable ?? randBool(0.4), + evChargingOnSite, + evChargingAvailable, wheelchairAccessible: item.wheelchairAccessible ?? randBool(0.25), hasSkiPass: item.hasSkiPass ?? randBool(0.2), }; @@ -632,7 +639,8 @@ async function main() { hasFreeParking: item.hasFreeParking ?? false, petsAllowed: item.petsAllowed, byTheLake: item.byTheLake, - evChargingAvailable: item.evChargingAvailable ?? false, + evChargingAvailable: item.evChargingAvailable ?? item.evChargingOnSite ?? false, + evChargingOnSite: item.evChargingOnSite ?? false, wheelchairAccessible: item.wheelchairAccessible ?? false, priceWeekdayEuros: item.priceWeekdayEuros, priceWeekendEuros: item.priceWeekendEuros, @@ -683,7 +691,8 @@ async function main() { hasFreeParking: item.hasFreeParking ?? false, petsAllowed: item.petsAllowed, byTheLake: item.byTheLake, - evChargingAvailable: item.evChargingAvailable ?? false, + evChargingAvailable: item.evChargingAvailable ?? item.evChargingOnSite ?? false, + evChargingOnSite: item.evChargingOnSite ?? false, wheelchairAccessible: item.wheelchairAccessible ?? false, priceWeekdayEuros: item.priceWeekdayEuros, priceWeekendEuros: item.priceWeekendEuros,