Skip to content

Commit

Permalink
feat: add profile/myMarathon tab
Browse files Browse the repository at this point in the history
  • Loading branch information
ruby10127130 committed Dec 10, 2024
1 parent 75d24a0 commit efa4828
Show file tree
Hide file tree
Showing 5 changed files with 339 additions and 0 deletions.
52 changes: 52 additions & 0 deletions components/Profile/MyMarathon/LoadingCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import Skeleton from '@mui/material/Skeleton';
import IconButton from '@mui/material/IconButton';
import LocationOnOutlinedIcon from '@mui/icons-material/LocationOnOutlined';
import MoreVertOutlinedIcon from '@mui/icons-material/MoreVertOutlined';
import {
StyledContainer,
StyledFooter,
StyledGroupCard,
StyledText,
StyledTitle,
StyledFlex,
StyledImageWrapper,
} from './MarathonCard.styled';

function LoadingCard() {
return (
<StyledGroupCard href="#">
<StyledImageWrapper>
<Skeleton
variant="rounded"
width="100%"
height={122}
animation="wave"
/>
</StyledImageWrapper>
<StyledContainer>
<StyledTitle>
<Skeleton width="60%" animation="wave" />
</StyledTitle>
<StyledText lineClamp="2" style={{ minHeight: '32px' }}>
<Skeleton animation="wave" />
<Skeleton width="50%" animation="wave" />
</StyledText>
<StyledFooter>
<StyledFlex>
<Skeleton
variant="rounded"
width={68}
height={24}
animation="wave"
/>
<IconButton size="small" disabled>
<MoreVertOutlinedIcon />
</IconButton>
</StyledFlex>
</StyledFooter>
</StyledContainer>
</StyledGroupCard>
);
}

export default LoadingCard;
95 changes: 95 additions & 0 deletions components/Profile/MyMarathon/MarathonCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { useState } from 'react';
import { useRouter } from 'next/router';
import { useDispatch } from 'react-redux';
import { fetchMarathonProfileById } from '@/redux/actions/marathon';
import MoreVertOutlinedIcon from '@mui/icons-material/MoreVertOutlined';
import Image from '@/shared/components/Image';
import emptyCoverImg from '@/public/assets/empty-cover.png';
import {
IconButton,
Menu,
} from '@mui/material';
import {
StyledGroupCard,
StyledImageWrapper,
StyledContainer,
StyledTitle,
StyledText,
StyledFooter,
StyledFlex,
StyledStatus,
StyledMenuItem
} from "./MarathonCard.styled";

export default function MarathonCard({ marathon }) {
const { title, isPublic } = marathon;
const router = useRouter();
const [anchorEl, setAnchorEl] = useState(null);
const reduxDispatch = useDispatch();
const handleMenu = (event) => {
event.preventDefault();
setAnchorEl(event.currentTarget);
};

const handleClose = () => {
setAnchorEl(null);
};
const handleClickEdit = () => {
setAnchorEl(null);
window.localStorage.setItem('fromProfilePage', 'click_edit');
reduxDispatch(fetchMarathonProfileById(marathon._id));
router.push('/learning-marathon/signup');
};
const handleClickDetail = () => {
setAnchorEl(null);
window.localStorage.setItem('fromProfilePage', 'click_detail');
reduxDispatch(fetchMarathonProfileById(marathon._id));
router.push('/learning-marathon/signup');
};
return (
<StyledGroupCard>
<StyledImageWrapper>
<Image
alt="未放封面"
src={emptyCoverImg.src}
/>
</StyledImageWrapper>
<StyledContainer>
<StyledTitle>{title}</StyledTitle>
<StyledText lineClamp="2" style={{ height: '42px' }}>
2025 春季學習馬拉松
</StyledText>
<StyledFooter>
<StyledFlex>
<StyledStatus>{isPublic ? "公開" : "不公開"}</StyledStatus>
<IconButton size="small" onClick={handleMenu}>
<MoreVertOutlinedIcon />
</IconButton>
</StyledFlex>
</StyledFooter>
</StyledContainer>
<Menu
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
open={Boolean(anchorEl)}
onClose={handleClose}
>
<StyledMenuItem onClick={handleClickDetail}>
檢視學習計畫
</StyledMenuItem>
<StyledMenuItem onClick={handleClickEdit}>
編輯學習計畫
</StyledMenuItem>
</Menu>

</StyledGroupCard>
);
};
116 changes: 116 additions & 0 deletions components/Profile/MyMarathon/MarathonCard.styled.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import styled from '@emotion/styled';
import {
Box,
MenuItem
} from '@mui/material';

