Skip to content

Commit 1159b1e

Browse files
authored
Merge pull request #114 from CapituloJaverianoACM/feat/update-ranking-scoreboard
Feat/update ranking scoreboard #96
2 parents 9e783b7 + 283e18e commit 1159b1e

File tree

11 files changed

+168
-52
lines changed

11 files changed

+168
-52
lines changed

.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
NEXT_PUBLIC_BACKEND_URL=
22
NEXT_PUBLIC_SUPABASE_URL=
3-
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=
3+
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=
4+
NEXT_PUBLIC_DEFAULT_IMAGE_URL=https://www.svgrepo.com/show/452030/avatar-default.svg

src/app/league/page.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { Contest } from "@/models/contest.model";
1010
import Footer from "@/components/shared/footer";
1111
import { getContestsWithPictures } from "@/controllers/contest.controller";
1212
import { useEffect, useState } from "react";
13+
import { createClient } from "@/lib/supabase/client";
14+
import { suscribe_leaderboard } from "@/lib/supabase/channel_subscribe";
1315

1416
const navLinks = [
1517
{ key: "home", label: "Inicio", href: "/" },
@@ -20,6 +22,7 @@ const navLinks = [
2022
href: "#upcoming-events",
2123
},
2224
{ key: "podium", label: "Podio", href: "#podium" },
25+
{ key: "rank", label: "Raking", href: "/rank" },
2326
];
2427

2528
export default function LeagueHomePage() {
@@ -31,19 +34,32 @@ export default function LeagueHomePage() {
3134
})[]
3235
>([]);
3336

37+
const [refresh_students, setRefreshStudents] = useState<boolean>(false);
38+
3439
useEffect(() => {
3540
getContestsWithPictures()
3641
.then(setContests)
3742
.catch(() => setContests([]));
3843
}, []);
3944

45+
useEffect(() => {
46+
const supabase = createClient();
47+
const channel = suscribe_leaderboard(supabase, () => {
48+
setRefreshStudents((prev) => !prev);
49+
});
50+
51+
return () => {
52+
supabase.removeChannel(channel);
53+
};
54+
}, []);
55+
4056
return (
4157
<HeroUIProvider>
4258
<MainNavbar navLinks={navLinks} />
4359
<Hero />
4460
<Rules />
4561
<UpcomingEvents events={contests} loadingInitialState />
46-
<Podium />
62+
<Podium refresh_toggle={refresh_students} />
4763
<Footer />
4864
</HeroUIProvider>
4965
);

