diff --git a/package.json b/package.json
index 887b75d..c7fbcfa 100644
--- a/package.json
+++ b/package.json
@@ -4,13 +4,14 @@
"private": true,
"type": "module",
"dependencies": {
- "@emotion/react": "^11.11.1",
+ "@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@fontsource/roboto": "^4.5.8",
"@mui/icons-material": "^5.11.16",
- "@mui/material": "^5.12.3",
+ "@mui/material": "^5.15.11",
"@mui/styled-engine-sc": "^5.14.7",
"@mui/system": "^5.14.7",
+ "@mui/x-data-grid": "^6.19.5",
"@react-oauth/google": "^0.11.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b727517..52600ef 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -6,7 +6,7 @@ settings:
dependencies:
'@emotion/react':
- specifier: ^11.11.1
+ specifier: ^11.11.4
version: 11.11.4(react@18.2.0)
'@emotion/styled':
specifier: ^11.11.0
@@ -18,7 +18,7 @@ dependencies:
specifier: ^5.11.16
version: 5.15.11(@mui/material@5.15.11)(react@18.2.0)
'@mui/material':
- specifier: ^5.12.3
+ specifier: ^5.15.11
version: 5.15.11(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0)
'@mui/styled-engine-sc':
specifier: ^5.14.7
@@ -26,6 +26,9 @@ dependencies:
'@mui/system':
specifier: ^5.14.7
version: 5.15.11(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0)
+ '@mui/x-data-grid':
+ specifier: ^6.19.5
+ version: 6.19.5(@mui/material@5.15.11)(@mui/system@5.15.11)(react-dom@18.2.0)(react@18.2.0)
'@react-oauth/google':
specifier: ^0.11.0
version: 0.11.1(react-dom@18.2.0)(react@18.2.0)
@@ -3180,6 +3183,28 @@ packages:
react-is: 18.2.0
dev: false
+ /@mui/x-data-grid@6.19.5(@mui/material@5.15.11)(@mui/system@5.15.11)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-jV1ZqwyFslKqFScSn4t+xc/tNxLHOeJjz3HoeK+Wdf5t3bPM69pg/jLeg8TmOkAUY62JmQKCLVmcGWiR3AqUKQ==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@mui/material': ^5.4.1
+ '@mui/system': ^5.4.1
+ react: ^17.0.0 || ^18.0.0
+ react-dom: ^17.0.0 || ^18.0.0
+ dependencies:
+ '@babel/runtime': 7.24.0
+ '@mui/material': 5.15.11(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0)
+ '@mui/system': 5.15.11(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0)
+ '@mui/utils': 5.15.11(react@18.2.0)
+ clsx: 2.1.0
+ prop-types: 15.8.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ reselect: 4.1.8
+ transitivePeerDependencies:
+ - '@types/react'
+ dev: false
+
/@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1:
resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==}
dependencies:
@@ -10951,6 +10976,10 @@ packages:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
dev: false
+ /reselect@4.1.8:
+ resolution: {integrity: sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==}
+ dev: false
+
/resolve-cwd@3.0.0:
resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
engines: {node: '>=8'}
diff --git a/src/components/Rank/RankGridView.js b/src/components/Rank/RankGridView.js
new file mode 100644
index 0000000..0ca32c6
--- /dev/null
+++ b/src/components/Rank/RankGridView.js
@@ -0,0 +1,82 @@
+import { Box, styled, Grid, ImageList } from "@mui/material";
+import HoverBox from "./HoverBox";
+import Item from "./Item";
+import { motion } from "framer-motion";
+import { useRecoilValue } from "recoil";
+import { useSetRecoilState } from "recoil";
+import { authorityState, isShowFullImageState } from "../../store/atom";
+import { AnimatePresence } from "framer-motion";
+
+const StyledItemSize = styled(Box)({
+ position: "relative",
+ width: "300px",
+ height: "300px",
+});
+
+export default function RankGridView({
+ setFullImageUrl,
+ setItemsHover,
+ teams,
+ itemsHover,
+}) {
+ const setIsShowFullImage = useSetRecoilState(isShowFullImageState);
+ const myAuthority = useRecoilValue(authorityState);
+
+ const handleFullImageOpen = async (imageUrl) => {
+ if (myAuthority !== "ADMIN") return;
+ if (!imageUrl) {
+ setFullImageUrl("/img/mainImg2.png");
+ } else setFullImageUrl(imageUrl);
+ setIsShowFullImage(true);
+ };
+
+ const handleMouseOver = (index) => {
+ setItemsHover((prev) => [
+ ...prev?.slice(0, index),
+ true,
+ ...prev?.slice(index + 1),
+ ]);
+ };
+ const handleMouseOut = (index) => {
+ setItemsHover((prev) => [
+ ...prev?.slice(0, index),
+ false,
+ ...prev?.slice(index + 1),
+ ]);
+ };
+
+ return (
+
+ {teams.map((item, index) => (
+ handleFullImageOpen(item?.thumbnail)}
+ >
+ handleMouseOver(index)}
+ onMouseOut={() => handleMouseOut(index)}
+ >
+
+ {itemsHover[index] && (
+
+ )}
+
+
+
+
+
+ ))}
+
+ );
+}
diff --git a/src/components/Rank/RankListView.js b/src/components/Rank/RankListView.js
new file mode 100644
index 0000000..2b28b25
--- /dev/null
+++ b/src/components/Rank/RankListView.js
@@ -0,0 +1,58 @@
+import { DataGrid } from "@mui/x-data-grid";
+const columns = [
+ { field: "id", headerName: "순위", width: 100 },
+ { field: "groupNm", headerName: "그룹", width: 100 },
+ {
+ field: "reportNm",
+ headerName: "제출한 레포트 수",
+ type: "number",
+ width: 300,
+ },
+ {
+ field: "totalMins",
+ headerName: "총 시간(분)",
+ type: "number",
+ width: 150,
+ },
+ {
+ field: "members",
+ headerName: "팀원",
+ type: "text",
+ width: 600,
+ },
+];
+
+export default function RankListView({ teams }) {
+ const addMedalEmogi = (id) => {
+ switch (id) {
+ case 1:
+ return "🥇";
+ case 2:
+ return "🥈";
+ case 3:
+ return "🥉";
+ default:
+ return id;
+ }
+ };
+
+ const convertTeams2List = (teams) => {
+ return teams?.map((team, idx) => {
+ return {
+ id: addMedalEmogi(idx + 1),
+ groupNm: team.id,
+ reportNm: team.reports,
+ totalMins: team.totalMinutes,
+ members: team.members.join(", "),
+ };
+ });
+ };
+
+ return (
+
+ );
+}
diff --git a/src/components/Rank/ViewToggleButton.js b/src/components/Rank/ViewToggleButton.js
new file mode 100644
index 0000000..31e732e
--- /dev/null
+++ b/src/components/Rank/ViewToggleButton.js
@@ -0,0 +1,29 @@
+import { Stack, ToggleButton, ToggleButtonGroup } from "@mui/material";
+
+import ViewListIcon from "@mui/icons-material/ViewList";
+import ViewModuleIcon from "@mui/icons-material/ViewModule";
+
+export default function ViewToggleButton({ view, setView }) {
+ const handleViewChange = (event, nextView) => {
+ setView(nextView);
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/pages/Rank/Rank.js b/src/pages/Rank/Rank.js
index f68e491..0435442 100644
--- a/src/pages/Rank/Rank.js
+++ b/src/pages/Rank/Rank.js
@@ -1,73 +1,45 @@
-import { Grid, ImageList, styled } from "@mui/material";
+import { styled } from "@mui/material";
import { Box } from "@mui/system";
import { useState } from "react";
import { getAllTeamsForRank } from "../../apis/rank";
-import HoverBox from "../../components/Rank/HoverBox";
-import { AnimatePresence, motion } from "framer-motion";
+import { motion } from "framer-motion";
import NoDataLottie from "../../components/common/NoDataLottie";
import { StyledColumnAlignLayout } from "../../components/common/StyledLayout";
import Title from "../../components/common/Title";
import { useQuery } from "react-query";
-import Item from "../../components/Rank/Item";
-import { useRecoilValue, useSetRecoilState } from "recoil";
-import { authorityState, isShowFullImageState } from "../../store/atom";
import FullImage from "../../components/Rank/FullImage";
+import ViewToggleButton from "../../components/Rank/ViewToggleButton";
+import { DataGrid } from "@mui/x-data-grid";
+import RankListView from "../../components/Rank/RankListView";
+import RankGridView from "../../components/Rank/RankGridView";
+
const StyledScrollBox = styled(Box)({
maxWidth: "1245px",
width: "100%",
overflow: "scroll",
});
-const StyledItemSize = styled(Box)({
- position: "relative",
- width: "300px",
- height: "300px",
-});
-
export default function Rank() {
const [teams, setTeams] = useState([]);
const [itemsHover, setItemsHover] = useState([]);
+ const [view, setView] = useState("list");
- const { isLoading } = useQuery(["AllTeamRanks"], getAllTeamsForRank, {
+ useQuery(["AllTeamRanks"], getAllTeamsForRank, {
casheTime: 10 * 60 * 1000,
onSuccess: (data) => {
setTeams(data.teams);
setItemsHover(new Array(data.teams.length).fill(false));
},
+ refetchOnWindowFocus: false,
});
- const handleMouseOver = (index) => {
- setItemsHover((prev) => [
- ...prev?.slice(0, index),
- true,
- ...prev?.slice(index + 1),
- ]);
- };
- const handleMouseOut = (index) => {
- setItemsHover((prev) => [
- ...prev?.slice(0, index),
- false,
- ...prev?.slice(index + 1),
- ]);
- };
-
- const setIsShowFullImage = useSetRecoilState(isShowFullImageState);
- const myAuthority = useRecoilValue(authorityState);
const [fullImageUrl, setFullImageUrl] = useState(null);
- const handleFullImageOpen = async (imageUrl) => {
- if (myAuthority !== "ADMIN") return;
- if (!imageUrl) {
- setFullImageUrl("/img/mainImg2.png");
- } else setFullImageUrl(imageUrl);
- setIsShowFullImage(true);
- };
-
return (
<>
@@ -82,38 +54,18 @@ export default function Rank() {
) : (
-
- {teams.map((item, index) => (
- handleFullImageOpen(item?.thumbnail)}
- >
- handleMouseOver(index)}
- onMouseOut={() => handleMouseOut(index)}
- >
-
- {itemsHover[index] && (
-
- )}
-
-
-
-
-
- ))}
-
+
+
+ {view === "list" ? (
+
+ ) : (
+
+ )}
)}