Skip to content

Commit

Permalink
Feature/empty group list (#96)
Browse files Browse the repository at this point in the history
* refactor: group list page

* refactor: change empty cover image

* feat: empty group list ui

* feat: error group list page
  • Loading branch information
JohnsonMao authored Oct 12, 2024
1 parent 8e9ec72 commit 4d6b00a
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 56 deletions.
7 changes: 5 additions & 2 deletions components/Group/GroupList/GroupCard.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import LocationOnOutlinedIcon from '@mui/icons-material/LocationOnOutlined';
import Image from '@/shared/components/Image';
import emptyCoverImg from '@/public/assets/empty-cover.png';
import { timeDuration } from '@/utils/date';
import emptyCoverWithBackgroundImg from '@/public/assets/empty-cover-with-background.png';
import {
StyledAreas,
StyledContainer,
Expand Down Expand Up @@ -31,7 +31,10 @@ function GroupCard({

return (
<StyledGroupCard href={`/group/detail?id=${_id}`}>
<Image alt={photoAlt || '未放封面'} src={photoURL || emptyCoverImg.src} />
<Image
alt={photoAlt || '未放封面'}
src={photoURL || emptyCoverWithBackgroundImg.src}
/>
<StyledContainer>
<StyledTitle>{title}</StyledTitle>
<StyledInfo>
Expand Down
190 changes: 142 additions & 48 deletions components/Group/GroupList/index.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { useEffect, Fragment } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import styled from '@emotion/styled';
import Button from '@mui/material/Button';
import useMediaQuery from '@mui/material/useMediaQuery';
import { Box } from '@mui/material';
import useSearchParamsManager from '@/hooks/useSearchParamsManager';
import { setQuery } from '@/redux/actions/group';
import Image from '@/shared/components/Image';
import emptyCoverImg from '@/public/assets/empty-cover.png';
import errorCoverImg from '@/public/assets/contacterror.png';
import GroupCard from './GroupCard';
import SkeletonGroupCard from './SkeletonGroupCard';

Expand Down Expand Up @@ -41,10 +44,93 @@ const StyledGroupList = styled.ul`
flex-wrap: wrap;
`;

const StyledFullItem = styled.li`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 412px;
.color-gray {
color: #536166;
}
.color-gray-light {
color: #92989a;
}
p {
margin-top: 20px;
}
span {
margin-top: 6px;
font-size: 14px;
}
`;

function shouldRenderDivider(
index,
isLast,
isMobileScreen,
isPadScreen,
isDeskTopScreen,
) {
return (
(isMobileScreen && !isLast) ||
(isPadScreen && !isLast && index % 2 === 1) ||
(isDeskTopScreen && !isLast && index % 3 === 2)
);
}

function GroupItems({ items, isMobileScreen, isPadScreen, isDeskTopScreen }) {
return (
Array.isArray(items) &&
items.map((data, index) => {
const isLast = index === items.length - 1;
return (
<Fragment key={data._id}>
<StyledGroupItem>
<GroupCard {...data} />
</StyledGroupItem>
{shouldRenderDivider(
index,
isLast,
isMobileScreen,
isPadScreen,
isDeskTopScreen,
) && <StyledDivider />}
</Fragment>
);
})
);
}

function SkeletonItems({ isMobileScreen, isPadScreen, isDeskTopScreen }) {
return Array.from({ length: isMobileScreen ? 3 : 6 }, (_, index) => {
const isLast = index === (isMobileScreen ? 2 : 5);
return (
<Fragment key={index}>
<StyledGroupItem>
<SkeletonGroupCard />
</StyledGroupItem>
{shouldRenderDivider(
index,
isLast,
isMobileScreen,
isPadScreen,
isDeskTopScreen,
) && <StyledDivider />}
</Fragment>
);
});
}

function GroupList() {
const dispatch = useDispatch();
const [getSearchParams] = useSearchParamsManager();
const { items, isLoading } = useSelector((state) => state.group);
const { items, isLoading, isError } = useSelector((state) => state.group);

const isMobileScreen = useMediaQuery('(max-width: 560px)');
const isPadScreen = useMediaQuery('(max-width: 767px)') && !isMobileScreen;
Expand All @@ -55,54 +141,62 @@ function GroupList() {
}, [getSearchParams]);

return (
<>
<StyledGroupList>
{isLoading ? (
// always show 3 || 6 skeleton cards in mobile || desktop screen
Array.from({ length: isMobileScreen ? 3 : 6 }, (_, index) => {
const isLast = index === (isMobileScreen ? 2 : 5);
const shouldRenderDivider =
(isMobileScreen && !isLast) ||
(isPadScreen && !isLast && index % 2 === 1) ||
(isDeskTopScreen && !isLast && index % 3 === 2);

return (
<Fragment key={index}>
<StyledGroupItem>
<SkeletonGroupCard />
</StyledGroupItem>
{shouldRenderDivider && <StyledDivider />}
</Fragment>
);
})
) : items?.length ? (
items.map((data, index) => {
const isLast = index === items.length - 1;
const shouldRenderDivider =
(isMobileScreen && !isLast) ||
(isPadScreen && !isLast && index % 2 === 1) ||
(isDeskTopScreen && !isLast && index % 3 === 2);

return (
<Fragment key={data._id}>
<StyledGroupItem>
<GroupCard {...data} />
</StyledGroupItem>
{shouldRenderDivider && <StyledDivider />}
</Fragment>
);
})
) : (
<li style={{ textAlign: 'center', width: '100%' }}>
哎呀!這裡好像沒有符合你條件的揪團,別失望!讓我們試試其他選項。
</li>
)}
</StyledGroupList>

<StyledGroupList>
<GroupItems
items={items}
isMobileScreen={isMobileScreen}
isPadScreen={isPadScreen}
isDeskTopScreen={isDeskTopScreen}
/>
{isLoading && (
<Box sx={{ textAlign: 'center', paddingTop: '32px' }}>搜尋揪團中~</Box>
<SkeletonItems
isMobileScreen={isMobileScreen}
isPadScreen={isPadScreen}
isDeskTopScreen={isDeskTopScreen}
/>
)}
{isError ? (
<StyledFullItem>
<Image
alt="異常錯誤"
src={errorCoverImg.src}
width={160}
height={142}
/>
<p className="color-gray-light">哎呀!有不明錯誤</p>
<Button
variant="outlined"
sx={{
marginTop: '20px',
fontSize: '16px',
color: '#536166',
borderColor: '#16B9B3',
borderRadius: '20px',
padding: '6px 48px',
}}
onClick={() => dispatch(setQuery(getSearchParams()))}
>
重新載入
</Button>
</StyledFullItem>
) : (
!isLoading &&
items.length === 0 && (
<StyledFullItem>
<Image
alt="查無資料"
src={emptyCoverImg.src}
width={160}
height={120}
/>
<p className="color-gray">哎呀!這裡沒有符合條件的揪團</p>
<span className="color-gray-light">
試著更改條件或搜尋其他關鍵字吧
</span>
</StyledFullItem>
)
)}
</>
</StyledGroupList>
);
}

Expand Down
4 changes: 1 addition & 3 deletions components/Group/More.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function More() {
<Box
sx={{ textAlign: 'center', paddingTop: '80px', paddingBottom: '100px' }}
>
{isMore ? (
{isMore && (
<Button
variant="outlined"
sx={{
Expand All @@ -26,8 +26,6 @@ export default function More() {
>
顯示更多
</Button>
) : (
'已經到底囉~'
)}
</Box>
);
Expand Down
Binary file added public/assets/empty-cover-with-background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified public/assets/empty-cover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions redux/reducers/group.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const initialState = {
items: [],
total: 0,
isLoading: true,
isError: false,
};

const reducer = (state = initialState, action) => {
Expand Down Expand Up @@ -39,13 +40,15 @@ const reducer = (state = initialState, action) => {
...state,
...(action.payload ?? {}),
isLoading: false,
isError: false,
};
}
case GET_GROUP_ITEMS_FAILURE: {
return {
...state,
total: 0,
isLoading: false,
isError: true,
};
}
default: {
Expand Down
6 changes: 3 additions & 3 deletions shared/components/Image/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Skeleton from '@mui/material/Skeleton';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import emptyCoverImg from '@/public/assets/empty-cover.png';
import emptyCoverWithBackgroundImg from '@/public/assets/empty-cover-with-background.png';
import { useState } from 'react';

const Loading = ({ height }) => (
Expand All @@ -17,13 +17,13 @@ const Image = ({
alt,
width = '100%',
height = '122px',
background = 'rgba(240, 240, 240, .8)',
background = 'transparent',
borderRadius = '8px',
}) => {
const [isError, setIsError] = useState(false);
return (
<LazyLoadImage
src={isError ? emptyCoverImg.src : src}
src={isError ? emptyCoverWithBackgroundImg.src : src}
alt={alt}
width={width}
height={height}
Expand Down

0 comments on commit 4d6b00a

Please sign in to comment.