Skip to content

Commit

Permalink
Merge pull request #156 from Step3-kakao-tech-campus/feat/#121
Browse files Browse the repository at this point in the history
로그인 정보 저장 방식 변경, 유저 정보 표시 컴포넌트 제작, 회원 정보 수정 페이지 제작
  • Loading branch information
JeonDoGyun authored Nov 5, 2023
2 parents fb80d35 + 412372a commit 95fc176
Show file tree
Hide file tree
Showing 21 changed files with 478 additions and 131 deletions.
Binary file removed public/assets/images/logo512.png
Binary file not shown.
Binary file added public/assets/images/shelterIcon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed public/assets/pet.png
Binary file not shown.
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import UrgentListPage from 'pages/profileList/urgentList/UrgentListPage';
import UpdatePage from 'pages/update/UpdatePage';
import HomePage from 'pages/home/HomePage';
import ValidateCheckLayout from 'layouts/ValidateCheckLayout';
import EditProfilePage from 'pages/editProfile/EditProfilePage';

const queryClient = new QueryClient({
defaultOptions: {
Expand All @@ -37,6 +38,7 @@ function App() {
<Route path="/register" element={<RegisterPage />} />
<Route path="/find-shelter" element={<MapPage />} />
<Route path="/pet-update/:id" element={<UpdatePage />} />
<Route path="/shelter/:id/edit" element={<EditProfilePage />} />
</ValidateCheckLayout>
<Routes>
<Route path="/login" element={<LoginPage />} />
Expand Down
8 changes: 3 additions & 5 deletions src/commons/InputGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import React from 'react';
import Container from 'commons/Container';
import Input from 'commons/Input';

export interface InputGroupProps {
id: string;
export interface InputGroupProps
extends React.HTMLAttributes<HTMLInputElement> {
name: string;
type: string;
placeholder: string;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
autocomplete?: string;
defaultValue?: string;
}

const InputGroup = ({
Expand Down
105 changes: 105 additions & 0 deletions src/commons/UserDropdownBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { useNavigate } from 'react-router-dom';
import { useState } from 'react';
import { getCookie, removeCookie, setCookie } from './cookie/cookie';

// 로그인 되었을 때 상태를 보여주는 SelectBox 제작
const UserDropdownBox = () => {
const token = getCookie('loginToken');
const options = ['My 정보 변경', 'My 보호소 이동', '로그아웃'];
const [isDropdownOpen, setIsDropdownOpen] = useState(false); // 드롭다운 메뉴 열림/닫힘 상태
const navigate = useNavigate();
const shelterId = getCookie('userAccountInfo');
const id = shelterId ? shelterId.split(' ')[1] : '';

const toggleDropdown = () => {
setIsDropdownOpen(!isDropdownOpen);
};

const removeToken = () => {
removeCookie('loginToken');
removeCookie('userAccountInfo');
};

const handleOptionClick = (option: string) => {
switch (option) {
case 'My 정보 변경':
navigate(`/shelter/${id}/edit`); // 아직 회원정보 수정 페이지 구현 안됨
break;
case 'My 보호소 이동':
navigate(`/shelter/${id}/1`);
break;
case '로그아웃':
removeToken();
setCookie('userAccountInfo', 'Not Login');
window.location.reload();
break;
default:
break;
}
setIsDropdownOpen(false);
};

if (!token) {
return (
<div className="flex gap-4 font-bold">
<button
className="border-2 box-content border-brand-color text-brand-color rounded-md py-1 px-4 transition duration-300 hover:bg-brand-color hover:text-white"
onClick={() => navigate('/login')}
>
로그인
</button>
<button
className="border-brand-color border-2 bg-brand-color text-white rounded-md py-1 px-4 transition duration-300 hover:bg-white hover:text-brand-color"
onClick={() => navigate('/signup')}
>
회원가입
</button>
</div>
);
}

return (
<div className="relative inline-block text-left">
<div>
<button
onClick={toggleDropdown}
type="button"
className="inline-flex justify-center w-full rounded-full border-2 border-gray-300 hover:border-gray-400 shadow-sm p-2 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100"
id="options-menu"
aria-haspopup="listbox"
aria-expanded="true"
>
<img
src="/assets/images/shelterIcon.png"
alt="유저 정보 아이콘"
className="w-8 h-8"
/>
</button>
</div>

{isDropdownOpen && (
<div className="origin-top-right absolute border-2 border-gray-300 hover:border-gray-400 right-0 w-48 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5">
<div
className="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu"
>
{options.map((option, index) => (
<div
key={index}
className="block px-4 py-2 text-sm font-bold text-gray-500 hover:bg-gray-100 hover:text-black cursor-pointer"
onClick={() => handleOptionClick(option)}
role="menuitem"
>
{option}
</div>
))}
</div>
</div>
)}
</div>
);
};

export default UserDropdownBox;
27 changes: 0 additions & 27 deletions src/commons/cookie/getUser.ts

This file was deleted.

49 changes: 49 additions & 0 deletions src/commons/modals/LoginGuideModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { setCookie } from 'commons/cookie/cookie';
import { useNavigate } from 'react-router-dom';
import { useRecoilState, useRecoilValue } from 'recoil';
import { tokenCheckState } from 'recoil/shelterState';

const LoginGuideModal = () => {
const navigate = useNavigate();
const [isLogined, setIsLogined] = useRecoilState(tokenCheckState); // default : true

if (isLogined) {
return null;
}

return (
<dialog
className="fixed z-50 flex justify-center w-full h-[20vh] opacity-80 hover:opacity-100 bottom-2 rounded-lg border-2 border-gray-300 text-black"
open={isLogined}
>
<div className="modal-content w-[600px] flex flex-col gap-4 justify-center">
<div className="font-bold text-center text-lg">
<div>로그인이 만료되었습니다.</div>
<div>다시 로그인 하시겠습니까?</div>
</div>
<div className="flex justify-evenly font-bold ">
<button
className="border-brand-color text-brand-color border-2 rounded-md px-4 py-1 transition duration-300 hover:bg-brand-color hover:text-white "
onClick={() => {
navigate('/login');
setIsLogined(true);
}}
>
로그인 하기
</button>
<button
className="bg-brand-color text-white rounded-md px-4 py-1 transition duration-300 hover:bg-white hover:text-brand-color"
onClick={() => {
setCookie('userAccountInfo', 'Not Login');
setIsLogined(true);
}}
>
로그아웃 유지하기
</button>
</div>
</div>
</dialog>
);
};

export default LoginGuideModal;
14 changes: 5 additions & 9 deletions src/commons/modals/RegisterModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface RegisterModalProps {
isError: boolean;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data: any;
errorText: string;
modalString: string;
}

Expand All @@ -22,9 +23,9 @@ const RegisterModal = ({
isSuccess,
isError,
data,
errorText,
modalString,
}: RegisterModalProps) => {
console.log(data);
if (isError || data?.success === false) {
return (
<div
Expand All @@ -37,10 +38,7 @@ const RegisterModal = ({
X
</button>
</div>
<span className="text-2xl text-brand-color">
{modalString}에 실패했습니다
</span>
<div className="text-red-600">{data?.error?.message}</div>
<span className="text-2xl text-brand-color">{errorText}</span>
<div className="flex w-2/3 justify-between mt-8">
<button
className="text-brand-color rounded-md font-bold border border-brand-color w-16 py-2"
Expand Down Expand Up @@ -71,9 +69,7 @@ const RegisterModal = ({
X
</button>
</div>
<span className="text-2xl text-brand-color">
{modalString}중입니다...
</span>
<span className="text-2xl text-brand-color">등록중입니다...</span>
<img
src="/assets/images/hourglass.png"
alt="hourglass"
Expand Down Expand Up @@ -149,7 +145,7 @@ const RegisterModal = ({
</div>
);
}
return <div>문제가 생겼습니다</div>;
return <div>{errorText}</div>;
};

export default RegisterModal;
17 changes: 3 additions & 14 deletions src/layouts/VGNB.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import LogoButton from 'commons/LogoButton';
import { getLoginState, removeToken } from 'commons/cookie/getUser';
import UserSelectBox from 'commons/UserDropdownBox';
import { Link } from 'react-router-dom';

export interface VGNBProps {
Expand Down Expand Up @@ -36,19 +36,8 @@ const VGNB = (props: VGNBProps) => {
<div className="bg-white text-center z-10 flex w-content justify-center border-b">
<div className="lg:hidden w-full flex flex-col items-center text-xl gap-8">
<div className="flex justify-center w-full gap-2">
<Link to="/login">
<button
className="border border-2 box-border border-brand-color text-brand-color rounded-md w-28 py-1"
onClick={removeToken}
>
{getLoginState()}
</button>
</Link>
<Link to="/signup">
<button className="border box-border border-brand-color border-2 bg-brand-color text-white rounded-md w-28 py-1">
회원가입
</button>
</Link>
{/* 여기서 변경 */}
<UserSelectBox />
</div>

<ol className="flex flex-col justify-center w-full gap-4">
Expand Down
20 changes: 3 additions & 17 deletions src/layouts/VLargeGNB.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Link } from 'react-router-dom';
import LogoButton from 'commons/LogoButton';
import { getLoginState, removeToken } from 'commons/cookie/getUser';
import UserSelectBox from 'commons/UserDropdownBox';

export interface VLargeGNBProps {
handleCategoryButtonClick: () => void;
Expand Down Expand Up @@ -59,22 +59,8 @@ const VLargeGNB = (props: VLargeGNBProps) => {
<Link to="/register">등록하기</Link>
</li>
</ol>

<div className="flex gap-4 font-bold">
<Link to="/login">
<button
className="border border-2 box-content border-brand-color text-brand-color rounded-md py-1 px-4 transition duration-300 hover:bg-brand-color hover:text-white"
onClick={removeToken}
>
{getLoginState()}
</button>
</Link>
<Link to="/signup">
<button className="border border-brand-color border-2 bg-brand-color text-white rounded-md py-1 px-4 transition duration-300 hover:bg-white hover:text-brand-color">
회원가입
</button>
</Link>
</div>
{/* 여기서 변경 */}
<UserSelectBox />
</div>
</div>
);
Expand Down
40 changes: 32 additions & 8 deletions src/layouts/ValidateCheckLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
import { validateExpiredToken } from 'commons/cookie/getUser';
import { getCookie } from 'commons/cookie/cookie';
import LoginGuideModal from 'commons/modals/LoginGuideModal';
import React, { useEffect } from 'react';
import { Routes } from 'react-router-dom';
import { Route, Routes } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { tokenCheckState } from 'recoil/shelterState';

interface LayoutProps {
children: React.ReactNode;
}

const ValidateCheckLayout: React.FC<LayoutProps> = ({ children }) => {
useEffect(() => {
validateExpiredToken();
const intervalId = setInterval(validateExpiredToken, 60000 * 30); // 30분에 한 번
const setIsLogined = useSetRecoilState(tokenCheckState);
const loginToken = getCookie('loginToken');
const userAccount = getCookie('userAccountInfo');

return () => clearInterval(intervalId);
}, []);
useEffect(() => {
if (!loginToken && !userAccount) {
// loginToken이 없으면 모달 열기
setIsLogined(false);
}
console.log('token 로직 동작');
}, [loginToken, userAccount]);

return <Routes>{children}</Routes>;
return (
<div>
<Routes>
<Route path="/" element={<LoginGuideModal />} />;
<Route path="/pet/:id" element={<LoginGuideModal />} />;
<Route path="/profile" element={<LoginGuideModal />} />;
<Route path="/shelter/:id/:page" element={<LoginGuideModal />} />;
<Route path="/profile/urgent/:page" element={<LoginGuideModal />} />;
<Route path="/profile/new/:page" element={<LoginGuideModal />} />;
<Route path="/register" element={<LoginGuideModal />} />;
<Route path="/find-shelter" element={<LoginGuideModal />} />;
<Route path="/pet-update/:id" element={<LoginGuideModal />} />;
</Routes>
{/* <LoginGuideModal /> */}
<Routes>{children}</Routes>
</div>
);
};

export default ValidateCheckLayout;
2 changes: 1 addition & 1 deletion src/pages/detailPet/DetailPetData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const DetailPetData = () => {
},
});
if (isLoading) return <div>로딩중</div>;
const labels = ['귀여움', '침착함', '유머감각', '외모', '의젓함'];
const labels = ['영리함', '친화력', '운동신경', '적응력', '활발함'];

const radarChartProps: RadarChartProps = {
setCanvas,
Expand Down
Loading

0 comments on commit 95fc176

Please sign in to comment.