diff --git a/vinvoor/package.json b/vinvoor/package.json
index 22b4c54..2f17aa0 100644
--- a/vinvoor/package.json
+++ b/vinvoor/package.json
@@ -18,10 +18,13 @@
"@mui/material": "^5.15.19",
"@types/js-cookie": "^3.0.6",
"@types/react-router-dom": "^5.3.3",
+ "@types/react-router-hash-link": "^2.4.9",
"js-cookie": "^3.0.5",
+ "mdi-material-ui": "^7.9.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-router-dom": "^6.23.1"
+ "react-router-dom": "^6.23.1",
+ "react-router-hash-link": "^2.4.3"
},
"devDependencies": {
"@types/react": "^18.2.66",
diff --git a/vinvoor/public/first-first-place-svgrepo-com.svg b/vinvoor/public/first-first-place-svgrepo-com.svg
new file mode 100644
index 0000000..12810a7
--- /dev/null
+++ b/vinvoor/public/first-first-place-svgrepo-com.svg
@@ -0,0 +1,28 @@
+
+
+
\ No newline at end of file
diff --git a/vinvoor/public/second-svgrepo-com.svg b/vinvoor/public/second-svgrepo-com.svg
new file mode 100644
index 0000000..c1f4bb3
--- /dev/null
+++ b/vinvoor/public/second-svgrepo-com.svg
@@ -0,0 +1,29 @@
+
+
+
\ No newline at end of file
diff --git a/vinvoor/public/third-svgrepo-com.svg b/vinvoor/public/third-svgrepo-com.svg
new file mode 100644
index 0000000..b9d2951
--- /dev/null
+++ b/vinvoor/public/third-svgrepo-com.svg
@@ -0,0 +1,29 @@
+
+
+
\ No newline at end of file
diff --git a/vinvoor/src/cards/CardsTable.tsx b/vinvoor/src/cards/CardsTable.tsx
index 4eeb7d3..8c463aa 100644
--- a/vinvoor/src/cards/CardsTable.tsx
+++ b/vinvoor/src/cards/CardsTable.tsx
@@ -12,7 +12,7 @@ import { Card } from "../types/cards";
import { TableOrder } from "../types/table";
import { CardsTableBody } from "./CardsTableBody";
import { CardsTableHead } from "./CardsTableHead";
-import { CardsTableToolbar } from "./CardsTableToolBar";
+import { CardsTableToolbar } from "./CardsTableToolbar";
interface CardTableProps {
cards: readonly Card[];
diff --git a/vinvoor/src/cards/CardsTableBody.tsx b/vinvoor/src/cards/CardsTableBody.tsx
index f103afb..0035265 100644
--- a/vinvoor/src/cards/CardsTableBody.tsx
+++ b/vinvoor/src/cards/CardsTableBody.tsx
@@ -43,9 +43,7 @@ export const CardsTableBody: FC = ({
{row[headCell.id]}
diff --git a/vinvoor/src/cards/CardsTableHead.tsx b/vinvoor/src/cards/CardsTableHead.tsx
index 6d0ff66..c8d5589 100644
--- a/vinvoor/src/cards/CardsTableHead.tsx
+++ b/vinvoor/src/cards/CardsTableHead.tsx
@@ -50,7 +50,7 @@ export const CardsTableHead: FC = ({
>;
}
-export const CardsTableToolbar: FC = ({
+export const CardsTableToolbar: FC = ({
selected,
setCards,
}) => {
@@ -32,7 +32,7 @@ export const CardsTableToolbar: FC = ({
{numSelected > 0 ? (
<>
diff --git a/vinvoor/src/leaderboard/Leaderboard.tsx b/vinvoor/src/leaderboard/Leaderboard.tsx
new file mode 100644
index 0000000..eed034c
--- /dev/null
+++ b/vinvoor/src/leaderboard/Leaderboard.tsx
@@ -0,0 +1,36 @@
+import { Divider, Paper, Table, TableContainer } from "@mui/material";
+import { useState } from "react";
+import { LoadingSkeleton } from "../components/LoadingSkeleton";
+import { useFetch } from "../hooks/useFetch";
+import { LeaderboardItem } from "../types/leaderboard";
+import { LeaderboardTableBody } from "./LeaderboardTableBody";
+import { LeaderboardTableToolbar } from "./LeaderboardTableToolbar";
+
+export const Leaderboard = () => {
+ const [leaderboardItems, setLeaderboardItems] = useState<
+ readonly LeaderboardItem[]
+ >([]);
+ const { loading, error: _ } = useFetch(
+ "leaderboard",
+ setLeaderboardItems
+ );
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/vinvoor/src/leaderboard/LeaderboardTableBody.tsx b/vinvoor/src/leaderboard/LeaderboardTableBody.tsx
new file mode 100644
index 0000000..81a286d
--- /dev/null
+++ b/vinvoor/src/leaderboard/LeaderboardTableBody.tsx
@@ -0,0 +1,76 @@
+import {
+ styled,
+ TableBody,
+ TableCell,
+ tableCellClasses,
+ TableRow,
+ Typography,
+} from "@mui/material";
+import { PodiumBronze, PodiumGold, PodiumSilver } from "mdi-material-ui";
+import { FC } from "react";
+import { leaderboardHeadCells, LeaderboardItem } from "../types/leaderboard";
+
+interface LeaderboardTableBodyProps {
+ leaderboardItems: readonly LeaderboardItem[];
+}
+
+const StyledTableCell = styled(TableCell)(({ theme }) => ({
+ [`&.${tableCellClasses.head}`]: {
+ backgroundColor: theme.palette.common.black,
+ color: theme.palette.common.white,
+ },
+ [`&.${tableCellClasses.body}`]: {
+ fontSize: 14,
+ },
+}));
+
+const StyledTableRow = styled(TableRow)(({ theme }) => ({
+ "&:nth-of-type(odd)": {
+ backgroundColor: theme.palette.action.hover,
+ },
+ // hide last border
+ "&:last-child td, &:last-child th": {
+ border: 0,
+ },
+}));
+
+const getPosition = (position: number) => {
+ switch (position) {
+ case 1:
+ return ;
+ case 2:
+ return ;
+ case 3:
+ return ;
+ default:
+ return {position};
+ }
+};
+
+export const LeaderboardTableBody: FC = ({
+ leaderboardItems: rows,
+}) => {
+ return (
+
+ {rows.map((row) => {
+ return (
+
+ {leaderboardHeadCells.map((headCell) => (
+
+ {headCell.id === "position" ? (
+ getPosition(row[headCell.id])
+ ) : (
+ {row[headCell.id]}
+ )}
+
+ ))}
+
+ );
+ })}
+
+ );
+};
diff --git a/vinvoor/src/leaderboard/LeaderboardTableHead.tsx b/vinvoor/src/leaderboard/LeaderboardTableHead.tsx
new file mode 100644
index 0000000..9f44f14
--- /dev/null
+++ b/vinvoor/src/leaderboard/LeaderboardTableHead.tsx
@@ -0,0 +1,16 @@
+import { TableCell, TableHead, TableRow, Typography } from "@mui/material";
+import { leaderboardHeadCells } from "../types/leaderboard";
+
+export const LeaderboardTableHead = () => {
+ return (
+
+
+ {leaderboardHeadCells.map((headCell) => (
+
+ {headCell.label}
+
+ ))}
+
+
+ );
+};
diff --git a/vinvoor/src/leaderboard/LeaderboardTableToolbar.tsx b/vinvoor/src/leaderboard/LeaderboardTableToolbar.tsx
new file mode 100644
index 0000000..4bd090f
--- /dev/null
+++ b/vinvoor/src/leaderboard/LeaderboardTableToolbar.tsx
@@ -0,0 +1,27 @@
+import { MoveDown } from "@mui/icons-material";
+import { Button, Toolbar, Typography } from "@mui/material";
+import { FC, useContext } from "react";
+import { HashLink } from "react-router-hash-link";
+import { UserContext } from "../user/UserProvider";
+
+interface LeaderboardTableToolbarProps {}
+
+export const LeaderboardTableToolbar: FC<
+ LeaderboardTableToolbarProps
+> = ({}) => {
+ const { user } = useContext(UserContext);
+
+ return (
+
+
+ Ranking
+
+
+
+
+
+ );
+};
diff --git a/vinvoor/src/main.tsx b/vinvoor/src/main.tsx
index b64ddd8..ea7dba5 100644
--- a/vinvoor/src/main.tsx
+++ b/vinvoor/src/main.tsx
@@ -6,8 +6,9 @@ import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { App } from "./App.tsx";
-import { Cards } from "./cards/Card.tsx";
+import { Cards } from "./cards/Cards.tsx";
import { ErrorPage } from "./errors/ErrorPage.tsx";
+import { Leaderboard } from "./leaderboard/Leaderboard.tsx";
import { Login } from "./user/Login.tsx";
import { Logout } from "./user/Logout.tsx";
@@ -29,6 +30,10 @@ const router = createBrowserRouter([
path: "cards",
element: ,
},
+ {
+ path: "leaderboard",
+ element: ,
+ },
],
},
]);
diff --git a/vinvoor/src/navbar/NavBar.tsx b/vinvoor/src/navbar/NavBar.tsx
index ec92d4c..4608b3b 100644
--- a/vinvoor/src/navbar/NavBar.tsx
+++ b/vinvoor/src/navbar/NavBar.tsx
@@ -7,7 +7,7 @@ import { NavBarPages } from "./NavBarPages";
import { NavBarSandwich } from "./NavBarSandwich";
import { NavBarUserMenu } from "./NavBarUserMenu";
-const pages = ["Cards"];
+const pages = ["Cards", "Leaderboard"];
const settings = ["Logout"];
export const NavBar = () => {
diff --git a/vinvoor/src/types/cards.ts b/vinvoor/src/types/cards.ts
index 0efd465..223a1d3 100644
--- a/vinvoor/src/types/cards.ts
+++ b/vinvoor/src/types/cards.ts
@@ -10,12 +10,12 @@ export const CardsHeadCells: readonly TableHeadCell[] = [
id: "serial",
label: "Serial",
align: "left",
- disablePadding: true,
+ padding: "none",
},
{
id: "createdAt",
label: "Created at",
align: "right",
- disablePadding: false,
+ padding: "normal",
},
];
diff --git a/vinvoor/src/types/leaderboard.ts b/vinvoor/src/types/leaderboard.ts
new file mode 100644
index 0000000..4493005
--- /dev/null
+++ b/vinvoor/src/types/leaderboard.ts
@@ -0,0 +1,28 @@
+import { TableHeadCell } from "./table";
+
+export interface LeaderboardItem {
+ position: number;
+ username: string;
+ totalDays: number;
+}
+
+export const leaderboardHeadCells: readonly TableHeadCell[] = [
+ {
+ id: "position",
+ label: "#",
+ align: "center",
+ padding: "checkbox",
+ },
+ {
+ id: "username",
+ label: "Username",
+ align: "left",
+ padding: "normal",
+ },
+ {
+ id: "totalDays",
+ label: "Total Days",
+ align: "right",
+ padding: "normal",
+ },
+];
diff --git a/vinvoor/src/types/table.ts b/vinvoor/src/types/table.ts
index 16adf90..3d4a059 100644
--- a/vinvoor/src/types/table.ts
+++ b/vinvoor/src/types/table.ts
@@ -1,10 +1,11 @@
export type TableOrder = "asc" | "desc";
type TableAlignOptions = "right" | "left" | "center";
+type TablePaddingOptions = "none" | "normal" | "checkbox";
export interface TableHeadCell {
id: keyof T;
label: string;
align: TableAlignOptions;
- disablePadding: boolean;
+ padding: TablePaddingOptions;
}
diff --git a/vinvoor/yarn.lock b/vinvoor/yarn.lock
index 3ce100e..78fe7ca 100644
--- a/vinvoor/yarn.lock
+++ b/vinvoor/yarn.lock
@@ -775,7 +775,7 @@
dependencies:
"@types/react" "*"
-"@types/react-router-dom@^5.3.3":
+"@types/react-router-dom@^5.3.0", "@types/react-router-dom@^5.3.3":
version "5.3.3"
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
@@ -784,6 +784,15 @@
"@types/react" "*"
"@types/react-router" "*"
+"@types/react-router-hash-link@^2.4.9":
+ version "2.4.9"
+ resolved "https://registry.yarnpkg.com/@types/react-router-hash-link/-/react-router-hash-link-2.4.9.tgz#b9f069fb5faeba2477426b3932205d080f72ba99"
+ integrity sha512-zl/VMj+lfJZhvjOAQXIlBVPNKSK+/fRG8AUHhlP9++LhlA2ziLeTmbRxIMJI3PCiCTS+W/FosEoDRoNOGH0OzA==
+ dependencies:
+ "@types/history" "^4.7.11"
+ "@types/react" "*"
+ "@types/react-router-dom" "^5.3.0"
+
"@types/react-router@*":
version "5.1.20"
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.20.tgz#88eccaa122a82405ef3efbcaaa5dcdd9f021387c"
@@ -1570,6 +1579,11 @@ loose-envify@^1.1.0, loose-envify@^1.4.0:
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
+mdi-material-ui@^7.9.1:
+ version "7.9.1"
+ resolved "https://registry.yarnpkg.com/mdi-material-ui/-/mdi-material-ui-7.9.1.tgz#f28dbd6883a8c6198ca78e19c9f23728b2599226"
+ integrity sha512-13Ecd6hbYZWzY1yLCwQRGqnYzIv9HjSbRM7Vl+4GsllBY6KWv0IhCr4gVlcMGf+ahJxBH3Ba5ImZICls1Aqtyg==
+
merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
@@ -1716,7 +1730,7 @@ prelude-ls@^1.2.1:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
-prop-types@^15.6.2, prop-types@^15.8.1:
+prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -1761,6 +1775,13 @@ react-router-dom@^6.23.1:
"@remix-run/router" "1.16.1"
react-router "6.23.1"
+react-router-hash-link@^2.4.3:
+ version "2.4.3"
+ resolved "https://registry.yarnpkg.com/react-router-hash-link/-/react-router-hash-link-2.4.3.tgz#570824d53d6c35ce94d73a46c8e98673a127bf08"
+ integrity sha512-NU7GWc265m92xh/aYD79Vr1W+zAIXDWp3L2YZOYP4rCqPnJ6LI6vh3+rKgkidtYijozHclaEQTAHaAaMWPVI4A==
+ dependencies:
+ prop-types "^15.7.2"
+
react-router@6.23.1:
version "6.23.1"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.23.1.tgz#d08cbdbd9d6aedc13eea6e94bc6d9b29cb1c4be9"