Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import SpeakerInfo from "@/components/SpeakerInfo.tsx";
import {AppProvider} from "@/context/AppContext.tsx";
import TicketsPage from "@/pages/TicketsPage.tsx";
import CodeOfConductSpeakers from "@/pages/CodeOfConductSpeakers.tsx";
import Agenda from "@/pages/Agenda.tsx";
import MediaKit from "@/pages/MediaKit.tsx";
import SessionPage from "@/pages/SessionPage.tsx";

const queryClient = new QueryClient();

Expand All @@ -35,8 +37,11 @@ const App = () => {
<Route path="/codigo-de-conducta-speakers" element={<CodeOfConductSpeakers />}/>
<Route path="/privacy-policy" element={<PrivacyPolicy/>}/>
<Route path="/speaker/:speakerId" element={<SpeakerInfo/>}/>
<Route path="/boletos" element={<TicketsPage />} />
<Route path="/agenda" element={<Agenda/>} />
<Route path="/boletos" element={<TicketsPage />} />
<Route path="/media-kit" element={<MediaKit />} />
<Route path="session/:sessionId" element={<SessionPage />} />
</Routes>
</main>
<Footer />
Expand Down
17 changes: 8 additions & 9 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,16 @@ const Header = () => {
<span>Se un Patrocinador</span>
</a>
<a
onClick={() => navigate('/#organizadores')}
onClick={() => navigate('/#speakers')}
className={cn("text-white hover:text-posadev-brightPink transition-colors duration-300 flex items-center space-x-1", isActive('/#comunidades') && "text-posadev-brightPink")}
>
<span>Organizadores</span>
<span>Speakers</span>
</a>
<a
onClick={() => navigate('/#galeria')}
onClick={() => navigate('/agenda')}
className={cn("text-white hover:text-posadev-brightPink transition-colors duration-300 flex items-center space-x-1", isActive('/#galeria') && "text-posadev-brightPink")}
>
<span>Galería</span>
<span>Agenda</span>
</a>
<a
onClick={() => {
Expand All @@ -92,17 +92,16 @@ const Header = () => {
className="md:hidden absolute top-16 left-0 right-0 bg-black/95 rounded-b-xl backdrop-blur-md border-b border-posadev-darkPink/20">
<nav className="flex flex-col gap-2 px-4 py-4 space-y-4">
<a
onClick={() => navigateMenu('/#organizadores')}
onClick={() => navigateMenu('/#speakers')}
className={cn("w-full text-white hover:text-posadev-brightPink transition-colors duration-300 flex items-center px-4 py-1 space-x-2", isActive('/#comunidades') && "text-posadev-brightPink")}
>
Organizadores
Speakers
</a>
<a
onClick={() => navigateMenu('/#galeria')}
onClick={() => navigateMenu('/agenda')}
className={cn("w-full flex items-center gap-2 text-white hover:text-posadev-brightPink transition-colors px-4 py-1 duration-300 space-x-2", isActive('/#galeria') && "text-posadev-brightPink")}
>
<Image className="w-4 h-4" aria-hidden="true"/>
Galería
Agenda
</a>
<a
onClick={() => navigateMenu('/#patrocinadores')}
Expand Down
42 changes: 42 additions & 0 deletions src/components/agenda/AgendaContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Card } from "../ui/card"
import {Badge} from "@/components/ui/badge.tsx";
import {useEffect} from "react";
import {getAgenda} from "@/https/fetch.ts";
import {useAppContext} from "@/context/AppContext.tsx";
import AgendaRow from "@/components/agenda/AgendaRow.tsx";

const AgendaContent = () => {
const {setAgenda, agenda, savedSessions} = useAppContext()
const {setDisplayAll, displayAll} = useAppContext()

useEffect(() => {
getAgenda().then((data) => {
setAgenda(data[0].timeSlots)
})
}, []);

if (agenda.length === 0) return (<></>)

if (!displayAll && savedSessions.size === 0) {
return (<Card className="w-full pb-8 md:px-6 relative shadow-gray-500">
<nav className="flex gap-4 pt-8 pb-2 sticky bg-white w-full">
<Badge variant={`${displayAll ? "default" : "ghost"}`} onClick={() => setDisplayAll(true)} role="button">Display All</Badge>
<Badge variant={`${displayAll ? "ghost" : "default"}`} onClick={() => setDisplayAll(false)} role="button">Display Saved</Badge>
</nav>
<h2 className="text-center text-alternative-700 text-2xl font-bold">Por favor guarda alguna sesion para verla aqui</h2>
</Card>)
}

return (
<Card className="w-full pb-8 md:px-6 relative shadow-gray-500">
<nav className="flex gap-4 pt-8 pb-2 sticky bg-white w-full">
<Badge variant={`${displayAll ? "default" : "ghost"}`} onClick={() => setDisplayAll(true)} role="button">Display All</Badge>
<Badge variant={`${displayAll ? "ghost" : "default"}`} onClick={() => setDisplayAll(false)} role="button">Display Saved</Badge>
</nav>
{agenda.map(({slotStart, rooms}) => (
<AgendaRow key={slotStart} slotStart={slotStart} rooms={rooms}/>
))}
</Card>
)
}
export default AgendaContent
41 changes: 41 additions & 0 deletions src/components/agenda/AgendaHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {Card} from "@/components/ui/card.tsx";
import AgendaItem from "@/components/agenda/AgendaItem.tsx";
import {Beer, CandyCane, Megaphone, Coffee, HandPlatter, IdCardLanyard} from "lucide-react";
import Markup from "@/components/ui/Markup.tsx";

const AgendaHeader = () => {
return (
<>
<header className="flex justify-center flex-col gap-4 items-center w-full">
<h1 className="text-alternative-700 text-3xl md:text-5xl font-bold">Agenda</h1>
<div className="w-20 h-1 bg-gradient-to-r from-posadev-darkPink to-posadev-brightPink mx-auto rounded-full"/>
</header>
<aside className="flex flex-col gap-4 w-full lg:w-3/4">
<Card className="p-4 flex flex-col gap-4 border-none bg-transparent shadow-none">
<p className="text-primary-800 font-bold">En este evento te invitamos a:</p>
<ul className="w-full list-none grid gap-x-6 gap-y-2 grid-cols-2">
<Markup id="li-charlas" text="Asistir a las charlas" />
<Markup id="li-empresas" text="Conocer a las empresas" />
<Markup id="li-actividades" text="Participar en las actividades" />
<Markup id="li-rifas" text="Participar en las rifas" />
<Markup id="li-feedback" text="Déjarnos feedback" />
<Markup id="li-networking" text="Hacer networking" />
<Markup id="li-comunidad" text="Visitar la sala comunidad (con actividades y small talks)" />
<Markup id="li-conducta" text="Respetar el código de conducta" />
<Markup id="li-fotos" text="Subir fotos a las redes y tagearnos" />
<Markup id="li-diversion" text="Pásartela increible !!!" />
</ul>
</Card>
<div className="flex md:flex-row flex-col gap-10 justify-center items-center md:items-baseline text-primary-500 mt-[72px]">
<AgendaItem time={'10:00 AM'} title={'Registro'} icon={IdCardLanyard } />
<AgendaItem time={'9:30am - 11:00am'} title={'Coffee break'} icon={Coffee} />
<AgendaItem time={'11:00 AM - 2:00 PM y 3:00 PM - 7:00 PM'} title={'Charlas'} icon={Megaphone} />
<AgendaItem time={'2:00 PM - 3:00 PM'} title={'Comida'} icon={HandPlatter} />
<AgendaItem time={'3:00 PM - 5:00 PM'} title={'Networking'} icon={CandyCane} />
<AgendaItem time={'7:00 PM - 9:00 PM'} title={'Clausura'} icon={Beer} />
</div>
</aside>
</>
)
}
export default AgendaHeader
19 changes: 19 additions & 0 deletions src/components/agenda/AgendaItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const AgendaItem = ({ icon: Icon, title, time }) => {
const [start, end] = time.split(" - ");

return (
<aside className="flex flex-col items-center text-center gap-2 w-[180px] hover:scale-105 transition-transform">
<span className="h-14 w-14 border-2 border-primary-500 flex items-center justify-center">
{Icon && <Icon className="h-6 w-6 text-primary-500" />}
</span>
<p className="font-medium text-primary-800">{title}</p>
<time className="flex flex-col items-center text-primary-600 text-sm leading-tight">
<span>{start}</span>
{end && <span className="text-xs">-</span>}
<span>{end}</span>
</time>
</aside>
)
}

export default AgendaItem
57 changes: 57 additions & 0 deletions src/components/agenda/AgendaRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, {useEffect, useState} from "react";
import RoomsExpandable from "@/components/agenda/RoomsExpandable.tsx";
import Rooms from "@/components/agenda/Rooms.tsx";
import {useIsMobile} from "@/hooks/use-mobile.tsx";
import {IRoomAgenda} from "@/types/agenda.ts";
import {formatTime} from "@/lib/utils.ts";
import {useAppContext} from "@/context/AppContext.tsx";

interface AgendaRowProps {
slotStart: string;
rooms: IRoomAgenda[]
}

const AgendaRow: React.FC<AgendaRowProps> = ({slotStart, rooms}) => {
const {displayAll} = useAppContext()
const isMobile = useIsMobile()
const eventDate = "2025-12-06"
const slotDateTime = new Date(`${eventDate}T${slotStart}`)
const now = new Date()
const endDateTime = new Date(rooms[0]?.session.endsAt)
const expandableLogic = (!displayAll && isMobile) || (!isMobile)

const endTime = endDateTime.toLocaleTimeString(["en-US"], {hour: '2-digit', minute:'2-digit'})
const hasPassed = now > endDateTime
const [, forceUpdate] = useState(0)

useEffect(() => {
let updateInterval = endDateTime.getTime() - now.getTime() + 60_000
if (updateInterval < 0) updateInterval = 0

const timer = setTimeout(() => {
forceUpdate(prev => prev + 1)
}, updateInterval)

return () => clearTimeout(timer)
}, [slotStart])

if (rooms.length === 0) return (
<section>
Cargando...
</section>
)

if (hasPassed) return null

return (
<section
className={`flex flex-col mt-8 mb-2.5 w-full transition-opacity duration-700 ${
hasPassed ? "opacity-0 pointer-events-none" : "opacity-100"
}`}
>
<Rooms rooms={rooms} slotStart={slotStart} endsAt={endTime} hide={false} />
<RoomsExpandable rooms={rooms} slotStart={slotStart} endsAt={endTime} hide={true} />
</section>
)
}
export default AgendaRow
79 changes: 79 additions & 0 deletions src/components/agenda/CardCharla.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { useEffect, useState } from "react";
import { IRoomAgenda } from "@/types/agenda.ts";
import { Link } from "react-router-dom";
import { useAppContext } from "@/context/AppContext.tsx";
import {ISession} from "@/types/speakers.ts";
import {Star} from "lucide-react";

interface CardEventProps {
room: IRoomAgenda;
}

const CardEvent: React.FC<CardEventProps> = ({ room }) => {
const { savedSessions, setSavedSessions, displayAll } = useAppContext();
const session = room.session;
const [isSaved, setIsSaved] = useState(false);

useEffect(() => {
if (!displayAll && !savedSessions.has(session)) return;
setIsSaved(savedSessions.has(session));
}, []);

const handleSaveSession = (session: ISession) => {
if (savedSessions.has(session)) {
savedSessions.delete(session);
setIsSaved(false);
} else {
savedSessions.add(session);
setIsSaved(true);
}
return setSavedSessions(savedSessions);
};

if (!displayAll && !savedSessions.has(session)) return null;
return (
<div id={`session-${session.id}`} className="flex flex-col gap-4 mt-6 w-full">
<h3 className="font-bold text-2xl">Sala: {room.name}</h3>
<WrappedCardEvent className={`${isSaved ? "bg-primary-700" : "bg-white"} relative flex flex-col gap-2 border-2 border-primary-600 rounded-lg w-full min-h-52 h-full shadow-sm shadow-gray-600 p-4 hover:scale-105 transition-all duration-300`}
session={session}
>
<h4 className={` text-2xl font-bold ${isSaved ? "text-white" : "text-alternative-600"}`}>
{session.title}
</h4>
<aside className="h-full" hidden={session.speakers.length == 0}>
{session.speakers.map((speaker) => (
<p key={speaker.id} className={`${isSaved ? "text-gray-300" : "text-gray-500"} font-bold text-2xl`}>{speaker.name}</p>
))}
</aside>
<p hidden={session.speakers.length > 0} className={`h-full ${isSaved ? "text-gray-300" : "text-gray-500"} font-bold text-2xl`}>{session.description}</p>
<button
type="button"
onClick={(e) => {
e.preventDefault();
handleSaveSession(session);
}}
className="self-end"
>
<Star className={`h-8 w-8 ${isSaved ? " fill-white text-primary-700 hover:text-white hover:fill-primary-600" : "text-primary-600 hover:fill-primary-600"} transition-colors duration-300`} />
</button>
</WrappedCardEvent>
</div>
);
};

interface WrappedCardEventProps {
session: ISession;
children: React.ReactNode;
className: string;
}

const WrappedCardEvent: React.FC<WrappedCardEventProps> = ({session, children, className}) => {
if (session.speakers.length === 0) return (<div className={className}>{children}</div>)
return <Link
to={`/session/${session.id}`}
className={className}
>{children}
</Link>
}

export default CardEvent;
26 changes: 26 additions & 0 deletions src/components/agenda/Rooms.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react";
import Adorno from "@/components/icons/Adorno.tsx";
import CardCharla from "@/components/agenda/CardCharla.tsx";
import {Card} from "@/components/ui/card.tsx";
import {formatTime} from "@/lib/utils.ts";

const Rooms = ({rooms, slotStart, hide, endsAt}) => {
if (hide) return null;
return (
<>
<Card className="flex items-center gap-4 p-2 bg-alternative-700 border-alternative-700">
<Adorno className="h-8 w-8" />
<time className="font-bold text-2xl text-white">{formatTime(slotStart)}</time>
<span aria-label="a" className="font-bold text-2xl text-gray-500">-</span>
<time className="font-bold text-2xl text-white">{endsAt}</time>
<h2 className="font-bold text-2xl text-gray-700"></h2>
</Card>
<div className="flex flex-col flex-wrap flex-grow md:flex-row gap-6 px-10 w-full">
{rooms.map((room) => {
return <CardCharla key={room.id} room={room} />
})}
</div>
</>
)
}
export default Rooms
34 changes: 34 additions & 0 deletions src/components/agenda/RoomsExpandable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {Accordion, AccordionContent, AccordionItem, AccordionTrigger} from "@/components/ui/accordion.tsx";
import CardCharla from "@/components/agenda/CardCharla.tsx";
import Adorno from "@/components/icons/Adorno.tsx";
import React from "react";
import {formatTime} from "@/lib/utils.ts";

const RoomsExpandable = ({rooms, slotStart, endsAt, hide}) => {
if (hide) return null;
return (
<Accordion type="single" collapsible className="">
<AccordionItem value={slotStart}>
<div className="flex items-center gap-4">
<AccordionTrigger className="flex gap-6 px-2">
<Adorno/>
<time className="font-bold text-2xl">{formatTime(slotStart)}</time>
<span aria-label="a" className="font-bold text-2xl text-gray-500">-</span>
<time className="font-bold text-2xl text-white">{endsAt}</time>
<h2 className="font-bold text-2xl text-gray-700"></h2>
Ver mas
</AccordionTrigger>
</div>
<AccordionContent>
{rooms.map((room) => (
<div className="flex flex-col md:flex-row gap-6" key={room.id}>
<CardCharla room={room}/>
</div>
))}
</AccordionContent>
</AccordionItem>
</Accordion>
)
}

export default RoomsExpandable
Loading