11import React from "react" ;
22import Link from "next/link" ;
33import { SpeakerService } from "@/services/SpeakerService" ;
4- import {
5- generateTimeInterval ,
6- getEventDay ,
7- getEventMonth ,
8- } from "@/utils/utils" ;
4+ import { SessionService } from "@/services/SessionService" ;
5+ import { generateTimeInterval } from "@/utils/utils" ;
96import ImageWithFallback from "@/components/ImageWithFallback" ;
7+ import { ShowMore } from "@/components/ShowMore" ;
8+ import { Calendar , Clock , MapPin } from "lucide-react" ;
109
1110export const dynamic = "force-dynamic" ;
1211
@@ -16,22 +15,10 @@ type Props = {
1615 } ;
1716} ;
1817
19- export async function generateMetadata ( { params } : Props ) {
20- const speaker = await SpeakerService . getSpeaker ( params . id ) ;
21- return {
22- title : speaker ? `${ speaker . name } • SINFO` : "Speaker • SINFO" ,
23- description : speaker ?. description || "Speaker details" ,
24- openGraph : speaker
25- ? {
26- images : speaker . img ? [ speaker . img ] : undefined ,
27- }
28- : undefined ,
29- } as any ;
30- }
31-
3218export default async function Page ( { params } : Props ) {
3319 const { id } = params ;
3420 const speaker = await SpeakerService . getSpeaker ( id ) ;
21+ const session = await SessionService . getSessionBySpeaker ( id ) ;
3522
3623 if ( ! speaker ) {
3724 return (
@@ -52,197 +39,133 @@ export default async function Page({ params }: Props) {
5239 ) ;
5340 }
5441
55- const mainSession = speaker . sessions ?. [ 0 ] ;
56- const sessionTitle = mainSession ?. name ;
57- const formattedDate = mainSession ?. date
58- ? `${ getEventMonth ( mainSession . date , false ) } ${ getEventDay ( mainSession . date ) } `
59- : undefined ;
60- const timeRange = mainSession ?. date
61- ? generateTimeInterval ( mainSession . date , mainSession ?. duration ?? 0 , {
62- onlyHours : true ,
63- } )
64- : undefined ;
65- const sessionPlace = mainSession ?. place || "Main Stage" ;
66-
67- const SpeakerPhoto = ( { className = "" } : { className ?: string } ) => (
68- < div className = { `relative flex justify-center ${ className } ` } >
69- < div className = "absolute top-1/2 left-1/2 -translate-x-1/2 md:-translate-x-[60%] -translate-y-1/2 w-[340px] h-[340px] md:w-[380px] md:h-[380px] bg-sinfo-quinary rounded-full overflow-hidden shadow-2xl" >
70- < ImageWithFallback
71- src = { speaker . img }
72- alt = { speaker . name }
73- className = "absolute inset-0 w-full h-full object-contain"
74- sizes = "(max-width: 768px) 100vw, 50vw"
75- width = { 380 }
76- height = { 380 }
77- />
78- </ div >
79- </ div >
80- ) ;
81-
82- const SessionInfo = ( { className = "" } : { className ?: string } ) =>
83- mainSession ? (
84- < div className = { `text-white ${ className } ` } >
85- < p className = "font-bold text-xl md:text-2xl" > { sessionPlace } </ p >
86- < p className = "text-lg md:text-xl" >
87- { formattedDate || "February 17" } • { timeRange || "16h30-17h20" }
88- </ p >
89- </ div >
90- ) : null ;
91-
92- const SessionTitleBox = ( { className = "" } : { className ?: string } ) =>
93- sessionTitle ? (
94- < div
95- className = { `bg-sinfo-primary border-4 border-white rounded-lg p-6 shadow-xl w-fit ${ className } ` }
96- >
97- < p className = "text-sinfo-quinary font-black uppercase text-2xl md:text-3xl leading-tight" >
98- { sessionTitle }
99- </ p >
100- </ div >
101- ) : null ;
102-
10342 return (
104- < main className = "w-full min-h-screen bg-sinfo-primary relative overflow-hidden" >
105- { /* Decorative circles */ }
106- < div className = "absolute left-0 bottom-0 w-28 h-28 md:w-40 md:h-40 bg-sinfo-tertiary rounded-tr-full z-10" />
107- < div className = "absolute right-0 bottom-0 w-28 h-28 md:w-40 md:h-40 bg-sinfo-secondary rounded-tl-full z-10" />
108-
109- { /* Glowing line separator - positioned where circles end */ }
110- < div className = "absolute bottom-0 md:bottom-0 left-0 right-0 z-[5]" >
111- < div className = "w-full h-[3px] bg-gradient-to-r from-transparent via-white/50 to-transparent animate-pulse shadow-[0_0_10px_rgba(255,255,255,0.5)]" />
112- </ div >
113-
114- < div className = "relative max-w-7xl mx-auto px-6 md:px-10 py-12 md:py-16" >
115- { /* Top decorative elements - only visible on desktop */ }
116- < div className = "hidden md:block absolute top-4 right-8 md:top-6 md:right-16 z-20" >
117- < ImageWithFallback
118- src = "/images/decorative-images/redElement.svg"
119- alt = "Decorative element"
120- width = { 80 }
121- height = { 80 }
122- className = "w-16 h-16 md:w-20 md:h-20"
123- />
43+ < main className = "min-h-screen bg-white" >
44+ { /* Hero Section */ }
45+ < section className = "bg-gradient-to-br from-sinfo-primary via-sinfo-primary to-sinfo-secondary py-16 sm:py-20 md:py-24" >
46+ < div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
47+ < div className = "text-center" >
48+ < h1 className = "text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-4 sm:mb-6" >
49+ Speakers
50+ </ h1 >
51+ < p className = "text-base sm:text-lg md:text-xl text-white/90 max-w-3xl mx-auto" >
52+ Meet the global voices that are shaping SINFO's excellence.
53+ </ p >
54+ </ div >
12455 </ div >
125-
126- { /* Main grid */ }
127- < div className = "flex flex-col md:grid md:grid-cols-2 gap-8 md:gap-12 md:items-start" >
128- { /* Mobile layout - custom order */ }
129- < div className = "md:hidden space-y-8 relative" >
130- { /* 1. Name and title */ }
131- < div >
132- < h1 className = "text-6xl font-black uppercase leading-[0.85] tracking-tight text-white" >
56+ </ section >
57+
58+ { /* Main Content */ }
59+ < section className = "py-12 sm:py-16 md:py-20" >
60+ < div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
61+ < div className = "grid grid-cols-1 lg:grid-cols-12 gap-12" >
62+ { /* Left Column - Info */ }
63+ < div className = "lg:col-span-7 xl:col-span-8 order-2 lg:order-1" >
64+ < h1 className = "text-4xl md:text-5xl font-bold text-gray-900 mb-2" >
13365 { speaker . name }
13466 </ h1 >
135- { ( speaker . title || speaker . company ?. name ) && (
136- < p className = "mt-4 text-sinfo-tertiary text-xl font-bold" >
137- { speaker . title }
138- </ p >
139- ) }
140- </ div >
67+ < h2 className = "text-xl md:text-2xl text-sinfo-primary font-medium mb-8" >
68+ { speaker . title }
69+ { speaker . company ?. name && (
70+ < span className = "text-gray-500" >
71+ , { speaker . company . name }
72+ </ span >
73+ ) }
74+ </ h2 >
75+
76+ < div className = "prose prose-lg text-gray-600 max-w-none mb-12 whitespace-pre-line" >
77+ { speaker . description }
78+ </ div >
14179
142- { /* Red element decoration after title, before photo */ }
143- < div className = "flex justify-end -mb-14" >
144- < ImageWithFallback
145- src = "/images/decorative-images/redElement.svg"
146- alt = "Decorative element"
147- width = { 56 }
148- height = { 56 }
149- className = "w-16 h-16"
150- />
80+ { /* Sessions */ }
81+ { speaker . sessions && speaker . sessions . length > 0 && (
82+ < div className = "border-t border-gray-200 pt-8" >
83+ < h3 className = "text-2xl font-bold text-sinfo-primary mb-6" >
84+ Sessions
85+ </ h3 >
86+ < div className = "space-y-6" >
87+ { speaker . sessions . map ( ( session ) => {
88+ const dateStr = session . date
89+ ? new Date ( session . date ) . toLocaleDateString ( "en-GB" , {
90+ day : "numeric" ,
91+ month : "short" ,
92+ year : "numeric" ,
93+ } )
94+ : "" ;
95+ const timeStr = session . date
96+ ? generateTimeInterval (
97+ session . date ,
98+ session . duration || 0 ,
99+ { onlyHours : true } ,
100+ )
101+ : "" ;
102+
103+ return (
104+ < div
105+ key = { session . id }
106+ className = "bg-gray-50 rounded-lg p-6 hover:shadow-md transition-shadow"
107+ >
108+ < div className = "flex flex-wrap gap-y-2 gap-x-6 text-sm font-semibold text-sinfo-secondary mb-3" >
109+ { session . date && (
110+ < div className = "flex items-center gap-1.5" >
111+ < Calendar className = "w-4 h-4" />
112+ < span > { dateStr } </ span >
113+ </ div >
114+ ) }
115+ { timeStr && (
116+ < div className = "flex items-center gap-1.5" >
117+ < Clock className = "w-4 h-4" />
118+ < span > { timeStr } </ span >
119+ </ div >
120+ ) }
121+ { session . place && (
122+ < div className = "flex items-center gap-1.5" >
123+ < MapPin className = "w-4 h-4" />
124+ < span > { session . place } </ span >
125+ </ div >
126+ ) }
127+ </ div >
128+ < h4 className = "text-xl font-bold text-gray-900" >
129+ { session . name }
130+ </ h4 >
131+ { session . description && (
132+ < ShowMore lines = { 3 } className = "mt-2 text-gray-600" >
133+ { session . description }
134+ </ ShowMore >
135+ ) }
136+ </ div >
137+ ) ;
138+ } ) }
139+ </ div >
140+ </ div >
141+ ) }
151142 </ div >
152143
153- { /* 2. Photo with decorative elements */ }
154- < div className = "space-y-8 relative -mt-14" >
155- { /* Speaker photo with yellow background */ }
156- < SpeakerPhoto className = "h-80" />
157-
158- { /* Small decorative box */ }
159- < div className = "max-w-[180px] -mt-9" >
144+ { /* Right Column - Image */ }
145+ < div className = "lg:col-span-5 xl:col-span-4 order-1 lg:order-2" >
146+ < div className = "relative aspect-square w-full max-w-md mx-auto rounded-full overflow-hidden shadow-xl bg-gray-200" >
160147 < ImageWithFallback
161- src = "/images/decorative-images/star.svg"
162- alt = "Decorative star"
163- width = { 100 }
164- height = { 100 }
165- className = "w-20 h-20 "
148+ src = { speaker . img }
149+ alt = { speaker . name }
150+ fill
151+ className = "object-cover"
152+ sizes = "(max-width: 768px) 100vw, 33vw "
166153 />
167154 </ div >
168155 </ div >
169-
170- { /* 3. Description box */ }
171- < div className = "bg-sinfo-light rounded-lg p-8 text-black flex items-center relative" >
172- < p className = "text-base font-bold leading-relaxed whitespace-pre-line" >
173- { speaker . description || "Texto texto bla bla" }
174- </ p >
175- </ div >
176-
177- { /* 4. Session title box */ }
178- < SessionTitleBox />
179-
180- { /* 5. Session info */ }
181- < SessionInfo />
182156 </ div >
183157
184- { /* Desktop layout - original two columns */ }
185- < div className = "hidden md:block space-y-8" >
186- { /* Name */ }
187- < div >
188- < h1 className = "text-7xl lg:text-8xl font-black uppercase leading-[0.85] tracking-tight text-white" >
189- { speaker . name }
190- </ h1 >
191- { ( speaker . title || speaker . company ?. name ) && (
192- < p className = "mt-4 text-sinfo-tertiary text-2xl font-bold" >
193- { speaker . title }
194- </ p >
195- ) }
196- </ div >
197-
198- { /* Description box with light background */ }
199- < div className = "bg-sinfo-light rounded-lg p-10 text-black flex items-center" >
200- < p className = "text-lg font-bold leading-relaxed whitespace-pre-line" >
201- { speaker . description || "Texto texto bla bla" }
202- </ p >
203- </ div >
204-
205- { /* Session info */ }
206- < SessionInfo />
207- </ div >
208-
209- { /* Desktop right column - Photo and session title */ }
210- < div className = "hidden md:block space-y-8 relative" >
211- { /* Speaker photo with yellow background */ }
212- < SpeakerPhoto className = "h-[340px]" />
213-
214- { /* Decorative plus */ }
215- < div className = "absolute right-8 top-1/2 w-20 h-20" >
216- < span className = "absolute left-1/2 top-0 bottom-0 w-[4px] bg-white -translate-x-1/2" />
217- < span className = "absolute top-1/2 left-0 right-0 h-[4px] bg-white -translate-y-1/2" />
218- </ div >
219-
220- { /* Small decorative box */ }
221- < div className = "max-w-[180px] -mt-9" >
222- < ImageWithFallback
223- src = "/images/decorative-images/star.svg"
224- alt = "Decorative star"
225- width = { 100 }
226- height = { 100 }
227- className = "w-24 h-24"
228- />
229- </ div >
230-
231- { /* Session title box */ }
232- < SessionTitleBox />
158+ { /* Back Button */ }
159+ < div className = "mt-12 text-center" >
160+ < Link
161+ href = "/speakers"
162+ className = "inline-flex items-center px-6 py-3 bg-sinfo-primary text-white rounded-lg font-semibold hover:opacity-95 transition"
163+ >
164+ ← Back to all speakers
165+ </ Link >
233166 </ div >
234167 </ div >
235-
236- { /* Bottom CTA button */ }
237- < div className = "mt-12 md:mt-16 flex justify-center relative z-10" >
238- < Link
239- href = "/speakers"
240- className = "bg-sinfo-senary text-white font-black py-5 px-16 md:py-6 md:px-20 rounded-full text-xl md:text-2xl uppercase shadow-2xl hover:opacity-90 transition-opacity"
241- >
242- SEE ALL SPEAKERS
243- </ Link >
244- </ div >
245- </ div >
168+ </ section >
246169 </ main >
247170 ) ;
248171}
0 commit comments