src/components/home/apple-cards-carousel.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,14 @@ export const Card = ({
204204
const containerRef = useRef<HTMLDivElement>(null);
205205
// Se quito currentIndex
206206
const { onCardClose } = useContext(CarouselContext);
207+
const handleOpen = () => {
208+
setOpen(true);
209+
};
207210

211+
const handleClose = () => {
212+
setOpen(false);
213+
onCardClose(index);
214+
};
208215
useEffect(() => {
209216
function onKeyDown(event: KeyboardEvent) {
210217
if (event.key === "Escape") {
@@ -224,15 +231,6 @@ export const Card = ({
224231

225232
useOutsideClick(containerRef, () => handleClose());
226233

227-
const handleOpen = () => {
228-
setOpen(true);
229-
};
230-
231-
const handleClose = () => {
232-
setOpen(false);
233-
onCardClose(index);
234-
};
235-
236234
return (
237235
<>
238236
<AnimatePresence>

src/components/league/sections/podium.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { LevelEnum } from "@/models/level.enum";
44
import { Student } from "@/models/student.model";
55
import { useEffect, useState } from "react";
66

7-
export function Podium() {
7+
/**
8+
* Muestra a los 3 mejores estudiantes de la liga
9+
* @param refresh_toggle es un booleano que se recibe desde el componente padre para saber cuando tiene que pedir los estudiantes
10+
*/
11+
export function Podium({ refresh_toggle }: { refresh_toggle: boolean }) {
812
const [loading, setloading] = useState<boolean>(true);
913

1014
const [students, setStudents] = useState<
@@ -21,6 +25,7 @@ export function Podium() {
2125
>([]);
2226

2327
const handlerGetPodiumStudents = async () => {
28+
console.log("Recuperando estudiantes...");
2429
try {
2530
const response = await getPodiumStudents();
2631

@@ -38,7 +43,7 @@ export function Podium() {
3843
useEffect(() => {
3944
handlerGetPodiumStudents();
4045
setloading(false);
41-
}, []);
46+
}, [refresh_toggle]);
4247

4348
useEffect(() => {
4449
setSortedStudents(() => {
@@ -74,7 +79,7 @@ export function Podium() {
7479
<PodiumContainer.Step
7580
showUserInfo
7681
showNumber
77-
bg_color="bg-[rgb(var(--azul-electrico-rgb)_/_0.2)] dark:bg-[rgb(var(--azul-electrico-rgb)_/_0.5)]"
82+
bg_color="bg-[rgb(var(--azul-electrico-rgb)_/_1)] dark:bg-[rgb(var(--azul-electrico-rgb)_/_1)]"
7883
className="border-[rgb(var(--azul-electrico-rgb)_/_0.2)] border-1"
7984
student={{
8085
order: s.order,
@@ -105,8 +110,7 @@ export function Podium() {
105110
showCrown
106111
showAvatar
107112
showNumber
108-
bg_color="bg-[rgb(var(--azul-electrico-rgb)_/_0.2)] dark:bg-[rgb(var(--azul-electrico-rgb)_/_0.5)]"
109-
className="border-[rgb(var(--azul-electrico-rgb)_/_0.2)] border-1"
113+
bg_color="bg-[rgb(var(--azul-electrico-rgb)_/_1)] dark:bg-[rgb(var(--azul-electrico-rgb)_/_1)]"
110114
student={s}
111115
></PodiumContainer.Step>
112116
),

src/components/league/sections/ranking.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,37 @@
1+
import { useEffect, useState } from "react";
12
import RankingComponent from "../ui/ranking-component";
3+
import { Podium } from "./podium";
4+
import { createClient } from "@/lib/supabase/client";
5+
import { suscribe_leaderboard } from "@/lib/supabase/channel_subscribe";
26

37
export const Ranking = () => {
8+
const [refresh, setRefresh] = useState<boolean>(false);
9+
10+
useEffect(() => {
11+
const supabase = createClient();
12+
const channel = suscribe_leaderboard(supabase, () =>
13+
setRefresh((prev) => !prev),
14+
);
15+
16+
return () => {
17+
supabase.removeChannel(channel);
18+
};
19+
}, []);
20+
421
return (
522
<div>
23+
<div className="mt-[-9vh]">
24+
<h1 className="text-center text-6xl">Ranking global</h1>
25+
</div>
26+
<Podium refresh_toggle={refresh} />
627
<RankingComponent.RankingContainer>
728
<RankingComponent.Padding className="max-w-[65rem] mx-auto">
829
<p className="text-lg font-semibold text-center">Ranking</p>
9-
<RankingComponent.RankingList student_number={20} />
30+
<RankingComponent.RankingList
31+
student_number={5}
32+
offset={3}
33+
refresh_toggle={refresh}
34+
/>
1035
</RankingComponent.Padding>
1136
</RankingComponent.RankingContainer>
1237
</div>

src/components/league/ui/Events/event-card.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ const WrapContainer = (props: {
9090
return <div className={`flex flex-wrap gap-2 ${className}`}>{children}</div>;
9191
};
9292

93-
export default {
93+
const event_card = {
9494
Container,
9595
Image,
9696
Title,
@@ -99,3 +99,5 @@ export default {
9999
Description,
100100
WrapContainer,
101101
};
102+
103+
export default event_card;

src/components/league/ui/podium/podium-component.tsx

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const Container = (props: {
2020
style={{
2121
height: `${((0.8 / steps_count) * (steps_count - step.order) + 0.2) * 100}%`,
2222
}}
23-
className="flex-grow"
23+
className="flex-grow flex-1"
2424
key={step.order}
2525
>
2626
{step.children}
@@ -53,21 +53,45 @@ const Step = (props: {
5353
showCrown = false,
5454
} = props;
5555

56-
const val = 1 - student.order * 0.045;
56+
const val = 1 - student.order * 0.25;
57+
58+
const roundedDirection =
59+
student.order == 0 ? "rounded-tl-[8rem]" : "rounded-tr-[8rem]";
5760

5861
return (
5962
<div className="flex flex-col gap-2 w-full h-full relative transition hover:scale-105 cursor-pointer">
60-
<div
61-
style={{
62-
backdropFilter: `brightness(${val})`,
63-
}}
64-
className={`flex flex-col items-center justify-center rounded-3xl flex-grow ${bg_color} ${className} shadow-md`}
65-
>
66-
<div className="h-[1.5rem] w-full"></div>
63+
<div className="relative flex flex-col items-center justify-center flex-grow overflow-hidden">
64+
{/* Fondo */}
65+
<div
66+
className={`absolute inset-0 shadow-md flex flex-col items-center justify-end ${bg_color} ${roundedDirection} ${className}`}
67+
style={{
68+
filter: `brightness(${val})`,
69+
}}
70+
></div>
6771

68-
{showNumber && (
69-
<b className="text-6xl text-white">{student.order + 1}°</b>
70-
)}
72+
<div className="absolute h-full flex flex-col gap-2 items-center justify-end z-10">
73+
{showNumber && (
74+
<b className="relative z-10 text-6xl text-white text-bold font-[var(--font-primary)]">
75+
#{student.order + 1}
76+
</b>
77+
)}
78+
{showUserInfo && (
79+
<div className=" text-white flex flex-col items-center justify-center p-2 text-center text-xs lg:text-base shadow font-[var(--font-secondary)]">
80+
<p
81+
className="m-0"
82+
title={`${student.student.name} ${student.student.surname}`}
83+
>
84+
{student.student.name}
85+
</p>
86+
<p className="m-0 flex gap-1 items-center">
87+
{" "}
88+
<IconCrown className="text-yellow-500" size={15} />
89+
{student.student.victory_count}{" "}
90+
<span className="hidden lg:flex">Victorias</span>
91+
</p>
92+
</div>
93+
)}
94+
</div>
7195
</div>
7296

7397
{showAvatar &&
@@ -99,23 +123,6 @@ const Step = (props: {
99123
<IconCrown className="flex lg:hidden" size={45} />
100124
</div>
101125
)}
102-
103-
{showUserInfo && (
104-
<div className="glassmorphic dark:glassmorphic-dark dark:text-white flex flex-col items-center justify-center p-2 text-center text-xs lg:text-base h-16 shadow">
105-
<p
106-
className="m-0"
107-
title={`${student.student.name} ${student.student.surname}`}
108-
>
109-
<b>{student.student.name}</b>
110-
</p>
111-
<p className="m-0 flex gap-1 items-center">
112-
{" "}
113-
<IconCrown className="text-yellow-500" size={15} />
114-
{student.student.victory_count}{" "}
115-
<span className="hidden lg:flex">Victorias</span>
116-
</p>
117-
</div>
118-
)}
119126
</div>
120127
);
121128
};

src/components/league/ui/ranking-component.tsx

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,23 @@ const StudentComponent = ({
6666
);
6767
};
6868

69+
/**
70+
* Muestra la información de `student_number` estudiantes
71+
* @param className clases normales de html, se puede agregar tailwind sin problema (a pesar que no sirva el autocompletado a veces)
72+
* @param student_number cantidad de estudiantes maxima que puede mostrar el ranking
73+
* @param offset cantidad de estudiantes a ignorar, se implemento para poner arriba el podio
74+
* @param refresh_toggle es un booleano que se recibe desde el componente padre para saber cuando tiene que pedir los estudiantes
75+
*/
6976
const RankingList = ({
7077
className = "",
7178
student_number = 20,
79+
offset = 0,
80+
refresh_toggle,
7281
}: {
7382
className?: string;
7483
student_number?: number;
84+
offset?: number;
85+
refresh_toggle: boolean;
7586
}) => {
7687
const SKELETON_RANKING_USERS_COUNT = 5;
7788

@@ -92,7 +103,10 @@ const RankingList = ({
92103

93104
const handlerGetRankingStudents = async () => {
94105
try {
95-
const response = await getRankingStudents({ student_number });
106+
const response = await getRankingStudents({
107+
student_number: student_number,
108+
offset,
109+
});
96110

97111
setStudents(response);
98112
} catch {
@@ -104,21 +118,28 @@ const RankingList = ({
104118

105119
useEffect(() => {
106120
handlerGetRankingStudents();
107-
}, []);
121+
}, [refresh_toggle]);
108122

109123
return (
110124
<div className={`flex flex-col gap-2 ${className}`}>
111125
{students.map((s, i) => {
112126
return (
113-
<StudentComponent skeleton={loading} student={s} key={i} pos={i} />
127+
<StudentComponent
128+
skeleton={loading}
129+
student={s}
130+
key={i}
131+
pos={i + offset}
132+
/>
114133
);
115134
})}
116135
</div>
117136
);
118137
};
119138

120-
export default {
139+
const exp = {
121140
RankingContainer,
122141
Padding,
123142
RankingList,
124143
};
144+
145+
export default exp;

src/components/sign-up/sign-up-form.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"use client";
2+
13
import { useState } from "react";
24
import { Input } from "../shared/ui/input";
35
import { Button } from "../shared/ui/button";

src/controllers/student.controller.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,14 @@ export async function getPodiumStudents(): Promise<Student[]> {
7575

7676
export async function getRankingStudents({
7777
student_number,
78+
offset = 0,
7879
}: {
7980
student_number: number;
81+
offset?: number;
8082
}): Promise<Student[]> {
8183
const res = await fetch(
8284
new URL(
83-
`/students?limit=${student_number}&ordercol=victory_count&subordercol=matches_count&subasc=1`,
85+
`/students?limit=${student_number}&ordercol=victory_count&subordercol=matches_count&subasc=1&offset=${offset}`,
8486
process.env.NEXT_PUBLIC_BACKEND_URL,
8587
),
8688
);

0 commit comments

Comments
 (0)