export const StyledGroupsWrapper = styled.div`
background-color: #ffffff;
max-width: 672px;
border-radius: 16px;
padding: 36px 40px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
@media (max-width: 767px) {
padding: 16px 20px;
}
${(props) => props.sx}
`;
export const StyledGroupCard = styled(Box)`
width: 100%;
display: flex;
position: relative;
background: #fff;
border-radius: 4px;
gap: 16px;
@media (max-width: 767px) {
flex-direction: column;
}
`;

export const StyledImageWrapper = styled.div`
flex: 1;
overflow: hidden;
img {
vertical-align: middle;
}
`;
export const StyledContainer = styled.div`
display: flex;
flex-direction: column;
justify-content: space-around;
flex: 1;
padding: 0 10px;
`;

export const StyledTitle = styled.h2`
font-size: 16px;
font-weight: bold;
line-height: 1.6;
margin-bottom: 4px;
display: -webkit-box;
color: #293a3d;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
`;
export const StyledText = styled.div`
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: ${(props) => props.lineClamp || '1'};
overflow: hidden;
color: ${(props) => props.color || '#536166'};
font-size: ${(props) => props.fontSize || '14px'};
word-break: break-word;
`;
export const StyledFooter = styled.footer`
display: flex;
justify-content: space-between;
align-items: center;
`;

export const StyledFlex = styled.div`
display: flex;
align-items: center;
gap: 8px;
width: 100%;
`;

export const StyledStatus = styled.div`
--bg-color: #def5f5;
--color: #16b9b3;
display: flex;
align-items: center;
width: max-content;
font-size: 12px;
padding: 4px 10px;
height: 24px;
background: var(--bg-color);
color: var(--color);
border-radius: 4px;
font-weight: 500;
gap: 4px;
margin-right: auto;
&::before {
content: '';
display: block;
width: 8px;
height: 8px;
background: var(--color);
border-radius: 50%;
}
&.finished {
--bg-color: #f3f3f3;
--color: #92989a;
}
`;
export const StyledMenuItem = styled(MenuItem)`
min-width: 146px;
`;
70 changes: 70 additions & 0 deletions components/Profile/MyMarathon/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
fetchUserByToken
} from '@/redux/actions/user';

import {
Typography,
Grid
} from '@mui/material';

import {
StyledGroupsWrapper,
} from "./MarathonCard.styled";
import MarathonCard from './MarathonCard';

const MyMarathon = ({ title, sx }) => {
const reduxDispatch = useDispatch();
const userState = useSelector((state) => { return state.user; });
const [marathons, setMarathons] = useState([]);
const { apiState } = userState;

useEffect(() => {
if (userState.token) {
setMarathons(userState.marathons);
reduxDispatch(fetchUserByToken(userState.token));
}
}, []);

useEffect(() => {
if (userState.marathons.length) {
setMarathons(userState.marathons);
}
}, [userState]);

useEffect(() => {
if (userState.apiState === 'Resolve' && userState.marathons.length) {
setMarathons(userState.marathons);
}
}, [apiState]);

return (
<StyledGroupsWrapper sx={sx}>
{title && (
<Typography
sx={{ fontSize: '22px', color: '#536166', fontWeight: 700, mb: 3 }}
>
{title}
</Typography>
)}
<Grid container spacing={1} rowGap={2}>
{marathons.length > 0 && (
marathons.map((marathon, _i) => {
return (
<Grid item sx={{ width: '100%' }}>
<MarathonCard
key={marathon._id}
marathon={marathon}
/>
</Grid>
);
})
)
}
</Grid>
</StyledGroupsWrapper>
);
};

export default MyMarathon;
6 changes: 6 additions & 0 deletions pages/profile/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Footer from '@/shared/components/Footer_v2';
import SEOConfig from '@/shared/components/SEO';
import Navigation from '@/shared/components/Navigation_v2';
import MyGroup from '@/components/Profile/MyGroup';
import MyMarathon from '@/components/Profile/MyMarathon';
import AccountSetting from '@/components/Profile/Accountsetting';
import useMediaQuery from '@mui/material/useMediaQuery';

Expand Down Expand Up @@ -79,6 +80,11 @@ const ProfilePage = () => {
tabLabel: '帳號設定',
view: <AccountSetting />,
},
{
id: 'my-marathon',
tabLabel: '學習計畫',
view: <MyMarathon title="我的學習計畫" userId={me?._id} />
}
];

const [value, setValue] = useState(() => {
Expand Down

0 comments on commit efa4828

Please sign in to comment.