diff --git a/src/common.module.scss b/src/common.module.scss index d9e3ab52..5e3edb78 100644 --- a/src/common.module.scss +++ b/src/common.module.scss @@ -26,12 +26,14 @@ body { font-family: Helvetica Neue, Helvetica, Arial, sans-serif; margin: 0; padding: 0; - background-color: #F5F5DD; + background-color: #FFFFFF; min-height: 1080px; /* 최소 높이 설정 */ + } -main { +body main { min-height: 1080px; /* 최소 높이 설정 */ + padding-top: 60px; /* 헤더 높이만큼 패딩을 추가 */ } a { diff --git a/src/components/admin/approval/ApprovalTable.js b/src/components/admin/approval/ApprovalTable.js index 68e19065..31e4b95f 100644 --- a/src/components/admin/approval/ApprovalTable.js +++ b/src/components/admin/approval/ApprovalTable.js @@ -14,6 +14,8 @@ import ApprovalButtons from "./ApprovalButton"; import TansPagination from "./TansPagination"; import TansTable from "./TansTable"; import ApprovalSummary from "./ApprovalSummary"; +import {checkAuthToken, getRefreshToken, getToken, getUserRole} from "../../../utils/authUtil"; +import {useNavigate} from "react-router-dom"; const ApprovalTable = () => { const [data, setData] = useState([]); @@ -23,6 +25,7 @@ const ApprovalTable = () => { const [startDate, setStartDate] = useState(new Date('2024-07-01')); const [endDate, setEndDate] = useState(new Date()); const [stats, setStats] = useState({}); + const navigate = useNavigate(); const table = useReactTable({ data, @@ -37,6 +40,7 @@ const ApprovalTable = () => { rowSelection, }, }); + const fetchApprovals = async () => { console.log('fetchApprovals 실행중!') @@ -46,6 +50,9 @@ const ApprovalTable = () => { // const token = localStorage.getItem('token'); // const refreshToken = localStorage.getItem('refreshToken'); + let userRole = getUserRole(); + console.log("userRole :",userRole); + const res = await fetch( `/admin/approve?start=${startISO}&end=${endISO}`, { @@ -53,8 +60,8 @@ const ApprovalTable = () => { headers: { 'Content-Type' : 'application/json', 'Cache-Control': 'no-cache', - // 'Authorization' : 'Bearer ' + getUserToken(), - // 'refreshToken': refreshToken, + // 'Authorization' : 'Bearer ' + getToken(), + // 'refreshToken': getRefreshToken(), }, }); if(!res.ok) { @@ -86,6 +93,24 @@ const ApprovalTable = () => { fetchApprovals(); }, [startDate, endDate]); + // useEffect 훅 사용하여 admin이 아닐 경우 접근 제한 + useEffect(() => { + debugger + const userInfo = checkAuthToken(navigate); + + if (userInfo) { + const requiredRole = 'admin'; // 단일 역할을 설정 + const userRole = getUserRole(); // 사용자 역할 가져오기 + + if (userRole !== requiredRole) { // 문자열 비교 + alert('접근 권한이 없습니다.'); + navigate('/main'); + return; + } + } + }, + []); + return (
diff --git a/src/components/auth/LoginForm.js b/src/components/auth/LoginForm.js index 155ed0f2..6c8551ae 100644 --- a/src/components/auth/LoginForm.js +++ b/src/components/auth/LoginForm.js @@ -28,6 +28,27 @@ const LoginForm = ({ userType, onVerificationSent }) => { return emailRegex.test(email); }; + //admin : customer 테이블에 저장되지만, role은 admin으로 저장됨 + const checkAdminDupId = async (email) => { + try { + const response = await fetch(`/customer/check?email=${email}`); + const result = await response.json(); + if (result) { + console.log(`입력하신 이메일 [ ${email} ] 은 customer 회원입니다.`); + setIsExistingUser(true); + return true; + } else { + console.error(`입력하신 이메일 [ ${email} ] 은 customer 회원이 아닙니다.`); + setIsExistingUser(false); + return false; + } + } catch (error) { + console.error('Error:', error); + return false; + } + } + + //customer const checkCustomerDupId = async (email) => { try { const response = await fetch(`/customer/check?email=${email}`); @@ -47,6 +68,7 @@ const LoginForm = ({ userType, onVerificationSent }) => { } } + //store const checkStoreDupId = async (email) => { try { const response = await fetch(`/store/check?email=${email}`); @@ -73,6 +95,8 @@ const LoginForm = ({ userType, onVerificationSent }) => { return await checkCustomerDupId(email); case 'store': return await checkStoreDupId(email); + case 'admin': + return await checkAdminDupId(email); default: return false; } diff --git a/src/components/auth/SignUpForm.js b/src/components/auth/SignUpForm.js index 950ba284..7f6b5392 100644 --- a/src/components/auth/SignUpForm.js +++ b/src/components/auth/SignUpForm.js @@ -31,6 +31,26 @@ const SignUpForm = ({ userType, onVerificationSent }) => { return emailRegex.test(email); }; + //admin : customer 테이블에 저장되지만, role은 admin으로 저장됨 + const checkAdminDupId = async (email) => { + try { + const response = await fetch(`/email/check?email=${email}`); + const result = await response.json(); + if (!result) { + console.log(`입력하신 이메일 [ ${email} ] 은 admin 회원이 아닙니다.`); + setIsExistingUser(false); + return true; + } else { + console.error(`입력하신 이메일 [ ${email} ] 은 admin 회원입니다.`); + setIsExistingUser(true); + return false; + } + } catch (error) { + console.error('Error:', error); + return false; + } + }; + //customer // 새로운 아이디 -> 중복검사 후 no -> 회원가입하기로 유도 const checkCustomerDupId = async (email) => { @@ -79,6 +99,8 @@ const checkDupId = async (email) => { return await checkCustomerDupId(email); case 'store': return await checkStoreDupId(email); + case 'admin' : + return await checkAdminDupId(email); default: return false; } @@ -99,7 +121,7 @@ const sendVerificationLinkForSignUp = async (email) => { }), }); if (response.ok) { - console.log('이메일이 성공적으로 전달되었습니다.',userType,email); + console.log('이메일이 성공적으로 전달되었습니다. usertype, email 확인하기 : ',userType,email); return true; } else { console.error('Failed to send verification link'); diff --git a/src/components/header/MyInfo.js b/src/components/header/MyInfo.js index 4918898c..093c6896 100644 --- a/src/components/header/MyInfo.js +++ b/src/components/header/MyInfo.js @@ -31,6 +31,8 @@ const MyInfo = () => { localStorage.setItem('userImage', data.productImg); } else if (getUserRole() === 'customer') { localStorage.setItem('userImage', data.profileImage); + } else if (getUserRole() === 'admin') { + localStorage.setItem('userImage', data.profileImage); } // 닉네임이 null 일 경우 저장하지 않음 @@ -59,8 +61,12 @@ const MyInfo = () => { } return ( + <> + {/*알림창*/} + +
- + {/*안녕하세요 {getSubName() ? getSubName() : userInfo.email}님!*/} @@ -86,9 +92,22 @@ const MyInfo = () => { onClick={() => handleIconClick("/customer")} /> + ) : getUserRole() === 'admin' ? ( + <> + {/* Admin 아이콘과 프로필 이미지 */} + + Customer Profile handleIconClick("/customer")} + /> + ADMIN + ) : null}
+ ); }; diff --git a/src/components/header/MyInfo.module.scss b/src/components/header/MyInfo.module.scss index 99f4eb40..c6236c2c 100644 --- a/src/components/header/MyInfo.module.scss +++ b/src/components/header/MyInfo.module.scss @@ -7,11 +7,6 @@ margin-left: 60px; } -.myInfoContainer { - display: flex; - align-items: center; -} - .myIconContainer { margin-left: auto; /* 오른쪽 끝으로 이동 */ display: flex; @@ -27,3 +22,20 @@ cursor: pointer; border: #03684e solid; } + +.admin { + position: absolute; + top: 55px; + font-size: 12px; +} + +@media (max-width: 400px) and (max-height: 844px) { + .myIconContainer { + display: none; + } + + .notificationIcon { + position: absolute; + left: 40px + } +} diff --git a/src/components/socket/Notification.module.scss b/src/components/socket/Notification.module.scss index 2caab420..e51a52aa 100644 --- a/src/components/socket/Notification.module.scss +++ b/src/components/socket/Notification.module.scss @@ -68,4 +68,13 @@ 50% { opacity: 0; } -} \ No newline at end of file +} + +// header 알림아이콘 반응형 크기 +@media (max-width: 400px) { + + .notify-icon { + position: absolute; + right: 10px; + } +} diff --git a/src/layout/Footer.module.scss b/src/layout/Footer.module.scss index 1a379fe1..ef929b2f 100644 --- a/src/layout/Footer.module.scss +++ b/src/layout/Footer.module.scss @@ -71,10 +71,10 @@ } .copyRight { - font-size: 24px; + font-size: 22px; position: absolute; - bottom: -35px; right: 100px; + bottom: 1px; } .footerLinks { @@ -116,7 +116,13 @@ font-size: 280px; width: 100%; max-width: 1920px; - margin-bottom: -80px; + margin-bottom: -40px; letter-spacing: 15px; text-align: center; +} + +@media (max-width: 400px) and (max-height: 844px) { + .footer { + display: none; + } } \ No newline at end of file diff --git a/src/layout/Header.js b/src/layout/Header.js index bba62144..fd72c041 100644 --- a/src/layout/Header.js +++ b/src/layout/Header.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import {getToken, getRefreshToken, extractArea} from '../utils/authUtil'; +import {Link, useNavigate} from 'react-router-dom'; +import {getToken, getRefreshToken, extractArea, getUserRole} from '../utils/authUtil'; import styles from './Header.module.scss'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import SideBarBtn from "../components/store/mypage-edit/SideBarBtn"; @@ -10,6 +10,9 @@ import MyInfo from "../components/header/MyInfo"; import {getCurrentLocation, initializeNaverMapsForHeader, reverseGeocode} from "../utils/locationUtil"; import SidebarModal from "../components/header/SidebarModal"; import SearchInput from "../components/search/SearchInput"; +import SideBar from "../components/store/mypage-edit/SideBar"; +import Edit from "../components/store/mypage-edit/Edit"; +import Notification from "../components/socket/Notification"; // 아이콘을 라이브러리에 추가 library.add(faMagnifyingGlass); @@ -73,54 +76,62 @@ const Header = () => { let userArea = extractArea(); + // 햄버거 버튼 기존 기능 되도록 수정 + const showHandler = () => { + setShow(prev => !prev); + } + return (
- {/* 햄버거 버튼 */} - {width > 400 && } - - {/* 로고 */} -
- - {/* 현재 위치 */} -
-
{userArea}
-
-
Now
-
- - - {/* 상점 검색 칸 */} - {/*로그인을 하지 않아도 검색칸은 보이되, 로그인이 필요한 서비스 안내하기*/} - { - getToken() && -
- - + {/* 햄버거 버튼 */} + {width <= 400 && } +
+ +
+ + + {/* 로고 */} + + + {/* 현재 위치 */} +
+
{userArea}
+
+
Now
+
+ + + {/* 상점 검색 칸 */} + {/*로그인을 하지 않으면 검색창이 사라짐*/} + { + getToken() && +
+ + +
+ } + + {/*리뷰 커뮤니티 메인*/} +
+ + {/* 로그인 및 회원가입 버튼 */} +
+ {isAuthenticated ? ( + + ) : ( + <> + +
+ + + )}
- } - - {/*리뷰 커뮤니티 메인*/} -
- - - {/* 로그인 및 회원가입 버튼 */} -
- {isAuthenticated ? ( - - ) : ( - <> - -
- - - )} -
- {/* 모달 */} - {modalVisible && } -
- ); + {/* 모달 */} + {modalVisible && } + +); } export default Header; diff --git a/src/layout/Header.module.scss b/src/layout/Header.module.scss index eb407e3b..ea2f53a7 100644 --- a/src/layout/Header.module.scss +++ b/src/layout/Header.module.scss @@ -19,6 +19,11 @@ button { max-width: 1920px; /* 최대 너비를 1920px로 제한 */ margin: 0 auto; /* 중앙 정렬 */ background-color: #F5F5DD; + + position: fixed; /* 화면에 고정 */ + top: 0; /* 화면 상단에 위치 */ + left: 0; /* 왼쪽에 고정 */ + z-index: 1000; /* 다른 요소 위에 위치 */ } /* 로고 버튼 */ @@ -29,6 +34,7 @@ button { background-size: contain; background-repeat: no-repeat; margin-left: 70px; + cursor: pointer; } /* 현재 위치 버튼 스타일 */ @@ -150,3 +156,102 @@ button { margin-top: 5px; margin-left: 10px; } + +// 햄버거 버튼 반응형 기능 안됨으로 인한 css 추가 +.container { + width: 80%; + margin: 20px auto 0; + border: 1px solid #ccc; + border-radius: 15px; + display: flex; + background: white; +} + +@media (max-width: 400px) { + .container { + width: 100%; + border: none; + margin-top: 0; + } +} + +// 반응형 헤더 +@media (max-width: 400px) { + .header { + flex-direction: row; /* 요소들을 가로로 정렬 */ + align-items: center; /* 세로 가운데 정렬 */ + padding: 10px; + height: 60px; + } + + .logoBtn { + position: absolute; + height: 40px; + width: 40px; + margin-left: 60px; + } + + .locationPinIcon { + //position: absolute; + //height: 32px; /* 크기를 줄이지 않음 */ + //width: 32px; + //margin-left: 10px; + display: none; + } + + .selectedAreaCategoryBtn { + position: absolute; + height: 15px; + width: 15px; + margin-left: 5px; + margin-top: 0; + left: 200px; + } + + // 각종 기능은 드롭다운모달로 볼 수있게 설정? + .reviewMainIcon { + //position: absolute; + //right: 5px; + //height: 40px; /* 크기를 줄이지 않음 */ + //width: 40px; + //margin-left: 10px; + display: none; + } + + //반응형일시 현재위치 렌더링 안함, 드롭다운으로 볼 수 있게 설정해보기. + .areaName { + display: none; + } + + .selectedAreaCategory { + font-size: 16px; + margin-left: 5px; + margin-bottom: 0; /* 아래쪽 여백 제거 */ + } + + .searchStoreSection { + width: 120px; + padding: 0 10px; + background-color: rgba(6, 101, 78, 0.5); /* 투명도 증가 */ + margin-left: 0; /* or 조정된 값으로 변경 */ + } + + .loginBtnSection { + display: flex; + flex-direction: row; /* 버튼들을 가로로 정렬 */ + gap: 5px; + margin-left: auto; /* 오른쪽으로 밀기 */ + margin-top: 0; + } + + .signInBtn, .signUpBtn { + font-size: 16px; + margin: 0; + } + + .dot { + margin-left: 0; + margin-top: 0; + display: none; /* 점 제거 */ + } +} \ No newline at end of file diff --git a/src/pages/auth/LoginPage.js b/src/pages/auth/LoginPage.js index 35ea7dd9..675ae2ea 100644 --- a/src/pages/auth/LoginPage.js +++ b/src/pages/auth/LoginPage.js @@ -28,32 +28,40 @@ const LoginPage = () => {
{!verificationSent && ( // 이메일 발송 후 사용자 유형 버튼 숨기기
- - + + +
)} - -
+ onResendEmail={handleResendEmail} + onVerificationSent={handleVerificationSent} + verificationSent={verificationSent} // verificationSent 상태 전달 + /> +
sign up 🌱 diff --git a/src/pages/auth/SignUpPage.js b/src/pages/auth/SignUpPage.js index fa6a4e2c..29fa4757 100644 --- a/src/pages/auth/SignUpPage.js +++ b/src/pages/auth/SignUpPage.js @@ -12,10 +12,12 @@ const SignUpPage = () => { setUserType(type); }; - const handleSignUp = (email, password) => { - console.log('User Sign Up:', email, password); + const handleSignUp = (email, userType) => { + console.log('User Sign Up:', email, userType); + // 여기서 userType을 백엔드에 전달하여 처리 }; + const handleResendEmail = () => { console.log('이메일 재전송 버튼 누름 !'); }; @@ -48,6 +50,14 @@ const SignUpPage = () => { > Store +
)} { const userInfo = await checkAuthToken(navigate); if (userInfo) { - const requiredRole = 'customer'; // 필요한 role 작성 필요 - if (userInfo.userType !== requiredRole) { + const requiredRoles = ['customer', 'admin']; // 필요한 역할 목록 + if (!requiredRoles.includes(userInfo.userType)) { alert('접근 권한이 없습니다.'); navigate('/main'); return; diff --git a/src/utils/authUtil.js b/src/utils/authUtil.js index ce98a841..6b002c84 100644 --- a/src/utils/authUtil.js +++ b/src/utils/authUtil.js @@ -136,6 +136,10 @@ export const checkAuthToken = async (navigate) => { const userType = tokenInfo.role; const email = tokenInfo.sub; + console.log(tokenInfo); + console.log(userType); + console.log(email); + return {token, refreshToken, userType, email}; }