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
27 changes: 17 additions & 10 deletions src/app/league/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { Contest } from "@/models/contest.model";
import { LevelEnum } from "@/models/level.enum";
import { Student } from "@/models/student.model";
import Footer from "@/components/shared/footer";
import { getContests } from "@/services/contest.service";
import { getContestsWithPictures } from "@/services/contest.service";
import { useEffect, useState } from "react";

const navLinks = [
{ key: "home", label: "Home", href: "/" },
Expand All @@ -23,14 +24,6 @@ const navLinks = [
{ key: "podium", label: "Podium", href: "#podium" },
];

let contests: Contest[] = [];

try {
contests = await getContests();
} catch {
contests = [];
}

const hard_coded_league_podium: { student: Student; order: number }[] = [
{
student: {
Expand Down Expand Up @@ -74,12 +67,26 @@ const hard_coded_league_podium: { student: Student; order: number }[] = [
];

export default function LeagueHomePage() {

const [contests, setContests] = useState < (Contest & {
picture: {
link: string;
};
})[]>([]);

useEffect(() => {
getContestsWithPictures()
.then(setContests)
.catch(() => setContests([]));
}, []);


return (
<HeroUIProvider>
<MainNavbar navLinks={navLinks} />
<Hero />
<Rules />
<UpcomingEvents events={contests} />
<UpcomingEvents events={contests} loadingInitialState />
<Podium students={hard_coded_league_podium} />
<Footer />
</HeroUIProvider>
Expand Down
235 changes: 125 additions & 110 deletions src/components/league/sections/upcoming-events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,137 +23,150 @@ const formatDateEvent = ({
};

const optionsHour: Intl.DateTimeFormatOptions = {
hour: "numeric",
minute: "2-digit",
hour12: true,
};
hour: 'numeric',
minute: '2-digit',
hour12: true
}

const formattedDate = new Date(date).toLocaleDateString("es-ES", optionsDate);
const formattedInitialHour = new Date(start_hour).toLocaleTimeString(
"es-ES",
optionsHour,
);
const formattedFinalHour = new Date(final_hour).toLocaleTimeString(
"es-ES",
optionsHour,
);
const formattedDate = new Date(date).toLocaleDateString('es-ES', optionsDate)
const formattedInitialHour = new Date(start_hour).toLocaleTimeString('es-ES', optionsHour)
const formattedFinalHour = new Date(final_hour).toLocaleTimeString('es-ES', optionsHour)

return `${formattedDate}, de ${formattedInitialHour} a ${formattedFinalHour}`;
};
return `${formattedDate}, de ${formattedInitialHour} a ${formattedFinalHour}`
}

export function UpcomingEvents({
events = [],
loadingInitialState = false
}: {
events: Contest[],
loadingInitialState?: boolean
}) {

export function UpcomingEvents({ events = [] }: { events: Contest[] }) {
// Diseño de una tarjeta para decir que no hay eventos

const NoEventsCard: ReactNode = (
<EventCard.Container key="unique" className="h-full justify-end w-[30rem]">
<div className="flex w-full aspect-video">
<EventCard.Image
src={"/Logo_Oscuro.png"}
className="!object-contain opacity-15 !w-2/3 m-auto"
/>
</div>
const NoEventsCard: ReactNode = <EventCard.Container key="unique" className="justify-end !w-[20rem] xl:!w-[30rem]">
<div className="flex w-full aspect-video">
<EventCard.Image src={"/Logo_Oscuro.svg"} className="!object-contain opacity-15 w-1/2 m-auto" />
</div>

<EventCard.Padding>
<EventCard.Title>
No hay eventos con tus parámetros de busqueda
</EventCard.Title>
<EventCard.Padding>
<EventCard.Title>
No hay eventos con tus parámetros de busqueda
</EventCard.Title>
<EventCard.Padding>
<EventCard.Description>
¡Mantene alerta a nuestras redes sociales! Así sabrás cuando
tengamos un evento de tu interés.
</EventCard.Description>
</EventCard.Padding>

<EventCard.RegisterButton
onClick={() => {
alert("click");
}}
>
Redes Sociales
</EventCard.RegisterButton>
<EventCard.Description>
¡Mantene alerta a nuestras redes sociales! Así sabrás cuando
tengamos un evento de tu interés.
</EventCard.Description>
</EventCard.Padding>
</EventCard.Container>
);

<EventCard.RegisterButton
onClick={() => {
alert("click");
}}
>
Redes Sociales
</EventCard.RegisterButton>
</EventCard.Padding>
</EventCard.Container>

// Diseño de un tarjeta skeleton

const SkeletonCard: ReactNode = <EventCard.Container key="unique" className="justify-end !w-[20rem] xl:!w-[30rem]">
<div className="flex w-full aspect-video">
<EventCard.Image src={"/Logo_Oscuro.svg"} className="!object-contain opacity-15 !w-2/3 m-auto" />
</div>

<EventCard.Padding>
<EventCard.Title className="bg-neutral-200 rounded" />
<EventCard.Padding>
<EventCard.Description className="bg-neutral-200 rounded" />
</EventCard.Padding>

<EventCard.RegisterButton onClick={() => {
alert('click')
}}>
{" "}
</EventCard.RegisterButton>

</EventCard.Padding>

</EventCard.Container>

// Diseño de las tarjetas

const AllCards = events.map((event) => {
const { date, start_hour, final_hour } = event;

return {
comp: (
<EventCard.Container
key={event._id}
className="h-full justify-end !w-[20rem] xl:!w-[30rem]"
>
{event.picture ? (
<EventCard.Image src={event.picture.link} />
) : (
<div className="flex w-full aspect-video">
<EventCard.Image
src={"/Logo_Oscuro.png"}
className="!object-contain opacity-15 !w-2/3 m-auto"
/>
</div>
)}

<EventCard.Padding>
<EventCard.WrapContainer>
<EventCard.Title>{event.name}</EventCard.Title>
{event.level == LevelEnum.Initial && (
<p
title="Nivel Inicial"
className="text-[--azul-electrico] m-0"
>
Inicial
</p>
)}
{event.level == LevelEnum.Advanced && (
<p title="Nivel Avanzado" className="text-red-400 m-0">
Avanzado
</p>
)}
</EventCard.WrapContainer>
const [loading, setLoading] = useState<boolean>(loadingInitialState)
const [AllCards, setAllCards] = useState<({
comp: ReactNode,
level: LevelEnum
})[]>([])

useEffect(() => {
setAllCards(
events.map(event => {

const { date, start_hour, final_hour } = event;

return {
comp: <EventCard.Container key={event._id} className="h-full justify-end !w-[20rem] xl:!w-[30rem]">
{event.picture ? <EventCard.Image src={event.picture.link} /> : <div className="flex w-full aspect-video">
<EventCard.Image src={"/Logo_Oscuro.svg"} className="!object-contain opacity-15 !w-2/3 m-auto" />
</div>}

<EventCard.Padding>
<EventCard.Description>
Salón {event.classroom} -{" "}
{formatDateEvent({
date,
start_hour,
final_hour,
})}
</EventCard.Description>
<EventCard.WrapContainer>
<EventCard.Title>
{event.name}
</EventCard.Title>
{event.level == LevelEnum.Initial && <p title="Nivel Inicial" className="text-[--azul-electrico] m-0">Inicial</p>}
{event.level == LevelEnum.Advanced && <p title="Nivel Avanzado" className="text-red-400 m-0">Avanzado</p>}
</EventCard.WrapContainer>

<EventCard.Padding>
<EventCard.Description>
Salón {event.classroom} - {formatDateEvent({
date, start_hour, final_hour
})}
</EventCard.Description>
</EventCard.Padding>


<EventCard.RegisterButton onClick={() => {
alert('click')
}}>

</EventCard.RegisterButton>
</EventCard.Padding>
</EventCard.Container>,
level: event.level == "Advanced" ? LevelEnum.Advanced : LevelEnum.Initial
}
})
)
}, [events])

const [cards, setCards] = useState<ReactNode[]>([])
const [filter, setFilter] = useState<"all" | "Initial" | "Advanced">("all");

useEffect(() => {
setCards(AllCards?.map(x => x.comp) ?? [])
if (AllCards.length > 0) setLoading(false)
}, [AllCards])

<EventCard.RegisterButton
onClick={() => {
alert("click");
}}
></EventCard.RegisterButton>
</EventCard.Padding>
</EventCard.Container>
),
level: event.level,
};
});

const [cards, setCards] = useState<ReactNode[]>(AllCards.map((x) => x.comp));
const [filter, setFilter] = useState<"all" | "initial" | "advanced">("all");

// Acá se filtran los eventos

useEffect(() => {
setCards(
AllCards.filter((x) => {
if (filter == "all") return true;
else if (filter == "advanced" && x.level == LevelEnum.Advanced)
return true;
else if (filter == "initial" && x.level == LevelEnum.Initial)
return true;
else if (filter == "Advanced" && x.level == LevelEnum.Advanced) return true;
else if (filter == "Initial" && x.level == LevelEnum.Initial) return true;
return false;
}).map((x) => x.comp),
);
}).map(x => x.comp)
)
if (cards.length > 0) setLoading(false)

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [filter]);

Expand All @@ -174,7 +187,9 @@ export function UpcomingEvents({ events = [] }: { events: Contest[] }) {
<LevelFilter filter={filter} setFilter={setFilter} />
</div>
</div>
<Carousel items={cards.length == 0 ? [NoEventsCard] : cards} />
<Carousel items={
loading ? [SkeletonCard] : (cards.length > 0 ? cards : [NoEventsCard])
} />
</div>
);
}
}
17 changes: 10 additions & 7 deletions src/components/league/ui/Events/event-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ const Image = (props: {
}

const Title = (props: {
children?: ReactNode | string
children?: ReactNode | string,
className?: string
}) => {
const { children } = props;
const { children, className = "" } = props;

return <p className="font-semibold text-md m-0 dark:text-[var(--azul-niebla)]">
return <p className={`font-semibold text-lg m-0 dark:text-[var(--azul-niebla)] min-h-7 ${className}`}>
{children}
</p>
}
Expand Down Expand Up @@ -53,7 +54,7 @@ const Description = (
) => {
const { className = "", children = null } = props;

return <p className={`text-xs text-neutral-800 dark:text-[var(--azul-niebla)] m-0 ${className}`}>
return <p className={`text-xs h-[3rem] max-h-[3rem] text-neutral-800 dark:text-[var(--azul-niebla)] m-0 ${className}`}>
{children}
</p>
}
Expand All @@ -68,9 +69,11 @@ const RegisterButton = (
const { onClick = () => { }, className = "", children = null } = props;

return <button className={`w-full text-white font-semibold p-2 bg-[rgb(var(--azul-electrico-rgb)_/_0.8)] dark:bg-[rgb(var(--azul-electrico-rgb)_/_0.6)] hover:brightness-105 hover:transition hover:duration-100 rounded-full ${className}`} onClick={onClick}>
{
children ?? "Registrarse"
}
<div className="h-6 text-base">
{
children ?? "Registrarse"
}
</div>
</button>

}
Expand Down
14 changes: 7 additions & 7 deletions src/components/league/ui/Events/level-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ export const LevelFilter = ({
filter,
setFilter
}: {
filter: "initial" | "all" | "advanced",
setFilter: Dispatch<SetStateAction<"initial" | "all" | "advanced">>
filter: "Initial" | "all" | "Advanced",
setFilter: Dispatch<SetStateAction<"Initial" | "all" | "Advanced">>
}) => {

const texts = {
"initial": "Inicial",
"Initial": "Inicial",
"all": "Todos",
"advanced": "Avanzado"
"Advanced": "Avanzado"
}

const LevelButton = ({ f }: { f: "initial" | "all" | "advanced" }) => {
const LevelButton = ({ f }: { f: "Initial" | "all" | "Advanced" }) => {
return <div onClick={() => setFilter(f)} className={`glassmorphic transition-all duration-300 ease-in-out select-none ${filter == f ? "shadow-md" : ""} px-2 p-1`}>
<p className="m-0">{texts[f]}</p>
</div>
Expand All @@ -23,8 +23,8 @@ export const LevelFilter = ({

return <div className="flex gap-2 items-center glassmorphic p-2">
<LevelButton f={"all"} />
<LevelButton f={"initial"} />
<LevelButton f={"advanced"} />
<LevelButton f={"Initial"} />
<LevelButton f={"Advanced"} />
</div>

}
Loading