Skip to content

Commit

Permalink
feat: Implement components with applied react-query lib
Browse files Browse the repository at this point in the history
  • Loading branch information
designsoo committed Feb 25, 2024
1 parent d118f51 commit 52b7ffb
Show file tree
Hide file tree
Showing 62 changed files with 967 additions and 502 deletions.
12 changes: 3 additions & 9 deletions src/components/layout/Container/Container.module.scss
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
$lg-header-height: 9.3rem;
$lg-footer-height: 16.4rem;
$sm-header-height: 6.3rem;
$sm-footer-height: 16rem;

.container {
min-height: calc(100vh - ($lg-header-height + $lg-footer-height));
@include column-flexbox;

@include responsive(M) {
min-height: calc(100vh - ($sm-header-height + $sm-footer-height));
}
width: 100%;
flex-grow: 1;
}
2 changes: 1 addition & 1 deletion src/components/layout/Container/index.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import styles from './Container.module.scss';
import classNames from 'classnames/bind';
import styles from './Container.module.scss';

const cx = classNames.bind(styles);

Expand Down
3 changes: 3 additions & 0 deletions src/components/layout/EmptyLayout/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const EmptyLayout = ({ children }) => <>{children}</>;

export default EmptyLayout;
1 change: 1 addition & 0 deletions src/components/layout/Footer/Footer.module.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.footer {
width: 100%;
background-color: $dark-blue;

.footer-container {
Expand Down
2 changes: 2 additions & 0 deletions src/components/layout/Footer/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const Footer = () => {
src={item.icon.url}
alt={item.icon.alt}
className={cx('social-list-item-link-icon')}
width={20}
height={20}
/>
</Link>
</li>
Expand Down
5 changes: 5 additions & 0 deletions src/components/layout/FullLayout/FullLayout.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.full-layout {
width: 100%;
min-height: 100vh;
background-color: $light-blue;
}
10 changes: 10 additions & 0 deletions src/components/layout/FullLayout/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import styles from './FullLayout.module.scss';
import classNames from 'classnames/bind';

const cx = classNames.bind(styles);

const FullLayout = ({ children }) => {
return <div className={cx('full-layout')}>{children}</div>;
};

export default FullLayout;
1 change: 1 addition & 0 deletions src/components/layout/Header/Header.module.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.gnb {
width: 100%;
background-color: $light-blue;

&-container {
Expand Down
29 changes: 15 additions & 14 deletions src/components/layout/Header/index.jsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
import Link from 'next/link';
import Image from 'next/image';

import styles from './Header.module.scss';
import classNames from 'classnames/bind';

import Api from 'apis/apiCall';
import { useAsync } from 'hooks/useAsync';

import { useQuery } from '@tanstack/react-query';
import { getUser } from 'apis/user';
import Profile from 'components/common/Profile';
import LinkButton from 'components/common/Button/LinkButton';

import { INITIAL_PROFILE_DATA } from 'constants/initialData';
import { ICON } from 'constants/importImg';
import styles from './Header.module.scss';

const cx = classNames.bind(styles);
const { logo } = ICON;

const Header = () => {
const {
data: { data: userData },
} = useAsync(() => Api.getUserData(), INITIAL_PROFILE_DATA);
const { data: profileData } = useQuery({
queryKey: ['user'],
queryFn: getUser,
});

const profileData = userData?.[0];
const isLoggedIn = !!profileData?.email;
const isLoggedIn = !!profileData?.id;

return (
<header className={cx('gnb')}>
<div className={cx('gnb-container')}>
<h1 className={cx('gnb-logo')}>
<Link href={'/'} className={cx('gnb-logo-link')}>
<Image src={logo.url} alt={logo.alt} className={cx('gnb-logo-link-image')} />
<Image
src={logo.url}
alt={logo.alt}
width={133}
height={24}
className={cx('gnb-logo-link-image')}
/>
</Link>
</h1>

Expand Down
6 changes: 6 additions & 0 deletions src/components/layout/MainLayout/MainLayout.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.main-layout {
@include column-flexbox;

width: 100%;
min-height: 100vh;
}
18 changes: 18 additions & 0 deletions src/components/layout/MainLayout/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import classNames from 'classnames/bind';
import Header from 'components/layout/Header';
import Footer from 'components/layout/Footer';
import styles from './MainLayout.module.scss';

const cx = classNames.bind(styles);

const MainLayout = ({ children }) => {
return (
<div className={cx('main-layout')}>
<Header />
{children}
<Footer />
</div>
);
};

export default MainLayout;
5 changes: 2 additions & 3 deletions src/constants/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const REFRESH_TOKEN = 'refreshToken';
export const AUTHORIZATION = 'Authorization';
export const REFRESH_TOKEN_URL = '/api/refresh-token';

export const isAccessToken = !!LocalStorage.getItem(ACCESS_TOKEN);
export const isLoggedIn = !!LocalStorage.getItem(ACCESS_TOKEN);

export const STATUS_CODE = {
BAD_REQUEST: 400,
Expand All @@ -19,7 +19,7 @@ export const STATUS_CODE = {
SUCCESS: 200,
};

export const AUTHENTICATION = {
export const USER_INPUT_VALIDATION = {
email: {
regex: /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/,
errorMessage: {
Expand All @@ -39,7 +39,6 @@ export const AUTHENTICATION = {
},
passwordConfirm: {
errorMessage: {
empty: '비밀번호를 입력해주세요.',
confirm: '비밀번호가 일치하지 않아요.',
},
},
Expand Down
3 changes: 3 additions & 0 deletions src/constants/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './auth';
export * from './importImg';
export * from './listOption';
4 changes: 2 additions & 2 deletions src/constants/listOption.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ export const SELECT_MENU = [
{
id: 1,
name: '삭제하기',
type: 'remove',
type: 'deleteLink',
},
{
id: 2,
name: '폴더에 추가',
type: 'add',
type: 'addLink',
},
];
104 changes: 36 additions & 68 deletions src/containers/auth/signin/AuthForm.jsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,60 @@
import { useEffect } from 'react';
import Link from 'next/link';
import Image from 'next/image';

import { useForm } from 'react-hook-form';
import { useFormContext } from 'react-hook-form';
import { useMutation } from '@tanstack/react-query';
import classNames from 'classnames/bind';
import styles from './AuthForm.module.scss';

import { signin } from 'services/api';
import Auth from 'apis/auth';
import InputField from 'components/common/InputField/InputField';
import BaseButton from 'components/common/Button/BaseButton';
import AuthFormHeader from './AuthFormHeader';
import AuthFormSocial from './AuthFormSocial';
import { USER_INPUT_VALIDATION } from 'constants/auth';
import { redirectToPage } from 'utils';

import { isAccessToken } from 'constants/auth';
import { SOCIAL_LOGIN } from 'constants/listOption';
import { ICON } from 'constants/importImg';

import TextField from 'components/common/TextField';
import StyledButton from 'components/common/Button/StyledButton';
import IconButton from 'components/common/Button/IconButton';
import styles from './AuthForm.module.scss';

const cx = classNames.bind(styles);
const { logo } = ICON;
const { email, password } = USER_INPUT_VALIDATION;

const AuthForm = () => {
const {
register,
handleSubmit,
setError,
formState: { errors },
} = useForm({ mode: 'onBlur' });

const onSubmit = (data) => signin(data, setError);

useEffect(() => {
if (isAccessToken) {
const { handleSubmit, setError, reset } = useFormContext();
const { mutate: signinMutation } = useMutation({
mutationFn: Auth.signin,
onSuccess: () => {
redirectToPage('/folder');
}
}, []);
reset();
},
onError: () => {
setError('email', { message: email.errorMessage.check });
setError('password', { message: password.errorMessage.check });
},
});

const onValid = (data) => {
signinMutation(data);
};

return (
<div className={cx('auth-form')}>
<fieldset>
<legend className='visually-hidden'>Login Linkbrary Account</legend>

<header className={cx('auth-form-header')}>
<h1 className={cx('logo')}>
<Link href={'/'}>
<Image width={210} src={logo.url} alt={logo.alt} />
</Link>
</h1>
<p className={cx('auth-form-header-info')}>
회원이 아니신가요?
<Link href={'/signup'}>
<span className={cx('auth-form-header-signup')}>회원 가입하기</span>
</Link>
</p>
</header>

<form onSubmit={handleSubmit(onSubmit)} className={cx('auth-form-login')}>
<TextField
<AuthFormHeader />
<form onSubmit={handleSubmit(onValid)} className={cx('auth-form-login')}>
<InputField
type='email'
name='email'
label='이메일'
register={register}
errors={errors}
placeholder='이메일을 입력해 주세요.'
autoComplete='email'
placeholder={email.errorMessage.empty}
/>
<TextField
<InputField
type='password'
name='password'
label='비밀번호'
register={register}
errors={errors}
placeholder='비밀번호를 입력해 주세요.'
maxLength={15}
autoComplete='current-password'
placeholder={password.errorMessage.empty}
/>
<StyledButton text='로그인' size='lg' type='submit' />
<BaseButton text='로그인' size='lg' type='submit' />
</form>

<div className={cx('auth-form-social')}>
<span className={cx('auth-form-social-title')}>소셜 로그인</span>
<ul className={cx('auth-form-social-list')}>
{SOCIAL_LOGIN.map((item) => (
<li key={item.id} className={cx('auth-form-social-item')}>
<Link href={item.url}>
<IconButton svg={item.src} iconSize={42} alt={item.alt} />
</Link>
</li>
))}
</ul>
</div>
<AuthFormSocial />
</fieldset>
</div>
);
Expand Down
5 changes: 5 additions & 0 deletions src/containers/auth/signin/AuthForm.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
text-align: center;
margin-bottom: 3rem;

&-container {
max-width: 23rem;
margin: 0 auto;
}

&-info {
@include text-style(16);

Expand Down
33 changes: 33 additions & 0 deletions src/containers/auth/signin/AuthFormHeader.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Link from 'next/link';
import Image from 'next/image';
import classNames from 'classnames/bind';
import { useFormContext } from 'react-hook-form';
import { ICON } from 'constants/importImg';
import styles from './AuthForm.module.scss';

const cx = classNames.bind(styles);
const { logo } = ICON;

const AuthFormHeader = () => {
const { reset } = useFormContext();

return (
<header className={cx('auth-form-header')}>
<div className={cx('auth-form-header-container')}>
<h1 className={cx('logo')}>
<Link href={'/'}>
<Image width={210} height={38} src={logo.url} alt={logo.alt} priority />
</Link>
</h1>
<p className={cx('auth-form-header-info')}>
회원이 아니신가요?
<Link href={'/signup'} onClick={() => reset()}>
<span className={cx('auth-form-header-signup')}>회원 가입하기</span>
</Link>
</p>
</div>
</header>
);
};

export default AuthFormHeader;
28 changes: 28 additions & 0 deletions src/containers/auth/signin/AuthFormSocial.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Link from 'next/link';

import classNames from 'classnames/bind';
import styles from './AuthForm.module.scss';

import { SOCIAL_LOGIN } from 'constants/listOption';
import IconButton from 'components/common/Button/IconButton';

const cx = classNames.bind(styles);

const AuthFormSocial = () => {
return (
<div className={cx('auth-form-social')}>
<span className={cx('auth-form-social-title')}>소셜 로그인</span>
<ul className={cx('auth-form-social-list')}>
{SOCIAL_LOGIN.map((item) => (
<li key={item.id} className={cx('auth-form-social-item')}>
<Link href={item.url}>
<IconButton svg={item.src} iconSize={42} alt={item.alt} />
</Link>
</li>
))}
</ul>
</div>
);
};

export default AuthFormSocial;
Loading

0 comments on commit 52b7ffb

Please sign in to comment.