Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
www
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PLN
www
Commits
242c66eb
Commit
242c66eb
authored
May 11, 2025
by
PLN (Algolia)
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
checkpoint header madness
parent
33f858b7
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
302 additions
and
226 deletions
+302
-226
ParVaguesFooter.js
next/components/ParVaguesFooter.js
+36
-39
ParVaguesHeader.js
next/components/ParVaguesHeader.js
+45
-16
parvagues.js
next/pages/parvagues.js
+1
-1
[id].js
next/pages/parvagues/live/[id].js
+220
-170
No files found.
next/components/ParVaguesFooter.js
View file @
242c66eb
...
...
@@ -17,55 +17,31 @@ export default function ParVaguesFooter() {
const
year
=
new
Date
().
getFullYear
();
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
=
"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 */
}
<
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"
>
Livecoding
de
musique
open
-
source
avec
TidalCycles
et
contr
ô
leur
MIDI
.
<
br
/>
Performances
algorithmiques
et
cr
é
ation
sonore
en
direct
.
<
/p
>
<
/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 */
}
<
div
>
<
h3
className
=
"text-[#d900ff] font-semibold text-lg mb-4"
>
Connect
<
/h3
>
<
div
className
=
"flex flex-wrap gap-4"
>
<
div
className
=
"flex flex-col items-center md:items-end"
>
<
div
className
=
"flex flex-wrap gap-4 justify-center md:justify-end"
>
{
socialLinks
.
map
((
link
,
index
)
=>
(
<
a
key
=
{
index
}
...
...
@@ -82,8 +58,29 @@ export default function ParVaguesFooter() {
<
/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 */
}
<
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
>
<
/div
>
<
/div
>
...
...
next/components/ParVaguesHeader.js
View file @
242c66eb
import
React
from
'react'
;
import
React
,
{
useState
,
useEffect
}
from
'react'
;
import
Link
from
'next/link'
;
import
Image
from
'next/image'
;
import
{
FaEnvelope
}
from
'react-icons/fa'
;
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
(
<
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
=
"flex items-center gap-2"
>
<
Link
href
=
"/parvagues"
className
=
"flex items-center"
>
<
div
className
=
"
w-10 h-10 relative mr-2
"
>
<
div
className
=
"
h-10 w-auto relative
"
>
<
Image
src
=
"/images/parvagues/
favicon.ico
"
src
=
"/images/parvagues/
logo_transparent.png
"
alt
=
"ParVagues Logo"
fill
width
=
{
40
}
height
=
{
40
}
className
=
"object-contain"
sizes
=
"40px"
/>
<
/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
>
{
eventName
&&
(
<>
<
span
className
=
"text-gray-500 mx-2"
>|<
/span
>
<
span
className
=
"text-white"
>
{
eventName
}
<
/span
>
<
/
>
)}
<
/div
>
{
/* Navigation Links */
}
...
...
@@ -47,10 +76,10 @@ export default function ParVaguesHeader({ eventName = null }) {
{
/* CTA button */
}
<
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
}
className
=
{
`
${
styles
.
ctaButton
}
${
showInHeader
?
'scale-90'
:
'scale-100'
}
transition-transform duration-300`
}
>
<
FaEnvelope
className
=
"inline mr-2"
/>
Invoquer
un
live
{
showInHeader
?
'Invoquer'
:
'Invoquer un live'
}
<
/a
>
<
/div
>
<
/header
>
...
...
next/pages/parvagues.js
View file @
242c66eb
...
...
@@ -332,7 +332,7 @@ d4 $ note ("<e3 fs3 <gs3 d4> <a3 df4>>" - 12)
<
GlitchText
text
=
"ParVagues"
className
=
{
styles
.
glitchEffect
}
burstFrequency
=
{
450
}
burstFrequency
=
{
450
0
}
/
>
<
/h1
>
<
p
className
=
{
styles
.
heroSubtitle
}
>
...
...
next/pages/parvagues/live/[id].js
View file @
242c66eb
...
...
@@ -35,23 +35,9 @@ export default function Live({ data, slug, images }) {
}
else
if
(
liveImage
)
{
setPosterImage
(
liveImage
);
}
// Could add color extraction here in the future
}
},
[
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
useEffect
(()
=>
{
const
updateCountdown
=
()
=>
{
...
...
@@ -150,19 +136,6 @@ export default function Live({ data, slug, images }) {
}).
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
const
scrollToSection
=
(
e
)
=>
{
e
.
preventDefault
();
...
...
@@ -180,12 +153,11 @@ export default function Live({ data, slug, images }) {
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
`
;
return
`
${
days
}
j
${
hours
}
h
${
minutes
}
m`
;
}
else
{
return
`
${
hours
}
h
${
minutes
}
m
${
seconds
}
s
`
;
return
`
${
hours
}
h
${
minutes
}
m`
;
}
};
...
...
@@ -218,8 +190,8 @@ export default function Live({ data, slug, images }) {
if
(
!
url
)
return
null
;
return
(
<
div
className
=
"my-
6
"
>
<
h3
className
=
"text-
xl font-semibold text-purple-400 mb-3
"
>
Audio
<
/h3
>
<
div
className
=
"my-
4
"
>
<
h3
className
=
"text-
lg font-semibold text-purple-400 mb-2
"
>
Audio
<
/h3
>
<
audio
controls
className
=
"w-full"
...
...
@@ -241,7 +213,8 @@ export default function Live({ data, slug, images }) {
customStyle={{
margin: 0,
borderRadius: '
8
px
',
fontSize: '
0.85
rem
'
fontSize: '
0.85
rem
',
maxHeight: '
200
px
' // Limit height
}}
>
{code}
...
...
@@ -254,172 +227,242 @@ export default function Live({ data, slug, images }) {
<>
<Head>
<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" />
</Head>
{/* Above fold section */}
<div className="flex flex-col min-h-screen text-white relative overflow-hidden">
{/* Background poster image with blur */}
{posterImage && (
<div className={styles.posterBackground}>
<Image
src={posterImage}
alt={data.frontmatter.title}
fill
className={styles.posterImageBG}
/>
<div className={styles.posterGradient}></div>
</div>
)}
<div className="flex flex-col min-h-screen bg-black text-white">
{/* Header with event title */}
<ParVaguesHeader title={data.frontmatter.title} />
{/* 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 hero content */}
<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">
<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">
<div className="grid md:grid-cols-5 gap-0">
{/* Left column: Title and info (3/5 width) */}
<div className="md:col-span-3 p-8 md:p-10">
<h1 className="text-4xl md:text-5xl font-bold mb-4">
<span className={`bg-gradient-to-r from-purple-400 via-pink-500 to-purple-600 bg-clip-text text-transparent z-10 ${styles.glitchEffect}`}>
{data.frontmatter.title}
</span>
</h1>
<h2 className="text-2xl md:text-3xl font-bold mb-4 text-gray-200 z-10">
{data.frontmatter.subtitle || '
ParVagues
'}
</h2>
<div className="text-gray-300 mb-6">
<p className="text-xl">{data.frontmatter.location}</p>
<p>{formatEventDate(data.frontmatter.date)} {data.frontmatter.time && `• ${data.frontmatter.time}`}</p>
</div>
{/* Hero Section - More compact */}
<section className="relative min-h-[60vh] md:min-h-[50vh] flex items-center overflow-hidden">
{/* Background Image with gradient overlay */}
{posterImage && (
<div className="absolute inset-0 z-0">
<Image
src={posterImage}
alt={data.frontmatter.title}
fill
className="object-cover"
priority
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/80 via-black/60 to-black"></div>
</div>
)}
<div className="container mx-auto px-4 py-8 relative z-10">
<div className="max-w-5xl mx-auto">
<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 */}
{timeToEvent !== null && (
<div className="mb-8">
{timeToEvent >= 0 ? (
<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="text-gray-300">avant l'
é
v
é
nement
<
/div
>
<
/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
>
)}
{pastEvent ? (
<div className="inline-block bg-gray-800 text-white text-sm px-3 py-1 rounded-full mb-4">
Événement passé
</div>
) : (
<div className="inline-block bg-purple-800 text-white text-sm px-3 py-1 rounded-full mb-4">
{timeToEvent === 0 ? "Aujourd'
hui
" : `Dans ${timeString}`}
</div>
)}
{
/* CTA button and scroll link */
}
<
div
className
=
"flex flex-wrap gap-4"
>
{
data
.
frontmatter
.
ctaURL
&&
!
pastEvent
&&
(
<div className="
flex
flex
-
col
space
-
y
-
2
mb
-
4
">
<div className="
flex
items
-
center
">
<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
href
=
{
data
.
frontmatter
.
ctaURL
}
target
=
"_blank"
href={data.frontmatter.
ticketLink}
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-[#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
href="
#
details
-
section
"
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
plu
s
Détail
s
</a>
</div>
</div>
{
/* Right column: status and teasers (2/5 width) */
}
<
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"
>
{
/* Upcoming drops section */
}
{
upcomingDrops
.
length
>
0
&&
(
<
div
className
=
"mb-8 bg-black/50 border border-[#d900ff]/30 rounded-lg p-4"
>
<
h3
className
=
"text-xl font-bold text-[#d900ff] mb-4"
>
Prochains
drops
<
/h3
>
<
div
className
=
"space-y-4"
>
{
upcomingDrops
.
map
((
drop
,
index
)
=>
(
<
div
key
=
{
index
}
className
=
"flex items-center justify-between gap-4 border-b border-[#d900ff]/10 pb-4 last:border-0 last:pb-0"
>
<
div
>
<
div
className
=
"text-lg font-medium text-gray-200"
>
{
drop
.
name
}
<
/div
>
<
div
className
=
"text-sm text-gray-400"
>
{
formatPreciseDateTime
(
drop
.
date
)}
<
/div
>
<
/div
>
<
div
className
=
"font-mono text-lg text-[#ff3d7b]"
>
{
getTimeDifferenceString
(
drop
.
date
)}
<
/div
>
<
/div
>
{/* Right Side: Upcoming Drops or Latest Teasing */}
<div>
{!pastEvent && upcomingDrops.length > 0 ? (
<div className="
bg
-
black
/
60
backdrop
-
blur
p
-
4
rounded
-
lg
border
border
-
purple
-
500
/
30
">
<h3 className="
text
-
lg
font
-
semibold
text
-
purple
-
400
mb
-
3
">Prochains drops</h3>
<ul className="
space
-
y
-
2
">
{upcomingDrops.map((drop, i) => (
<li key={i} className="
flex
items
-
center
justify
-
between
bg
-
black
/
50
p
-
2
rounded
">
<span className="
text
-
sm
">{drop.name}</span>
<span className="
text
-
xs
bg
-
purple
-
900
/
60
px
-
2
py
-
1
rounded
">
{getTimeDifferenceString(drop.date)}
</span>
</li>
))}
<
/div
>
<
div
className
=
"mt-4 text-sm text-center text-gray-400 italic"
>
Reviens
à
ces
moments
pr
é
cis
pour
ne
rien
manquer
...
<
/div
>
</ul>
</div>
)}
{
/* Teasings */
}
{
teasingsToShow
.
length
>
0
&&
(
<
div
>
<
h3
className
=
"text-xl font-bold text-[#d900ff] mb-4"
>
Teasings
<
/h3
>
<
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
>
) : teasingsToShow.length > 0 ? (
<div className="
bg
-
black
/
60
backdrop
-
blur
p
-
4
rounded
-
lg
border
border
-
purple
-
500
/
30
">
<h3 className="
text
-
lg
font
-
semibold
text
-
purple
-
400
mb
-
2
">
{pastEvent ? "
Highlights
" : "
Teasing
"}
</h3>
<div dangerouslySetInnerHTML={renderMarkdown(teasingsToShow[0])} className="
prose
prose
-
sm
prose
-
invert
max
-
w
-
none
" />
</div>
)}
)
: null
}
</div>
</div>
<
/
article
>
</
div
>
</div>
<
/
div
>
</
section
>
{
/* Details section */
}
<
section
id
=
"details-section"
className
=
{
styles
.
sectionContainer
}
>
<
div
className
=
"max-w-6xl mx-auto px-4 md:px-6 lg:px-8"
>
<
div
className
=
"grid grid-cols-1 md:grid-cols-2 gap-6 lg:gap-8"
>
{
/* Description */
}
<
div
>
<
h2
className
=
"text-2xl font-bold text-[#d900ff] mb-6"
>
À
propos
de
cet
é
v
é
nement
<
/h2
>
<
div
className
=
"prose prose-lg prose-invert"
dangerouslySetInnerHTML
=
{
renderMarkdown
(
data
.
content
)}
/
>
{/* Details Section - More compact */}
<section id="
details
-
section
" className="
bg
-
black
py
-
8
">
<div className="
container
mx
-
auto
px
-
4
">
<div className="
max
-
w
-
5
xl
mx
-
auto
grid
md
:
grid
-
cols
-
12
gap
-
6
">
{/* Main Content */}
<div className="
md
:
col
-
span
-
8
">
{/* Main Content */}
<div className="
bg
-
gray
-
900
/
40
rounded
-
lg
p
-
4
mb
-
4
">
<h2 className="
text
-
xl
font
-
bold
mb
-
3
text
-
purple
-
400
">À propos</h2>
<div
dangerouslySetInnerHTML={renderMarkdown(data.content)}
className="
prose
prose
-
sm
prose
-
invert
max
-
w
-
none
"
/>
</div>
{/* Media */}
{
data
.
frontmatter
.
youtubeURL
&&
renderYouTubeEmbed
(
data
.
frontmatter
.
youtubeURL
)}
{
data
.
frontmatter
.
audioURL
&&
renderAudioPlayer
(
data
.
frontmatter
.
audioURL
)}
{data.frontmatter.youtubeUrl && (
<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>
{
/* Gallery */
}
<
div
>
{
images
&&
images
.
length
>
0
&&
(
<
div
>
<
h2
className
=
"text-2xl font-bold text-[#d900ff] mb-6"
>
Galerie
<
/h2
>
<
ImageGallery
images
=
{
images
}
slug
=
{
slug
}
alt
=
{
data
.
frontmatter
.
title
}
className
=
"filter grayscale hover:grayscale-0"
{/* Sidebar */}
<div className="
md
:
col
-
span
-
4
">
{/* More Teasings */}
{teasingsToShow.length > 1 && (
<div className="
bg
-
gray
-
900
/
40
rounded
-
lg
p
-
4
mb
-
4
">
<h3 className="
text
-
lg
font
-
semibold
text
-
purple
-
400
mb
-
2
">Plus de teasings</h3>
<div className="
space
-
y
-
3
">
{teasingsToShow.slice(1).map((teasing, i) => (
<div
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>
)}
{/* 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>
{/* Full Gallery - only if more than 3 images */}
{images && images.length > 3 && (
<div id="
full
-
gallery
" className="
max
-
w
-
5
xl
mx
-
auto
mt
-
8
">
<ImageGallery images={images} slug={slug} />
</div>
)}
</div>
</section>
</main>
{
/* Footer */
}
<ParVaguesFooter />
</div>
</>
...
...
@@ -428,26 +471,33 @@ export default function Live({ data, slug, images }) {
export async function getStaticPaths() {
const lives = getAllLives();
const
paths
=
lives
.
map
(
(
live
)
=>
({
params
:
{
id
:
live
.
slug
}
,
const paths = lives.map(
live
=> ({
params: { id: live.slug }
}));
return {
paths,
fallback
:
'blocking'
,
fallback:
false
,
};
}
export async function getStaticProps({ params }) {
const
data
=
await
getLiveData
(
params
.
id
);
const
images
=
getLivesImages
(
params
.
id
);
return
{
props
:
{
data
,
slug
:
params
.
id
,
images
,
},
revalidate
:
60
,
// Revalidate every minute
};
try {
const liveData = getLiveData(params.id);
const images = getLivesImages(params.id);
return {
props: {
data: liveData,
slug: params.id,
images,
},
revalidate: 60,
};
} catch (error) {
console.error(`Error getting data for ${params.id}:`, error);
return {
notFound: true,
};
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment