Skip to content

Commit

Permalink
Merge pull request #132 from Step3-kakao-tech-campus/feat/#119
Browse files Browse the repository at this point in the history
feat: 로그인, 회원가입 validation 방식 수정, 로더 추가
  • Loading branch information
JeonDoGyun authored Oct 22, 2023
2 parents 0525dbd + c133490 commit ada6535
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 93 deletions.
50 changes: 49 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
"react-dom": "^18.2.0",
"react-router-dom": "^6.16.0",
"react-scripts": "5.0.1",
"react-spinners": "^0.13.8",
"recoil": "^0.7.7",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
"web-vitals": "^2.1.4",
"yup": "^1.3.2"
},
"scripts": {
"start": "react-scripts start",
Expand Down
4 changes: 4 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
name="description"
content="Web site created using create-react-app"
/>
<script
type="text/javascript"
src="//dapi.kakao.com/v2/maps/sdk.js?appkey=%REACT_APP_KAKAO_KEY%"
></script>
<title>애니모리</title>
</head>
<body>
Expand Down
71 changes: 35 additions & 36 deletions src/pages/login/LoginInputForm.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,23 @@
import React, { useState } from 'react';
import { useRecoilState } from 'recoil';
import { useNavigate } from 'react-router-dom';
import { shelterLoginState } from 'recoil/shelterState';
import { ShelterLoginType, shelterLoginState } from 'recoil/shelterState';
import * as Yup from 'yup';
import VLoginInputForm from './VLoginInputForm';
import { setCookie } from '../../commons/cookie/cookie';

const LoginInputForm = () => {
const [userInfo, setUserInfo] = useRecoilState(shelterLoginState);
const [isEmailEmpty, setIsEmailEmpty] = useState(false);
const [isPasswordEmpty, setIsPasswordEmpty] = useState(false);
const [errorText, setErrorText] = useState('이메일을 입력해주세요');

const [errors, setErrors] = useState<Partial<ShelterLoginType>>({});
const [isLoading, setIsLoading] = useState<boolean>(false);
const navigate = useNavigate();

const emailValidate = (text: string) => {
const emailReg = /^[\w.-]+@[\w.-]+\.\w+$/g; // email형식
if (!emailReg.test(text)) {
setErrorText('이메일 형식에 맞게 입력해주세요');
setIsEmailEmpty(true);
return;
}
setErrorText('');
setIsEmailEmpty(false);
};

const checkEmpty = () => {
if (!userInfo.email) {
setErrorText('이메일을 입력해주세요');
setIsEmailEmpty(true);
}
if (!userInfo.password) {
setIsPasswordEmpty(true);
}
};
const validationSchema = Yup.object().shape({
email: Yup.string()
.email('이메일 형식에 맞게 입력해주세요.')
.required('이메일을 입력해주세요.'),
password: Yup.string().required('비밀번호를 입력해주세요.'),
});

const userfetch = () => {
fetch(`${process.env.REACT_APP_URI}/account/login`, {
Expand All @@ -52,32 +37,48 @@ const LoginInputForm = () => {
const slicedToken = jwtToken.split(' ')[1];
setCookie('loginToken', slicedToken);
} else {
console.log('Token이 Null');
console.log('로그인 실패로 token이 Null');
}

return res.json();
})

.then((data) => {
if (data.success) {
navigate('/');
} else {
// 형식은 맞지만 입력된 값이 가입되지 않은 계정일 때
alert(data.error.message);
}
setIsLoading(false);
});
};

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// email, password 비었는지 확인
checkEmpty();
// email, password 보내기
userfetch();
validationSchema
.validate(userInfo, { abortEarly: false })
.then(() => {
// email, password 보내기
setIsLoading(true);
userfetch();
setErrors({});
})
.catch((err) => {
const newErrors: Partial<ShelterLoginType> = {};
err.inner.forEach(
(er: { path: string; message: string | undefined }) => {
newErrors[er.path as keyof ShelterLoginType] = er.message;
},
);
setErrors(newErrors);
});
};

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const target = event.target as HTMLInputElement;
if (target.id === 'id') {
setUserInfo((prev) => ({ ...prev, email: target.value }));
emailValidate(target.value);
setIsEmailEmpty(true);
} else if (target.id === 'password') {
setUserInfo((prev) => ({ ...prev, password: target.value }));
}
Expand All @@ -86,12 +87,10 @@ const LoginInputForm = () => {
const LoginInputFormProps = {
handleChange,
handleSubmit,
isEmailEmpty,
isPasswordEmpty,
errorText,
errors,
isLoading,
};

// submit에 userInfo를 넣어주거나 button에서 보내도록 하는 것 필요!!!!
return <VLoginInputForm {...LoginInputFormProps} />;
};

Expand Down
33 changes: 18 additions & 15 deletions src/pages/login/VLoginInputForm.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
import InputGroup from 'commons/InputGroup';
import { ShelterLoginType } from 'recoil/shelterState';
import { ClipLoader } from 'react-spinners';

interface LoginInputFormProps {
errors: Partial<ShelterLoginType>;
isLoading: boolean;
handleChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
isEmailEmpty: boolean;
isPasswordEmpty: boolean;
errorText: string;
}

interface ErrorTextProps {
isEmpty: boolean;
text: string;
interface ValidationProps {
text?: string;
}

const ErrorText = ({ isEmpty, text }: ErrorTextProps) => {
return <div>{isEmpty && <span className="text-red-500">{text}</span>}</div>;
const ValidateText = ({ text }: ValidationProps) => {
return <div className="text-red-500">{text}</div>;
};

const VLoginInputForm = ({
errors,
isLoading,
handleChange,
handleSubmit,
isEmailEmpty,
isPasswordEmpty,
errorText,
}: LoginInputFormProps) => {
return (
<form
Expand All @@ -39,7 +38,7 @@ const VLoginInputForm = ({
}}
autocomplete="off"
/>
<ErrorText isEmpty={isEmailEmpty} text={errorText} />
<ValidateText text={errors.email} />

<InputGroup
id="password"
Expand All @@ -51,9 +50,13 @@ const VLoginInputForm = ({
}}
autocomplete="off"
/>
<ErrorText isEmpty={isPasswordEmpty} text="비밀번호를 입력해주세요." />
<button className="bg-brand-color text-white w-full rounded-md p-2">
로그인
<ValidateText text={errors.password} />
<button className="flex justify-center items-center bg-brand-color text-white w-full rounded-md p-2">
{isLoading ? (
<ClipLoader size={20} color="#fff" loading={isLoading} />
) : (
'로그인'
)}
</button>
</form>
);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/login/VLoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const VLoginPage = ({ redirectSignupPage }: Props) => {
backgroundPosition: 'center',
}}
>
<div className="absolute sm:top-[150px] md:top-[120px] lg:top-[100px]">
<div className="absolute top-[100px] lg:top-[90px]">
<LogoButton
imageClassName="sm:w-6 md:w-8 lg:w-12"
logoClassName="sm:text-2xl md:text-3xl lg:text-4xl"
Expand Down
4 changes: 3 additions & 1 deletion src/pages/map/MapPage.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import Map from 'pages/map/Map';
import GNB from 'layouts/GNB';
import TestMap from './TestMap';

const MapPage = () => {
return (
<div>
<GNB />
<Map />
{/* <Map /> */}
<TestMap />
</div>
);
};
Expand Down
42 changes: 42 additions & 0 deletions src/pages/map/TestMap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { useEffect, useState } from 'react';

const { kakao } = window;

// 지도 띄우기
const LoadMap = () => {
const [map, setMap] = useState(null);

// useEffect []로 script 태그 내용 넣어주는게 가장 간단한 방식 + API 문서대로 하기 쉬울듯
useEffect(() => {
const container = document.getElementById('map');
const options = {
// 중간 위치 -> 나중에 사용자 위치로 바꿔야 됨
center: new kakao.maps.LatLng(35.175483, 126.906988),
// 확대 수준
level: 4,
};
const kakaoMap = new kakao.maps.Map(container, options);
setMap(kakaoMap);
}, []);

return <div id="map" className="w-[500px] h-[400px] mx-2"></div>;
};

const TestMap = () => {
// const [data, isLoading, isSuccess] = useQuery(['shelter'], () => {
// fetch(`${process.env.REACT_APP_URI}/shelter/filter`, {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify(),
// }).then((res) => {
// return res.json();
// });
// });
const currentPosition = { lat: 35.1759293, lon: 126.9149701 };

return <LoadMap />;
};

export default TestMap;
Loading

0 comments on commit ada6535

Please sign in to comment.