Skip to content

Commit

Permalink
Feat(Analyze): 학습 분석 (#46)
Browse files Browse the repository at this point in the history
- /analysis url로 접속
- Appbar의 학습비서 -> 학습 분석으로 접속
- 추천 모의고사는 추가 예정
  • Loading branch information
godzz733 authored Oct 14, 2024
1 parent 8648a87 commit bb7aa0c
Show file tree
Hide file tree
Showing 34 changed files with 1,373 additions and 63 deletions.
1 change: 0 additions & 1 deletion src/api/apis/mainFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,5 @@ export const mainfetch = async <T>(
headers.Authorization = `Bearer ${newAccessToken}`;
response = await fetch(url, { ...fetchOptions, headers });
}

return response;
};
18 changes: 18 additions & 0 deletions src/api/types/analysis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
interface VulnerableSubject {
subjectId: number;
subjectName: string;
vulnerableRate: number;
}

interface VulnerableTag {
tagId: number;
tagName: string;
vulnerableRate: number;
}

interface AnalysisToday {
studyModeCount: number;
examModeCount: number;
studyModeCorrectRate: number;
examModeCorrectRate: number;
}
76 changes: 76 additions & 0 deletions src/app/analysis/atom/UserAnalysisInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Box, Typography } from "@mui/material";
import { useEffect, useState } from "react";

type UserInfo = {
nickname: string;
profileImage: string;
};

const UserAnalysisInfo = () => {
const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
const [certificateInfo, setCertificateInfo] = useState<CertificateType | null>(null);
useEffect(() => {
const getUserInfo = localStorage.getItem("userInfo");
const getCertificateInfo = localStorage.getItem("certificate");
if (getUserInfo) {
setUserInfo(JSON.parse(getUserInfo));
}
if (getCertificateInfo) {
setCertificateInfo(JSON.parse(getCertificateInfo));
}
}, []);
if (!userInfo || !certificateInfo) {
return <>로딩중...</>;
}

return (
<Box
sx={{
width: "100%",
paddingX: {
xs: "22px",
sm: "0px",
},
boxSizing: "border-box",
}}
>
<Box
sx={{
display: "flex",
justifyContent: {
xs: "center",
sm: "flex-start",
},
}}
>
<Typography
variant="h1"
fontSize={{
xs: "18px",
sm: "32px",
}}
marginBottom={{
xs: "20px",
sm: "44px",
}}
>
{userInfo!.nickname}님의&nbsp;
<Typography
component="span"
variant="h1"
color="var(--c-main)"
fontSize={{
xs: "18px",
sm: "32px",
}}
>
{certificateInfo!.name}&nbsp;
</Typography>
학습 분석
</Typography>
</Box>
</Box>
);
};

export default UserAnalysisInfo;
197 changes: 197 additions & 0 deletions src/app/analysis/atom/correctRateCircle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import { Typography, useMediaQuery, useTheme } from "@mui/material";
import styled, { keyframes } from "styled-components";
import { memo, useEffect, useState } from "react";

const rotateRight = (end: string) => keyframes`
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(${end});
}
`;

const rotateLeft = (end: string) => keyframes`
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(${end});
}
`;

const fillBackground = (bgColor: string) => keyframes`
0% {
background-color: #fff;
}
99% {
background-color: #fff;
}
100% {
background-color: ${bgColor};
}
`;

const Main = styled.div<{ size: number; disabled?: boolean; cursor?: string }>`
width: ${props => props.size}px;
height: ${props => props.size}px;
margin: 10px 0px;
position: relative;
background-color: ${props => (props.disabled ? "transparent" : props.theme.bgColor)};
border-radius: 100%;
cursor: ${props => props.cursor};
`;

const Inner = styled.div<{ size: number; disabled?: boolean }>`
z-index: 4;
position: absolute;
top: 50%;
left: 50%;
width: ${props => props.size - props.theme.innerSize}px;
height: ${props => props.size - props.theme.innerSize}px;
margin-left: ${props => -(props.size - props.theme.innerSize) / 2}px;
margin-top: ${props => -(props.size - props.theme.innerSize) / 2}px;
border-radius: 100%;
background-color: ${props => (props.disabled ? "transparent" : "#fff")};
animation: ${props => !props.disabled && fillBackground(props.theme.innerBgColor)} 2s linear both;
`;

const HoldingRight = styled.div<{ size: number; disabled?: boolean }>`
position: absolute;
width: 100%;
height: 100%;
clip: rect(0px, ${props => props.size}px, ${props => props.size}px, ${props => props.size / 2}px);
background-color: ${props => (props.disabled ? "transparent" : props.theme.emptyBgColor)};
border-radius: 100%;
`;

