Commit 242c66eb by PLN (Algolia)

checkpoint header madness

parent 33f858b7
...@@ -17,55 +17,31 @@ export default function ParVaguesFooter() { ...@@ -17,55 +17,31 @@ export default function ParVaguesFooter() {
const year = new Date().getFullYear(); const year = new Date().getFullYear();
return ( return (
<footer className="bg-black border-t border-[#d900ff]/20 py-12 relative overflow-hidden"> <footer className="bg-black border-t border-[#d900ff]/20 py-8 relative overflow-hidden">
<div className={styles.neonGradient}></div> <div className={styles.neonGradient}></div>
<div className="max-w-6xl mx-auto px-4 relative z-10"> <div className="max-w-6xl mx-auto px-4 relative z-10">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8"> <div className="relative">
<Image
src="/images/parvagues/logo_text.png"
alt="ParVagues Logo"
width={48}
height={48}
className="object-contain"
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{/* About Column */} {/* About Column */}
<div> <div>
<div className="flex items-center mb-4">
<div className="w-12 h-12 relative mr-3">
{/* <Image
src="/images/parvagues/logo.png"
alt="ParVagues Logo"
fill
className="object-contain"
sizes="48px"
/> */}
</div>
<h3 className="text-[#d900ff] font-semibold text-lg">ParVagues</h3>
</div>
<p className="text-gray-400 text-sm mb-4"> <p className="text-gray-400 text-sm mb-4">
Livecoding de musique open-source avec TidalCycles et contrôleur MIDI.<br /> Livecoding de musique open-source avec TidalCycles et contrôleur MIDI.<br />
Performances algorithmiques et création sonore en direct. Performances algorithmiques et création sonore en direct.
</p> </p>
</div> </div>
{/* Navigation Links */}
<div className="w-full bg-black py-2">
<div className="max-w-6xl mx-auto flex justify-center items-center space-x-6 text-xs tracking-widest uppercase">
<Link href="/parvagues#music" className="text-gray-300 hover:text-[#ff3d7b] transition-colors">
MUSIQUE
</Link>
<Link href="/parvagues#performances" className="text-gray-300 hover:text-[#ff3d7b] transition-colors">
PERFORMANCES
</Link>
<Link href="/parvagues#about" className="text-gray-300 hover:text-[#ff3d7b] transition-colors">
À PROPOS
</Link>
<a
href="mailto:parvagues@nech.pl?subject=Booking Request"
className="text-gray-300 hover:text-[#ff3d7b] transition-colors"
>
RÉSERVER UN LIVE
</a>
</div>
</div>
{/* Social Column */} {/* Social Column */}
<div> <div className="flex flex-col items-center md:items-end">
<h3 className="text-[#d900ff] font-semibold text-lg mb-4">Connect</h3> <div className="flex flex-wrap gap-4 justify-center md:justify-end">
<div className="flex flex-wrap gap-4">
{socialLinks.map((link, index) => ( {socialLinks.map((link, index) => (
<a <a
key={index} key={index}
...@@ -82,8 +58,29 @@ export default function ParVaguesFooter() { ...@@ -82,8 +58,29 @@ export default function ParVaguesFooter() {
</div> </div>
</div> </div>
{/* Navigation Links - more compact */}
<div className="w-full bg-black py-4 my-4">
<div className="flex justify-center items-center space-x-4 text-xs tracking-widest uppercase flex-wrap">
<Link href="/parvagues#music" className="text-gray-300 hover:text-[#ff3d7b] transition-colors">
MUSIQUE
</Link>
<Link href="/parvagues#performances" className="text-gray-300 hover:text-[#ff3d7b] transition-colors">
PERFORMANCES
</Link>
<Link href="/parvagues#about" className="text-gray-300 hover:text-[#ff3d7b] transition-colors">
À PROPOS
</Link>
<a
href="mailto:parvagues@nech.pl?subject=Booking Request"
className="text-gray-300 hover:text-[#ff3d7b] transition-colors"
>
RÉSERVER
</a>
</div>
</div>
{/* Bottom Bar */} {/* Bottom Bar */}
<div className="border-t border-gray-800 mt-8 pt-8 text-center text-gray-500 text-sm"> <div className="border-t border-gray-800 mt-4 pt-4 text-center text-gray-500 text-xs">
<p>© {year} ParVagues. All code is open-source.</p> <p>© {year} ParVagues. All code is open-source.</p>
</div> </div>
</div> </div>
......
import React from 'react'; import React, { useState, useEffect } from 'react';
import Link from 'next/link'; import Link from 'next/link';
import Image from 'next/image'; import Image from 'next/image';
import { FaEnvelope } from 'react-icons/fa'; import { FaEnvelope } from 'react-icons/fa';
import styles from '@/styles/parvagues.module.css'; import styles from '@/styles/parvagues.module.css';
import { useRouter } from 'next/router';
export default function ParVaguesHeader({ eventName = null }) { // Custom hook to track scroll position
function useScrolledPast(threshold = 100) {
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const onScroll = () => {
setScrolled(window.scrollY > threshold);
};
// Initial check
onScroll();
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, [threshold]);
return scrolled;
}
export default function ParVaguesHeader({ eventName = null, title = null }) {
const router = useRouter();
const isHome = router.pathname === '/parvagues';
const showInHeader = useScrolledPast(300);
const headerTitle = title || eventName || 'ParVagues';
return ( return (
<header <header
className="fixed top-0 left-0 w-full z-50 bg-black/80 backdrop-blur-md border-b border-[#d900ff]/20 shadow-lg" className="sticky top-0 left-0 w-full z-50 bg-black/80 backdrop-blur-md border-b border-[#d900ff]/20 shadow-lg transition-all duration-300"
> >
<div className="max-w-6xl mx-auto px-4 py-3 flex justify-between items-center"> <div className="max-w-6xl mx-auto px-4 py-3 flex justify-between items-center">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Link href="/parvagues" className="flex items-center"> <Link href="/parvagues" className="flex items-center">
<div className="w-10 h-10 relative mr-2"> <div className="h-10 w-auto relative">
<Image <Image
src="/images/parvagues/favicon.ico" src="/images/parvagues/logo_transparent.png"
alt="ParVagues Logo" alt="ParVagues Logo"
fill width={40}
height={40}
className="object-contain" className="object-contain"
sizes="40px"
/> />
</div> </div>
<span className="text-[#d900ff] text-xl font-bold">ParVagues</span>
{/* Title animation based on scroll */}
<div className="overflow-hidden ml-2">
<span
className={`text-[#d900ff] text-xl font-bold transform transition-all duration-500 ${
showInHeader ? 'opacity-100 translate-y-0' : 'opacity-0 -translate-y-8'
}`}
>
{headerTitle}
</span>
</div>
</Link> </Link>
{eventName && (
<>
<span className="text-gray-500 mx-2">|</span>
<span className="text-white">{eventName}</span>
</>
)}
</div> </div>
{/* Navigation Links */} {/* Navigation Links */}
...@@ -47,10 +76,10 @@ export default function ParVaguesHeader({ eventName = null }) { ...@@ -47,10 +76,10 @@ export default function ParVaguesHeader({ eventName = null }) {
{/* CTA button */} {/* CTA button */}
<a <a
href="mailto:parvagues@nech.pl?subject=Booking Request&body=Bonjour,%0D%0A%0D%0AJe souhaiterais discuter d'une possibilité de performance live coding." href="mailto:parvagues@nech.pl?subject=Booking Request&body=Bonjour,%0D%0A%0D%0AJe souhaiterais discuter d'une possibilité de performance live coding."
className={styles.ctaButton} className={`${styles.ctaButton} ${showInHeader ? 'scale-90' : 'scale-100'} transition-transform duration-300`}
> >
<FaEnvelope className="inline mr-2" /> <FaEnvelope className="inline mr-2" />
Invoquer un live {showInHeader ? 'Invoquer' : 'Invoquer un live'}
</a> </a>
</div> </div>
</header> </header>
......
...@@ -332,7 +332,7 @@ d4 $ note ("<e3 fs3 <gs3 d4> <a3 df4>>" - 12) ...@@ -332,7 +332,7 @@ d4 $ note ("<e3 fs3 <gs3 d4> <a3 df4>>" - 12)
<GlitchText <GlitchText
text="ParVagues" text="ParVagues"
className={styles.glitchEffect} className={styles.glitchEffect}
burstFrequency={450} burstFrequency={4500}
/> />
</h1> </h1>
<p className={styles.heroSubtitle}> <p className={styles.heroSubtitle}>
......
...@@ -35,23 +35,9 @@ export default function Live({ data, slug, images }) { ...@@ -35,23 +35,9 @@ export default function Live({ data, slug, images }) {
} else if (liveImage) { } else if (liveImage) {
setPosterImage(liveImage); setPosterImage(liveImage);
} }
// Could add color extraction here in the future
} }
}, [images]); }, [images]);
// Fix scroll handling for header
useEffect(() => {
const handleScroll = () => {
setScrolled(window.scrollY > window.innerHeight);
};
// Initial check
handleScroll();
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);
// Calculate time to event and set appropriate teasings // Calculate time to event and set appropriate teasings
useEffect(() => { useEffect(() => {
const updateCountdown = () => { const updateCountdown = () => {
...@@ -150,19 +136,6 @@ export default function Live({ data, slug, images }) { ...@@ -150,19 +136,6 @@ export default function Live({ data, slug, images }) {
}).format(date); }).format(date);
}; };
// Format precise date with time (HH:MM:SS)
const formatPreciseDateTime = (date) => {
return new Intl.DateTimeFormat('fr-FR', {
day: 'numeric',
month: 'long',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).format(date);
};
// Scroll to details section // Scroll to details section
const scrollToSection = (e) => { const scrollToSection = (e) => {
e.preventDefault(); e.preventDefault();
...@@ -180,12 +153,11 @@ export default function Live({ data, slug, images }) { ...@@ -180,12 +153,11 @@ export default function Live({ data, slug, images }) {
const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24)); const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
const hours = Math.floor((timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const hours = Math.floor((timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60)); const minutes = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeDiff % (1000 * 60)) / 1000);
if (days > 0) { if (days > 0) {
return `${days}j ${hours}h ${minutes}m ${seconds}s`; return `${days}j ${hours}h ${minutes}m`;
} else { } else {
return `${hours}h ${minutes}m ${seconds}s`; return `${hours}h ${minutes}m`;
} }
}; };
...@@ -218,8 +190,8 @@ export default function Live({ data, slug, images }) { ...@@ -218,8 +190,8 @@ export default function Live({ data, slug, images }) {
if (!url) return null; if (!url) return null;
return ( return (
<div className="my-6"> <div className="my-4">
<h3 className="text-xl font-semibold text-purple-400 mb-3">Audio</h3> <h3 className="text-lg font-semibold text-purple-400 mb-2">Audio</h3>
<audio <audio
controls controls
className="w-full" className="w-full"
...@@ -241,7 +213,8 @@ export default function Live({ data, slug, images }) { ...@@ -241,7 +213,8 @@ export default function Live({ data, slug, images }) {
customStyle={{ customStyle={{
margin: 0, margin: 0,
borderRadius: '8px', borderRadius: '8px',
fontSize: '0.85rem' fontSize: '0.85rem',
maxHeight: '200px' // Limit height
}} }}
> >
{code} {code}
...@@ -254,172 +227,242 @@ export default function Live({ data, slug, images }) { ...@@ -254,172 +227,242 @@ export default function Live({ data, slug, images }) {
<> <>
<Head> <Head>
<title>{data.frontmatter.title} - ParVagues</title> <title>{data.frontmatter.title} - ParVagues</title>
<meta name="description" content={data.frontmatter.description} /> <meta name="description" content={data.frontmatter.description || `ParVagues live: ${data.frontmatter.title}`} />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/images/parvagues/favicon.ico" /> <link rel="icon" href="/images/parvagues/favicon.ico" />
</Head> </Head>
{/* Above fold section */} <div className="flex flex-col min-h-screen bg-black text-white">
<div className="flex flex-col min-h-screen text-white relative overflow-hidden"> {/* Header with event title */}
{/* Background poster image with blur */} <ParVaguesHeader title={data.frontmatter.title} />
{posterImage && (
<div className={styles.posterBackground}>
<Image
src={posterImage}
alt={data.frontmatter.title}
fill
className={styles.posterImageBG}
/>
<div className={styles.posterGradient}></div>
</div>
)}
{/* Neon gradient overlay */}
<div className={styles.neonGradient}></div>
{/* Use the ParVaguesHeader component */}
<ParVaguesHeader eventName={data.frontmatter.title} />
{/* Main content with flex-auto to push footer to bottom */}
<main className="flex-auto"> <main className="flex-auto">
{/* Main hero content */} {/* Hero Section - More compact */}
<div className="relative z-10 flex items-center justify-center min-h-screen pt-16 pb-20"> <section className="relative min-h-[60vh] md:min-h-[50vh] flex items-center overflow-hidden">
<div className="w-full max-w-6xl mx-auto px-4 md:px-6 lg:px-8"> {/* Background Image with gradient overlay */}
<article className="w-full mx-auto bg-black/80 backdrop-filter backdrop-blur-sm rounded-2xl overflow-hidden border border-[#d900ff]/20 shadow-xl shadow-[#d900ff]/20"> {posterImage && (
<div className="grid md:grid-cols-5 gap-0"> <div className="absolute inset-0 z-0">
{/* Left column: Title and info (3/5 width) */} <Image
<div className="md:col-span-3 p-8 md:p-10"> src={posterImage}
<h1 className="text-4xl md:text-5xl font-bold mb-4"> alt={data.frontmatter.title}
<span className={`bg-gradient-to-r from-purple-400 via-pink-500 to-purple-600 bg-clip-text text-transparent z-10 ${styles.glitchEffect}`}> fill
{data.frontmatter.title} className="object-cover"
</span> priority
</h1> />
<h2 className="text-2xl md:text-3xl font-bold mb-4 text-gray-200 z-10"> <div className="absolute inset-0 bg-gradient-to-b from-black/80 via-black/60 to-black"></div>
{data.frontmatter.subtitle || 'ParVagues'} </div>
</h2> )}
<div className="text-gray-300 mb-6">
<p className="text-xl">{data.frontmatter.location}</p> <div className="container mx-auto px-4 py-8 relative z-10">
<p>{formatEventDate(data.frontmatter.date)} {data.frontmatter.time && `• ${data.frontmatter.time}`}</p> <div className="max-w-5xl mx-auto">
</div> <div className="grid md:grid-cols-2 gap-6 items-center">
{/* Left Side: Event Info */}
<div>
<h1 className="text-4xl md:text-5xl font-bold mb-2 text-white">{data.frontmatter.title}</h1>
{/* Countdown or date display */} {pastEvent ? (
{timeToEvent !== null && ( <div className="inline-block bg-gray-800 text-white text-sm px-3 py-1 rounded-full mb-4">
<div className="mb-8"> Événement passé
{timeToEvent >= 0 ? ( </div>
<div className="inline-flex items-center bg-black/60 p-4 rounded-lg border border-[#d900ff]/30"> ) : (
<div className="font-mono text-2xl font-bold mr-3 text-[#d900ff]">{timeString}</div> <div className="inline-block bg-purple-800 text-white text-sm px-3 py-1 rounded-full mb-4">
<div className="text-gray-300">avant l'événement</div> {timeToEvent === 0 ? "Aujourd'hui" : `Dans ${timeString}`}
</div>
) : (
<div className="inline-flex items-center bg-black/60 p-4 rounded-lg border border-[#d900ff]/30">
<div className="text-gray-300">Événement passé</div>
</div>
)}
</div> </div>
)} )}
{/* CTA button and scroll link */} <div className="flex flex-col space-y-2 mb-4">
<div className="flex flex-wrap gap-4"> <div className="flex items-center">
{data.frontmatter.ctaURL && !pastEvent && ( <span className="text-purple-400 w-24">Date:</span>
<span>{formatEventDate(data.frontmatter.date)}</span>
</div>
<div className="flex items-center">
<span className="text-purple-400 w-24">Lieu:</span>
<span>{data.frontmatter.venue || 'À annoncer'}</span>
</div>
{data.frontmatter.city && (
<div className="flex items-center">
<span className="text-purple-400 w-24">Ville:</span>
<span>{data.frontmatter.city}</span>
</div>
)}
</div>
{/* Action Buttons */}
<div className="flex flex-wrap gap-2 mt-4">
{data.frontmatter.ticketLink && !pastEvent && (
<a <a
href={data.frontmatter.ctaURL} href={data.frontmatter.ticketLink}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="inline-flex items-center justify-center px-5 py-3 border border-transparent text-base font-medium rounded-md text-white bg-[#8900b3] hover:bg-[#a700d1] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#d900ff]" className="bg-gradient-to-r from-purple-600 to-pink-600 px-4 py-2 rounded-md text-white font-medium hover:from-purple-700 hover:to-pink-700 transition-all shadow-lg"
> >
{data.frontmatter.ctaText || 'S\'inscrire'} Billets
</a>
)}
{data.frontmatter.eventLink && (
<a
href={data.frontmatter.eventLink}
target="_blank"
rel="noopener noreferrer"
className="bg-gray-800 hover:bg-gray-700 px-4 py-2 rounded-md text-white font-medium transition-all"
>
Info Event
</a> </a>
)} )}
<a <a
href="#details-section" href="#details-section"
onClick={scrollToSection} onClick={scrollToSection}
className="inline-flex items-center justify-center px-5 py-3 border border-gray-300 text-base font-medium rounded-md text-gray-200 bg-black/40 hover:bg-black/60 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500" className="bg-transparent border border-purple-500 hover:bg-purple-900/30 px-4 py-2 rounded-md text-white font-medium transition-all"
> >
En savoir plus Détails
</a> </a>
</div> </div>
</div> </div>
{/* Right column: status and teasers (2/5 width) */} {/* Right Side: Upcoming Drops or Latest Teasing */}
<div className="md:col-span-2 bg-black/80 p-8 md:p-10 border-t md:border-t-0 md:border-l border-[#d900ff]/20"> <div>
{/* Upcoming drops section */} {!pastEvent && upcomingDrops.length > 0 ? (
{upcomingDrops.length > 0 && ( <div className="bg-black/60 backdrop-blur p-4 rounded-lg border border-purple-500/30">
<div className="mb-8 bg-black/50 border border-[#d900ff]/30 rounded-lg p-4"> <h3 className="text-lg font-semibold text-purple-400 mb-3">Prochains drops</h3>
<h3 className="text-xl font-bold text-[#d900ff] mb-4">Prochains drops</h3> <ul className="space-y-2">
<div className="space-y-4"> {upcomingDrops.map((drop, i) => (
{upcomingDrops.map((drop, index) => ( <li key={i} className="flex items-center justify-between bg-black/50 p-2 rounded">
<div key={index} className="flex items-center justify-between gap-4 border-b border-[#d900ff]/10 pb-4 last:border-0 last:pb-0"> <span className="text-sm">{drop.name}</span>
<div> <span className="text-xs bg-purple-900/60 px-2 py-1 rounded">
<div className="text-lg font-medium text-gray-200">{drop.name}</div> {getTimeDifferenceString(drop.date)}
<div className="text-sm text-gray-400">{formatPreciseDateTime(drop.date)}</div> </span>
</div> </li>
<div className="font-mono text-lg text-[#ff3d7b]">{getTimeDifferenceString(drop.date)}</div>
</div>
))} ))}
</div> </ul>
<div className="mt-4 text-sm text-center text-gray-400 italic">
Reviens à ces moments précis pour ne rien manquer...
</div>
</div> </div>
)} ) : teasingsToShow.length > 0 ? (
<div className="bg-black/60 backdrop-blur p-4 rounded-lg border border-purple-500/30">
{/* Teasings */} <h3 className="text-lg font-semibold text-purple-400 mb-2">
{teasingsToShow.length > 0 && ( {pastEvent ? "Highlights" : "Teasing"}
<div> </h3>
<h3 className="text-xl font-bold text-[#d900ff] mb-4">Teasings</h3> <div dangerouslySetInnerHTML={renderMarkdown(teasingsToShow[0])} className="prose prose-sm prose-invert max-w-none" />
<div className="space-y-6">
{teasingsToShow.map((teasing, index) => (
<div key={index} className="bg-black/50 border border-[#d900ff]/30 rounded-lg p-4">
<div dangerouslySetInnerHTML={renderMarkdown(teasing)} className="prose prose-sm prose-invert max-w-none" />
</div>
))}
</div>
</div> </div>
)} ) : null}
</div> </div>
</div> </div>
</article> </div>
</div> </div>
</div> </section>
{/* Details section */} {/* Details Section - More compact */}
<section id="details-section" className={styles.sectionContainer}> <section id="details-section" className="bg-black py-8">
<div className="max-w-6xl mx-auto px-4 md:px-6 lg:px-8"> <div className="container mx-auto px-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 lg:gap-8"> <div className="max-w-5xl mx-auto grid md:grid-cols-12 gap-6">
{/* Main Content */}
{/* Description */} <div className="md:col-span-8">
<div> {/* Main Content */}
<h2 className="text-2xl font-bold text-[#d900ff] mb-6">À propos de cet événement</h2> <div className="bg-gray-900/40 rounded-lg p-4 mb-4">
<div <h2 className="text-xl font-bold mb-3 text-purple-400">À propos</h2>
className="prose prose-lg prose-invert" <div
dangerouslySetInnerHTML={renderMarkdown(data.content)} dangerouslySetInnerHTML={renderMarkdown(data.content)}
/> className="prose prose-sm prose-invert max-w-none"
/>
</div>
{/* Media */} {/* Media */}
{data.frontmatter.youtubeURL && renderYouTubeEmbed(data.frontmatter.youtubeURL)} {data.frontmatter.youtubeUrl && (
{data.frontmatter.audioURL && renderAudioPlayer(data.frontmatter.audioURL)} <div className="mb-4">
{renderYouTubeEmbed(data.frontmatter.youtubeUrl)}
</div>
)}
{data.frontmatter.audioUrl && renderAudioPlayer(data.frontmatter.audioUrl)}
{/* Code Sample */}
{data.frontmatter.codeSnippet && (
<div className="bg-gray-900/40 rounded-lg p-4 mb-4">
<h3 className="text-lg font-semibold text-purple-400 mb-2">Code Snippet</h3>
{renderCodeBlock(data.frontmatter.codeSnippet)}
</div>
)}
</div> </div>
{/* Gallery */} {/* Sidebar */}
<div> <div className="md:col-span-4">
{images && images.length > 0 && ( {/* More Teasings */}
<div> {teasingsToShow.length > 1 && (
<h2 className="text-2xl font-bold text-[#d900ff] mb-6">Galerie</h2> <div className="bg-gray-900/40 rounded-lg p-4 mb-4">
<ImageGallery <h3 className="text-lg font-semibold text-purple-400 mb-2">Plus de teasings</h3>
images={images} <div className="space-y-3">
slug={slug} {teasingsToShow.slice(1).map((teasing, i) => (
alt={data.frontmatter.title} <div
className="filter grayscale hover:grayscale-0" key={i}
className="bg-black/60 p-3 rounded"
>
<div
dangerouslySetInnerHTML={renderMarkdown(teasing)}
className="prose prose-xs prose-invert max-w-none"
/>
</div>
))}
</div>
</div>
)}
{/* Artists */}
{data.frontmatter.artists && (
<div className="bg-gray-900/40 rounded-lg p-4 mb-4">
<h3 className="text-lg font-semibold text-purple-400 mb-2">Artistes</h3>
<div
dangerouslySetInnerHTML={renderMarkdown(data.frontmatter.artists)}
className="prose prose-sm prose-invert max-w-none"
/> />
</div> </div>
)} )}
{/* Mini Gallery - Limited to 3 images */}
{images && images.length > 0 && (
<div className="bg-gray-900/40 rounded-lg p-4">
<h3 className="text-lg font-semibold text-purple-400 mb-2">Photos</h3>
<div className="grid grid-cols-3 gap-2">
{images.slice(0, 3).map((image, i) => (
<div key={i} className="relative aspect-square rounded overflow-hidden">
<Image
src={image}
alt={`${data.frontmatter.title} - image ${i+1}`}
fill
className="object-cover hover:scale-110 transition-transform duration-300"
/>
</div>
))}
</div>
{images.length > 3 && (
<div className="mt-2 text-center">
<button
onClick={() => {
const gallery = document.getElementById('full-gallery');
gallery?.scrollIntoView({ behavior: 'smooth' });
}}
className="text-xs text-purple-400 hover:text-purple-300"
>
Voir les {images.length} photos →
</button>
</div>
)}
</div>
)}
</div> </div>
</div> </div>
{/* Full Gallery - only if more than 3 images */}
{images && images.length > 3 && (
<div id="full-gallery" className="max-w-5xl mx-auto mt-8">
<ImageGallery images={images} slug={slug} />
</div>
)}
</div> </div>
</section> </section>
</main> </main>
{/* Footer */}
<ParVaguesFooter /> <ParVaguesFooter />
</div> </div>
</> </>
...@@ -428,26 +471,33 @@ export default function Live({ data, slug, images }) { ...@@ -428,26 +471,33 @@ export default function Live({ data, slug, images }) {
export async function getStaticPaths() { export async function getStaticPaths() {
const lives = getAllLives(); const lives = getAllLives();
const paths = lives.map((live) => ({ const paths = lives.map(live => ({
params: { id: live.slug }, params: { id: live.slug }
})); }));
return { return {
paths, paths,
fallback: 'blocking', fallback: false,
}; };
} }
export async function getStaticProps({ params }) { export async function getStaticProps({ params }) {
const data = await getLiveData(params.id); try {
const images = getLivesImages(params.id); const liveData = getLiveData(params.id);
const images = getLivesImages(params.id);
return {
props: { return {
data, props: {
slug: params.id, data: liveData,
images, slug: params.id,
}, images,
revalidate: 60, // Revalidate every minute },
}; revalidate: 60,
};
} catch (error) {
console.error(`Error getting data for ${params.id}:`, error);
return {
notFound: true,
};
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment