Skip to content
Merged
Binary file added public/images/speakers/Alexis_Alva_Nunez.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Alonso_Astroza.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Ariane_Carvajal.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Daniel_Hernandez.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/David_Cortes.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Dylan_Oteiza.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Francisco_Alfaro.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Francisco_Ponce.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Gabriel_Grobier_Romo.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Ingrid_Solis_Gonzalez.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Jesus_Henriquez.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Lia_Da_Silva.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Marcelo_Cavieres.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Pablo_Jimenez.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Pia_Aedo.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Sebastian_flores.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/speakers/Valeska_Canales.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/images/speakers/ana-gonzalez.webp
Binary file not shown.
Binary file removed public/images/speakers/carla-herrera.webp
Binary file not shown.
Binary file removed public/images/speakers/carlos-rojas.webp
Binary file not shown.
Binary file removed public/images/speakers/daniel-castro.webp
Binary file not shown.
Binary file removed public/images/speakers/felipe-rojas.webp
Diff not rendered.
Binary file removed public/images/speakers/jorge-mendez.webp
Diff not rendered.
Binary file removed public/images/speakers/laura-perez.webp
Diff not rendered.
Binary file removed public/images/speakers/patricia-morales.webp
Diff not rendered.
Binary file removed public/images/speakers/roberto-gutierrez.webp
Diff not rendered.
28 changes: 23 additions & 5 deletions src/app/[city]/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ export default async function CityPage({ params }) {
notFound();
}

// Función para extraer la hora de inicio
const getStartTime = (timeStr) => {
if (timeStr.includes(' - ')) {
return timeStr.split(' - ')[0];
}
return timeStr;
};

// Ordenar eventos por hora
const sortedSchedule = [...data.schedule].sort((a, b) => {
return getStartTime(a.time).localeCompare(getStartTime(b.time));
});

