Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE] feat: 에러, 로딩 페이지 구현 및 적용 #155

Merged
merged 11 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions frontend/src/assets/alertTriangle.svg
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 frontend/src/assets/home.svg
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 frontend/src/assets/reload.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/components/ReviewPreviewCard/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export const Visibility = styled.div`
display: flex;
gap: 0.6rem;
align-items: center;

font-size: 1.6rem;
font-weight: 700;

Expand Down
11 changes: 9 additions & 2 deletions frontend/src/components/common/Button/index.tsx
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

버튼에 텍스트 뿐 아니라 아이콘까지 추가돼서 이와 같이 코드를 작성하신 것 같아요. 추후에 텍스트, 아이콘 이외의 요소들이 더 들어올 상황이 생길 수도 있을 것 같아요. 이런 경우에 optional로 props를 계속 받기보다는, 공통 버튼 컴포넌트는 Children만을 받아서 그대로 출력해주는 것은 어떨까요?

그렇게 되면 버튼을 사용하는 쪽에서 필요한 요소들을 유연하게 넣을 수 있고, 현재 코드에서의 icon도 그 종류가 특정될 수 있을 것 같아요.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

너무 동의합니다!
에프이가 말했던 것처럼 추가적으로 요소들이 더 들어오면 아래 코드처럼 옵셔널로 props를 받는 상황이 많아질텐데.... children을 받는 방법이 더 효율적일 것 같네요! 고마워요~~😊

// 안 좋은 코드
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  buttonType: ButtonType;
  text: string;
  image?: string;
  imageDescription?: string;
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@ import * as S from './styles';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
buttonType: ButtonType;
text: string;
image?: string;
imageDescription?: string;
}

const Button = ({ buttonType, text }: ButtonProps) => {
return <S.Button buttonType={buttonType}>{text}</S.Button>;
const Button = ({ buttonType, text, image, imageDescription, onClick }: ButtonProps) => {
return (
<S.Button buttonType={buttonType} onClick={onClick}>
{image && <S.Image src={image} alt={imageDescription || ''} />}
{text}
</S.Button>
);
};

export default Button;
4 changes: 4 additions & 0 deletions frontend/src/components/common/Button/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,7 @@ export const Button = styled.button<{ buttonType: ButtonType }>`

${({ buttonType, theme }) => getButtonStyle(buttonType, theme)};
`;

export const Image = styled.img`
margin-right: 0.8rem;
`;
2 changes: 2 additions & 0 deletions frontend/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import App from '@/App';

import DetailedReviewPage from './pages/DetailedReviewPage';
import ErrorPage from './pages/ErrorPage';
import ReviewPreviewListPage from './pages/ReviewPreviewListPage';
import ReviewWritingPage from './pages/ReviewWriting';
import globalStyles from './styles/globalStyles';
Expand All @@ -15,6 +16,7 @@ const router = createBrowserRouter([
{
path: '/',
element: <App />,
errorElement: <ErrorPage />,
children: [
{
path: 'user',
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/pages/DetailedReviewPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ReviewComment } from '@/components';
import { DetailReviewData } from '@/types';

import { getDetailedReviewApi } from '../../apis/review';
import LoadingPage from '../LoadingPage';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰 상세페이지에 LoadingPage를 적용시켰군요!
develop에 머지 후 리뷰 상세페이지의 suspense에 적용해볼게요. 😝


import KeywordSection from './components/KeywordSection';
import ReviewDescription from './components/ReviewDescription';
Expand All @@ -24,8 +25,9 @@ const DetailedReviewPage = () => {
useEffect(() => {
const fetchReview = async () => {
try {
const result = await getDetailedReviewApi({ reviewId: Number(id), memberId: Number(memberId) });
setIsLoading(true);

const result = await getDetailedReviewApi({ reviewId: Number(id), memberId: Number(memberId) });
setDetailReview(result);
setErrorMessage('');
} catch (error) {
Expand All @@ -38,7 +40,7 @@ const DetailedReviewPage = () => {
fetchReview();
}, [id]);

if (isLoading) return <div>Loading...</div>;
if (isLoading) return <LoadingPage />;

if (errorMessage) return <div>Error: {errorMessage}</div>;
if (!detailedReview) return <div>Error: 상세보기 리뷰 데이터를 가져올 수 없어요.</div>;
Expand Down
56 changes: 56 additions & 0 deletions frontend/src/pages/ErrorPage/components/ErrorSection/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import AlertTriangle from '@/assets/alertTriangle.svg';
import HomeIcon from '@/assets/home.svg';
import ReloadIcon from '@/assets/reload.svg';
import { Button } from '@/components';
import { ButtonType } from '@/types/styles';

import * as S from './styles';

interface ErrorSectionProps {
errorMessage: string;
handleReload: () => void;
handleGoHome: () => void;
}

const ErrorSection = ({ errorMessage, handleReload, handleGoHome }: ErrorSectionProps) => {
const buttons = [
{
buttonType: 'primary' as ButtonType,
text: '새로고침하기',
image: ReloadIcon,
imageDescription: '새로고침 이미지',
onClick: handleReload,
},
{
buttonType: 'secondary' as ButtonType,
text: '홈으로 이동하기',
image: HomeIcon,
imageDescription: '홈 이미지',
onClick: handleGoHome,
},
];

return (
<S.Layout>
<S.ErrorLogoWrapper>
<img src={AlertTriangle} alt="에러 로고" />
</S.ErrorLogoWrapper>
<S.ErrorMessage>{errorMessage}</S.ErrorMessage>
<S.Container>
{buttons.map((button, index) => (
<S.ButtonContainer key={index}>
<Button
buttonType={button.buttonType}
text={button.text}
image={button.image}
imageDescription={button.imageDescription}
onClick={button.onClick}
/>
</S.ButtonContainer>
))}
</S.Container>
</S.Layout>
);
};

export default ErrorSection;
45 changes: 45 additions & 0 deletions frontend/src/pages/ErrorPage/components/ErrorSection/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import styled from '@emotion/styled';

export const Layout = styled.div`
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);

