Skip to content

Commit

Permalink
게시글 개수 표시 기능 구현 (#719)
Browse files Browse the repository at this point in the history
* fix: resize이벤트를 미디어 쿼리 감지로 변경

브라우저 더블클릭하여 전체화면으로 변경시에는 미디어쿼리만 변경되어 javascript가 감지하지 못하는 오류 발견

* refactor: 소속이 비어있는 목데이터 추가

* design: 기술 아이콘 크기 변경

* refactor: 소속 없을 시 빈 문자열로 반환하도록 변경

* refactor: 이벤트 내용 수정

* design: 랭킹 border-radius 적용

* feat: 게시물 개수 불러오는 기능 구현

* design: 반응형 디자인 변경

* refactor: 사이드 위젯 key 추가
  • Loading branch information
gyeongza authored Feb 19, 2024
1 parent 631a9ff commit 6d3da8a
Show file tree
Hide file tree
Showing 14 changed files with 121 additions and 66 deletions.
Original file line number Diff line number Diff line change
@@ -1,28 +1,47 @@
import React from 'react';
import { css, keyframes, styled } from 'styled-components';
import { REVIEW_STATUS_FILTER_TEXT, REVIEW_STATUS_LABEL_TEXT } from '@/constants';
import { REVIEW_STATUS_FILTER_TEXT } from '@/constants';
import Text from '@/components/common/Text/Text';
import Flex from '@/components/common/Flex/Flex';
import { ReviewStatusFilter } from '@/types/runnerPost';
import useTotalCount from '@/hooks/query/useTotalCount';

interface Props {
reviewStatus: string;
reviewStatus: ReviewStatusFilter;
handleClickRadioButton: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

const RunnerPostFilter = ({ reviewStatus, handleClickRadioButton }: Props) => {
const { totalCounts, isAllLoaded } = useTotalCount();

return (
<S.FilterContainer>
<S.LabelList>
{Object.entries(REVIEW_STATUS_FILTER_TEXT).map(([value, text]) => (
<S.StatusLabel key={value}>
<S.RadioButton
type="radio"
name="reviewStatus"
value={value}
checked={reviewStatus === value}
onChange={handleClickRadioButton}
/>
<S.Label $isSelected={reviewStatus === value}>{text}</S.Label>
</S.StatusLabel>
))}
{Object.entries(REVIEW_STATUS_FILTER_TEXT).map(([value, text]) => {
const isSelected = reviewStatus === value;

return (
<Flex align="center" key={value}>
<S.StatusLabel>
<S.RadioButton
type="radio"
name="reviewStatus"
value={value}
checked={isSelected}
onChange={handleClickRadioButton}
/>
<S.Label $isSelected={isSelected}>
<Flex align="end" gap={3}>
<Text color={isSelected ? 'red' : 'gray600'}>{text}</Text>
<Text color={isSelected ? 'red' : 'gray600'} typography="t8">
({isAllLoaded ? totalCounts[value as ReviewStatusFilter] : 0})
</Text>
</Flex>
</S.Label>
</S.StatusLabel>
</Flex>
);
})}
</S.LabelList>
</S.FilterContainer>
);
Expand Down Expand Up @@ -96,7 +115,6 @@ const S = {
font-size: 18px;
font-weight: 700;
color: ${({ $isSelected }) => ($isSelected ? 'var(--baton-red)' : 'var(--gray-600)')};
&::after {
${({ $isSelected }) => ($isSelected ? underLine : null)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@ import Label from '@/components/common/Label/Label';
import { REVIEW_STATUS_LABEL_TEXT } from '@/constants';
import eyeIcon from '@/assets/eye-icon.svg';
import applicantIcon from '@/assets/applicant-icon.svg';
import useViewport from '@/hooks/useViewport';

const RunnerPostItem = ({
runnerPostData: { runnerPostId, title, deadline, tags, runnerProfile, watchedCount, applicantCount, reviewStatus },
}: {
runnerPostData: RunnerPost;
}) => {
const { isMobile } = useViewport();

const { goToRunnerPostPage } = usePageRouter();

const handlePostClick = () => {
Expand All @@ -29,9 +26,9 @@ const RunnerPostItem = ({
<S.DeadLineContainer>
<S.DeadLine>{deadline.replace('T', ' ')} 까지</S.DeadLine>
<Label
height={isMobile ? '18px' : '22px'}
height={'18px'}
colorTheme={reviewStatus === 'NOT_STARTED' ? 'WHITE' : reviewStatus === 'IN_PROGRESS' ? 'RED' : 'GRAY'}
fontSize={isMobile ? '10px' : ''}
fontSize={'10px'}
>
{REVIEW_STATUS_LABEL_TEXT[reviewStatus]}
</Label>
Expand All @@ -45,11 +42,7 @@ const RunnerPostItem = ({
<S.RightSideContainer>
{runnerProfile ? (
<S.ProfileContainer>
<Avatar
width={isMobile ? '30px' : '50px'}
height={isMobile ? '30px' : '50px'}
imageUrl={runnerProfile.imageUrl}
/>
<Avatar width={'30px'} height={'30px'} imageUrl={runnerProfile.imageUrl} />
<S.ProfileName>{runnerProfile.name}</S.ProfileName>
</S.ProfileContainer>
) : null}
Expand All @@ -76,7 +69,7 @@ const S = {
min-width: 340px;
width: 100%;
height: max-content;
padding: 35px 40px;
padding: 25px 30px;
border: 0.5px solid var(--gray-500);
border-radius: 12px;
Expand All @@ -89,16 +82,12 @@ const S = {
transform: scale(1.015);
outline: 1.5px solid var(--baton-red);
}
@media (max-width: 768px) {
padding: 25px 30px;
}
`,

PostTitle: styled.p`
margin-bottom: 15px;
font-size: 28px;
font-size: 20px;
font-weight: 700;
@media (max-width: 768px) {
Expand All @@ -114,6 +103,7 @@ const S = {

DeadLine: styled.p`
margin-bottom: 60px;
font-size: 14px;
color: var(--gray-600);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,8 @@ const S = {
display: flex;
flex-direction: column;
align-items: center;
gap: 30px;
gap: 20px;
width: 100%;
@media (max-width: 768px) {
gap: 20px;
}
`,
};
2 changes: 1 addition & 1 deletion frontend/src/components/TechLabel/TechLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const S = {
padding: ${({ $hideText }) => ($hideText ? 0 : '1px 8px')};
font-size: ${({ $hideText }) => ($hideText ? '14px' : '12px')};
font-size: ${({ $hideText }) => ($hideText ? '16px' : '12px')};
line-height: 18px;
border-radius: 2em;
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/common/SideWidget/RankerItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ const RankerItem = ({ supporter, onClick }: RankerItemProps) => {
return (
<ListWrapper as="li" align="center" key={supporter.supporterId} onClick={onClick}>
<Flex align="center" gap={10}>
<Text typography="t7">{supporter.rank}</Text>
<Text>{supporter.rank}</Text>
<Avatar width="35px" height="35px" imageUrl={supporter.imageUrl} />
<Flex direction="column" gap={10}>
<Flex gap={10} align="center">
<Flex direction="column" align="start">
<Text>{supporter.name}</Text>
<Text typography="t8" color="gray500">
@{supporter.company}
{supporter.company ? `@${supporter.company}` : ''}
</Text>
</Flex>
</Flex>
Expand Down Expand Up @@ -56,6 +56,7 @@ const fadeIn = keyframes`

const ListWrapper = styled(Flex)`
padding: 10px 15px;
border-radius: 12px;
& {
cursor: pointer;
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/common/SideWidget/SideWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const SideWidget = ({ children, title }: SideWidgetProps) => {
return (
<Container>
<Flex direction="column" gap={10}>
<Text typography="t4" $bold={true}>
<Text typography="t5" $bold={true}>
{title}
</Text>
<ContentsContainer>{children}</ContentsContainer>
Expand Down Expand Up @@ -72,7 +72,11 @@ const SideWidgetList = ({ data }: SideWidgetListProps) => {
/>
) : (
data.map((supporter) => (
<RankerItem supporter={supporter} onClick={() => handleClickRanker(supporter.supporterId)} />
<RankerItem
key={supporter.supporterId}
supporter={supporter}
onClick={() => handleClickRanker(supporter.supporterId)}
/>
))
)}
</ListContainer>
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export const REVIEW_STATUS_FILTER_TEXT: Record<ReviewStatusFilter, string> = {
};

export const REVIEW_STATUS: ReviewStatus[] = Object.keys(REVIEW_STATUS_LABEL_TEXT) as ReviewStatus[];

export const REVIEW_STATUS_FILTER: ReviewStatusFilter[] = Object.keys(
REVIEW_STATUS_FILTER_TEXT,
) as ReviewStatusFilter[];

export const RUNNER_POST_OPTIONS = ['대기중인 리뷰', '진행중인 리뷰', '완료된 리뷰'];
export const SUPPORTER_POST_OPTIONS = ['신청한 리뷰', '진행중인 리뷰', '완료된 리뷰'];

Expand Down
34 changes: 34 additions & 0 deletions frontend/src/hooks/query/useTotalCount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useQueries } from '@tanstack/react-query';
import { getRunnerPost } from '@/apis/apis';
import { ReviewStatusFilter } from '@/types/runnerPost';
import { REVIEW_STATUS_FILTER } from '@/constants';

const useTotalCount = () => {
const reviewStatuses = REVIEW_STATUS_FILTER;

const queryResults = useQueries({
queries: reviewStatuses.map((status) => ({
queryKey: ['runnerPostTotalCount', status],

queryFn: () => getRunnerPost({ limit: 0, reviewStatus: status }).then((res) => res.pageInfo.totalCount),
})),
});

const isLoaded = queryResults.map((data) => {
return data.isLoading;
});

const isAllLoaded = isLoaded.every((don) => don === false);

const totalCounts = queryResults.reduce((acc, result, index) => {
if (result.isSuccess) {
acc[reviewStatuses[index]] = result.data;
}

return acc;
}, {} as { [key in ReviewStatusFilter]?: number });

return { totalCounts, isAllLoaded };
};

export default useTotalCount;
17 changes: 11 additions & 6 deletions frontend/src/hooks/useViewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ function useViewport() {
const [isMobile, setIsMobile] = useState(false);
const [isLoaded, setIsLoaded] = useState(false);

const handleResize = () => {
setIsMobile(window.innerWidth <= 768 || window.outerWidth <= 768);
};

useLayoutEffect(() => {
const mediaQuery = window.matchMedia('(max-width: 768px)');

const handleResize = () => {
setIsMobile(mediaQuery.matches);
};

handleResize();
setIsLoaded(true);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);

mediaQuery.addEventListener('change', handleResize);
return () => {
mediaQuery.removeEventListener('change', handleResize);
};
}, []);

return {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/mocks/data/rank.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"reviewedCount": 3,
"imageUrl": "profile.jpg",
"githubUrl": "https://github.com/cookienc",
"technicalTags": ["java", "spring"],
"company": "우아한테크코스"
"technicalTags": ["java", "spring", "javascript", "react", "typescript"],
"company": ""
},
{
"rank": 2,
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/mocks/data/runnerPostList.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
],
"pageInfo": {
"isLast": false,
"nextCursor": 1
"nextCursor": 1,
"totalCount": 129
}
}
8 changes: 4 additions & 4 deletions frontend/src/pages/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { usePageRouter } from '@/hooks/usePageRouter';
import { useRunnerPostList } from '@/hooks/query/useRunnerPostList';
import useViewport from '@/hooks/useViewport';
import { ReviewStatus } from '@/types/runnerPost';
import { useContext, useState } from 'react';
import { useContext, useEffect, useState } from 'react';
import { styled } from 'styled-components';
import { isLogin } from '@/apis/auth';
import SideWidget from '@/components/common/SideWidget/SideWidget';
Expand Down Expand Up @@ -77,10 +77,10 @@ const MainPage = () => {
</S.LeftSideContainer>
<S.RightSideContainer>
<Button
width={isMobile ? '160px' : '190px'}
width={'160px'}
onClick={handleClickPostButton}
colorTheme="WHITE"
fontSize={isMobile ? '14px' : '18px'}
fontSize={isMobile ? '14px' : '16px'}
ariaLabel="리뷰 요청 글 작성"
>
리뷰 요청 글 작성하기
Expand Down Expand Up @@ -135,7 +135,7 @@ const S = {
`,

TitleWrapper: styled.header`
margin: 72px 0 53px 0;
margin: 20px 0 53px 0;
@media (max-width: 768px) {
margin: 40px 0 20px 0;
Expand Down
22 changes: 11 additions & 11 deletions frontend/src/pages/NoticePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,25 +93,25 @@ const NoticePage = () => {
<S.EventImageWrapper>
<S.EventImage src={EventImage}></S.EventImage>
</S.EventImageWrapper>
<S.EventMessage>🥲 코드 리뷰를 받아보고 싶은데 스스로 구현해 본 프로젝트는 없는데.. </S.EventMessage>
<S.EventMessage>🤔 내가 짠 코드는 어떨까?</S.EventMessage>
<br />
<S.EventMessage>
라는 고민을 가진 분들을 위해 바톤에서 작은 미션을 준비했습니다. 보스 몬스터 잡기 미션을 직접 구현해보고
</S.EventMessage>
<S.EventMessage>
<S.EventMessageBold>
바톤에 리뷰 요청 글을 작성하신 분들에 한해 바톤에서 직접 코드 리뷰를 해드릴 예정
🥲 코드 리뷰를 받아보고 싶은데 스스로 구현해 본 프로젝트는 없는데..
</S.EventMessageBold>
입니다.
</S.EventMessage>
<S.EventMessage>
<S.EventMessageBold>🤔 내가 짠 코드는 어떨까?</S.EventMessageBold>
</S.EventMessage>
<br />
<S.EventMessage>라는 고민을 가진 분들을 위해 바톤에서 작은 미션을 준비했습니다.</S.EventMessage>
<S.EventMessage>
<br />
<S.EventMessageBold>사용 언어는 java, javascript 두 가지이며</S.EventMessageBold>
<S.EventMessageBold>아래 미션 시작하기 버튼을 통해 시작</S.EventMessageBold>하실 수 있습니다.
<S.EventMessageBold>
사용 언어는 java, javascript 두 가지이며 아래 미션 시작하기 버튼을 통해 시작
</S.EventMessageBold>
하실 수 있습니다.
</S.EventMessage>
<S.EventMessage>
공부하면서 궁금했던 점들을 미션 구현을 통해 정리해보고 바톤에서
공부하면서 궁금했던 점들을 미션 구현을 통해 정리해보고 바톤에서{' '}
<S.EventMessageBold>코드 리뷰 받아보세요 !</S.EventMessageBold>
</S.EventMessage>

Expand Down
3 changes: 2 additions & 1 deletion frontend/src/types/runnerPost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,12 @@ export interface CreateRunnerPostRequest {
export interface PageInfo {
isLast: boolean;
nextCursor: number;
totalCount: number;
}

interface requestParams {
tagName?: string;
reviewStatus: ReviewStatus | null;
reviewStatus: ReviewStatus | ReviewStatusFilter | null;
}

export interface getRunnerPostRequestParams extends pageParamsRequest, requestParams {}
Expand Down

0 comments on commit 6d3da8a

Please sign in to comment.