return (
<>
{/* Hero Section */}
Expand All @@ -59,6 +72,7 @@ export default async function CityPage({ params }) {
<p className="text-xl md:text-2xl opacity-90">{data.date}</p>
<p className="text-lg opacity-80">{data.venue}</p>
</HeroSection>

{/* Sección de introducción - Solo se muestra si existe */}
{data.introduction && (
<section className="container-py">
Expand Down Expand Up @@ -104,18 +118,22 @@ export default async function CityPage({ params }) {

{/* Registro Section */}
<section id="registro" className="container-py">
<FeatureGuard featureName="registration"cityData={data}>
<FeatureGuard featureName="registration" cityData={data}>
<RegistrationForm />
</FeatureGuard>
</section>

{/* Agenda*/}
<section className="container-py">
<h2 className="section-title">Agenda</h2>
{data.length > 0 ? (
{sortedSchedule.length > 0 ? (
<div className="space-y-4 md:space-y-6 mt-6 md:mt-8 max-w-4xl mx-auto">
{data.schedule.map((talk, index) => (
<TalkCard key={talk.id} talk={talk} />
{sortedSchedule.map((talk) => (
<TalkCard
key={talk.id}
talk={talk}
showRoom={true} // Mostrar la sala en la vista de ciudad
/>
))}
</div>
) : (
Expand Down Expand Up @@ -230,4 +248,4 @@ export default async function CityPage({ params }) {
</footer>
</>
);
}
}
95 changes: 27 additions & 68 deletions src/app/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import Image from "next/image";
import CountdownTimer from "@/components/CountdownTimer";
import ChileMap from "@/components/ChileMap";
import TalkCard from "@/components/TalkCard";
import RegistrationForm from "@/components/RegistrationForm";
import HeroSection from "@/components/HeroSection";
import SponsorList from "./sponsors/components/SponsorList";
import featuredTalks from "@/data/featuredTalks";
import { FeatureGuard } from "@/components/FeatureManagement/FeatureGuard";
import allTalks from "@/data/talks";
import RegistrationState from "@/components/RegistrationState";
import EmptyState from "@/components/EmptyState";
import cityData from "@/data/cities";
Expand Down Expand Up @@ -49,28 +48,38 @@ export default function Home() {
{/* Agenda / Charlas Section */}
<section className="container-py">
<h2 className="section-title">Charlas Destacadas</h2>

{featuredTalks && featuredTalks.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-2 gap-6 mt-8 max-w-5xl mx-auto">
{featuredTalks.map((talk, index) => (
<TalkCard key={index} talk={talk} />
))}
</div>
) : (
<EmptyState context="global" />
)}
{featuredTalks && featuredTalks.length > 0 && (
<div className="flex justify-center mt-8">
<>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-2 gap-6 mt-8 max-w-5xl mx-auto">
{featuredTalks.map((talk, index) => (
<TalkCard key={index} talk={talk} />
))}
</div>
<div className="flex justify-center mt-8">
<Link href="/talks" className="btn-primary">
Ver todas las charlas
</Link>
</div>
</>
) : allTalks && allTalks.length > 0 ? (
<div className="text-center mt-8 max-w-xl mx-auto">
<p className="mb-4 text-lg font-semibold">
Ya puedes revisar la agenda del evento PyDay 2025
</p>
<Link href="/talks" className="btn-primary">
Ver todas las charlas
Ver agenda completa
</Link>
</div>
) : (
<EmptyState context="global" />
)}
</section>

{/* Registro Section */}
<section className="container-py">
<h2 className="section-title">Registro PyDay 2025</h2>
<RegistrationState context="global" />
<h2 className="section-title">Registro PyDay 2025</h2>
<RegistrationState context="global" />
</section>

{/* Acerca de PyDay */}
Expand All @@ -93,63 +102,12 @@ export default function Home() {
</div>
</section>

{/* Patrocinadores Section - Versión Simplificada */}
{/* Patrocinadores Section */}
<section id="patrocinadores" className="container-py">
<h2
className="section-title font-bold
tracking-wider text-4xl mb-0"
>
<h2 className="section-title font-bold tracking-wider text-4xl mb-0">
Nuestros Patrocinadores
</h2>
{/* FIXME: Escoger formato de Patrocinadores */}
<SponsorList />
{/* Patrocinadores Premium */}
{/* <div className="mt-8">
<h3 className="text-2xl font-bold text-center mb-6 text-yellow-400">
Patrocinadores Premium
</h3>
<div className="flex justify-center">
<Link>
href="https://www.python.org/psf-landing/"
target="_blank"
rel="noopener noreferrer"
className="bg-white/10 backdrop-blur p-6 rounded-lg hover:bg-white/20 transition-all duration-300 flex items-center justify-center"
>
<div className="relative h-32 w-64">
<Image
src="/images/logos/logos/psf-logo.webp"
alt="Python Software Foundation"
fill
className="object-contain"
/>
</div>
</Link>
</div>
</div> */}

{/* Patrocinadores Básico */}
{/* <div className="mt-12">
<h3 className="text-2xl font-bold text-center mb-6 text-gray-300">
Patrocinadores Básico
</h3>
<div className="flex justify-center">
<Link>
href="https://aws.amazon.com/es/"
target="_blank"
rel="noopener noreferrer"
className="bg-white/10 backdrop-blur p-6 rounded-lg hover:bg-white/20 transition-all duration-300 flex items-center justify-center"
>
<div className="relative h-24 w-48">
<Image
src="/images/logos/logos/aws.webp"
alt="Amazon Web Services"
fill
className="object-contain"
/>
</div>
</Link>
</div>
</div> */}

{/* CTA para patrocinadores */}
<div className="mt-16 max-w-3xl mx-auto text-center">
Expand All @@ -174,3 +132,4 @@ export default function Home() {
</>
);
}

119 changes: 49 additions & 70 deletions src/app/talks/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Link from "next/link";
import allTalks from "@/data/talks";
import cityData from "@/data/cities";
import EmptyState from "@/components/EmptyState";
import { FaSearch } from "react-icons/fa";

export default function TalksPage() {
const [selectedCity, setSelectedCity] = useState("");
Expand All @@ -25,18 +26,18 @@ export default function TalksPage() {
const filteredTalks = allTalks.filter((talk) => {
const cityInfo = cityData[talk.city];
const searchTerms = debouncedSearch.toLowerCase();

const cityMatch = !selectedCity || talk.city === selectedCity;
const categoryMatch =
!selectedCategory || talk.category === selectedCategory;
const dateMatch = selectedDay ? cityInfo.date === selectedDay : true;

const categoryMatch = !selectedCategory || talk.category === selectedCategory;
const dateMatch = selectedDay ? cityInfo?.date === selectedDay : true;

const searchMatch =
talk.title.toLowerCase().includes(searchTerms) ||
talk.description.toLowerCase().includes(searchTerms) ||
talk.tags?.some((tag) => tag.toLowerCase().includes(searchTerms)) ||
talk.speaker.name.toLowerCase().includes(searchTerms);

(talk.tags && talk.tags.some(tag => tag.toLowerCase().includes(searchTerms))) ||
(talk.speaker?.name && talk.speaker.name.toLowerCase().includes(searchTerms)) ||
(talk.speakers && talk.speakers.some(s => s.name.toLowerCase().includes(searchTerms)));

return cityMatch && categoryMatch && dateMatch && searchMatch;
});

Expand Down Expand Up @@ -99,7 +100,8 @@ export default function TalksPage() {
))}
</div>
</div>
{/* Filtros de charlas */}

{/* Filtros de charlas (estructura original) */}
<div className="mb-8 backdrop-blur-sm rounded-lg p-4 md:p-6">
<div className="flex flex-col md:flex-row gap-4 md:items-center justify-between">
<div className="flex items-center gap-2 flex-wrap">
Expand All @@ -125,7 +127,6 @@ export default function TalksPage() {
<option value="tecnica">Técnica</option>
<option value="comunidad">Comunidad</option>
<option value="caso-de-exito">Caso de Éxito</option>
<option value="keynote">Keynote</option>
</select>
</div>
<div className="relative">
Expand All @@ -136,73 +137,51 @@ export default function TalksPage() {
onChange={(e) => setSearchQuery(e.target.value)}
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"
/>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-4 w-4 absolute left-3 top-2 text-py-text/60"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
<FaSearch className="h-4 w-4 absolute left-3 top-2 text-py-text/60" />
</div>
</div>
</div>

{/* Renderizado de charlas */}
{/* Renderizado agrupado por sala */}
{filteredTalks.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredTalks.map((talk) => {
const cityInfo = cityData[talk.city];
return (
<TalkCard
key={talk.id}
talk={{
...talk,
date: cityInfo.date,
location: cityInfo.name,
cityInfo: cityInfo,
}}
/>
);
})}
</div>
Object.entries(
filteredTalks.reduce((acc, talk) => {
const room = talk.room || "Sala no asignada";
if (!acc[room]) acc[room] = [];
acc[room].push(talk);
return acc;
}, {})
).map(([roomName, talksInRoom]) => {
// Ordena por hora dentro de la sala
const sortedTalks = talksInRoom.sort((a, b) =>
a.time.localeCompare(b.time)
);

return (
<div key={roomName} className="mb-12">
<h2 className="text-xl font-bold mb-4 text-py-green">{roomName}</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{sortedTalks.map((talk) => {
const cityInfo = cityData[talk.city];
return (
<TalkCard
key={talk.id}
talk={{
...talk,
date: cityInfo.date,
location: cityInfo.name,
cityInfo: cityInfo,
}}
/>
);
})}
</div>
</div>
);
})
) : (
<EmptyState context="global" />
)}

{/* TODO: API para que funcione: */}
{/* Subscribe for updates */}
{/* <div className="mt-16 bg-green-800/30 backdrop-blur-sm rounded-lg p-6 md:p-8">
<div className="max-w-4xl mx-auto">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6">
<div>
<h3 className="text-xl md:text-2xl font-bold mb-2">
¿Quieres recibir actualizaciones?
</h3>
<p className="text-sm md:text-base text-white/80">
Suscríbete para recibir notificaciones sobre nuevas charlas y
cambios en el programa.
</p>
</div>
<div className="flex flex-col sm:flex-row gap-3">
<input
type="email"
placeholder="Tu correo electrónico"
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"
/>
<button className="btn-primary whitespace-nowrap py-2">
Suscribirse
</button>
</div>
</div>
</div>
</div> */}
</div>
);
}
}
19 changes: 12 additions & 7 deletions src/components/FeatureManagement/FeatureGuard.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ export const FeatureGuard = ({ children, featureName, cityData }) => {
registration: {
enabled: process.env.NEXT_PUBLIC_FEATURE_REGISTRATION === "true",
title: `Registro PyDay ${cityData?.name || "2025"}`,
subtitle: cityData?.registrationLink
? "¡Regístrate ahora para asegurar tu lugar en el PyDay 2025!"
: "El registro abrirá próximamente. Mantente atento a nuestras redes sociales",
showCTA: !!cityData?.registrationLink,
buttonText: "Registrarme ahora",
href: cityData?.registrationLink || "",
},
subtitle:
cityData?.registrationStatus === "soldout"
? "Los cupos se han agotado. ¡Gracias por tu interés!"
: cityData?.registrationLink
? "¡Regístrate ahora para asegurar tu lugar en el PyDay 2025!"
: "El registro abrirá próximamente. Mantente atento a nuestras redes sociales",
showCTA:
cityData?.registrationStatus !== "soldout" &&
!!cityData?.registrationLink,
buttonText: "Registrarme ahora",
href: cityData?.registrationLink || "",
},
sponsors: {
enabled: process.env.NEXT_PUBLIC_FEATURE_SPONSORS === "true",
title: "Patrocinadores PyDay",
Expand Down
Loading