diff --git a/src/App.tsx b/src/App.tsx index 8c7962a..3e80249 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,7 +10,7 @@ import Header from "@/components/Header.tsx"; import React from "react"; import PrivacyPolicy from "@/pages/PrivacyPolicy.tsx"; import Footer from "@/components/Footer.tsx"; -import SpeakerInfo from "@/components/SpeakerInfo.tsx"; +import SpeakerInfo from "@/components/speakers/SpeakerInfo.tsx"; import {AppProvider, useAppContext} from "@/context/AppContext.tsx"; import TicketsPage from "@/pages/TicketsPage.tsx"; import CodeOfConductSpeakers from "@/pages/CodeOfConductSpeakers.tsx"; diff --git a/src/components/agenda/CardCharla.tsx b/src/components/agenda/CardCharla.tsx index b996be7..3482a01 100644 --- a/src/components/agenda/CardCharla.tsx +++ b/src/components/agenda/CardCharla.tsx @@ -1,18 +1,19 @@ import React, { useEffect, useState } from "react"; import { IRoomAgenda } from "@/types/agenda.ts"; import { useAppContext } from "@/context/AppContext.tsx"; -import {ISession} from "@/types/speakers.ts"; import {Star} from "lucide-react"; import {Button} from "@/components/ui/button.tsx"; import {useNavigate} from "react-router-dom"; +import {ISession} from "@/types/sessions.ts"; interface CardEventProps { - room: IRoomAgenda; + room?: IRoomAgenda; + session: ISession; + className?: string; } -const CardEvent: React.FC = ({ room }) => { +const CardEvent: React.FC = ({ room, session, className }) => { const { savedSessions, setSavedSessions, displayAll } = useAppContext(); - const session = room.session; const navigate = useNavigate(); const [isSaved, setIsSaved] = useState(false); @@ -39,16 +40,15 @@ const CardEvent: React.FC = ({ room }) => { if (!displayAll && !savedSessions.has(session.id)) return null; return ( -
-

Sala: {room.name}

- + { room &&

Sala: {room.name}

} +
-
{ session.speakers.length > 0 && ( - )} - + ); }; -interface WrappedCardEventProps { - session: ISession; - children: React.ReactNode; - className: string; -} -const WrappedCardEvent: React.FC = ({session, children, className}) => { - if (session.speakers.length === 0) return (
{children}
) - return
{children} -
-} export default CardEvent; diff --git a/src/components/agenda/Rooms.tsx b/src/components/agenda/Rooms.tsx index aab3c39..be1ff47 100644 --- a/src/components/agenda/Rooms.tsx +++ b/src/components/agenda/Rooms.tsx @@ -1,11 +1,16 @@ import React from "react"; import CardCharla from "@/components/agenda/CardCharla.tsx"; +import {IRoomAgenda} from "@/types/agenda.ts"; -const Rooms = ({rooms}) => { +interface RoomsProps { + rooms: IRoomAgenda[] +} + +const Rooms: React.FC = ({rooms}) => { return (
{rooms.map((room) => { - return + return })}
) diff --git a/src/components/agenda/RoomsExpandable.tsx b/src/components/agenda/RoomsExpandable.tsx deleted file mode 100644 index 347412a..0000000 --- a/src/components/agenda/RoomsExpandable.tsx +++ /dev/null @@ -1,26 +0,0 @@ -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"; -import Carousel from "@/components/Carousel.tsx"; -import {IRoomAgenda} from "@/types/agenda.ts"; -import {Card} from "@/components/ui/card.tsx"; - -const RoomsExpandable = ({rooms, slotStart, endsAt, hide}) => { - if (hide) return null; - return ( -
- - - - - - -

-
- } /> -
- ) -} - -export default RoomsExpandable \ No newline at end of file diff --git a/src/components/session/SessionCard.tsx b/src/components/session/SessionCard.tsx index ccbc0c2..d899ed0 100644 --- a/src/components/session/SessionCard.tsx +++ b/src/components/session/SessionCard.tsx @@ -2,9 +2,16 @@ import {Card, CardFooter} from "@/components/ui/card.tsx"; import {Badge} from "@/components/ui/badge.tsx"; import {Speech} from "lucide-react"; import React from "react"; +import {ISessionInfo} from "@/types/sessions.ts"; -const SessionCard = ({session}) => { +interface SessionCardProps { + session: ISessionInfo +} + +const SessionCard: React.FC = ({session}) => { const category = session.category + const startTime = new Date(session.startsAt).toLocaleTimeString(["en-US"], {hour: '2-digit', minute:'2-digit'}) + const endTime = new Date(session.endsAt).toLocaleTimeString(["en-US"], {hour: '2-digit', minute:'2-digit'}) return (

@@ -13,6 +20,9 @@ const SessionCard = ({session}) => {

{session.title}

+ { startTime && endTime && +

-

+ } Descripción

{session.description}

diff --git a/src/components/session/SessionTracks.tsx b/src/components/session/SessionTracks.tsx new file mode 100644 index 0000000..618af11 --- /dev/null +++ b/src/components/session/SessionTracks.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import {Card} from "@/components/ui/card.tsx"; +import CardCharla from "@/components/agenda/CardCharla.tsx"; +import {Speech} from "lucide-react"; +import {Badge} from "@/components/ui/badge.tsx"; +import {formatTiemstamp} from "@/lib/utils.ts"; +import {ISessionInfo} from "@/types/sessions.ts"; + +interface SessionTracksProps { + currentSession?: ISessionInfo + sessions: ISessionInfo[] + category: string +} + +const SessionTracks: React.FC = ({sessions, category, currentSession}) => { + const tracks = sessions.filter(session => session.category === category && session.id !== currentSession.id); + return ( + + Charlas de {category} +
+ { + tracks.map(session => ( +
+

+ + - + +

+ +
+ )) + } +
+
+ ) +} + +export default SessionTracks \ No newline at end of file diff --git a/src/components/Speaker.tsx b/src/components/speakers/Speaker.tsx similarity index 100% rename from src/components/Speaker.tsx rename to src/components/speakers/Speaker.tsx diff --git a/src/components/SpeakerInfo.tsx b/src/components/speakers/SpeakerInfo.tsx similarity index 77% rename from src/components/SpeakerInfo.tsx rename to src/components/speakers/SpeakerInfo.tsx index 1a12fc8..b651f8a 100644 --- a/src/components/SpeakerInfo.tsx +++ b/src/components/speakers/SpeakerInfo.tsx @@ -1,21 +1,22 @@ import {ISpeaker} from "@/types/speakers.ts"; -import {Card, CardFooter} from "@/components/ui/card.tsx"; +import {Card} from "@/components/ui/card.tsx"; import React, {useEffect, useState} from "react"; import {useLocation, useNavigate, useParams} from "react-router-dom"; import SocialMedia from "@/components/SocialMedia.tsx"; -import {Badge} from "@/components/ui/badge.tsx"; import { ChevronLeft, Speech} from "lucide-react"; import Gradient from "@/components/Gradient.tsx"; import {useAppContext} from "@/context/AppContext.tsx"; import {findSpeaker, scrollToTop} from "@/lib/utils.ts"; import Shared from "@/components/Shared.tsx"; import Loading from "@/pages/Loading.tsx"; +import SessionCard from "@/components/session/SessionCard.tsx"; +import SessionTracks from "@/components/session/SessionTracks.tsx"; const SpeakerInfo = () => { const navigate = useNavigate() const location = useLocation(); const { speakerId } = useParams(); - const { speakers } = useAppContext(); + const { speakers, sessions } = useAppContext(); const speaker = location.state?.speaker as ISpeaker; const [currentSpeaker, setCurrentSpeaker] = useState() const fullUrl = `${window.location.origin}${location.pathname}${location.search}${location.hash}`; @@ -32,7 +33,7 @@ const SpeakerInfo = () => { } }, [speaker, speakers]); - if (!currentSpeaker) { + if (!currentSpeaker || speakers.length == 0) { return (
@@ -75,20 +76,15 @@ const SpeakerInfo = () => {
- -

- Charla -

-

- {currentSpeaker.sessions.title} -

- Descripción -

{currentSpeaker.sessions.description}

- - Track: {currentSpeaker.category} - -
+ { + currentSpeaker.sessions.map(session => ( + + )) + } + { currentSpeaker.sessions.length == 1 && + + } ) } diff --git a/src/components/Speakers.tsx b/src/components/speakers/Speakers.tsx similarity index 95% rename from src/components/Speakers.tsx rename to src/components/speakers/Speakers.tsx index 9488e4b..81205e2 100644 --- a/src/components/Speakers.tsx +++ b/src/components/speakers/Speakers.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import Speaker from "@/components/Speaker.tsx"; +import Speaker from "@/components/speakers/Speaker.tsx"; import Carousel, {GridConfig} from "@/components/Carousel.tsx"; import {useAppContext} from "@/context/AppContext.tsx"; import {ISpeaker} from "@/types/speakers.ts"; diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index e577ef4..18f8072 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -16,6 +16,8 @@ const badgeVariants = cva( "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", outline: "text-foreground", ghost: "border border-primary-600 bg-white text-primary-600 transition-colors duration-300", + border: "border border-alternative-600 bg-white text-alternative-600 transition-colors duration-300", + alternative: "border-transparent bg-alternative-600 text-white transition-colors duration-300", }, }, defaultVariants: { diff --git a/src/context/AppContext.tsx b/src/context/AppContext.tsx index 683b359..8d008b2 100644 --- a/src/context/AppContext.tsx +++ b/src/context/AppContext.tsx @@ -1,11 +1,12 @@ import { createContext, useContext, useState, useEffect } from "react"; -import {ISession, ISessionInfo, ISpeaker} from "@/types/speakers.ts"; +import { ISpeaker} from "@/types/speakers.ts"; import {getAll} from "@/https/fetch.ts"; import {AppStatus} from "@/types/types.ts"; -import {addSessionSpeakers, getCategoryNameSessions} from "@/lib/utils.ts"; +import {addInformationToSession, addSessionSpeakers} from "@/lib/utils.ts"; import {ITimeSlot} from "@/types/agenda.ts"; import ErrorPage from "@/pages/ErrorPage.tsx"; import Loading from "@/pages/Loading.tsx"; +import {ISessionInfo} from "@/types/sessions.ts"; interface AppContextType { @@ -13,6 +14,7 @@ interface AppContextType { sessions: ISessionInfo[]; agenda: ITimeSlot[]; appStatus: AppStatus; + setAppStatus: (status: AppStatus) => void setAgenda: (agenda: ITimeSlot[]) => void; savedSessions: Set; setSavedSessions: (sessions: Set) => void; @@ -28,6 +30,7 @@ export const AppProvider = ({ children }) => { const [appStatus, setAppStatus] = useState(AppStatus.Loading); const [displayAll, setDisplayAll] = useState(true); const [sessions, setSessions] = useState([]); + const [error, setError] = useState(null) const [savedSessions, setSavedSessions] = useState>(() => { const raw = localStorage.getItem("savedSessions"); return raw ? new Set(JSON.parse(raw)) : new Set(); @@ -39,18 +42,20 @@ export const AppProvider = ({ children }) => { useEffect(() => { getAll() - .then((data) => { - const categories = data.categories[0].items + .then(({categories, sessions, speakers, rooms}) => { + const categoriesItems = categories[0].items + const sessionsInfo = sessions.map(session => addInformationToSession(session, speakers, categoriesItems)) const getSpeakersWithSessions = addSessionSpeakers( - data.sessions, - data.speakers, - categories + sessionsInfo, + speakers ); + console.log(getSpeakersWithSessions[0]); setSpeakers(getSpeakersWithSessions); - setSessions(getCategoryNameSessions(categories, data.sessions)); + setSessions(sessionsInfo); setAppStatus(AppStatus.Success); }) - .catch(() => { + .catch(e => { + setError(e); setAppStatus(AppStatus.Error); }); }, []); @@ -65,6 +70,7 @@ export const AppProvider = ({ children }) => { speakers, agenda, appStatus, + setAppStatus }; return ( @@ -75,7 +81,7 @@ export const AppProvider = ({ children }) => { )} - {appStatus === AppStatus.Error && } + {appStatus === AppStatus.Error && } {appStatus === AppStatus.Success && children} diff --git a/src/https/fetch.ts b/src/https/fetch.ts index 0a4b4b0..b9c2b8d 100644 --- a/src/https/fetch.ts +++ b/src/https/fetch.ts @@ -1,4 +1,5 @@ -import {IAgenda, IConferenceData, ISpeaker} from "@/types/speakers.ts"; +import {IConferenceData, ISpeaker} from "@/types/speakers.ts"; +import {IAgenda} from "@/types/agenda.ts"; export const getSpeakers = async () => { const response = await fetch('https://sessionize.com/api/v2/n25df8kw/view/Speakers'); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index de8eb87..0131421 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,7 @@ import { clsx, type ClassValue } from "clsx" import { twMerge } from "tailwind-merge" -import {ICategory, ISession, ISessionInfo, ISpeaker} from "@/types/speakers.ts"; +import {ICategory, ISpeaker, ISpeakerData, ISpeakerId} from "@/types/speakers.ts"; +import {ISessionData, ISessionInfo} from "@/types/sessions.ts"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) @@ -22,26 +23,44 @@ export const handleCopy = async (toast, dismiss, text: string) => { } }; -export const addSessionSpeakers = (sessions: ISession[], speakers: ISpeaker[], categories: ICategory[]): ISpeaker[] => { - return speakers.map((speaker) => { - const session = sessions.find((session) => session.speakers.includes(speaker.id)); - const category = categories.find((category) => session.categoryItems.includes(category.id)) +export const addSessionSpeakers = (sessions: ISessionInfo[], speakers: ISpeakerData[]): ISpeaker[] => { + return speakers.map((speaker) => { + const sessionsSpeaker = sessions.filter((session) => { + return speaker.sessions.includes(Number(session.id)) + }) return { - ...speaker, - sessions: session, - category: category.name + ...speaker, + sessions: sessionsSpeaker, }; }); } -export const getCategoryNameSessions = (categories: ICategory[], sessions: ISession[]): ISessionInfo[] => { - return sessions.map(session => { - const category = categories.find(category => session.categoryItems.includes(category.id)); + +export const getCategoryNameSession = (categories: ICategory[], session: ISessionData): string => { + const category = categories.find(category => session.categoryItems.includes(category.id)); + return category ? category.name : "" +} + +export const getSpeakersById = (speakers: ISpeakerData[], speakerIds: string[]): ISpeakerId[] => { + return speakers.filter(speaker => speakerIds.includes(speaker.id)).map(speaker => { return { - ...session, - category: category ? category.name : "" + id: speaker.id, + name: speaker.fullName, } - }) + }); +} + +export const addInformationToSession = (session: ISessionData, speakers: ISpeakerData[], categories: ICategory[]): ISessionInfo => { + const sessionSpeakers = getSpeakersById(speakers, session.speakers) + return { + ...session, + speakers: sessionSpeakers, + category: getCategoryNameSession(categories, session) + } +} + +export const formatTiemstamp = (timestamp: string) => { + return new Date(timestamp).toLocaleTimeString(["en-US"], {hour: '2-digit', minute:'2-digit'}) } export const findSpeaker = (speakers: ISpeaker[], id: string) => speakers.find((speaker) => speaker.id === id); diff --git a/src/pages/ErrorPage.tsx b/src/pages/ErrorPage.tsx index 3c3b6c6..fb4e2fc 100644 --- a/src/pages/ErrorPage.tsx +++ b/src/pages/ErrorPage.tsx @@ -1,7 +1,13 @@ import Gradient from "@/components/Gradient.tsx"; import Tree from "@/components/icons/Tree.tsx"; +import React from "react"; -const ErrorPage = () => { +interface ErrorPageProps { + error?: Error +} + +const ErrorPage: React.FC = ({error}) => { + console.log(error) return ( diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index 2f02387..afda000 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -4,7 +4,7 @@ import Gallery from '@/components/Gallery'; import {useLocation} from "react-router-dom"; import BecomeSponsor from "@/components/BecomeSponsor.tsx"; import Organizers from "@/components/Organizers.tsx"; -import Speakers from "@/components/Speakers.tsx"; +import Speakers from "@/components/speakers/Speakers.tsx"; import CommunitiesAllies from "@/components/communities/CommunitiesAllies.tsx"; import Sponsors from "@/components/Sponsors.tsx"; diff --git a/src/pages/SessionPage.tsx b/src/pages/SessionPage.tsx index aa55db4..4cdde95 100644 --- a/src/pages/SessionPage.tsx +++ b/src/pages/SessionPage.tsx @@ -4,7 +4,8 @@ import {useNavigate, useParams} from "react-router-dom"; import Gradient from "@/components/Gradient.tsx"; import {ChevronLeft} from "lucide-react"; import React, {useEffect} from "react"; -import {scrollToTop} from "@/lib/utils.ts"; +import { scrollToTop} from "@/lib/utils.ts"; +import SessionTracks from "@/components/session/SessionTracks.tsx"; const SessionPage = () => { const navigate = useNavigate() @@ -13,20 +14,24 @@ const SessionPage = () => { const session = sessions.find(session => session.id == Number(sessionId)) const handleGoBack = () => { - navigate("/agenda", { - state: { - sessionId: session.id - } - }) + if (window.history.length > 1){ + navigate(-1) + } else { + navigate("/agenda", { + state: { + sessionId: session.id + } + }) + } } useEffect(() => { scrollToTop() - }, []); + }, [sessionId]); return ( - + ) } diff --git a/src/types/sessions.ts b/src/types/sessions.ts new file mode 100644 index 0000000..fefaf40 --- /dev/null +++ b/src/types/sessions.ts @@ -0,0 +1,27 @@ +import {ISpeakerId} from "@/types/speakers.ts"; + +export interface ISession { + id: number + title: string + description: string + startsAt: string + endsAt: string + speakers: ISpeakerId[] + categoryItems: number[] + roomId: string +} + +export interface ISessionInfo extends ISession { + category: string +} + +export interface ISessionData { + id: number + title: string + description: string + startsAt: string + endsAt: string + speakers: string[] + categoryItems: number[] + roomId: string +} \ No newline at end of file diff --git a/src/types/speakers.ts b/src/types/speakers.ts index bdf3f32..54e959a 100644 --- a/src/types/speakers.ts +++ b/src/types/speakers.ts @@ -1,3 +1,5 @@ +import {ISession, ISessionData, ISessionInfo} from "@/types/sessions.ts"; + export interface ISpeakerId { id: string name: string @@ -11,24 +13,22 @@ export interface ISpeaker { bio: string tagLine: string profilePicture: string - sessions: ISession + sessions: ISessionInfo[] isTopSpeaker: boolean links: ILink[] - category: string } -export interface ISession { - id: number - title: string - description: string - startAt: string - endsAt: string - speakers: ISpeakerId[] - categoryItems: number[] -} - -export interface ISessionInfo extends ISession { - category: string +export interface ISpeakerData { + id: string + firstName: string + lastName: string + fullName: string + bio: string + tagLine: string + profilePicture: string + sessions: number[] + isTopSpeaker: boolean + links: ILink[] } export interface ILink { @@ -61,8 +61,8 @@ export interface IRoomData extends IRoom { } export interface IConferenceData { - sessions: ISession[]; - speakers: ISpeaker[]; + sessions: ISessionData[]; + speakers: ISpeakerData[]; categories: [{ items: ICategory[] }]