Skip to content

Commit

Permalink
[SP0] 404, 500 에러페이지 제작 (#378)
Browse files Browse the repository at this point in the history
* fix: Roundbutton에서 사용하지 않는 isReverse prop 삭제

* fix: RoundButton 공통컴포넌트 수정

* feat: 404 페이지 퍼블리싱

* feat: 에러페이지 에셋 상수화

* feat: 에러페이지 라이팅 상수화

* feat: 에러코드 상수 분리

* feat: ErrorPage로 404, 500 재사용

* design: 문의하기 버튼 추가

* feat: 문의하기 next/link로 수정 및 링크 연결

* feat: 에러코드svg 상수파일 -> 컴포넌트로 변경

* feat: 에러페이지 assets 반응형 대비

* feat: 에러페이지 반응형 작업

* feat: 에러코드 컴포넌트 반응형 작업

* chore: 에러코드 Wrapper를 ErrorPage->ErrorCode로 이동

* feat: 에러페이지 애니메이션 작업

* design: 반응형 기준 수정 및 ErrorCode 코드 정리

* fix: 모바일뷰에서 아이콘 애니메이션 해제

* fix: 문의하기 버튼 겹치지 않도록 min-height 추가

* fix: ErrorCode 컴포넌트에 쓰이는 스타일 파일 분리

* style: code formatter prettier 적용

* chore: 34기 모집배너 컴포넌트 제거
  • Loading branch information
lydiacho authored Mar 23, 2024
1 parent 772f9b7 commit c56d3e9
Show file tree
Hide file tree
Showing 21 changed files with 263 additions and 67 deletions.
7 changes: 3 additions & 4 deletions src/components/common/RoundButton/RoundButton.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { DetailedHTMLProps, HTMLAttributes, ReactElement } from 'react';
import * as S from './RoundButton.style';
import * as S from './style';

interface ButtonProps
extends DetailedHTMLProps<HTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
children: string | ReactElement;
isReverse?: boolean;
}