const HoldingLeft = styled.div<{ size: number; disabled?: boolean }>`
position: absolute;
width: 100%;
height: 100%;
clip: rect(0px, ${props => props.size / 2}px, ${props => props.size}px, 0px);
background-color: ${props => (props.disabled ? "transparent" : props.theme.emptyBgColor)};
border-radius: 100%;
`;

const FillRight = styled.div<{ rotate: string; size: number; disabled?: boolean }>`
position: absolute;
width: 100%;
height: 100%;
border-radius: 100%;
clip: rect(0px, ${props => props.size / 2}px, ${props => props.size}px, 0px);
background-color: ${props => (props.disabled ? "transparent" : props.theme.fillBgColor)};
animation: ${props => !props.disabled && rotateRight(props.rotate)} 1s linear both;
`;

const FillLeft = styled.div<{ rotate: string; size: number; disabled?: boolean }>`
position: absolute;
width: 100%;
height: 100%;
border-radius: 100%;
clip: rect(0px, ${props => props.size}px, ${props => props.size}px, ${props => props.size / 2}px);
background-color: ${props => (props.disabled ? "transparent" : props.theme.fillBgColor)};
animation: ${props => !props.disabled && rotateLeft(props.rotate)} 1s linear both;
animation-delay: 1s;
`;

const Middle = styled.div<{ size: number }>`
z-index: 5;
position: absolute;
top: 50%;
left: 50%;
width: ${props => props.size}px;
height: ${props => props.size}px;
transform: translate(-50%, -50%);
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
color: ${props => props.theme.fontColor};
`;

type CorrectRateCircleProps = {
data: string;
rotateRightDegree: string;
rotateLeftDegree: string;
fillBorderColor: string;
emptyBorderColor: string;
innerBackgroundColor: string;
disabled?: boolean;
isXs?: boolean;
isSm?: boolean;
achive: boolean;
};

const CorrectRateCircle = memo(
({
data,
rotateRightDegree,
rotateLeftDegree,
fillBorderColor,
emptyBorderColor,
innerBackgroundColor,
disabled = false,
achive = false,
}: CorrectRateCircleProps) => {
const theme = useTheme();
const isXs = useMediaQuery(theme.breakpoints.down("sm"));
const isSm = useMediaQuery(theme.breakpoints.between("sm", "md"));
const [fontColor, setFontColor] = useState(disabled ? "grey" : "black");
const [size, setSize] = useState(isXs ? 30 : isSm ? 48 : 64);
const [innerSize, setInnerSize] = useState(isXs ? 6 : isSm ? 8 : 10);
// useEffect(() => {
// if (!disabled && parseFloat(rotateRightDegree) + parseFloat(rotateLeftDegree) === 360) {
// const timeout = setTimeout(() => {
// setFontColor("white");
// }, 2010);
// return () => clearTimeout(timeout);
// }
// }, [disabled, rotateRightDegree, rotateLeftDegree]);

return (
<Main size={size} disabled={disabled || achive} theme={{ bgColor: emptyBorderColor }}>
<Inner
size={size}
disabled={disabled || achive}
theme={{ innerBgColor: innerBackgroundColor, innerSize: innerSize }}
/>
<HoldingRight
size={size}
disabled={disabled || achive}
theme={{ emptyBgColor: emptyBorderColor }}
>
<FillRight
rotate={rotateRightDegree}
size={size}
disabled={disabled || achive}
theme={{ fillBgColor: fillBorderColor }}
/>
</HoldingRight>
<HoldingLeft
size={size}
disabled={disabled || achive}
theme={{ emptyBgColor: emptyBorderColor }}
>
<FillLeft
rotate={rotateLeftDegree}
size={size}
disabled={disabled || achive}
theme={{ fillBgColor: fillBorderColor }}
/>
</HoldingLeft>
<Middle size={size - innerSize} theme={{ fontColor }}>
<Typography
fontSize={isXs ? "10px" : isSm ? "14px" : "18px"}
variant="h4"
style={{ color: disabled ? "grey" : fontColor }}
>
{data}
</Typography>
</Middle>
</Main>
);
}
);

export default CorrectRateCircle;
31 changes: 31 additions & 0 deletions src/app/analysis/atom/progressBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Box } from "@mui/material";

const ProgressBar = ({ progress }: { progress: number }) => {
return (
<Box
sx={{
width: {
xs: "120px",
sm: "194px",
},
height: {
xs: "8px",
sm: "12px",
},
borderRadius: "30px",
backgroundColor: "#f2f2f2",
}}
>
<Box
sx={{
width: `${progress}%`,
height: "100%",
borderRadius: "30px",
backgroundColor: "var(--c-main)",
}}
></Box>
</Box>
);
};

export default ProgressBar;
Loading

0 comments on commit bb7aa0c

Please sign in to comment.