feat: ajoute une page pour afficher les spectacles passés et intègre leur grille sur la page d'accueil
This commit is contained in:
parent
140666183d
commit
74cfe041ae
5 changed files with 343 additions and 39 deletions
147
src/components/GrilleSpectaclesPassés.astro
Normal file
147
src/components/GrilleSpectaclesPassés.astro
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
---
|
||||
const { evenements, limite = 6, afficherLienVoirTout = true } = Astro.props;
|
||||
const evenementsAffiches = evenements.slice(0, limite);
|
||||
---
|
||||
|
||||
{evenementsAffiches.length > 0 && (
|
||||
<div class="spectacles-passes">
|
||||
<div class="grille-affiches">
|
||||
{evenementsAffiches.map(evenement => (
|
||||
evenement.affiche && (
|
||||
<a href={`/evenements/${evenement.slug}`} class="affiche-link">
|
||||
<img
|
||||
src={evenement.affiche.fields.file.url}
|
||||
alt={`Affiche de ${evenement.nom}`}
|
||||
class="affiche-thumb"
|
||||
/>
|
||||
<div class="affiche-overlay">
|
||||
<span class="affiche-nom">{evenement.nom}</span>
|
||||
<span class="affiche-date">{new Date(evenement.date).toLocaleDateString("fr-FR")}</span>
|
||||
</div>
|
||||
</a>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
{afficherLienVoirTout && evenements.length > limite && (
|
||||
<a href="/evenements" class="voir-tout">Voir tous nos spectacles passés →</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<style>
|
||||
.spectacles-passes {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.grille-affiches {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.affiche-link {
|
||||
position: relative;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.affiche-link:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.affiche-thumb {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
aspect-ratio: 3/4;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.affiche-overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.8), transparent);
|
||||
color: white;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.affiche-nom {
|
||||
font-weight: bold;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.affiche-date {
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.voir-tout {
|
||||
display: inline-block;
|
||||
margin-top: 1rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background-color: orange;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border: black solid 2px;
|
||||
border-radius: 0.4rem;
|
||||
font-weight: bold;
|
||||
box-shadow: 0.2em 0.2em 0.5em rgba(0,0,0,0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.voir-tout:hover {
|
||||
background-color: darkorange;
|
||||
transform: translateX(5px);
|
||||
box-shadow: 0.3em 0.3em 0.7em rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.grille-affiches {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.affiche-overlay {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.affiche-nom {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.affiche-date {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.grille-affiches {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.affiche-overlay {
|
||||
padding: 0.4rem;
|
||||
}
|
||||
|
||||
.affiche-nom {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.affiche-date {
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -18,6 +18,7 @@ export interface ContentFulEvenement {
|
|||
lieuUrl: EntryFieldTypes.Text,
|
||||
position: EntryFieldTypes.Location,
|
||||
affiche: EntryFieldTypes.AssetLink,
|
||||
album?: EntryFieldTypes.Text,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +32,8 @@ export const evenementFromContentfull = ({
|
|||
affiche,
|
||||
lieu,
|
||||
lieuUrl,
|
||||
position
|
||||
position,
|
||||
album
|
||||
}
|
||||
}: Entry<ContentFulEvenement>): Évènement => ({
|
||||
nom: nom as string,
|
||||
|
|
@ -41,7 +43,8 @@ export const evenementFromContentfull = ({
|
|||
affiche,
|
||||
lieu: lieu as string | undefined,
|
||||
lieuUrl: lieuUrl as string | undefined,
|
||||
position
|
||||
position,
|
||||
album: album as string | undefined
|
||||
})
|
||||
|
||||
export const sortByDate = (evenements: Entry<ContentFulEvenement>[]): Entry<ContentFulEvenement>[] => {
|
||||
|
|
@ -55,7 +58,7 @@ export const sortByDate = (evenements: Entry<ContentFulEvenement>[]): Entry<Cont
|
|||
type ContentfulDate = `${number}-${number}-${number}T${number}:${number}:${number}Z`
|
||||
|
||||
interface Évènement {
|
||||
nom: string, slug: string, description: Document, date: ContentfulDate, affiche: unknown, lieu?: string | undefined, lieuUrl?: string | undefined, position: unknown
|
||||
nom: string, slug: string, description: Document, date: ContentfulDate, affiche: unknown, lieu?: string | undefined, lieuUrl?: string | undefined, position: unknown, album?: string | undefined
|
||||
}
|
||||
|
||||
function estÀVenir(évènement: Évènement): boolean {
|
||||
|
|
@ -68,6 +71,10 @@ function estÀVenir(évènement: Évènement): boolean {
|
|||
return (dateEvenement.getTime() - aujourdhui.getTime() >= 0);
|
||||
}
|
||||
|
||||
function estPassé(évènement: Évènement): boolean {
|
||||
return !estÀVenir(évènement);
|
||||
}
|
||||
|
||||
export const fetchEvenements = async () => {
|
||||
const entries = await contentfulClient.getEntries<ContentFulEvenement>({
|
||||
content_type: "evenement",
|
||||
|
|
@ -78,6 +85,17 @@ export const fetchEvenements = async () => {
|
|||
.filter(estÀVenir)
|
||||
}
|
||||
|
||||
export const fetchEvenementsPassés = async () => {
|
||||
const entries = await contentfulClient.getEntries<ContentFulEvenement>({
|
||||
content_type: "evenement",
|
||||
});
|
||||
|
||||
return sortByDate(entries.items)
|
||||
.map(evenementFromContentfull)
|
||||
.filter(estPassé)
|
||||
.reverse() // Afficher les plus récents en premier
|
||||
}
|
||||
|
||||
export const fetchEvenement = async (slug: string) => {
|
||||
const entries = await contentfulClient.getEntries<ContentFulEvenement>({
|
||||
content_type: "evenement",
|
||||
|
|
|
|||
67
src/pages/evenements.astro
Normal file
67
src/pages/evenements.astro
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import { fetchEvenementsPassés } from "../lib/contentful";
|
||||
import GrilleSpectaclesPassés from "../components/GrilleSpectaclesPassés.astro";
|
||||
|
||||
export const prerender = false
|
||||
|
||||
const evenementsPassés = await fetchEvenementsPassés()
|
||||
---
|
||||
|
||||
<Layout title="Nos spectacles passés - Les Particules">
|
||||
<main>
|
||||
<a href="../" style="text-decoration: none; color: black; display: inline">
|
||||
<img alt="Logo" src="/les-particules-bleu-sur-noir-pastille.svg" style="width:80px; vertical-align: middle; display: inline" />
|
||||
</a>
|
||||
<h1 style="display: inline; margin-left: 0.2em">
|
||||
Nos spectacles passés
|
||||
</h1>
|
||||
|
||||
{evenementsPassés.length > 0 ? (
|
||||
<div class="dates">
|
||||
<p class="intro">
|
||||
Découvrez les spectacles que nous avons eu le plaisir de jouer !
|
||||
</p>
|
||||
<GrilleSpectaclesPassés evenements={evenementsPassés} afficherLienVoirTout={false} limite={999} />
|
||||
</div>
|
||||
) : (
|
||||
<div class="dates">
|
||||
<p class="intro">
|
||||
Aucun spectacle passé pour le moment. Revenez bientôt !
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
main {
|
||||
margin: auto;
|
||||
padding: 1.5rem;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.dates {
|
||||
line-height: 1.6;
|
||||
margin: 1rem 0;
|
||||
border: 1px solid rgba(var(--accent), 25%);
|
||||
background-color: white;
|
||||
padding: 1rem;
|
||||
border-radius: 0.4rem;
|
||||
box-shadow: 0.2em 0.2em 1em black;
|
||||
}
|
||||
|
||||
.intro {
|
||||
font-size: 1.1em;
|
||||
margin-bottom: 1.5em;
|
||||
color: rgb(68,68,68);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -9,14 +9,16 @@ export const prerender = false
|
|||
|
||||
const {slug} = Astro.params
|
||||
|
||||
const { nom, description, date, lieu, lieuUrl, affiche } = await fetchEvenement(slug)
|
||||
const { nom, description, date, lieu, lieuUrl, affiche, album } = await fetchEvenement(slug)
|
||||
|
||||
const Wrapper = lieuUrl ? 'a' : 'div'
|
||||
|
||||
---
|
||||
<Layout>
|
||||
<h1>{nom}</h1>
|
||||
<div class="content">
|
||||
{affiche && <img alt=`Affiche de ${nom}` src={affiche?.fields.file.url} />}
|
||||
<div class="infos">
|
||||
<Card title={date ? new Date(date as string).toLocaleDateString("fr-FR") : ""}>
|
||||
<div>
|
||||
à <Wrapper target="_blank" href={lieuUrl}>{lieu}</Wrapper>
|
||||
|
|
@ -25,6 +27,21 @@ const Wrapper = lieuUrl ? 'a' : 'div'
|
|||
description && <article set:html={documentToHtmlString(description)} />
|
||||
}
|
||||
</Card>
|
||||
|
||||
{album && (
|
||||
<div class="album">
|
||||
<div class="flickr-embed">
|
||||
<iframe
|
||||
src={`${album}/player/`}
|
||||
width="100%"
|
||||
height="500"
|
||||
frameborder="0"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
|
|
@ -42,31 +59,77 @@ const Wrapper = lieuUrl ? 'a' : 'div'
|
|||
</style>
|
||||
|
||||
<style>
|
||||
img {
|
||||
float:left;
|
||||
width: 30%;
|
||||
margin: 1em;
|
||||
.content {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.content > img {
|
||||
flex: 0 0 30%;
|
||||
max-width: 30%;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.infos {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
article {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.album {
|
||||
margin: 2rem 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
max-width: 80ch; /* Même largeur max que la Card */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.album h2 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 1rem;
|
||||
color: rgb(68,68,68);
|
||||
}
|
||||
|
||||
.flickr-embed {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Styles pour rendre l'embed Flickr responsive */
|
||||
.flickr-embed {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Force l'iframe Flickr à être responsive */
|
||||
.flickr-embed iframe {
|
||||
max-width: 100% !important;
|
||||
width: 100% !important;
|
||||
min-height: 400px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.flickr-embed img {
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
@media (max-width: 48em) {
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
img {
|
||||
float:none;
|
||||
.content > img {
|
||||
width: 100%;
|
||||
border: black solid 0.1em;
|
||||
border-radius: 1em;
|
||||
margin: 1em auto;
|
||||
margin: 0 auto 1em;
|
||||
box-shadow: 0.2em 0.2em 1em black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import Card from '../components/Card.astro';
|
||||
import { fetchEvenements } from "../lib/contentful";
|
||||
import { fetchEvenements, fetchEvenementsPassés } from "../lib/contentful";
|
||||
import Evenement from "../components/Evenement.astro";
|
||||
import FlickrGallery from "../components/FlickrGallery.astro";
|
||||
import GrilleSpectaclesPassés from "../components/GrilleSpectaclesPassés.astro";
|
||||
|
||||
export const prerender = false
|
||||
|
||||
const evenements = await fetchEvenements()
|
||||
const evenementsPassés = await fetchEvenementsPassés()
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -17,6 +18,8 @@ const evenements = await fetchEvenements()
|
|||
📌 Albi, Occitanie
|
||||
</p>
|
||||
|
||||
{evenements.length > 0 && (
|
||||
<>
|
||||
<h2>Retrouvez-nous prochainement</h2>
|
||||
<div class="dates">
|
||||
<div class="evenements">
|
||||
|
|
@ -25,16 +28,22 @@ const evenements = await fetchEvenements()
|
|||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<h2>Nos derniers spectacles</h2>
|
||||
<FlickrGallery />
|
||||
{evenementsPassés.length > 0 && (
|
||||
<>
|
||||
<h2>Nos spectacles passés</h2>
|
||||
<GrilleSpectaclesPassés evenements={evenementsPassés} limite={6} />
|
||||
</>
|
||||
)}
|
||||
|
||||
<ul role="list" class="link-card-grid">
|
||||
<Card
|
||||
title="Les Particules Fines"
|
||||
href="/fines"
|
||||
>
|
||||
Cours d’impro adultes débutant·e·s
|
||||
Cours d'impro adultes débutant·e·s
|
||||
</Card>
|
||||
<Card
|
||||
href="./inscription-newsletter"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue