@@ -6,6 +6,7 @@ import Link from "next/link";
66import allTalks from "@/data/talks" ;
77import cityData from "@/data/cities" ;
88import EmptyState from "@/components/EmptyState" ;
9+ import { FaSearch } from "react-icons/fa" ;
910
1011export default function TalksPage ( ) {
1112 const [ selectedCity , setSelectedCity ] = useState ( "" ) ;
@@ -25,18 +26,18 @@ export default function TalksPage() {
2526 const filteredTalks = allTalks . filter ( ( talk ) => {
2627 const cityInfo = cityData [ talk . city ] ;
2728 const searchTerms = debouncedSearch . toLowerCase ( ) ;
28-
29+
2930 const cityMatch = ! selectedCity || talk . city === selectedCity ;
30- const categoryMatch =
31- ! selectedCategory || talk . category === selectedCategory ;
32- const dateMatch = selectedDay ? cityInfo . date === selectedDay : true ;
33-
31+ const categoryMatch = ! selectedCategory || talk . category === selectedCategory ;
32+ const dateMatch = selectedDay ? cityInfo ?. date === selectedDay : true ;
33+
3434 const searchMatch =
3535 talk . title . toLowerCase ( ) . includes ( searchTerms ) ||
3636 talk . description . toLowerCase ( ) . includes ( searchTerms ) ||
37- talk . tags ?. some ( ( tag ) => tag . toLowerCase ( ) . includes ( searchTerms ) ) ||
38- talk . speaker . name . toLowerCase ( ) . includes ( searchTerms ) ;
39-
37+ ( talk . tags && talk . tags . some ( tag => tag . toLowerCase ( ) . includes ( searchTerms ) ) ) ||
38+ ( talk . speaker ?. name && talk . speaker . name . toLowerCase ( ) . includes ( searchTerms ) ) ||
39+ ( talk . speakers && talk . speakers . some ( s => s . name . toLowerCase ( ) . includes ( searchTerms ) ) ) ;
40+
4041 return cityMatch && categoryMatch && dateMatch && searchMatch ;
4142 } ) ;
4243
@@ -99,7 +100,8 @@ export default function TalksPage() {
99100 ) ) }
100101 </ div >
101102 </ div >
102- { /* Filtros de charlas */ }
103+
104+ { /* Filtros de charlas (estructura original) */ }
103105 < div className = "mb-8 backdrop-blur-sm rounded-lg p-4 md:p-6" >
104106 < div className = "flex flex-col md:flex-row gap-4 md:items-center justify-between" >
105107 < div className = "flex items-center gap-2 flex-wrap" >
@@ -125,7 +127,6 @@ export default function TalksPage() {
125127 < option value = "tecnica" > Técnica</ option >
126128 < option value = "comunidad" > Comunidad</ option >
127129 < option value = "caso-de-exito" > Caso de Éxito</ option >
128- < option value = "keynote" > Keynote</ option >
129130 </ select >
130131 </ div >
131132 < div className = "relative" >
@@ -136,73 +137,51 @@ export default function TalksPage() {
136137 onChange = { ( e ) => setSearchQuery ( e . target . value ) }
137138 className = "text-py-text border border-py-text/20 rounded pl-9 pr-3 py-1.5 w-full md:w-64 text-sm focus:outline-none focus:ring-2 focus:ring-py-green"
138139 />
139- < svg
140- xmlns = "http://www.w3.org/2000/svg"
141- className = "h-4 w-4 absolute left-3 top-2 text-py-text/60"
142- fill = "none"
143- viewBox = "0 0 24 24"
144- stroke = "currentColor"
145- >
146- < path
147- strokeLinecap = "round"
148- strokeLinejoin = "round"
149- strokeWidth = { 2 }
150- d = "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
151- />
152- </ svg >
140+ < FaSearch className = "h-4 w-4 absolute left-3 top-2 text-py-text/60" />
153141 </ div >
154142 </ div >
155143 </ div >
156144
157- { /* Renderizado de charlas */ }
145+ { /* Renderizado agrupado por sala */ }
158146 { filteredTalks . length > 0 ? (
159- < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" >
160- { filteredTalks . map ( ( talk ) => {
161- const cityInfo = cityData [ talk . city ] ;
162- return (
163- < TalkCard
164- key = { talk . id }
165- talk = { {
166- ...talk ,
167- date : cityInfo . date ,
168- location : cityInfo . name ,
169- cityInfo : cityInfo ,
170- } }
171- />
172- ) ;
173- } ) }
174- </ div >
147+ Object . entries (
148+ filteredTalks . reduce ( ( acc , talk ) => {
149+ const room = talk . room || "Sala no asignada" ;
150+ if ( ! acc [ room ] ) acc [ room ] = [ ] ;
151+ acc [ room ] . push ( talk ) ;
152+ return acc ;
153+ } , { } )
154+ ) . map ( ( [ roomName , talksInRoom ] ) => {
155+ // Ordena por hora dentro de la sala
156+ const sortedTalks = talksInRoom . sort ( ( a , b ) =>
157+ a . time . localeCompare ( b . time )
158+ ) ;
159+
160+ return (
161+ < div key = { roomName } className = "mb-12" >
162+ < h2 className = "text-xl font-bold mb-4 text-py-green" > { roomName } </ h2 >
163+ < div className = "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" >
164+ { sortedTalks . map ( ( talk ) => {
165+ const cityInfo = cityData [ talk . city ] ;
166+ return (
167+ < TalkCard
168+ key = { talk . id }
169+ talk = { {
170+ ...talk ,
171+ date : cityInfo . date ,
172+ location : cityInfo . name ,
173+ cityInfo : cityInfo ,
174+ } }
175+ />
176+ ) ;
177+ } ) }
178+ </ div >
179+ </ div >
180+ ) ;
181+ } )
175182 ) : (
176183 < EmptyState context = "global" />
177184 ) }
178-
179- { /* TODO: API para que funcione: */ }
180- { /* Subscribe for updates */ }
181- { /* <div className="mt-16 bg-green-800/30 backdrop-blur-sm rounded-lg p-6 md:p-8">
182- <div className="max-w-4xl mx-auto">
183- <div className="flex flex-col md:flex-row md:items-center justify-between gap-6">
184- <div>
185- <h3 className="text-xl md:text-2xl font-bold mb-2">
186- ¿Quieres recibir actualizaciones?
187- </h3>
188- <p className="text-sm md:text-base text-white/80">
189- Suscríbete para recibir notificaciones sobre nuevas charlas y
190- cambios en el programa.
191- </p>
192- </div>
193- <div className="flex flex-col sm:flex-row gap-3">
194- <input
195- type="email"
196- placeholder="Tu correo electrónico"
197- className="bg-black/30 text-white border border-white/20 rounded px-4 py-2 w-full sm:w-64 text-sm focus:outline-none focus:ring-2 focus:ring-green-500"
198- />
199- <button className="btn-primary whitespace-nowrap py-2">
200- Suscribirse
201- </button>
202- </div>
203- </div>
204- </div>
205- </div> */ }
206185 </ div >
207186 ) ;
208- }
187+ }
0 commit comments