Skip to content

Commit

Permalink
[merge] 로그인 및 상단 네비게이션 UI 변경
Browse files Browse the repository at this point in the history
로그인 UI 변경
  • Loading branch information
Im-younique authored Jun 8, 2024
2 parents cdd0c10 + 1dec392 commit df090f9
Show file tree
Hide file tree
Showing 27 changed files with 767 additions and 269 deletions.
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@
"isomorphic-dompurify": "^2.2.0",
"lowlight": "^3.1.0",
"next": "^14.0.4",
"next-redux-wrapper": "^8.1.0",
"openai": "^4.8.0",
"postcss-nesting": "^12.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.11",
"react-redux": "^8.0.5",
"redux-persist": "^6.0.0",
"socketjs-client": "^1.0.2",
"tiptap-extension-resize-image": "^1.0.2",
"typescript": "5.0.2",
Expand Down
Binary file added client/public/images/check.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 9 additions & 2 deletions client/src/app/StoreProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import { useRef } from 'react';
import { Provider } from 'react-redux';
import { makeStore, AppStore, store } from '../lib/redux/store';
import { makeStore, AppStore, store, persistor } from '../lib/redux/store';
import { PersistGate } from 'redux-persist/integration/react';

export default function StoreProvider({
children,
Expand All @@ -15,5 +16,11 @@ export default function StoreProvider({
storeRef.current = makeStore();
}

return <Provider store={store}>{children}</Provider>;
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
{children}
</PersistGate>
</Provider>
);
}
34 changes: 34 additions & 0 deletions client/src/app/login/components/GithubLoginButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use client';

import { Icon } from '@iconify/react';

//hooks
import useModal from '@/hooks/modal/useModal';

//constant
import { errorMessage, ModalType } from '@/constants/constant';

export default function GithubLoginButton() {
const { openModal } = useModal();

return (
<div className="grid py-4">
<button
onClick={() =>
openModal({
type: ModalType.ERROR,
message: errorMessage.notComplete,
})
}
className="relative p-3 border border-gray-900 hover:bg-blue-gray-scale-100 active:bg-blue-gary-scale-100 disabled:bg-gray-scale-50 disabled:border-gray-800 disabled:text-blue-gray-600"
>
<Icon
className="absolute top-2 left-2"
icon="mdi:github"
fontSize={34}
/>
<span>깃허브로 로그인</span>
</button>
</div>
);
}
34 changes: 34 additions & 0 deletions client/src/app/login/components/GoogleLoginButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use client';

import { Icon } from '@iconify/react';

//hooks
import useModal from '@/hooks/modal/useModal';

//constant
import { errorMessage, ModalType } from '@/constants/constant';

export default function GoogleLoginButton() {
const { openModal } = useModal();

return (
<div className="grid py-4">
<button
onClick={() =>
openModal({
type: ModalType.ERROR,
message: errorMessage.notComplete,
})
}
className="relative p-3 border border-gray-900 hover:bg-blue-gray-scale-100 active:bg-blue-gary-scale-100 disabled:bg-gray-scale-50 disabled:border-gray-800 disabled:text-blue-gray-600"
>
<Icon
className="absolute top-2 left-2"
icon="devicon:google"
fontSize={34}
/>
<span>구글로 로그인</span>
</button>
</div>
);
}
188 changes: 188 additions & 0 deletions client/src/app/login/components/LoginForm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
'use client';

import { useState, useCallback } from 'react';
import { useRouter } from 'next/navigation';

//redux
import { useDispatch, useSelector } from 'react-redux';
import { logIn } from '@/lib/redux/features/auth/authSlice';

//components
import { Tooltip } from '@/components';

//hooks
import useModal from '@/hooks/modal/useModal';

//icons
import { EyeOnIcon, EyeOffIcon } from '@/assets/Icon';

//formik
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';

//services
import { userManager } from '@/service/user';

//constant
import {
initialLoginValue,
errorMessage,
errorCodeToMessage,
ModalType,
} from '@/constants/constant';

//types
import { IReduxState } from '@/types/redux/IReduxState';

const ValidationSchema = Yup.object().shape({
userId: Yup.string().required(errorMessage.blankID),
password: Yup.string().required(errorMessage.blankPassword),
});

interface ILogin {
userId: string;
password: string;
}

export default function LoginForm() {
const [viewPassword, setViewPassword] = useState(false);

const dispatch = useDispatch();

const router = useRouter();

const modalState = useSelector((state: IReduxState) => state.modal);
const { openModal } = useModal();

const handleSubmit = useCallback(
async (sendData: ILogin, setSubmitting: (value: boolean) => void) => {
if (modalState.type === ModalType.CLOSE) {
setSubmitting(true);
try {
const userData = await userManager.loginUser({ data: sendData });
dispatch(logIn(userData));
router.replace(window.sessionStorage.getItem('previousURL') || '/');
} catch (error) {
if (error instanceof Error) {
type Code = 'USER_NOT_EXIST' | 'INVALID_PASSWORD';
openModal({
type: ModalType.ERROR,
message:
errorCodeToMessage[error.message as Code] || errorMessage.error,
});
} else {
openModal({
type: ModalType.ERROR,
message: errorMessage.error,
});
}
} finally {
setSubmitting(false);
}
}
},
[modalState]
);
return (
<Formik
initialValues={initialLoginValue}
validationSchema={ValidationSchema}
onSubmit={(data, { setSubmitting }) => handleSubmit(data, setSubmitting)}
>
{({ errors, touched, isSubmitting }) => (
<Form>
<div className="flex flex-col gap-5">
<div className="relative flex flex-col gap-3.5">
<label className="text-body3" htmlFor="user-id-input">
아이디
</label>
<Field
type="text"
name="userId"
id="user-id-input"
placeholder="아이디를 입력하세요."
className={`${
touched.userId && errors.userId && 'border-red-scale-600'
} border border-blue-gray-scale-50 py-[7px] px-4 text-body3 text-gray-scale-600 focus:outline-none`}
/>
<ErrorMessage
name="userId"
component="span"
className="absolute -bottom-4 left-2 text-caption1 text-red-scale-600"
/>
</div>
<div className="relative flex flex-col gap-3.5">
<label className="text-body3" htmlFor="password-input">
비밀번호
</label>
<div className="relative flex">
<Field
type={viewPassword ? 'text' : 'password'}
name="password"
id="password-input"
placeholder="비밀번호를 입력하세요."
autoComplete="on"
className={`${
touched.password &&
errors.password &&
'border-red-scale-600'
} flex-grow border border-blue-gray-scale-50 py-[7px] px-4 text-body3 text-gray-scale-600 focus:outline-none`}
/>
<div
className="absolute right-2 top-2 hover:cursor-pointer"
onClick={() => setViewPassword((prev) => !prev)}
>
{viewPassword ? <EyeOnIcon /> : <EyeOffIcon />}
</div>
</div>
<ErrorMessage
name="password"
component="span"
className="absolute -bottom-4 left-2 text-caption1 text-red-scale-600"
/>
</div>
</div>
<div className="flex justify-between my-6">
<Tooltip tooltipText="현재 로그인 유지만 지원합니다">
<div className="flex items-center gap-2">
<input
type="checkbox"
id="keep-logged-check"
className="border appearance-none size-[18px] border-gray-scale-500 checked:bg-black checked:bg-[url('/images/check.png')] checked:bg-no-repeat checked:bg-center hover:cursor-not-allowed"
checked={true}
disabled={true}
/>
<label
className="text-body3 text-gray-scale-600 hover:cursor-not-allowed"
htmlFor="keep-logged-check"
>
로그인 상태유지
</label>
</div>
</Tooltip>
<span
className="underline text-body3 text-blue-gray-scale-500 underline-offset-3 hover:cursor-pointer"
onClick={() =>
openModal({
type: ModalType.ERROR,
message: errorMessage.notComplete,
})
}
>
비밀번호 찾기
</span>
</div>
<div className="grid pt-2 pb-6 text-center">
<button
className="p-3 text-black bg-yellow-scale-400 hover:bg-yellow-scale-500 active:bg-yellow-scale-500 disabled:bg-yellow-scale-100 disabled:text-gray-scale-700"
type="submit"
disabled={isSubmitting}
>
<span className="font-bold text-body2">로그인</span>
</button>
</div>
</Form>
)}
</Formik>
);
}
10 changes: 0 additions & 10 deletions client/src/app/login/layout.tsx

This file was deleted.

Loading

0 comments on commit df090f9

Please sign in to comment.