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"