display: flex;
flex-direction: column;
gap: 3rem;
align-items: center;
justify-content: center;
`;

export const ErrorLogoWrapper = styled.div`
& > img {
width: 15rem;
height: 15rem;
}
`;

export const ErrorMessage = styled.span`
font-size: 2rem;
`;

export const Container = styled.div`
display: flex;
gap: 3.5rem;
align-items: center;
justify-content: center;
`;

export const ButtonContainer = styled.div`
display: flex;
align-items: center;
justify-content: center;
margin-top: 3rem;

& > button {
width: 17rem;
height: 5rem;
font-size: 1.4rem;
}
`;
43 changes: 43 additions & 0 deletions frontend/src/pages/ErrorPage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useNavigate } from 'react-router';

import { Main, PageLayout, SideModal, Sidebar, Topbar } from '@/components';
import { useSidebar } from '@/hooks';

import ErrorSection from './components/ErrorSection';

const ERROR_MESSAGE = {
serverUnstable: '서버와의 통신이 불안정합니다.',
};

const ErrorPage = () => {
const { isSidebarHidden, isSidebarModalOpen, closeSidebar, openSidebar } = useSidebar();
const navigate = useNavigate();

const handleReload = () => {
navigate(0);
};

const handleGoHome = () => {
navigate('/'); // TODO: 홈 페이지 경로가 결정되면 변경 필요
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO 주석 좋아요!

};

return (
<PageLayout>
{isSidebarModalOpen && (
<SideModal isSidebarHidden={isSidebarHidden}>
<Sidebar closeSidebar={closeSidebar} />
</SideModal>
)}
<Topbar openSidebar={openSidebar} />
<Main>
<ErrorSection
errorMessage={ERROR_MESSAGE.serverUnstable}
handleReload={handleReload}
handleGoHome={handleGoHome}
/>
</Main>
</PageLayout>
);
};

export default ErrorPage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as S from './styles';

const LoadingBar = () => {
return <S.LoadingBar />;
};

export default LoadingBar;
41 changes: 41 additions & 0 deletions frontend/src/pages/LoadingPage/components/LoadingBar/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import styled from '@emotion/styled';

export const LoadingBar = styled.div`
position: relative;

display: block;

width: 30rem;
height: 2rem;

background-color: ${({ theme }) => theme.colors.lightPurple};
border-radius: 3rem;

::before {
content: '';

position: absolute;
top: 0;
left: 0;

width: 0%;
height: 100%;

background: ${({ theme }) => theme.colors.primary};
border-radius: 3rem;

animation: moving 1s ease-in-out infinite;
}

@keyframes moving {
50% {
width: 100%;
}

100% {
right: 0;
left: unset;
width: 0;
}
}
`;
13 changes: 13 additions & 0 deletions frontend/src/pages/LoadingPage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import LoadingBar from './components/LoadingBar';
import * as S from './styles';

const LoadingPage = () => {
return (
<S.Container>
<S.Text>Loading ....</S.Text>
<LoadingBar />
</S.Container>
);
};

export default LoadingPage;
21 changes: 21 additions & 0 deletions frontend/src/pages/LoadingPage/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import styled from '@emotion/styled';

export const Container = styled.div`
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);

display: flex;
flex-direction: column;
gap: 1.5rem;
align-items: center;
justify-content: center;

width: 100%;
`;

export const Text = styled.span`
font-size: ${({ theme }) => theme.fontSize.basic};
color: ${({ theme }) => theme.colors.primary};
`;
4 changes: 3 additions & 1 deletion frontend/src/pages/ReviewPreviewListPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { getReviewListApi } from '@/apis/review';
import ReviewPreviewCard from '@/components/ReviewPreviewCard';
import { ReviewPreview } from '@/types';

import LoadingPage from '../LoadingPage';

import SearchSection from './components/SearchSection';
import * as S from './styles';

Expand Down Expand Up @@ -44,7 +46,7 @@ const ReviewPreviewListPage = () => {
<S.Layout>
<SearchSection onChange={() => {}} options={OPTIONS} placeholder={USER_SEARCH_PLACE_HOLDER} />
<S.ReviewSection>
{loading && <p>로딩 중...</p>}
{loading && <LoadingPage />}
{error && <p>{error}</p>}
{!loading &&
!error &&
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/pages/ReviewWriting/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import Button from '@/components/common/Button';
import { REVIEW } from '@/constants/review';
import { Keyword, ReviewContent, ReviewData, WritingReviewInfoData } from '@/types';

import LoadingPage from '../LoadingPage';

import KeywordButton from './components/KeywordButton';
import RevieweeComment from './components/RevieweeComment';
import ReviewItem from './components/ReviewItem';
Expand Down Expand Up @@ -74,7 +76,7 @@ const ReviewWritingPage = () => {
}
};

if (!dataToWrite) return <div>Loading...</div>;
if (!dataToWrite) return <LoadingPage />;

return (
<S.ReviewWritingPage onSubmit={handleSubmitReview}>
Expand Down