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
fc9f96aa
Commit
fc9f96aa
authored
May 11, 2025
by
PLN (Algolia)
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
next: iterate on pages
parent
ae3a9078
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
273 additions
and
244 deletions
+273
-244
ParVaguesHeader.js
next/components/ParVaguesHeader.js
+17
-4
parvagues.js
next/pages/parvagues.js
+13
-26
[id].js
next/pages/parvagues/live/[id].js
+223
-214
index.js
next/pages/parvagues/live/index.js
+20
-0
No files found.
next/components/ParVaguesHeader.js
View file @
fc9f96aa
import
React
from
'react'
;
import
React
,
{
useState
,
useEffect
}
from
'react'
;
import
Link
from
'next/link'
;
import
{
FaEnvelope
}
from
'react-icons/fa'
;
import
styles
from
'@/styles/parvagues.module.css'
;
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
(
<
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
=
"flex items-center gap-2"
>
...
...
@@ -37,10 +50,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
}
transition-all duration-300`
}
className
=
{
`
${
styles
.
ctaButton
}
transition-all duration-300
${
scrolled
?
'bg-purple-600 scale-90'
:
''
}
`
}
>
<
FaEnvelope
className
=
"inline mr-2"
/>
Invoquer
un
live
{
scrolled
?
'Invoquer'
:
'Invoquer un live'
}
<
/a
>
<
/div
>
<
/header
>
...
...
next/pages/parvagues.js
View file @
fc9f96aa
...
...
@@ -36,7 +36,8 @@ function CodeBlock({ children, height = '400px', isTerminal = false }) {
margin
:
0
,
borderRadius
:
isTerminal
?
'0 0 8px 8px'
:
'8px'
,
height
:
'auto'
,
maxHeight
:
isTerminal
?
`calc(
${
height
}
- 30px)`
:
height
maxHeight
:
isTerminal
?
`calc(
${
height
}
- 30px)`
:
height
,
fontSize
:
'0.85rem'
}}
>
{
children
}
...
...
@@ -374,7 +375,7 @@ d4 $ note ("<e3 fs3 <gs3 d4> <a3 df4>>" - 12)
<
div
className
=
{
styles
.
splitSection
}
>
<
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'
)}
>
<
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)
<
/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'
)}
>
<
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)
<
/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'
)}
>
<
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
>
<
/div
>
<
div
className
=
{
`
${
styles
.
bulletPoint
}
cursor-pointer
${
selectedSection
===
'code'
?
'text-purple-400 border-l-2 border-purple-400 pl-4'
:
''
}
`
}
onClick
=
{()
=>
setSelectedSection
(
'code'
)}
>
<
h3
className
=
"text-xl font-semibold text-purple-400 mb-2"
>
Code
<
/h3
>
<
p
className
=
"text-gray-300"
>
Voir
un
exemple
de
pattern
TidalCycles
en
action
<
/p
>
<
/div
>
<
/div
>
<
div
>
<
div
className
=
"mb-4 flex gap-4"
>
<
button
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
<
/button
>
<
/div
>
{
renderSectionContent
()}
<
/div
>
<
/div
>
...
...
next/pages/parvagues/live/[id].js
View file @
fc9f96aa
...
...
@@ -2,23 +2,26 @@ import { useEffect, useState, useRef } from 'react';
import
{
getAllLives
,
getLiveData
,
getLivesImages
}
from
'../../../lib/livesData'
;
import
ImageGallery
from
'@/components/ImageGallery'
;
import
ParVaguesHeader
from
'@/components/ParVaguesHeader'
;
import
ParVaguesFooter
from
'@/components/ParVaguesFooter'
;
import
Head
from
'next/head'
;
import
Link
from
'next/link'
;
import
Image
from
'next/image'
;
import
{
marked
}
from
'marked'
;
import
Prism
from
'prismjs'
;
import
'prismjs/components/prism-haskell'
;
import
SyntaxHighlighter
from
"react-syntax-highlighter"
;
import
{
atomOneDark
}
from
"react-syntax-highlighter/dist/cjs/styles/hljs"
;
import
styles
from
'@/styles/parvagues.module.css'
;
import
{
FaEnvelope
,
FaYoutube
}
from
'react-icons/fa'
;
export
default
function
Live
({
data
,
slug
,
images
})
{
const
[
timeToEvent
,
setTimeToEvent
]
=
useState
(
null
);
const
[
timeString
,
setTimeString
]
=
useState
(
''
);
const
[
currentTeasing
,
setCurrentTeasing
]
=
useState
(
null
);
const
[
pastEvent
,
setPastEvent
]
=
useState
(
false
);
const
[
teasingsToShow
,
setTeasingsToShow
]
=
useState
([]);
const
[
upcomingDrops
,
setUpcomingDrops
]
=
useState
([]);
const
[
posterImage
,
setPosterImage
]
=
useState
(
null
);
const
[
mainColor
,
setMainColor
]
=
useState
(
'#a855f7'
);
const
[
isHeaderSticky
,
setIsHeaderSticky
]
=
useState
(
false
);
const
[
scrolled
,
setScrolled
]
=
useState
(
false
);
const
headerRef
=
useRef
(
null
);
// Find poster or live image for background
...
...
@@ -36,19 +39,10 @@ export default function Live({ data, slug, images }) {
}
},
[
images
]);
// Handle Prism code highlighting
useEffect
(()
=>
{
if
(
typeof
window
!==
'undefined'
)
{
Prism
.
highlightAll
();
}
},
[
teasingsToShow
]);
// Fix scroll handling for sticky header
// Fix scroll handling for header
useEffect
(()
=>
{
const
handleScroll
=
()
=>
{
const
scrollPosition
=
window
.
scrollY
;
const
threshold
=
window
.
innerHeight
*
0.7
;
// Lower threshold to 70% of viewport height
setIsHeaderSticky
(
scrollPosition
>
threshold
);
setScrolled
(
window
.
scrollY
>
100
);
};
// Initial check
...
...
@@ -66,6 +60,14 @@ export default function Live({ data, slug, images }) {
const
timeDiff
=
eventDate
-
now
;
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
)
{
setTimeToEvent
(
daysDiff
);
setPastEvent
(
false
);
...
...
@@ -86,6 +88,32 @@ export default function Live({ data, slug, images }) {
// Always show most recent teasing at the top
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
{
setTimeToEvent
(
-
1
);
setPastEvent
(
true
);
...
...
@@ -100,7 +128,7 @@ export default function Live({ data, slug, images }) {
};
updateCountdown
();
const
interval
=
setInterval
(
updateCountdown
,
1000
*
60
*
60
);
// Update every hour
const
interval
=
setInterval
(
updateCountdown
,
1000
);
// Update every second
return
()
=>
clearInterval
(
interval
);
},
[
data
.
frontmatter
]);
...
...
@@ -122,6 +150,19 @@ 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
();
...
...
@@ -129,6 +170,25 @@ export default function Live({ data, slug, images }) {
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
const
renderYouTubeEmbed
=
(
url
)
=>
{
if
(
!
url
)
return
null
;
...
...
@@ -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: '
8
px
',
fontSize: '
0.85
rem
'
}}
>
{code}
</SyntaxHighlighter>
</div>
);
};
return (
<>
<Head>
...
...
@@ -178,12 +258,7 @@ export default function Live({ data, slug, images }) {
</Head>
{/* Above fold section */}
<div
className="min-h-screen text-white relative overflow-hidden"
style={{
background: mainColor ? `radial-gradient(circle at 50% 0%, ${mainColor}20 0%, #000 60%)` : '
#
000
'
}}
>
<div className="flex flex-col min-h-screen text-white relative overflow-hidden">
{/* Background poster image with blur */}
{posterImage && (
<div className={styles.posterBackground}>
...
...
@@ -198,215 +273,149 @@ export default function Live({ data, slug, images }) {
)}
{/* 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 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
-
6
xl
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
-
2
xl
overflow
-
hidden
border
border
-
purple
-
500
/
20
shadow
-
xl
shadow
-
purple
-
900
/
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
-
4
xl
md
:
text
-
5
xl
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
-
2
xl
md
:
text
-
3
xl
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>
{/* 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
-
purple
-
500
/
30
">
<div className="
text
-
3
xl
font
-
bold
mr
-
3
text
-
purple
-
400
">J-{timeToEvent}</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
-
purple
-
500
/
30
">
<div className="
text
-
gray
-
300
">Événement passé</div>
</div>
)}
{/* 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-purple-500/20 shadow-xl shadow-purple-900/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>
)}
{/* CTA button and scroll link */}
<div className="
flex
flex
-
wrap
gap
-
4
">
{data.frontmatter.ctaURL && !pastEvent && (
{/* 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-purple-500/30">
<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
>
)
:
(
<
div
className
=
"inline-flex items-center bg-black/60 p-4 rounded-lg border border-purple-500/30"
>
<
div
className
=
"text-gray-300"
>
É
v
é
nement
pass
é
<
/div
>
<
/div
>
)}
<
/div
>
)}
{
/* CTA button and scroll link */
}
<
div
className
=
"flex flex-wrap gap-4"
>
{
data
.
frontmatter
.
ctaURL
&&
!
pastEvent
&&
(
<
a
href
=
{
data
.
frontmatter
.
ctaURL
}
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'
}
<
/a
>
)}
<
a
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
"
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"
>
{data.frontmatter.ctaText || "
S
'inscrire"}
En
savoir
plus
<
/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-purple-500/20"
>
{
/* Upcoming drops section */
}
{
upcomingDrops
.
length
>
0
&&
(
<
div
className
=
"mb-8 bg-black/50 border border-purple-500/30 rounded-lg p-4"
>
<
h3
className
=
"text-xl font-bold text-purple-400 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-purple-500/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-purple-300"
>
{
getTimeDifferenceString
(
drop
.
date
)}
<
/div
>
<
/div
>
))}
<
/div
>
<
div
className
=
"mt-4 text-sm text-center text-gray-400 italic"
>
Reviens
à
ces
moments
pr
é
cis
pour
ne
rien
manquer
...
<
/div
>
<
/div
>
)}
<a
href="#details-section"
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"
>
Détails
</a>
{
/* Teasings */
}
{
teasingsToShow
.
length
>
0
&&
(
<
div
>
<
h3
className
=
"text-xl font-bold text-purple-400 mb-4"
>
Teasings
<
/h3
>
<
div
className
=
"space-y-6"
>
{
teasingsToShow
.
map
((
teasing
,
index
)
=>
(
<
div
key
=
{
index
}
className
=
"bg-black/50 border border-purple-500/30 rounded-lg p-4"
>
<
div
dangerouslySetInnerHTML
=
{
renderMarkdown
(
teasing
)}
className
=
"prose prose-sm prose-invert max-w-none"
/>
<
/div
>
))}
<
/div
>
<
/div
>
)}
<
/div
>
<
/div
>
</div>
</article>
</div>
</div>
{/* Floating tags in background */}
{data.frontmatter.tags && data.frontmatter.tags.length > 0 && (
<div className="absolute inset-0 overflow-hidden pointer-events-none">
{data.frontmatter.tags.map((tag, index) => (
<div
key={index}
className="absolute text-purple-500/20 font-bold text-3xl md:text-6xl uppercase select-none"
style={{
top: `${Math.random() * 100}%`,
left: `${Math.random() * 100}%`,
transform: `rotate(${Math.random() * 60 - 30}deg)`,
opacity: 0.1 + Math.random() * 0.3
}}
>
{tag}
</div>
))}
<
/article
>
<
/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>
)}
{/* Content description */}
<div className="prose prose-purple prose-invert max-w-none">
<div dangerouslySetInnerHTML={renderMarkdown(data.content)} />
</div>
{
/* 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-purple-400 mb-6"
>
À
propos
de
cet
é
v
é
nement
<
/h2
>
<
div
className
=
"prose prose-lg prose-invert"
dangerouslySetInnerHTML
=
{
renderMarkdown
(
data
.
content
)}
/
>
{/* 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>
)}
{/* Teasings archive */}
{teasingsToShow.length > 0 && (
<details className="group mt-12">
<summary className="cursor-pointer p-4 bg-black/50 rounded-lg hover:bg-black/70 transition-colors border border-purple-500/10">
<span className="font-mono text-purple-400">// decrypt teasings</span>
</summary>
<div className="mt-4 space-y-4 pl-4 border-l-2 border-purple-500/20">
{teasingsToShow.map((teasing, i) => (
<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>
</details>
)}
{
/* Media */
}
{
data
.
frontmatter
.
youtubeURL
&&
renderYouTubeEmbed
(
data
.
frontmatter
.
youtubeURL
)}
{
data
.
frontmatter
.
audioURL
&&
renderAudioPlayer
(
data
.
frontmatter
.
audioURL
)}
<
/div
>
) : (
// Pre-event view
<div className="space-y-12">
{/* Description */}
<div className="prose prose-purple prose-invert max-w-none">
<div dangerouslySetInnerHTML={renderMarkdown(data.frontmatter.description)} />
</div>
{/* Teasings */}
{teasingsToShow.length > 0 && (
<div className="space-y-8">
<h2 className="text-3xl font-bold mb-4">
<span className="bg-gradient-to-r from-purple-400 to-pink-600 bg-clip-text text-transparent">
Teasings
</span>
</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
className="prose prose-purple prose-invert prose-pre:bg-gray-900/70 prose-pre:border prose-pre:border-purple-500/20 max-w-none"
dangerouslySetInnerHTML={renderMarkdown(teasing)}
/>
</div>
))}
</div>
)}
{/* CTA button if available */}
{data.frontmatter.ctaURL && (
<div className="text-center py-8">
<a
href={data.frontmatter.ctaURL}
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"
>
{data.frontmatter.ctaText || data.frontmatter.title}
</a>
{
/* Gallery */
}
<
div
>
{
images
&&
images
.
length
>
0
&&
(
<
div
>
<
h2
className
=
"text-2xl font-bold text-purple-400 mb-6"
>
Galerie
<
/h2
>
<
ImageGallery
images
=
{
images
}
slug
=
{
slug
}
alt
=
{
data
.
frontmatter
.
title
}
/
>
<
/div
>
)}
<
/div
>
)}
</article>
</main>
<
/div
>
<
/div
>
<
/section
>
<
/main
>
{/* Footer */}
<footer className={styles.footer}>
<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>
{
/* Footer */
}
<
ParVaguesFooter
/>
<
/div
>
<
/
>
);
...
...
next/pages/parvagues/live/index.js
0 → 100644
View file @
fc9f96aa
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
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