2026-03-05 19:35:02 +01:00
|
|
|
---
|
|
|
|
|
import Layout from '../../layouts/Layout.astro';
|
|
|
|
|
import { Icon } from 'astro-icon/components';
|
2026-03-05 22:22:32 +01:00
|
|
|
import { getLiveStory, storyblokEditable } from '@storyblok/astro';
|
|
|
|
|
import { fetchSpectacles, fetchAgenda, mapStoryToSpectacle } from '../../lib/storyblok';
|
2026-03-05 19:35:02 +01:00
|
|
|
|
2026-03-05 22:22:32 +01:00
|
|
|
// Requis pour le build statique (ignoré en SSR)
|
|
|
|
|
export async function getStaticPaths() {
|
|
|
|
|
const [spectacles, agenda] = await Promise.all([fetchSpectacles(), fetchAgenda()]);
|
2026-03-05 19:35:02 +01:00
|
|
|
return spectacles.map(s => ({
|
|
|
|
|
params: { id: s.id },
|
2026-03-05 22:22:32 +01:00
|
|
|
props: {
|
|
|
|
|
spectacle: s,
|
|
|
|
|
upcomingDates: agenda
|
|
|
|
|
.filter(event => event.spectacleId === s.id)
|
|
|
|
|
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()),
|
|
|
|
|
},
|
2026-03-05 19:35:02 +01:00
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { id } = Astro.params;
|
2026-03-05 22:22:32 +01:00
|
|
|
const liveStory = await getLiveStory(Astro);
|
2026-03-05 19:35:02 +01:00
|
|
|
|
2026-03-05 22:22:32 +01:00
|
|
|
let spectacle;
|
|
|
|
|
let upcomingDates;
|
|
|
|
|
|
|
|
|
|
if (liveStory) {
|
|
|
|
|
spectacle = mapStoryToSpectacle(liveStory);
|
|
|
|
|
const agenda = await fetchAgenda();
|
|
|
|
|
upcomingDates = agenda
|
|
|
|
|
.filter(event => event.spectacleId === spectacle.id)
|
|
|
|
|
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
|
|
|
|
|
} else if (Astro.props.spectacle) {
|
|
|
|
|
({ spectacle, upcomingDates } = Astro.props);
|
|
|
|
|
} else {
|
|
|
|
|
const [spectacles, agenda] = await Promise.all([fetchSpectacles(), fetchAgenda()]);
|
|
|
|
|
spectacle = spectacles.find(s => s.id === id);
|
|
|
|
|
upcomingDates = agenda
|
|
|
|
|
.filter(event => event.spectacleId === id)
|
|
|
|
|
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!spectacle) {
|
|
|
|
|
return Astro.redirect('/spectacles/');
|
|
|
|
|
}
|
2026-03-05 19:35:02 +01:00
|
|
|
---
|
|
|
|
|
|
|
|
|
|
<Layout title={`${spectacle.title} — Compagnie AspiRêves`}>
|
2026-03-05 22:22:32 +01:00
|
|
|
<div {...storyblokEditable(spectacle._blok)} class="pt-24 md:pt-32 pb-24 min-h-screen bg-cloud overflow-hidden relative">
|
2026-03-05 19:35:02 +01:00
|
|
|
<!-- Background -->
|
|
|
|
|
<div class="absolute top-0 right-0 w-96 h-96 bg-dream-purple/20 rounded-full filter blur-[100px] -translate-y-1/2 translate-x-1/2"></div>
|
|
|
|
|
<div class="absolute bottom-0 left-0 w-96 h-96 bg-dream-blue/20 rounded-full filter blur-[100px] translate-y-1/2 -translate-x-1/2"></div>
|
|
|
|
|
|
|
|
|
|
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
|
|
|
|
<!-- Back button -->
|
|
|
|
|
<a
|
|
|
|
|
href="/spectacles/"
|
|
|
|
|
class="animate-entrance group flex items-center gap-3 mb-10 font-sans font-bold text-night/60 hover:text-dream-purple transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<Icon name="lucide:arrow-left" size={20} class="group-hover:-translate-x-1 transition-transform" />
|
|
|
|
|
Tous les spectacles
|
|
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
<!-- Hero -->
|
|
|
|
|
<div class="flex flex-col md:flex-row gap-10 md:gap-16 items-center mb-16 md:mb-24">
|
|
|
|
|
<div class="fade-scale w-full md:w-1/2">
|
|
|
|
|
<div class="relative aspect-[4/3] overflow-hidden rounded-[32px] md:rounded-[60px] shadow-2xl shadow-night/10 border-4 md:border-8 border-white">
|
|
|
|
|
<img
|
|
|
|
|
src={spectacle.image}
|
|
|
|
|
alt={spectacle.title}
|
|
|
|
|
class="object-cover w-full h-full"
|
|
|
|
|
referrerpolicy="no-referrer"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="fade-up w-full md:w-1/2 space-y-6 text-center md:text-left" style="transition-delay: 0.2s">
|
|
|
|
|
{spectacle.retired && (
|
|
|
|
|
<div class="inline-block px-4 py-1 rounded-full bg-night/10 text-night/50 font-sans text-xs font-bold uppercase tracking-widest">
|
|
|
|
|
Spectacle retraité
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<h1 class="font-display text-4xl sm:text-6xl md:text-7xl text-night">{spectacle.title}</h1>
|
|
|
|
|
|
|
|
|
|
<div class="flex flex-wrap justify-center md:justify-start gap-3">
|
|
|
|
|
<div class="flex items-center gap-2 bg-dream-blue/20 px-5 py-2 rounded-2xl text-sm font-sans font-bold">
|
|
|
|
|
<Icon name="lucide:users" size={16} class="text-dream-blue" />
|
|
|
|
|
<span>{spectacle.age}</span>
|
|
|
|
|
</div>
|
|
|
|
|
{spectacle.duration && (
|
|
|
|
|
<div class="flex items-center gap-2 bg-dream-pink/20 px-5 py-2 rounded-2xl text-sm font-sans font-bold">
|
|
|
|
|
<Icon name="lucide:clock" size={16} class="text-dream-pink" />
|
|
|
|
|
<span>{spectacle.duration}</span>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{spectacle.summary && (
|
2026-03-05 22:55:19 +01:00
|
|
|
<p class="font-sans text-lg md:text-xl text-night/70 leading-relaxed whitespace-pre-line">{spectacle.summary}</p>
|
2026-03-05 19:35:02 +01:00
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{spectacle.credits && (
|
|
|
|
|
<p class="font-sans text-sm text-night/50 font-bold uppercase tracking-widest">{spectacle.credits}</p>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{spectacle.dossierPro && spectacle.dossierPro !== '#' && (
|
|
|
|
|
<a
|
|
|
|
|
href={spectacle.dossierPro}
|
|
|
|
|
target="_blank"
|
|
|
|
|
rel="noreferrer"
|
|
|
|
|
class="inline-flex items-center gap-3 bg-night text-cloud px-8 py-4 rounded-full font-sans font-bold tracking-wide hover:bg-night/90 transition-all hover:scale-105 shadow-lg shadow-night/20"
|
|
|
|
|
>
|
|
|
|
|
<Icon name="lucide:download" size={18} />
|
|
|
|
|
Dossier Pédagogique
|
|
|
|
|
</a>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Gallery -->
|
|
|
|
|
{spectacle.gallery.length > 0 && (
|
|
|
|
|
<section class="fade-up mb-16 md:mb-24">
|
|
|
|
|
<h2 class="font-display text-3xl md:text-5xl text-night mb-8 md:mb-12 text-center">Galerie</h2>
|
|
|
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-6 md:gap-8">
|
|
|
|
|
{spectacle.gallery.map((img, index) => (
|
|
|
|
|
<div class="aspect-[4/3] overflow-hidden rounded-[24px] md:rounded-[40px] shadow-xl shadow-night/5 border-4 border-white">
|
|
|
|
|
<img
|
|
|
|
|
src={img}
|
|
|
|
|
alt={`${spectacle.title} - photo ${index + 1}`}
|
|
|
|
|
class="object-cover w-full h-full"
|
|
|
|
|
referrerpolicy="no-referrer"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<!-- Upcoming dates -->
|
|
|
|
|
{upcomingDates.length > 0 && (
|
|
|
|
|
<section class="fade-up">
|
|
|
|
|
<h2 class="font-display text-3xl md:text-5xl text-night mb-8 md:mb-12 text-center">Prochaines dates</h2>
|
|
|
|
|
<div class="space-y-4 max-w-2xl mx-auto">
|
|
|
|
|
{upcomingDates.map(event => {
|
|
|
|
|
const dateObj = new Date(event.date);
|
|
|
|
|
return (
|
|
|
|
|
<div class="flex flex-col sm:flex-row gap-6 p-6 md:p-8 rounded-[24px] bg-white shadow-lg shadow-night/5 border border-dream-purple/10 items-center">
|
|
|
|
|
<div class="flex-shrink-0 flex flex-col items-center justify-center w-20 h-20 md:w-24 md:h-24 rounded-2xl bg-gradient-to-br from-dream-purple to-dream-blue text-white shadow-md">
|
|
|
|
|
<span class="font-sans text-[10px] md:text-xs font-bold uppercase tracking-widest opacity-80">
|
|
|
|
|
{dateObj.toLocaleString('fr-FR', { month: 'short' })}
|
|
|
|
|
</span>
|
|
|
|
|
<span class="font-display text-3xl md:text-4xl">
|
|
|
|
|
{dateObj.getDate()}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex-grow text-center sm:text-left">
|
|
|
|
|
<div class="flex items-center justify-center sm:justify-start gap-2 font-sans text-sm text-night/50 mb-1">
|
|
|
|
|
<Icon name="lucide:calendar" size={14} />
|
|
|
|
|
<span>{dateObj.toLocaleString('fr-FR', { weekday: 'long', hour: '2-digit', minute: '2-digit' })}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex items-center justify-center sm:justify-start gap-2 font-sans font-bold text-night">
|
|
|
|
|
<Icon name="lucide:map-pin" size={14} class="text-dream-coral" />
|
|
|
|
|
<span>{event.location}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{event.bookingLink && (
|
|
|
|
|
<a
|
|
|
|
|
href={event.bookingLink}
|
|
|
|
|
target="_blank"
|
|
|
|
|
rel="noreferrer"
|
|
|
|
|
class="bg-dream-coral text-white px-6 py-3 rounded-full font-sans font-bold text-sm hover:scale-105 transition-transform shadow-md"
|
|
|
|
|
>
|
|
|
|
|
Réserver
|
|
|
|
|
</a>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</Layout>
|