Skip to content

Commit 68f8554

Browse files
authored
Merge pull request #232 from posadev/communities-agenda
feat: improve ui
2 parents aa051f8 + f6206f4 commit 68f8554

File tree

12 files changed

+161
-101
lines changed

12 files changed

+161
-101
lines changed

src/App.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ import React from "react";
1111
import PrivacyPolicy from "@/pages/PrivacyPolicy.tsx";
1212
import Footer from "@/components/Footer.tsx";
1313
import SpeakerInfo from "@/components/SpeakerInfo.tsx";
14-
import {AppProvider} from "@/context/AppContext.tsx";
14+
import {AppProvider, useAppContext} from "@/context/AppContext.tsx";
1515
import TicketsPage from "@/pages/TicketsPage.tsx";
1616
import CodeOfConductSpeakers from "@/pages/CodeOfConductSpeakers.tsx";
1717
import Agenda from "@/pages/Agenda.tsx";
1818
import MediaKit from "@/pages/MediaKit.tsx";
1919
import SessionPage from "@/pages/SessionPage.tsx";
20+
import {AppStatus} from "@/types/types.ts";
21+
import Loading from "@/pages/Loading.tsx";
22+
import ErrorPage from "@/pages/ErrorPage.tsx";
2023

2124
const queryClient = new QueryClient();
2225

src/components/Photo.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {ZoomIn} from "lucide-react";
2-
import React from "react";
2+
import React, {useState} from "react";
33
import {IImage} from "@/types/types.ts";
44

55
export interface PhotoProps {
@@ -9,6 +9,8 @@ export interface PhotoProps {
99
}
1010

1111
const Photo: React.FC<PhotoProps> = ({image, setSelectedImage, index}) => {
12+
const [loaded, setLoaded] = useState(false);
13+
1214
const openLightbox = (index: number) => {
1315
setSelectedImage(index);
1416
document.body.style.overflow = 'hidden';
@@ -27,7 +29,8 @@ const Photo: React.FC<PhotoProps> = ({image, setSelectedImage, index}) => {
2729
alt={image.alt}
2830
decoding="async"
2931
loading="lazy"
30-
className="w-full h-64 object-cover transition-transform duration-500 group-hover:scale-110 hover-scale"
32+
className={`w-full h-64 object-cover transition-transform duration-500 group-hover:scale-110 hover-scale ${loaded ? "blur-0" : "blur-xl bg-gray-200"}`}
33+
onLoad={() => setLoaded(true)}
3134
/>
3235
{/* Overlay */}
3336
<div className="absolute inset-0 bg-black/20 lg:group-hover:bg-black/60 flex justify-between align-middle via-transparent to-transparent opacity-100 lg:opacity-0 group-hover:opacity-100 transition-opacity duration-300">

src/components/Speaker.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import React, {useState} from "react";
22
import {ISpeaker} from "@/types/speakers.ts";
33
import {Card} from "@/components/ui/card.tsx";
44
import SocialMedia from "@/components/SocialMedia.tsx";
@@ -10,14 +10,20 @@ interface SpeakerProps {
1010

1111
const Speaker: React.FC<SpeakerProps> = ({speaker}) => {
1212
const navigate = useNavigate();
13+
const [loaded, setLoaded] = useState(false);
1314

1415
const handleClick = (speaker: ISpeaker) => {
1516
navigate(`/speaker/${speaker.id}`, {state: {speaker}});
1617
}
1718

1819
return (
1920
<article className="h-full w-full mx-1 flex flex-col justify-center items-center">
20-
<img src={speaker.profilePicture} alt={speaker.fullName} className="h-60 w-60 rounded-3xl" loading="lazy"/>
21+
<img
22+
src={speaker.profilePicture}
23+
alt={speaker.fullName}
24+
className={`h-60 w-60 rounded-3xl transition-all duration-500 ${loaded ? "blur-0" : "blur-xl bg-gray-200"}`}
25+
onLoad={() => setLoaded(true)}
26+
/>
2127
<Card className="bg-white/10 border-0 shadow-none text-white flex flex-col justify-start gap-4 h-52 p-6 w-full max-w-[400px]">
2228
<p className="text-2xl font-bold">{`${speaker.firstName.split(" ")[0]} ${speaker.lastName.split(" ")[0]}`}</p>
2329
<p className="my-2 text-xl break-words line-clamp-2 overflow-hidden flex-shrink-0 rounded">{speaker.tagLine}</p>

src/components/agenda/AgendaContent.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const AgendaContent = () => {
1818
if (agenda.length === 0) return (<></>)
1919

2020
if (!displayAll && savedSessions.size === 0) {
21-
return (<Card className="w-full pb-8 md:px-6 relative shadow-gray-500 min-h-60">
21+
return (<Card className="w-full pb-8 md:px-6 shadow-gray-500 min-h-60">
2222
<nav className="flex gap-4 pt-8 pb-2 sticky bg-white w-full h-20">
2323
<Badge variant={`${displayAll ? "default" : "ghost"}`} onClick={() => setDisplayAll(true)} role="button">Mostrar todos</Badge>
2424
<Badge variant={`${displayAll ? "ghost" : "default"}`} onClick={() => setDisplayAll(false)} role="button">Mostrar guardados</Badge>
@@ -28,14 +28,16 @@ const AgendaContent = () => {
2828
}
2929

3030
return (
31-
<Card className="w-full pb-8 md:px-6 relative shadow-gray-500">
32-
<nav className="flex gap-4 mt-4 py-2 px-4 sticky top-[80px] z-20 bg-white w-full h-14">
31+
<Card className="w-full pb-8 shadow-gray-500">
32+
<nav className="flex gap-4 mt-4 py-2 px-4 sticky top-[80px] z-20 bg-white w-full h-14 border-b border-gray-200">
3333
<Badge variant={`${displayAll ? "default" : "ghost"}`} onClick={() => setDisplayAll(true)} role="button">Mostrar todos</Badge>
3434
<Badge variant={`${displayAll ? "ghost" : "default"}`} onClick={() => setDisplayAll(false)} role="button">Mostrar guardados</Badge>
3535
</nav>
36+
<aside className="flex flex-col md:px-6">
3637
{agenda.map(({slotStart, rooms}) => (
3738
<AgendaRow key={slotStart} slotStart={slotStart} rooms={rooms}/>
3839
))}
40+
</aside>
3941
</Card>
4042
)
4143
}

src/components/agenda/AgendaHeader.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const AgendaHeader = () => {
1111
<div className="w-20 h-1 bg-gradient-to-r from-posadev-darkPink to-posadev-brightPink mx-auto rounded-full"/>
1212
</header>
1313
<aside className="flex flex-col gap-4 w-full lg:w-3/4">
14-
<Card className="p-4 flex flex-col gap-4 border-none bg-transparent shadow-none">
14+
<Card className="px-4 md:p-4 flex flex-col gap-4 border-none bg-transparent shadow-none">
1515
<p className="text-primary-800 font-bold">En este evento te invitamos a:</p>
1616
<ul className="w-full list-none grid gap-x-6 gap-y-2 grid-cols-2">
1717
<Markup id="li-charlas" text="Asistir a las charlas" />
@@ -26,7 +26,7 @@ const AgendaHeader = () => {
2626
<Markup id="li-diversion" text="Pásartela increible !!!" />
2727
</ul>
2828
</Card>
29-
<div className="flex md:flex-row flex-col gap-10 justify-center items-center md:items-baseline text-primary-500 mt-[72px]">
29+
<div className="flex md:flex-row flex-col gap-10 justify-center items-baseline text-primary-500 mt-[72px] px-4 md:px-0">
3030
<AgendaItem time={['9:00 AM - 3:00 PM']} title={'Registro'} icon={IdCardLanyard } />
3131
<AgendaItem time={['9:30am - 11:00am']} title={'Coffee break'} icon={Coffee} />
3232
<AgendaItem time={['10:15 AM - 2:00 PM','3:55 PM - 7:00 PM']} title={'Charlas'} icon={Megaphone} />

src/components/agenda/AgendaItem.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,22 @@ const AgendaItem: React.FC<AgendaItemProps> = ({ icon: Icon, title, time }) => {
1515
const isSingleTime = time.length === 1;
1616

1717
return (
18-
<aside className="flex flex-col items-center text-center gap-2 w-[180px] hover:scale-105 transition-transform">
19-
<span className="h-14 w-14 border-2 border-primary-500 flex items-center justify-center">
18+
<aside className="flex md:flex-col items-start md:items-center justify-center gap-4 md:gap-2 w-full md:w-[180px] hover:scale-105 transition-transform">
19+
<span className="h-14 min-w-14 max-w-14 border-2 border-primary-500 flex items-center justify-center">
2020
{Icon && <Icon className="h-6 w-6 text-primary-500" />}
2121
</span>
22-
<p className="font-medium text-primary-800">{title}</p>
23-
{
24-
time.map((timeString, index) => (
25-
<time key={`time-${index}`} className={`flex ${isSingleTime ? "flex-col" : "flex-row flex-wrap gap-2"} items-center text-primary-600 text-sm leading-tight`}>
26-
<span>{splitTime(timeString).start}</span>
27-
{splitTime(timeString).end && <span className="text-xs">-</span>}
28-
<span>{splitTime(timeString).end}</span>
29-
</time>
30-
))
31-
}
22+
<div className="flex flex-col md:gap-y-2 w-full items-start justify-start md:items-center text-center h-full">
23+
<p className="font-medium text-primary-800">{title}</p>
24+
{
25+
time.map((timeString, index) => (
26+
<time key={`time-${index}`} className={`flex ${isSingleTime ? "md:flex-col lg:flex-row lg:gap-2" : "md:flex-col lg:flex-row flex-wrap lg:gap-2"} text-primary-600 text-sm leading-tight`}>
27+
<span>{splitTime(timeString).start}</span>
28+
{splitTime(timeString).end && <span aria-label="a" className="text-xs">-</span>}
29+
<span>{splitTime(timeString).end}</span>
30+
</time>
31+
))
32+
}
33+
</div>
3234
</aside>
3335
)
3436
}

src/components/agenda/AgendaRow.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import React, {useEffect, useState} from "react";
22
import Rooms from "@/components/agenda/Rooms.tsx";
3-
import {useIsMobile} from "@/hooks/use-mobile.tsx";
43
import {IRoomAgenda} from "@/types/agenda.ts";
54
import {formatTime} from "@/lib/utils.ts";
6-
import {useAppContext} from "@/context/AppContext.tsx";
75
import Adorno from "@/components/icons/Adorno.tsx";
86
import {Card} from "@/components/ui/card.tsx";
97

@@ -13,13 +11,10 @@ interface AgendaRowProps {
1311
}
1412

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

2419
const endTime = endDateTime.toLocaleTimeString(["en-US"], {hour: '2-digit', minute:'2-digit'})
2520
const hasPassed = now > endDateTime
@@ -50,14 +45,18 @@ const AgendaRow: React.FC<AgendaRowProps> = ({slotStart, rooms}) => {
5045
hasPassed ? "opacity-0 pointer-events-none" : "opacity-100"
5146
}`}
5247
>
53-
<Card className="flex items-center gap-4 p-2 bg-alternative-700 border-alternative-700 sticky top-[136px] z-10">
48+
<Card className="
49+
flex items-center gap-4 p-2
50+
bg-alternative-700 border-alternative-700
51+
sticky top-[136px] z-10
52+
md:static md:top-auto md:z-auto
53+
">
5454
<Adorno className="h-8 w-8" />
5555
<time className="font-bold text-2xl text-white">{formatTime(slotStart)}</time>
5656
<span aria-label="a" className="font-bold text-2xl text-gray-500">-</span>
5757
<time className="font-bold text-2xl text-white">{endTime}</time>
58-
<h2 className="font-bold text-2xl text-gray-700"></h2>
5958
</Card>
60-
<Rooms rooms={rooms} slotStart={slotStart} endsAt={endTime} hide={false} />
59+
<Rooms rooms={rooms} />
6160
</section>
6261
)
6362
}
Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import React, { useEffect, useState } from "react";
22
import { IRoomAgenda } from "@/types/agenda.ts";
3-
import { Link } from "react-router-dom";
43
import { useAppContext } from "@/context/AppContext.tsx";
54
import {ISession} from "@/types/speakers.ts";
65
import {Star} from "lucide-react";
6+
import {Button} from "@/components/ui/button.tsx";
7+
import {useNavigate} from "react-router-dom";
78

89
interface CardEventProps {
910
room: IRoomAgenda;
@@ -12,6 +13,7 @@ interface CardEventProps {
1213
const CardEvent: React.FC<CardEventProps> = ({ room }) => {
1314
const { savedSessions, setSavedSessions, displayAll } = useAppContext();
1415
const session = room.session;
16+
const navigate = useNavigate();
1517
const [isSaved, setIsSaved] = useState(false);
1618

1719
useEffect(() => {
@@ -21,11 +23,12 @@ const CardEvent: React.FC<CardEventProps> = ({ room }) => {
2123

2224
const handleSaveSession = (session: ISession) => {
2325
// @ts-ignore
24-
setSavedSessions(prev => {
25-
const newSet = new Set(prev);
26+
setSavedSessions((prev: Set<number>) => {
27+
const newSet: Set<number> = new Set(prev);
2628
if (newSet.has(session.id)) {
2729
newSet.delete(session.id);
2830
setIsSaved(false);
31+
if (navigator.vibrate) navigator.vibrate(25);
2932
} else {
3033
newSet.add(session.id);
3134
setIsSaved(true);
@@ -36,30 +39,46 @@ const CardEvent: React.FC<CardEventProps> = ({ room }) => {
3639

3740
if (!displayAll && !savedSessions.has(session.id)) return null;
3841
return (
39-
<div id={`session-${session.id}`} className="flex flex-col gap-4 mt-6 w-full">
40-
<h3 className="font-bold text-2xl">Sala: {room.name}</h3>
41-
<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`}
42-
session={session}
42+
<div id={`session-${session.id}`} className="flex flex-col gap-4 mt-6">
43+
<h3 className={`font-bold text-2xl`}>Sala: {room.name}</h3>
44+
<WrappedCardEvent className={`relative
45+
bg-white shadow-sm shadow-gray-600 border-2 border-primary-600
46+
flex flex-col gap-2 rounded-lg min-h-52 h-full p-4
47+
transition-all duration-300
48+
`}
49+
session={session}
4350
>
44-
<h4 className={` text-2xl font-bold ${isSaved ? "text-white" : "text-alternative-600"}`}>
51+
<aside className="flex w-full items-center">
52+
{isSaved && (
53+
<span className="bg-primary-500 text-white text-xs font-bold px-2 py-1 rounded-full shadow w-20 text-center justify-self-start">Guardado</span>
54+
)}
55+
<div className="flex-1" />
56+
<button
57+
type="button"
58+
onClick={(e) => {
59+
e.preventDefault();
60+
handleSaveSession(session);
61+
}}
62+
className={`bg-white justify-self-end group flex justify-center items-center gap-2 border border-primary-500 font-bold px-2 py-1 rounded-lg transition-colors duration-300`}
63+
>
64+
<Star className={`h-4 w-4 text-primary-500 ${isSaved ? "fill-primary-500" : "fill-white group-hover:fill-primary-500"}`}/>
65+
</button>
66+
</aside>
67+
68+
<h4 className={` text-2xl font-bold text-alternative-600 ${isSaved ? "" : ""}`}>
4569
{session.title}
4670
</h4>
47-
<aside className="h-full" hidden={session.speakers.length == 0}>
71+
<div className="h-full" hidden={session.speakers.length == 0}>
4872
{session.speakers.map((speaker) => (
49-
<p key={speaker.id} className={`${isSaved ? "text-gray-300" : "text-gray-500"} font-bold text-2xl`}>{speaker.name}</p>
73+
<p key={speaker.id} className={`${isSaved ? "text-gray-600" : "text-gray-500"} font-bold text-2xl`}>{speaker.name}</p>
5074
))}
51-
</aside>
52-
<p hidden={session.speakers.length > 0} className={`h-full ${isSaved ? "text-gray-300" : "text-gray-500"} font-bold text-2xl`}>{session.description}</p>
53-
<button
54-
type="button"
55-
onClick={(e) => {
56-
e.preventDefault();
57-
handleSaveSession(session);
58-
}}
59-
className="self-end"
60-
>
61-
<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`} />
62-
</button>
75+
</div>
76+
<p hidden={session.speakers.length > 0} className={`h-full ${isSaved ? "text-gray-600" : "text-gray-500"} font-bold text-2xl`}>{session.description}</p>
77+
{ session.speakers.length > 0 && (
78+
<Button variant="ghost" onClick={() => navigate(`/session/${session.id}`)}>
79+
Ver detalles
80+
</Button>
81+
)}
6382
</WrappedCardEvent>
6483
</div>
6584
);
@@ -73,11 +92,10 @@ interface WrappedCardEventProps {
7392

7493
const WrappedCardEvent: React.FC<WrappedCardEventProps> = ({session, children, className}) => {
7594
if (session.speakers.length === 0) return (<div className={className}>{children}</div>)
76-
return <Link
77-
to={`/session/${session.id}`}
95+
return <div
7896
className={className}
7997
>{children}
80-
</Link>
98+
</div>
8199
}
82100

83101
export default CardEvent;

src/components/agenda/Rooms.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import React from "react";
22
import CardCharla from "@/components/agenda/CardCharla.tsx";
33

4-
const Rooms = ({rooms, slotStart, hide, endsAt}) => {
5-
if (hide) return null;
4+
const Rooms = ({rooms}) => {
65
return (
7-
<>
8-
<div className="flex flex-col flex-wrap flex-grow md:flex-row gap-6 px-10 w-full">
6+
<div className="grid grid-cols-[repeat(auto-fit,minmax(280px,1fr))] gap-6 px-4 w-full">
97
{rooms.map((room) => {
108
return <CardCharla key={room.id} room={room} />
119
})}
1210
</div>
13-
</>
1411
)
1512
}
1613
export default Rooms

src/components/ui/button.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const buttonVariants = cva(
1616
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
1717
secondary:
1818
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
19-
ghost: "hover:bg-accent hover:text-accent-foreground",
19+
ghost: "bg-white border text-primary-600 hover:border-primary-500",
2020
link: "text-primary underline-offset-4 hover:underline",
2121
},
2222
size: {

0 commit comments

Comments
 (0)