Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 56 additions & 22 deletions src/components/AccommodationPage.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
import { hotels } from '../data/hotels'
import { accommodationTexts } from '../i18n/accommodation'
import { menuTexts } from '../i18n/menu'
import StatusIcon from './icons/StatusIcon.astro'
Expand Down Expand Up @@ -108,29 +109,62 @@ const areaDesc = (slug: AreaSlug) => t[`accommodation.areas.${slug}.desc` as key
<div class="h-px bg-white/20 flex-1" aria-hidden="true"></div>
</div>

<div
class="bg-pycon-gray-100/50 p-12 rounded-3xl border border-pycon-yellow/30 text-center relative overflow-hidden group"
>
<!-- Shine effect -->
<div
class="absolute -top-full -left-full w-[300%] h-[300%] bg-linear-to-br from-transparent via-pycon-yellow/5 to-transparent rotate-45 transition-transform duration-1000 motion-safe:group-hover:translate-x-[33%] motion-safe:group-hover:translate-y-[33%] pointer-events-none"
aria-hidden="true"
>
</div>

<div class="relative z-10">
<StatusIcon
type="construction"
size="xl"
role="img"
aria-label={t['accommodation.a11y.constructionIcon']}
/>
<h3 class="text-2xl font-bold text-pycon-yellow mb-4">{t['accommodation.hotels.subtitle']}</h3>
<p class="text-xl text-white font-medium max-w-2xl mx-auto italic opacity-80">
{t['accommodation.hotels.disclaimer']}
</p>
</div>
<div class="mb-8 max-w-3xl">
<h3 class="text-2xl font-bold text-pycon-yellow mb-3">{t['accommodation.hotels.subtitle']}</h3>
<p class="text-pycon-gray-25 text-lg leading-relaxed">{t['accommodation.hotels.disclaimer']}</p>
</div>

<ul class="grid sm:grid-cols-2 lg:grid-cols-3 gap-8 list-none m-0 p-0">
{
hotels.map((hotel, index) => {
const promoId = `hotel-promo-${index}`
return (
<li class="flex flex-col bg-pycon-black/40 p-8 rounded-2xl border border-white/5 hover:border-pycon-orange/50 transition-all motion-safe:hover:-translate-y-2">
<h4 class="text-xl font-bold text-pycon-orange mb-6 italic">{hotel.name}</h4>

{hotel.promoCode ? (
<dl class="mb-6 m-0">
<dt id={promoId} class="text-sm text-pycon-black-25 mb-1 uppercase tracking-wide">
{t['accommodation.hotels.promoCode']}
</dt>
<dd class="m-0" aria-labelledby={promoId}>
<code class="text-white font-mono text-lg font-bold tracking-wide not-italic">
{hotel.promoCode}
</code>
</dd>
</dl>
) : (
<p class="text-pycon-black-25 text-sm leading-relaxed mb-6">
{t['accommodation.hotels.useDirectLink']}
</p>
)}

<div class="mt-auto flex flex-col gap-3">
<a
href={hotel.bookingUrl}
target="_blank"
rel="noopener noreferrer"
aria-label={`${t['accommodation.hotels.book']}: ${hotel.name} ${menuT.new_tab}`}
class="inline-flex items-center justify-center gap-2 bg-pycon-orange text-white font-bold px-4 py-3 rounded-lg motion-safe:hover:scale-105 transition-transform no-underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-pycon-yellow"
>
{t['accommodation.hotels.book']}
</a>

<a
href={hotel.mapsUrl}
target="_blank"
rel="noopener noreferrer"
aria-label={`${t['accommodation.hotels.maps']}: ${hotel.name} ${menuT.new_tab}`}
class="inline-flex items-center justify-center gap-2 border border-white/20 text-pycon-black-25 font-medium px-4 py-3 rounded-lg hover:border-pycon-yellow/50 hover:text-white transition-colors no-underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-pycon-yellow"
>
{t['accommodation.hotels.maps']}
</a>
</div>
</li>
)
})
}
</ul>
</section>

<!-- Apartments -->
Expand Down
23 changes: 23 additions & 0 deletions src/data/hotels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { IHotel } from '../types/hotels'

/** Recommended hotels with PyConES promo codes. */
export const hotels: IHotel[] = [
{
name: 'Travelodge Barcelona Poblenou',
bookingUrl: 'https://www.travelodge.es/hoteles-barcelona/poblenou',
promoCode: 'PYCONES2026BCN',
mapsUrl: 'https://maps.app.goo.gl/xAmHRV413JHyWHUx8',
},
{
name: 'Hotel Jazz',
bookingUrl:
'https://reservations.hoteljazz.com/hotel/nn_jazz?adultos=1&fini=06-11-2026&noches=2&origin=grponline&epc=U1RNVEhKQkdSUDFCQioqLFBZVEhPTgo=',
mapsUrl: 'https://maps.app.goo.gl/ZULETA9Zp2sZW1NL9',
},
{
name: 'EasyHotel Barcelona Fira',
bookingUrl: 'https://www.easyhotel.com/es/hotels/spain/barcelona/barcelona-fira',
promoCode: 'PYTHON',
mapsUrl: 'https://maps.app.goo.gl/9MErH52umw4XHjFq6',
},
]
6 changes: 5 additions & 1 deletion src/i18n/accommodation/ca.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ export const ca = {
'accommodation.hotels.title': 'Hotels Recomanats',
'accommodation.hotels.subtitle': 'Convenis i opcions properes',
'accommodation.hotels.disclaimer':
'Estem treballant per tancar convenis exclusius amb descomptes per als assistents. Torna aviat per veure els codis promocionals!',
'Fes servir el codi promocional en reservar per aplicar el descompte de PyConES.',
'accommodation.hotels.promoCode': 'Codi promocional',
'accommodation.hotels.useDirectLink': "Fes servir l'enllaç de reserva per aplicar el descompte.",
'accommodation.hotels.book': 'Reservar hotel',
'accommodation.hotels.maps': 'Veure a Google Maps',
'accommodation.areas.title': 'Millors Zones',
'accommodation.areas.transportIntro.p1':
'Si has de tornar a casa en tren, les millors opcions són Sants, Eixample Esquerra o Les Corts (per anar a Sants Estació).',
Expand Down
7 changes: 5 additions & 2 deletions src/i18n/accommodation/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ export const en = {
'accommodation.hero.subtitle': 'Recommendations for your stay in Barcelona',
'accommodation.hotels.title': 'Recommended Hotels',
'accommodation.hotels.subtitle': 'Agreements and nearby options',
'accommodation.hotels.disclaimer':
'We are working on exclusive discounts for attendees. Check back soon for promo codes!',
'accommodation.hotels.disclaimer': 'Use the promo code when booking to apply the PyConES discount.',
'accommodation.hotels.promoCode': 'Promo code',
'accommodation.hotels.useDirectLink': 'Use the booking link to apply the discount.',
'accommodation.hotels.book': 'Book hotel',
'accommodation.hotels.maps': 'View on Google Maps',
'accommodation.areas.title': 'Best Areas',
'accommodation.areas.transportIntro.p1':
'If you are heading home by train, the best options are Sants, Eixample Esquerra, or Les Corts (for reaching Sants Estació).',
Expand Down
6 changes: 5 additions & 1 deletion src/i18n/accommodation/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ export const es = {
'accommodation.hotels.title': 'Hoteles Recomendados',
'accommodation.hotels.subtitle': 'Convenios y opciones cercanas',
'accommodation.hotels.disclaimer':
'Estamos trabajando para cerrar convenios exclusivos con descuentos para los asistentes. ¡Vuelve pronto para ver los códigos promocionales!',
'Usa el código promocional al reservar para aplicar el descuento de PyConES.',
'accommodation.hotels.promoCode': 'Código promocional',
'accommodation.hotels.useDirectLink': 'Usa el enlace de reserva para aplicar el descuento.',
'accommodation.hotels.book': 'Reservar hotel',
'accommodation.hotels.maps': 'Ver en Google Maps',
'accommodation.areas.title': 'Mejores Zonas',
'accommodation.areas.transportIntro.p1':
'Si tienes que volver a casa en tren, las mejores opciones son Sants, Eixample Esquerra o Les Corts (para ir a Sants Estació).',
Expand Down
7 changes: 7 additions & 0 deletions src/types/hotels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface IHotel {
name: string
bookingUrl: string
/** Omit when discount is applied via the booking URL only. */
promoCode?: string
mapsUrl: string
}
Loading