Skip to content

Commit

Permalink
Merge pull request #91 from HGU-WALAB/#90/listView
Browse files Browse the repository at this point in the history
Feature: 순위 페이지 리스트 뷰 기능 추가
  • Loading branch information
ohinhyuk authored Mar 2, 2024
2 parents 0b684d4 + 4a0186a commit 7564b63
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 74 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
33 changes: 31 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

82 changes: 82 additions & 0 deletions src/components/Rank/RankGridView.js
Original file line number Diff line number Diff line change
@@ -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 (
<ImageList
cols={4}
gap={15}
component={motion.div}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
>
{teams.map((item, index) => (
<Grid
item
key={index}
onClick={() => handleFullImageOpen(item?.thumbnail)}
>
<StyledItemSize
onMouseOver={() => handleMouseOver(index)}
onMouseOut={() => handleMouseOut(index)}
>
<AnimatePresence>
{itemsHover[index] && (
<HoverBox
members={item.members}
reports={item.reports}
totalMinutes={item.totalMinutes}
/>
)}
</AnimatePresence>

<Item index={index} item={item} itemsHover={itemsHover} />
</StyledItemSize>
</Grid>
))}
</ImageList>
);
}
58 changes: 58 additions & 0 deletions src/components/Rank/RankListView.js
Original file line number Diff line number Diff line change
@@ -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 (
<DataGrid
rows={convertTeams2List(teams)}
columns={columns}
density="compact"
/>
);
}
29 changes: 29 additions & 0 deletions src/components/Rank/ViewToggleButton.js
Original file line number Diff line number Diff line change
@@ -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 (
<Stack direction="row" justifyContent="end" width="100%">
<ToggleButtonGroup
size="small"
orientation="horizontal"
value={view}
exclusive
onChange={handleViewChange}
color="primary"
>
<ToggleButton value="list" aria-label="list">
<ViewListIcon />
</ToggleButton>
<ToggleButton value="grid" aria-label="grid">
<ViewModuleIcon />
</ToggleButton>
</ToggleButtonGroup>
</Stack>
);
}
92 changes: 22 additions & 70 deletions src/pages/Rank/Rank.js
Original file line number Diff line number Diff line change
@@ -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 (
<>
<FullImage fullImageUrl={fullImageUrl} />
Expand All @@ -82,38 +54,18 @@ export default function Rank() {
<NoDataLottie />
) : (
<StyledScrollBox>
<ImageList
cols={4}
gap={15}
component={motion.div}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
>
{teams.map((item, index) => (
<Grid
item
key={index}
onClick={() => handleFullImageOpen(item?.thumbnail)}
>
<StyledItemSize
onMouseOver={() => handleMouseOver(index)}
onMouseOut={() => handleMouseOut(index)}
>
<AnimatePresence>
{itemsHover[index] && (
<HoverBox
members={item.members}
reports={item.reports}
totalMinutes={item.totalMinutes}
/>
)}
</AnimatePresence>

<Item index={index} item={item} itemsHover={itemsHover} />
</StyledItemSize>
</Grid>
))}
</ImageList>
<ViewToggleButton view={view} setView={setView} />
<Box height="20px" />
{view === "list" ? (
<RankListView teams={teams} />
) : (
<RankGridView
setFullImageUrl={setFullImageUrl}
setItemsHover={setItemsHover}
teams={teams}
itemsHover={itemsHover}
/>
)}
</StyledScrollBox>
)}

Expand Down

0 comments on commit 7564b63

Please sign in to comment.