Commit fc9f96aa by PLN (Algolia)

next: iterate on pages

parent ae3a9078
import React from 'react'; import React, { useState, useEffect } from 'react';
import Link from 'next/link'; import Link from 'next/link';
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';
export default function ParVaguesHeader({ eventName = null }) { export default function ParVaguesHeader({ eventName = null }) {
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => {
setScrolled(window.scrollY > window.innerHeight);
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
return ( return (
<header <header
className="sticky top-0 z-50 bg-gradient-to-b from-black/90 to-black/70 backdrop-blur-md transition-all duration-300 border-b border-purple-500/20 shadow-lg" className="fixed top-0 left-0 w-full z-50 bg-gradient-to-b from-black/90 to-black/70 backdrop-blur-md transition-all duration-300 border-b border-purple-500/20 shadow-lg"
> >
<div className="max-w-6xl mx-auto px-4 py-4 flex justify-between items-center"> <div className="max-w-6xl mx-auto px-4 py-4 flex justify-between items-center">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
...@@ -37,10 +50,10 @@ export default function ParVaguesHeader({ eventName = null }) { ...@@ -37,10 +50,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} transition-all duration-300`} className={`${styles.ctaButton} transition-all duration-300 ${scrolled ? 'bg-purple-600 scale-90' : ''}`}
> >
<FaEnvelope className="inline mr-2" /> <FaEnvelope className="inline mr-2" />
Invoquer un live {scrolled ? 'Invoquer' : 'Invoquer un live'}
</a> </a>
</div> </div>
</header> </header>
......
...@@ -36,7 +36,8 @@ function CodeBlock({ children, height = '400px', isTerminal = false }) { ...@@ -36,7 +36,8 @@ function CodeBlock({ children, height = '400px', isTerminal = false }) {
margin: 0, margin: 0,
borderRadius: isTerminal ? '0 0 8px 8px' : '8px', borderRadius: isTerminal ? '0 0 8px 8px' : '8px',
height: 'auto', height: 'auto',
maxHeight: isTerminal ? `calc(${height} - 30px)` : height maxHeight: isTerminal ? `calc(${height} - 30px)` : height,
fontSize: '0.85rem'
}} }}
> >
{children} {children}
...@@ -374,7 +375,7 @@ d4 $ note ("<e3 fs3 <gs3 d4> <a3 df4>>" - 12) ...@@ -374,7 +375,7 @@ d4 $ note ("<e3 fs3 <gs3 d4> <a3 df4>>" - 12)
<div className={styles.splitSection}> <div className={styles.splitSection}>
<div> <div>
<div <div
className={`${styles.bulletPoint} cursor-pointer ${selectedSection === 'potentiel' ? 'text-purple-400' : ''}`} className={`${styles.bulletPoint} cursor-pointer ${selectedSection === 'potentiel' ? 'text-purple-400 border-l-2 border-purple-400 pl-4' : ''}`}
onClick={() => setSelectedSection('potentiel')} onClick={() => setSelectedSection('potentiel')}
> >
<h3 className="text-xl font-semibold text-purple-400 mb-2">Potentiel</h3> <h3 className="text-xl font-semibold text-purple-400 mb-2">Potentiel</h3>
...@@ -382,7 +383,7 @@ d4 $ note ("<e3 fs3 <gs3 d4> <a3 df4>>" - 12) ...@@ -382,7 +383,7 @@ d4 $ note ("<e3 fs3 <gs3 d4> <a3 df4>>" - 12)
</div> </div>
<div <div
className={`${styles.bulletPoint} cursor-pointer ${selectedSection === 'composition' ? 'text-purple-400' : ''}`} className={`${styles.bulletPoint} cursor-pointer ${selectedSection === 'composition' ? 'text-purple-400 border-l-2 border-purple-400 pl-4' : ''}`}
onClick={() => setSelectedSection('composition')} onClick={() => setSelectedSection('composition')}
> >
<h3 className="text-xl font-semibold text-purple-400 mb-2">Composition</h3> <h3 className="text-xl font-semibold text-purple-400 mb-2">Composition</h3>
...@@ -390,37 +391,23 @@ d4 $ note ("<e3 fs3 <gs3 d4> <a3 df4>>" - 12) ...@@ -390,37 +391,23 @@ d4 $ note ("<e3 fs3 <gs3 d4> <a3 df4>>" - 12)
</div> </div>
<div <div
className={`${styles.bulletPoint} cursor-pointer ${selectedSection === 'performance' ? 'text-purple-400' : ''}`} className={`${styles.bulletPoint} cursor-pointer ${selectedSection === 'performance' ? 'text-purple-400 border-l-2 border-purple-400 pl-4' : ''}`}
onClick={() => setSelectedSection('performance')} onClick={() => setSelectedSection('performance')}
> >
<h3 className="text-xl font-semibold text-purple-400 mb-2">Performance</h3> <h3 className="text-xl font-semibold text-purple-400 mb-2">Performance</h3>
<p className="text-gray-300">Live improvise avec contrôleur MIDI pour une interactivité totale</p> <p className="text-gray-300">Live improvise avec contrôleur MIDI pour une interactivité totale</p>
</div> </div>
</div>
<div> <div
<div className="mb-4 flex gap-4"> className={`${styles.bulletPoint} cursor-pointer ${selectedSection === 'code' ? 'text-purple-400 border-l-2 border-purple-400 pl-4' : ''}`}
<button
onClick={() => setSelectedSection('code')} onClick={() => setSelectedSection('code')}
className={`px-4 py-2 rounded-md transition-all ${
selectedSection === 'code'
? 'bg-purple-500 text-white'
: 'bg-gray-800 text-gray-300 hover:bg-gray-700'
}`}
>
Code
</button>
<button
onClick={() => setSelectedSection('potentiel')}
className={`px-4 py-2 rounded-md transition-all ${
selectedSection === 'potentiel'
? 'bg-purple-500 text-white'
: 'bg-gray-800 text-gray-300 hover:bg-gray-700'
}`}
> >
Samples <h3 className="text-xl font-semibold text-purple-400 mb-2">Code</h3>
</button> <p className="text-gray-300">Voir un exemple de pattern TidalCycles en action</p>
</div> </div>
</div>
<div>
{renderSectionContent()} {renderSectionContent()}
</div> </div>
</div> </div>
......
...@@ -2,23 +2,26 @@ import { useEffect, useState, useRef } from 'react'; ...@@ -2,23 +2,26 @@ import { useEffect, useState, useRef } from 'react';
import { getAllLives, getLiveData, getLivesImages } from '../../../lib/livesData'; import { getAllLives, getLiveData, getLivesImages } from '../../../lib/livesData';
import ImageGallery from '@/components/ImageGallery'; import ImageGallery from '@/components/ImageGallery';
import ParVaguesHeader from '@/components/ParVaguesHeader'; import ParVaguesHeader from '@/components/ParVaguesHeader';
import ParVaguesFooter from '@/components/ParVaguesFooter';
import Head from 'next/head'; import Head from 'next/head';
import Link from 'next/link'; import Link from 'next/link';
import Image from 'next/image'; import Image from 'next/image';
import { marked } from 'marked'; import { marked } from 'marked';
import Prism from 'prismjs'; import SyntaxHighlighter from "react-syntax-highlighter";
import 'prismjs/components/prism-haskell'; import { atomOneDark } from "react-syntax-highlighter/dist/cjs/styles/hljs";
import styles from '@/styles/parvagues.module.css'; import styles from '@/styles/parvagues.module.css';
import { FaEnvelope, FaYoutube } from 'react-icons/fa'; import { FaEnvelope, FaYoutube } from 'react-icons/fa';
export default function Live({ data, slug, images }) { export default function Live({ data, slug, images }) {
const [timeToEvent, setTimeToEvent] = useState(null); const [timeToEvent, setTimeToEvent] = useState(null);
const [timeString, setTimeString] = useState('');
const [currentTeasing, setCurrentTeasing] = useState(null); const [currentTeasing, setCurrentTeasing] = useState(null);
const [pastEvent, setPastEvent] = useState(false); const [pastEvent, setPastEvent] = useState(false);
const [teasingsToShow, setTeasingsToShow] = useState([]); const [teasingsToShow, setTeasingsToShow] = useState([]);
const [upcomingDrops, setUpcomingDrops] = useState([]);
const [posterImage, setPosterImage] = useState(null); const [posterImage, setPosterImage] = useState(null);
const [mainColor, setMainColor] = useState('#a855f7'); const [mainColor, setMainColor] = useState('#a855f7');
const [isHeaderSticky, setIsHeaderSticky] = useState(false); const [scrolled, setScrolled] = useState(false);
const headerRef = useRef(null); const headerRef = useRef(null);
// Find poster or live image for background // Find poster or live image for background
...@@ -36,19 +39,10 @@ export default function Live({ data, slug, images }) { ...@@ -36,19 +39,10 @@ export default function Live({ data, slug, images }) {
} }
}, [images]); }, [images]);
// Handle Prism code highlighting // Fix scroll handling for header
useEffect(() => {
if (typeof window !== 'undefined') {
Prism.highlightAll();
}
}, [teasingsToShow]);
// Fix scroll handling for sticky header
useEffect(() => { useEffect(() => {
const handleScroll = () => { const handleScroll = () => {
const scrollPosition = window.scrollY; setScrolled(window.scrollY > 100);
const threshold = window.innerHeight * 0.7; // Lower threshold to 70% of viewport height
setIsHeaderSticky(scrollPosition > threshold);
}; };
// Initial check // Initial check
...@@ -66,6 +60,14 @@ export default function Live({ data, slug, images }) { ...@@ -66,6 +60,14 @@ export default function Live({ data, slug, images }) {
const timeDiff = eventDate - now; const timeDiff = eventDate - now;
const daysDiff = Math.floor(timeDiff / (1000 * 60 * 60 * 24)); const daysDiff = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
// Format precise time string (HH:MM:SS)
const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
const hours = Math.floor((timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeDiff % (1000 * 60)) / 1000);
setTimeString(`${days}j ${hours}h ${minutes}m ${seconds}s`);
if (timeDiff > 0) { if (timeDiff > 0) {
setTimeToEvent(daysDiff); setTimeToEvent(daysDiff);
setPastEvent(false); setPastEvent(false);
...@@ -86,6 +88,32 @@ export default function Live({ data, slug, images }) { ...@@ -86,6 +88,32 @@ export default function Live({ data, slug, images }) {
// Always show most recent teasing at the top // Always show most recent teasing at the top
setTeasingsToShow(teasings.reverse()); setTeasingsToShow(teasings.reverse());
// Set upcoming drops information
const drops = [];
if (data.frontmatter.drop1date && new Date(data.frontmatter.drop1date) > now) {
drops.push({
date: new Date(data.frontmatter.drop1date),
name: data.frontmatter.drop1name || "Teasing mysterieux"
});
}
if (data.frontmatter.drop2date && new Date(data.frontmatter.drop2date) > now) {
drops.push({
date: new Date(data.frontmatter.drop2date),
name: data.frontmatter.drop2name || "Révélation cryptique"
});
}
if (data.frontmatter.drop3date && new Date(data.frontmatter.drop3date) > now) {
drops.push({
date: new Date(data.frontmatter.drop3date),
name: data.frontmatter.drop3name || "Manifestation finale"
});
}
setUpcomingDrops(drops);
} else { } else {
setTimeToEvent(-1); setTimeToEvent(-1);
setPastEvent(true); setPastEvent(true);
...@@ -100,7 +128,7 @@ export default function Live({ data, slug, images }) { ...@@ -100,7 +128,7 @@ export default function Live({ data, slug, images }) {
}; };
updateCountdown(); updateCountdown();
const interval = setInterval(updateCountdown, 1000 * 60 * 60); // Update every hour const interval = setInterval(updateCountdown, 1000); // Update every second
return () => clearInterval(interval); return () => clearInterval(interval);
}, [data.frontmatter]); }, [data.frontmatter]);
...@@ -122,6 +150,19 @@ export default function Live({ data, slug, images }) { ...@@ -122,6 +150,19 @@ 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();
...@@ -129,6 +170,25 @@ export default function Live({ data, slug, images }) { ...@@ -129,6 +170,25 @@ export default function Live({ data, slug, images }) {
section?.scrollIntoView({ behavior: 'smooth' }); section?.scrollIntoView({ behavior: 'smooth' });
}; };
// Calculate time difference in precise format
const getTimeDifferenceString = (targetDate) => {
const now = new Date();
const timeDiff = targetDate - now;
if (timeDiff <= 0) return "Maintenant";
const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
const hours = Math.floor((timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeDiff % (1000 * 60)) / 1000);
if (days > 0) {
return `${days}j ${hours}h ${minutes}m ${seconds}s`;
} else {
return `${hours}h ${minutes}m ${seconds}s`;
}
};
// Render YouTube embed if URL is provided // Render YouTube embed if URL is provided
const renderYouTubeEmbed = (url) => { const renderYouTubeEmbed = (url) => {
if (!url) return null; if (!url) return null;
...@@ -170,6 +230,26 @@ export default function Live({ data, slug, images }) { ...@@ -170,6 +230,26 @@ export default function Live({ data, slug, images }) {
); );
}; };
// Render code blocks with SyntaxHighlighter
const renderCodeBlock = (code, language = 'haskell') => {
return (
<div className={styles.codeContainer}>
<SyntaxHighlighter
language={language}
style={atomOneDark}
wrapLongLines={true}
customStyle={{
margin: 0,
borderRadius: '8px',
fontSize: '0.85rem'
}}
>
{code}
</SyntaxHighlighter>
</div>
);
};
return ( return (
<> <>
<Head> <Head>
...@@ -178,12 +258,7 @@ export default function Live({ data, slug, images }) { ...@@ -178,12 +258,7 @@ export default function Live({ data, slug, images }) {
</Head> </Head>
{/* Above fold section */} {/* Above fold section */}
<div <div className="flex flex-col min-h-screen text-white relative overflow-hidden">
className="min-h-screen text-white relative overflow-hidden"
style={{
background: mainColor ? `radial-gradient(circle at 50% 0%, ${mainColor}20 0%, #000 60%)` : '#000'
}}
>
{/* Background poster image with blur */} {/* Background poster image with blur */}
{posterImage && ( {posterImage && (
<div className={styles.posterBackground}> <div className={styles.posterBackground}>
...@@ -198,17 +273,10 @@ export default function Live({ data, slug, images }) { ...@@ -198,17 +273,10 @@ export default function Live({ data, slug, images }) {
)} )}
{/* Use the ParVaguesHeader component */} {/* Use the ParVaguesHeader component */}
<ParVaguesHeader isSticky={isHeaderSticky} eventName={data.frontmatter.title} /> <ParVaguesHeader eventName={data.frontmatter.title} />
{/* Fixed top CTA that shows only when not in sticky mode */}
<a
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} ${isHeaderSticky ? 'opacity-0' : 'opacity-100'} transition-opacity duration-300`}
>
<FaEnvelope className="inline mr-2" />
Invoquer un live
</a>
{/* Main content with flex-auto to push footer to bottom */}
<main className="flex-auto">
{/* Main hero content */} {/* Main hero content */}
<div className="relative z-10 flex items-center justify-center min-h-screen pt-16 pb-20"> <div className="relative z-10 flex items-center justify-center min-h-screen pt-16 pb-20">
<div className="w-full max-w-6xl mx-auto px-4 md:px-6 lg:px-8"> <div className="w-full max-w-6xl mx-auto px-4 md:px-6 lg:px-8">
...@@ -234,7 +302,7 @@ export default function Live({ data, slug, images }) { ...@@ -234,7 +302,7 @@ export default function Live({ data, slug, images }) {
<div className="mb-8"> <div className="mb-8">
{timeToEvent >= 0 ? ( {timeToEvent >= 0 ? (
<div className="inline-flex items-center bg-black/60 p-4 rounded-lg border border-purple-500/30"> <div className="inline-flex items-center bg-black/60 p-4 rounded-lg border border-purple-500/30">
<div className="text-3xl font-bold mr-3 text-purple-400">J-{timeToEvent}</div> <div className="font-mono text-2xl font-bold mr-3 text-purple-400">{timeString}</div>
<div className="text-gray-300">avant l'événement</div> <div className="text-gray-300">avant l'événement</div>
</div> </div>
) : ( ) : (
...@@ -250,163 +318,104 @@ export default function Live({ data, slug, images }) { ...@@ -250,163 +318,104 @@ export default function Live({ data, slug, images }) {
{data.frontmatter.ctaURL && !pastEvent && ( {data.frontmatter.ctaURL && !pastEvent && (
<a <a
href={data.frontmatter.ctaURL} href={data.frontmatter.ctaURL}
className="inline-block bg-gradient-to-r from-purple-500 to-pink-500 text-white px-8 py-3 rounded-lg font-semibold hover:from-purple-600 hover:to-pink-600 transition-all transform hover:scale-105 shadow-lg shadow-purple-500/20" target="_blank"
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-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500"
> >
{data.frontmatter.ctaText || "S'inscrire"} {data.frontmatter.ctaText || 'S\'inscrire'}
</a> </a>
)} )}
<a <a
href="#details-section" href="#details-section"
onClick={scrollToSection} onClick={scrollToSection}
className="inline-block bg-black/70 text-white border border-purple-500/30 px-8 py-3 rounded-lg font-semibold hover:bg-black/90 hover:border-purple-500/50 transition-all" 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"
> >
Détails En savoir plus
</a> </a>
</div> </div>
</div> </div>
</div>
</article>
</div>
</div>
{/* Floating tags in background */} {/* Right column: status and teasers (2/5 width) */}
{data.frontmatter.tags && data.frontmatter.tags.length > 0 && ( <div className="md:col-span-2 bg-black/80 p-8 md:p-10 border-t md:border-t-0 md:border-l border-purple-500/20">
<div className="absolute inset-0 overflow-hidden pointer-events-none"> {/* Upcoming drops section */}
{data.frontmatter.tags.map((tag, index) => ( {upcomingDrops.length > 0 && (
<div <div className="mb-8 bg-black/50 border border-purple-500/30 rounded-lg p-4">
key={index} <h3 className="text-xl font-bold text-purple-400 mb-4">Prochains drops</h3>
className="absolute text-purple-500/20 font-bold text-3xl md:text-6xl uppercase select-none" <div className="space-y-4">
style={{ {upcomingDrops.map((drop, index) => (
top: `${Math.random() * 100}%`, <div key={index} className="flex items-center justify-between gap-4 border-b border-purple-500/10 pb-4 last:border-0 last:pb-0">
left: `${Math.random() * 100}%`, <div>
transform: `rotate(${Math.random() * 60 - 30}deg)`, <div className="text-lg font-medium text-gray-200">{drop.name}</div>
opacity: 0.1 + Math.random() * 0.3 <div className="text-sm text-gray-400">{formatPreciseDateTime(drop.date)}</div>
}}
>
{tag}
</div>
))}
</div> </div>
)} <div className="font-mono text-lg text-purple-300">{getTimeDifferenceString(drop.date)}</div>
</div> </div>
))}
{/* Below fold section */}
<div id="details-section" className="min-h-screen bg-black text-white relative">
{/* Gradient overlay */}
<div className="absolute inset-0" style={{
background: `linear-gradient(135deg, #000 0%, ${mainColor}10 50%, #000 100%)`
}} />
{/* Content */}
<div className="relative z-10">
<main className="w-full max-w-6xl mx-auto px-4 md:px-6 lg:px-8 py-16">
<article className="bg-black/80 backdrop-filter backdrop-blur-sm rounded-2xl overflow-hidden border border-purple-500/20 shadow-xl shadow-purple-900/20 p-6 md:p-10">
{pastEvent ? (
// Post-event view
<div className="space-y-12">
{/* Media section */}
{(data.frontmatter.video || data.frontmatter.audio) && (
<div className="space-y-8">
<h2 className="text-3xl font-bold mb-8">
<span className="bg-gradient-to-r from-purple-400 to-pink-600 bg-clip-text text-transparent">
Captures
</span>
</h2>
{data.frontmatter.video && renderYouTubeEmbed(data.frontmatter.video)}
{data.frontmatter.audio && renderAudioPlayer(data.frontmatter.audio)}
</div> </div>
)} <div className="mt-4 text-sm text-center text-gray-400 italic">
Reviens à ces moments précis pour ne rien manquer...
{/* Content description */}
<div className="prose prose-purple prose-invert max-w-none">
<div dangerouslySetInnerHTML={renderMarkdown(data.content)} />
</div> </div>
{/* Gallery if available */}
{images && images.length > 0 && (
<div className="mt-12">
<h2 className="text-3xl font-bold mb-8">
<span className="bg-gradient-to-r from-purple-400 to-pink-600 bg-clip-text text-transparent">
Gallerie
</span>
</h2>
<ImageGallery images={images} slug={slug} />
</div> </div>
)} )}
{/* Teasings archive */} {/* Teasings */}
{teasingsToShow.length > 0 && ( {teasingsToShow.length > 0 && (
<details className="group mt-12"> <div>
<summary className="cursor-pointer p-4 bg-black/50 rounded-lg hover:bg-black/70 transition-colors border border-purple-500/10"> <h3 className="text-xl font-bold text-purple-400 mb-4">Teasings</h3>
<span className="font-mono text-purple-400">// decrypt teasings</span> <div className="space-y-6">
</summary> {teasingsToShow.map((teasing, index) => (
<div className="mt-4 space-y-4 pl-4 border-l-2 border-purple-500/20"> <div key={index} className="bg-black/50 border border-purple-500/30 rounded-lg p-4">
{teasingsToShow.map((teasing, i) => ( <div dangerouslySetInnerHTML={renderMarkdown(teasing)} className="prose prose-sm prose-invert max-w-none" />
<div key={i} className="bg-black/40 rounded p-4">
<div
className="prose prose-purple prose-invert prose-pre:bg-gray-900/70 prose-pre:border prose-pre:border-purple-500/20 max-w-none text-sm"
dangerouslySetInnerHTML={renderMarkdown(teasing)}
/>
</div> </div>
))} ))}
</div> </div>
</details> </div>
)} )}
</div> </div>
) : ( </div>
// Pre-event view </article>
<div className="space-y-12"> </div>
{/* Description */}
<div className="prose prose-purple prose-invert max-w-none">
<div dangerouslySetInnerHTML={renderMarkdown(data.frontmatter.description)} />
</div> </div>
{/* Teasings */} {/* Details section */}
{teasingsToShow.length > 0 && ( <section id="details-section" className={styles.sectionContainer}>
<div className="space-y-8"> <div className="max-w-6xl mx-auto px-4 md:px-6 lg:px-8">
<h2 className="text-3xl font-bold mb-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-6 lg:gap-8">
<span className="bg-gradient-to-r from-purple-400 to-pink-600 bg-clip-text text-transparent">
Teasings {/* Description */}
</span> <div>
</h2> <h2 className="text-2xl font-bold text-purple-400 mb-6">À propos de cet événement</h2>
{teasingsToShow.map((teasing, i) => (
<div key={i} className="bg-black/60 backdrop-blur-sm rounded-lg p-6 border border-purple-500/20">
<div <div
className="prose prose-purple prose-invert prose-pre:bg-gray-900/70 prose-pre:border prose-pre:border-purple-500/20 max-w-none" className="prose prose-lg prose-invert"
dangerouslySetInnerHTML={renderMarkdown(teasing)} dangerouslySetInnerHTML={renderMarkdown(data.content)}
/> />
{/* Media */}
{data.frontmatter.youtubeURL && renderYouTubeEmbed(data.frontmatter.youtubeURL)}
{data.frontmatter.audioURL && renderAudioPlayer(data.frontmatter.audioURL)}
</div> </div>
))}
</div>
)}
{/* CTA button if available */} {/* Gallery */}
{data.frontmatter.ctaURL && ( <div>
<div className="text-center py-8"> {images && images.length > 0 && (
<a <div>
href={data.frontmatter.ctaURL} <h2 className="text-2xl font-bold text-purple-400 mb-6">Galerie</h2>
className="inline-block bg-gradient-to-r from-purple-500 to-pink-500 text-white px-8 py-4 rounded-lg font-semibold hover:from-purple-600 hover:to-pink-600 transition-all transform hover:scale-105 shadow-lg shadow-purple-500/20 text-xl" <ImageGallery
> images={images}
{data.frontmatter.ctaText || data.frontmatter.title} slug={slug}
</a> alt={data.frontmatter.title}
/>
</div> </div>
)} )}
</div> </div>
)} </div>
</article> </div>
</section>
</main> </main>
{/* Footer */} {/* Footer */}
<footer className={styles.footer}> <ParVaguesFooter />
<p className="text-gray-400">© {new Date().getFullYear()} ParVagues</p>
<Link href="/parvagues" className="mt-4 inline-block text-gray-500 text-sm hover:text-purple-400 transition-colors">
← Retour à ParVagues
</Link>
</footer>
</div>
</div> </div>
</> </>
); );
......
import { useEffect } from 'react';
import { useRouter } from 'next/router';
export default function LiveRedirect() {
const router = useRouter();
useEffect(() => {
router.replace('/parvagues');
}, [router]);
return (
<div className="min-h-screen bg-black text-white flex items-center justify-center">
<div className="text-center">
<h1 className="text-2xl mb-4">Redirection...</h1>
<p>Vous allez être redirigé vers la page principale de ParVagues.</p>
</div>
</div>
);
}
\ No newline at end of file
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