function RoundButton({ children, isReverse = false, ...props }: ButtonProps) {
function RoundButton({ children, ...props }: ButtonProps) {
return (
<S.Root isReverse={isReverse} {...props}>
<S.Root type='button' {...props}>
{children}
</S.Root>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import styled from '@emotion/styled';
import { colors } from '@sopt-makers/colors';

interface StyleProps {
isReverse?: boolean;
}

export const Root = styled.button<StyleProps>`
export const Root = styled.button`
display: flex;
align-items: center;
height: 50px;
padding: 10px 30px;
height: 60px;
padding: 12px 28px;
border-radius: 99px;
background: ${colors.gray10};
color: ${colors.gray950};
font-size: 24px;
font-size: 22px;
font-weight: 600;
line-height: 150%; /* 36px */
letter-spacing: -0.48px;
cursor: pointer;
@media (max-width: 428px) {
padding: 8px 22px;
font-size: 18px;
}
`;
50 changes: 2 additions & 48 deletions src/pages/404.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,3 @@
import styled from '@emotion/styled';
import { colors } from '@sopt-makers/colors';
import { useRouter } from 'next/router';
import { Header } from '@src/components';
import RoundButton from '@src/components/common/RoundButton';
import { Page404 } from '@src/views/ErrorPage';

function Wrong() {
const router = useRouter();

const handleButtonClick = () => {
router.push('/');
};

return (
<>
<Header />
<Styled.Root>
<span>잘못된 경로예요</span>
<RoundButton onClick={handleButtonClick} isReverse={true}>
홈으로 가기
</RoundButton>
</Styled.Root>
</>
);
}

export default Wrong;

const Styled = {
Root: styled.section`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 48px;
width: 100%;
height: 100vh;
& span {
color: ${colors.gray10};
font-size: 48px;
font-weight: 600;
line-height: 150%; /* 72px */
letter-spacing: -0.96px;
}
`,
};
export default Page404;
3 changes: 3 additions & 0 deletions src/pages/500.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Page500 } from '@src/views/ErrorPage';

export default Page500;
45 changes: 45 additions & 0 deletions src/views/ErrorPage/ErrorPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useRouter } from 'next/router';
import { Header } from '@src/components';
import RoundButton from '@src/components/common/RoundButton';
import ErrorCode from './components/ErrorCode';
import ERROR_BUTTON from './constants/errorButton';
import ERROR_MESSAGE from './constants/errorMessage';
import * as S from './styles';

interface ErrorPageProps {
code: 404 | 500;
}

function ErrorPage({ code }: ErrorPageProps) {
const router = useRouter();
const CODE_KEY: 'CODE404' | 'CODE500' = `CODE${code}`;

const handleButtonClick = () => {
code === 404 ? router.push('/') : router.back();
};

return (
<>
<Header />
<S.Root>
<S.TopSection>
<ErrorCode code={code} />
<S.ErrorText>{ERROR_MESSAGE[CODE_KEY]}</S.ErrorText>
<RoundButton onClick={handleButtonClick}>{ERROR_BUTTON[CODE_KEY]}</RoundButton>
</S.TopSection>
{code === 500 && (
<S.ContactLink
href="https://walla.my/sopt_official"
target="_blank"
rel="noopener noreferrer"
>
문의하기
</S.ContactLink>
)}
</S.Root>
</>
);
}

export const Page404 = () => <ErrorPage code={404} />;
export const Page500 = () => <ErrorPage code={500} />;
4 changes: 4 additions & 0 deletions src/views/ErrorPage/assets/ic_404_back.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/views/ErrorPage/assets/ic_404_front.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 src/views/ErrorPage/assets/ic_404_ghost.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/views/ErrorPage/assets/ic_404_ghost_dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/views/ErrorPage/assets/ic_500_back.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 src/views/ErrorPage/assets/ic_500_cone.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/views/ErrorPage/assets/ic_500_cone_dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/views/ErrorPage/assets/ic_500_front.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions src/views/ErrorPage/assets/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ReactComponent as Ic404Front } from './ic_404_front.svg';
import { ReactComponent as Ic404Back } from './ic_404_back.svg';
import { ReactComponent as Ic404Ghost } from './ic_404_ghost.svg';
import { ReactComponent as Ic404GhostDark } from './ic_404_ghost_dark.svg';
import { ReactComponent as Ic500Front } from './ic_500_front.svg';
import { ReactComponent as Ic500Back } from './ic_500_back.svg';
import { ReactComponent as Ic500Cone } from './ic_500_cone.svg';
import { ReactComponent as Ic500ConeDark } from './ic_500_cone_dark.svg';

export { Ic404Front, Ic404Back, Ic404Ghost, Ic404GhostDark, Ic500Cone, Ic500Back, Ic500Front, Ic500ConeDark };
16 changes: 16 additions & 0 deletions src/views/ErrorPage/components/ErrorCode.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import styled from '@emotion/styled';
import { motion } from 'framer-motion';

export const ErrorCode = styled(motion.div)`
display: flex;
align-items: center;
gap: 6px;
position: relative;
`;
export const ErrorIcon = styled(motion.div)`
display: flex;
justify-content: center;
position: absolute;
width: 100%;
`;
77 changes: 77 additions & 0 deletions src/views/ErrorPage/components/ErrorCode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useIsMobile } from '@src/hooks/useDevice';
import {
Ic404Back,
Ic404Front,
Ic404Ghost,
Ic404GhostDark,
Ic500Back,
Ic500Cone,
Ic500ConeDark,
Ic500Front,
} from '../assets';
import * as S from './ErrorCode.style';

interface ErrorCodeProps {
code: 404 | 500;
}
export default function ErrorCode({ code }: ErrorCodeProps) {
const isMobile = useIsMobile('428px');
const SIZE = {
height: 92,
icon: 150,
};

const codeVariant = {
initial: { gap: '6px' },
whileHover: { gap: '159px' },
};

const iconVariant = {
initial: { opacity: 0 },
whileHover: { opacity: 1 },
};

const animationProps = {
initial: 'initial',
whileHover: 'whileHover',
transition: { duration: 0.3 },
};

return (
<S.ErrorCode {...animationProps} variants={codeVariant}>
{code === 404 ? (
<>
{!isMobile && (
<>
<Ic404Front height={SIZE.height} />
<Ic404Back height={SIZE.height} />
</>
)}
{isMobile ? (
<Ic404GhostDark />
) : (
<S.ErrorIcon {...animationProps} variants={iconVariant}>
<Ic404Ghost height={SIZE.icon} />
</S.ErrorIcon>
)}
</>
) : (
<>
{!isMobile && (
<>
<Ic500Front height={SIZE.height} />
<Ic500Back height={SIZE.height} />
</>
)}
{isMobile ? (
<Ic500ConeDark />
) : (
<S.ErrorIcon {...animationProps} variants={iconVariant}>
<Ic500Cone height={SIZE.icon} />
</S.ErrorIcon>
)}
</>
)}
</S.ErrorCode>
);
}
6 changes: 6 additions & 0 deletions src/views/ErrorPage/constants/errorButton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const ERROR_BUTTON = {
CODE404 : '홈으로 가기',
CODE500 : '이전 페이지로 가기',
};

export default ERROR_BUTTON;
6 changes: 6 additions & 0 deletions src/views/ErrorPage/constants/errorMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const ERROR_MESSAGE = {
CODE404 : '존재하지 않는 페이지예요',
CODE500 : '알 수 없는 오류가 발생했어요',
};

export default ERROR_MESSAGE;
3 changes: 3 additions & 0 deletions src/views/ErrorPage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Page404, Page500 } from './ErrorPage';

export { Page404, Page500 };
51 changes: 51 additions & 0 deletions src/views/ErrorPage/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import styled from '@emotion/styled';
import Link from 'next/link';

export const Root = styled.main`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
width: 100%;
height: 100vh;
min-height: 400px;
padding-bottom: 20vh;
overflow: scroll;
`;
export const TopSection = styled.section`
display: flex;
flex-direction: column;
align-items: center;
gap: 34px;
width: 100%;
`;
export const ErrorText = styled.p`
color: #fcfcfc;
font-size: calc(24px + 1vw);
font-weight: 600;
line-height: 150%;
letter-spacing: -0.96px;
`;
export const ContactLink = styled(Link)`
position: absolute;
bottom: 17vh;
color: #fff;
font-size: 24px;
font-weight: 600;
line-height: 150%; /* 36px */
letter-spacing: -0.48px;
text-decoration-line: underline;
cursor: pointer;
@media (max-width: 428px) {
font-size: 18px;
}
`;
7 changes: 0 additions & 7 deletions src/views/MainPage/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,10 @@ import IntroSection from '@src/views/MainPage/components/IntroSection';
import Banner from './components/Banner';
import Introduce from './components/Introduce';
import ScrollInteractiveLogo from './components/ScrollInteractiveLogo';
import TopBanner from './components/TopBanner';
import usePost from './hooks/usePost';
import useCheckTime from '../../hooks/useCheckTime';

function MainPage() {
const isValid = useCheckTime(); // 모집 시작 여부
usePost(); // 방문자 증가

return (
<PageLayout>
{isValid && <TopBanner/>}
<Banner />
<Introduce />
<IntroSection />
Expand Down

0 comments on commit c56d3e9

Please sign in to comment.