diff --git a/package-lock.json b/package-lock.json index 1e61862..cfa0918 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,8 @@ "react-naver-maps": "^0.1.3", "react-router-dom": "^7.1.1", "react-table": "^7.8.0", - "styled-components": "^6.1.14" + "styled-components": "^6.1.14", + "zustand": "^5.0.3" }, "devDependencies": { "@eslint/js": "^9.17.0", @@ -4948,6 +4949,35 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.3.tgz", + "integrity": "sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 983e7dd..412d05b 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "react-naver-maps": "^0.1.3", "react-router-dom": "^7.1.1", "react-table": "^7.8.0", - "styled-components": "^6.1.14" + "styled-components": "^6.1.14", + "zustand": "^5.0.3" }, "devDependencies": { "@eslint/js": "^9.17.0", diff --git a/src/App.tsx b/src/App.tsx index ee6efed..0863e18 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import Router from './Router'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import GlobalStyle from './styles/globalStyle'; import './styles/font.css'; +import { NotificationProvider } from './components/NotificationProvider'; function App() { const queryClient = new QueryClient(); return ( @@ -14,6 +15,7 @@ function App() { + > ); } diff --git a/src/apis/slopeManage.tsx b/src/apis/slopeManage.tsx index bd2070d..dcc8703 100644 --- a/src/apis/slopeManage.tsx +++ b/src/apis/slopeManage.tsx @@ -21,7 +21,6 @@ export const slopeManageAPI = { }, }); console.log('엑셀 업로드', response); - alert(`${response.data.message}\n${response.data.count}건 추가되었습니다.`); return response.data; }, createSlope: async (newSlope: Slope) => { diff --git a/src/components/NotificationProvider.tsx b/src/components/NotificationProvider.tsx new file mode 100644 index 0000000..88a4066 --- /dev/null +++ b/src/components/NotificationProvider.tsx @@ -0,0 +1,41 @@ +import { useEffect } from 'react'; +import Snackbar from '@mui/material/Snackbar'; +import Alert from '@mui/material/Alert'; +import { useNotificationStore } from '../hooks/notificationStore'; + +export const NotificationProvider = () => { + const { isOpen, message, severity, autoHideDuration, hideNotification } = + useNotificationStore(); + + // 컴포넌트 언마운트 시 알림 초기화 (선택 사항) + useEffect(() => { + return () => { + if (isOpen) { + hideNotification(); + } + }; + }, []); + + const handleClose = ( + _event?: React.SyntheticEvent | Event, + reason?: string + ) => { + if (reason === 'clickaway') { + return; + } + hideNotification(); + }; + + return ( + + + {message} + + + ); +}; diff --git a/src/hooks/notificationStore.ts b/src/hooks/notificationStore.ts new file mode 100644 index 0000000..b88ecce --- /dev/null +++ b/src/hooks/notificationStore.ts @@ -0,0 +1,37 @@ +import { create } from 'zustand'; +import { AlertColor } from '@mui/material/Alert'; + +interface NotificationState { + isOpen: boolean; + message: string; + severity: AlertColor; + autoHideDuration: number | null; + showNotification: ( + message: string, + options?: { + severity?: AlertColor; + autoHideDuration?: number | null; + } + ) => void; + hideNotification: () => void; +} + +export const useNotificationStore = create((set) => ({ + isOpen: false, + message: '', + severity: 'info', + autoHideDuration: 4000, + + showNotification: (message, options = {}) => + set({ + isOpen: true, + message, + severity: options.severity || 'info', + autoHideDuration: + options.autoHideDuration !== undefined + ? options.autoHideDuration + : 4000, + }), + + hideNotification: () => set({ isOpen: false }), +})); diff --git a/src/pages/LoginPage/LoginPage.tsx b/src/pages/LoginPage/LoginPage.tsx index af3f5a0..c0e24d7 100644 --- a/src/pages/LoginPage/LoginPage.tsx +++ b/src/pages/LoginPage/LoginPage.tsx @@ -7,6 +7,7 @@ import Join from './components/Join'; const LoginPage = () => { const [isLogin, setIsLogin] = useState(true); + return ( @@ -30,7 +31,15 @@ const LoginPage = () => { - {isLogin ? : } + {isLogin ? ( + + ) : ( + { + setIsLogin(true); + }} + /> + )} ); @@ -44,6 +53,8 @@ const Background = styled.div` display: flex; justify-content: center; align-items: center; + overflow-x: hidden; + padding: 10px 0px; `; const LogoContainer = styled.img` width: 100%; diff --git a/src/pages/LoginPage/components/Join.tsx b/src/pages/LoginPage/components/Join.tsx index 842af09..3f3af72 100644 --- a/src/pages/LoginPage/components/Join.tsx +++ b/src/pages/LoginPage/components/Join.tsx @@ -5,8 +5,12 @@ import { authAPI, JoinFormType } from '../../../apis/Auth'; import styled from 'styled-components'; import PrivacyPolicyModal from '../../MapPage/components/map/PrivacyPolicyModal'; import TermsofUseModal from '../../MapPage/components/map/TermsofUseModal'; +import { useNotificationStore } from '../../../hooks/notificationStore'; -const Join = () => { +interface joinPropsType { + completeJoin: () => void; +} +const Join = ({ completeJoin }: joinPropsType) => { const [joinForm, setJoinForm] = useState({ name: '', phone: '', @@ -25,6 +29,10 @@ const Join = () => { const [agreementError, setAgreementError] = useState(false); const [isPrivacyPolicyOpen, setIsPrivacyPolicyOpen] = useState(false); const [isTermofUseOpen, setIsTermofUseOpen] = useState(false); + const showNotification = useNotificationStore( + (state) => state.showNotification + ); + const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target; @@ -39,7 +47,7 @@ const Join = () => { [name]: value, })); } - console.log(joinForm); + // console.log(joinForm); }; const handlePwCheckChange = (e: React.ChangeEvent) => { @@ -84,7 +92,7 @@ const Join = () => { const joinMutation = useMutation({ mutationFn: (data: JoinFormType) => authAPI.join(data), onSuccess: () => { - alert('회원가입이 완료되었습니다.'); + showNotification('회원가입 성공!', { severity: 'success' }); setJoinForm({ name: '', phone: '', @@ -98,9 +106,13 @@ const Join = () => { terms: false, privacy: false, }); + completeJoin(); }, - onError: (error) => { - alert('회원가입에 실패했습니다. 다시 시도해주세요.'); + onError: (error: any) => { + const errorMes = + error.response?.data?.message || + '회원가입에 실패했습니다. 다시 시도해주세요.'; + showNotification(errorMes, { severity: 'error', autoHideDuration: 6000 }); console.error('join Error:', error); }, }); @@ -116,7 +128,9 @@ const Join = () => { if (pwVerfiy && isFormFilled && isAgreementsChecked) { joinMutation.mutate(joinForm); } else { - alert('정보를 정확히 입력하고 필수 약관에 동의해주세요.'); + showNotification('정보를 정확히 입력하고 필수 약관에 동의해주세요.', { + severity: 'error', + }); } }; diff --git a/src/pages/LoginPage/components/Login.tsx b/src/pages/LoginPage/components/Login.tsx index ffc4dba..33d0e84 100644 --- a/src/pages/LoginPage/components/Login.tsx +++ b/src/pages/LoginPage/components/Login.tsx @@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom'; import { ErrorText, Input, InputWrapper, LoginButton } from './Style'; import { useMutation } from '@tanstack/react-query'; import { authAPI, LoginFormType } from '../../../apis/Auth'; +import { useNotificationStore } from '../../../hooks/notificationStore'; const Login = () => { const nav = useNavigate(); @@ -11,20 +12,29 @@ const Login = () => { password: '', }); const [isPhoneValid, setIsPhoneValid] = useState(true); - + const showNotification = useNotificationStore( + (state) => state.showNotification + ); const joinMutation = useMutation({ mutationFn: (data: LoginFormType) => authAPI.login(data), onSuccess: () => { - alert('로그인 성공'); + showNotification('로그인 성공!', { severity: 'success' }); setLoginForm({ phone: '', password: '', }); + nav('/manage/map'); }, - onError: (error) => { - alert('로그인에 실패했습니다. 다시 시도해주세요.'); - console.error('join Error:', error); + onError: (error: any) => { + const errorMes = + error.response?.data?.message || + '로그인에 실패했습니다. 다시 시도해주세요.'; + showNotification(errorMes, { + severity: 'error', + autoHideDuration: 6000, + }); + console.error('login Error:', error); }, }); @@ -35,7 +45,7 @@ const Login = () => { if (isFormFilled) { joinMutation.mutate(loginForm); } else { - alert('빈칸 없이 입력해주세요.'); + showNotification('빈칸 없이 입력해주세요', { severity: 'warning' }); } }; const handleChange = (e: React.ChangeEvent) => { @@ -58,34 +68,36 @@ const Login = () => { } }; return ( - - { - if (e.key === '-' || e.key === '+' || e.key === 'e') { - e.preventDefault(); - setIsPhoneValid(false); - setTimeout(() => setIsPhoneValid(true), 2000); - } - }} - /> - {!isPhoneValid && ( - "-"를 제외한 숫자만 입력해 주세요 - )} - - 로그인 - + <> + + { + if (e.key === '-' || e.key === '+' || e.key === 'e') { + e.preventDefault(); + setIsPhoneValid(false); + setTimeout(() => setIsPhoneValid(true), 2000); + } + }} + /> + {!isPhoneValid && ( + "-"를 제외한 숫자만 입력해 주세요 + )} + + 로그인 + + > ); }; diff --git a/src/pages/ManagePage/StepSlope/pages/SteepSlopeAdd.tsx b/src/pages/ManagePage/StepSlope/pages/SteepSlopeAdd.tsx index e0c649f..a6eb2df 100644 --- a/src/pages/ManagePage/StepSlope/pages/SteepSlopeAdd.tsx +++ b/src/pages/ManagePage/StepSlope/pages/SteepSlopeAdd.tsx @@ -5,6 +5,7 @@ import { slopeManageAPI } from '../../../../apis/slopeManage'; import AddSlope from '../components/AddSlopeContainer'; import { Slope } from '../../../../apis/slopeMap'; import CloudUploadRoundedIcon from '@mui/icons-material/CloudUploadRounded'; +import { useNotificationStore } from '../../../../hooks/notificationStore'; interface FileInputContainerProps { $isDragActive?: boolean; $hasFile?: boolean; @@ -17,7 +18,10 @@ const SteepSlopeAdd: React.FC = () => { const [fileName, setFileName] = useState(''); const [isUploading, setIsUploading] = useState(false); const fileInputRef = useRef(null); - + //전역 알림 + const showNotification = useNotificationStore( + (state) => state.showNotification + ); // 드래그 시작 핸들러 const handleDragStart = (event: DragEvent): void => { event.preventDefault(); @@ -86,8 +90,10 @@ const SteepSlopeAdd: React.FC = () => { console.log('파일 업로드 시작:', uploadedFile); const formData = new FormData(); formData.append('file', uploadedFile); - await slopeManageAPI.uploadExcelSlope(formData); - + const data = await slopeManageAPI.uploadExcelSlope(formData); + showNotification(`${data.message}\n${data.count}건 추가되었습니다.`, { + severity: 'success', + }); // 업로드 성공 후 상태 초기화 setUploadedFile(null); setFileName(''); @@ -95,6 +101,9 @@ const SteepSlopeAdd: React.FC = () => { fileInputRef.current.value = ''; } } catch (error) { + showNotification('파일 업로드 오류', { + severity: 'error', + }); console.error('파일 업로드 오류:', error); } finally { setIsUploading(false); diff --git a/src/pages/ManagePage/StepSlope/pages/SteepSlopeLookUp.tsx b/src/pages/ManagePage/StepSlope/pages/SteepSlopeLookUp.tsx index 12e59aa..e4a8441 100644 --- a/src/pages/ManagePage/StepSlope/pages/SteepSlopeLookUp.tsx +++ b/src/pages/ManagePage/StepSlope/pages/SteepSlopeLookUp.tsx @@ -16,7 +16,11 @@ import { import { useVirtualizer } from '@tanstack/react-virtual'; import { FilterFn } from '@tanstack/react-table'; import { rankItem } from '@tanstack/match-sorter-utils'; -import { useInfiniteQuery, useQueryClient } from '@tanstack/react-query'; +import { + useInfiniteQuery, + useMutation, + useQueryClient, +} from '@tanstack/react-query'; import { Slope } from '../../../../apis/slopeMap'; import styled from 'styled-components'; @@ -34,6 +38,7 @@ import CachedRoundedIcon from '@mui/icons-material/CachedRounded'; import TravelExploreRoundedIcon from '@mui/icons-material/TravelExploreRounded'; import DownloadRoundedIcon from '@mui/icons-material/DownloadRounded'; import SearchRoundedIcon from '@mui/icons-material/SearchRounded'; +import { useNotificationStore } from '../../../../hooks/notificationStore'; const FETCH_SIZE = 50; declare module '@tanstack/react-table' { @@ -96,7 +101,7 @@ const SteepSlopeLookUp = () => { initialPageParam: 0, }); - // //flatData를 통해 페이지 계산 + //flatData를 통해 페이지 계산 const flatData = useMemo(() => { return data?.pages.flatMap((page) => page.data) ?? []; }, [data]); @@ -530,16 +535,43 @@ const SteepSlopeLookUp = () => { } }; - const handleDownload = async () => { - try { - await slopeManageAPI.downloadExcel({ - searchQuery: searchQuery || undefined, - city: selectedRegion?.city, - county: selectedRegion?.county, + // 알림 함수 가져오기 + const showNotification = useNotificationStore( + (state) => state.showNotification + ); + + // 다운로드 mutation 설정 + const { mutate: downloadExcel, isPending: isDownloading } = useMutation({ + mutationFn: (params: { + searchQuery?: string; + city?: string; + county?: string; + }) => slopeManageAPI.downloadExcel(params), + + onSuccess: () => { + showNotification('엑셀 파일 다운로드가 완료되었습니다.', { + severity: 'success', + }); + }, + + onError: (error: any) => { + const errorMessage = + error.response?.data?.message || '다운로드에 실패했습니다.'; + showNotification(errorMessage, { + severity: 'error', + autoHideDuration: 6000, }); - } catch (error) { console.error('다운로드 실패:', error); - } + }, + }); + + // 다운로드 버튼 클릭 핸들러 + const handleDownload = () => { + downloadExcel({ + searchQuery: searchQuery || undefined, + city: selectedRegion?.city, + county: selectedRegion?.county, + }); }; return ( @@ -606,7 +638,7 @@ const SteepSlopeLookUp = () => { /> 초기화 - + { color: '#24478f', }} /> - 다운로드 + {isDownloading ? '다운로드 중...' : '엑셀 다운로드'} diff --git a/src/pages/ManagePage/User/pages/UserLookUp.tsx b/src/pages/ManagePage/User/pages/UserLookUp.tsx index 7b9ffa2..3a8a7be 100644 --- a/src/pages/ManagePage/User/pages/UserLookUp.tsx +++ b/src/pages/ManagePage/User/pages/UserLookUp.tsx @@ -21,6 +21,7 @@ import Pagination from '../../components/Pagination'; import SearchRoundedIcon from '@mui/icons-material/SearchRounded'; import DoneRoundedIcon from '@mui/icons-material/DoneRounded'; import ClearRoundedIcon from '@mui/icons-material/ClearRounded'; +import { useNotificationStore } from '../../../../hooks/notificationStore'; const columnHelper = createColumnHelper(); declare module '@tanstack/react-table' { @@ -31,15 +32,18 @@ declare module '@tanstack/react-table' { const UserLookUp = () => { const queryClient = useQueryClient(); + const showNotification = useNotificationStore( + (state) => state.showNotification + ); //승인API const ApproveMutation = useMutation({ mutationFn: (id: number) => userAPI.approveUser(id), onSuccess: () => { - alert('승인 성공'); + showNotification('승인 성공!', { severity: 'success' }); queryClient.invalidateQueries({ queryKey: ['users'] }); }, onError: (error) => { - alert('승인에 실패했습니다. 다시 시도해주세요.'); + showNotification('승인에 실패했습니다.', { severity: 'error' }); console.error('approve Error:', error); }, }); @@ -47,11 +51,11 @@ const UserLookUp = () => { const DeleteMutation = useMutation({ mutationFn: (id: number) => userAPI.deleteUser(id), onSuccess: () => { - alert('삭제 성공'); + showNotification('삭제 성공!', { severity: 'success' }); queryClient.invalidateQueries({ queryKey: ['users'] }); }, onError: (error) => { - alert('삭제에 실패했습니다. 다시 시도해주세요.'); + showNotification('삭제에 실패했습니다.', { severity: 'error' }); console.error('approve Error:', error); }, }); diff --git a/src/pages/ManagePage/User/pages/UserModi.tsx b/src/pages/ManagePage/User/pages/UserModi.tsx index 8a365bd..d3a4219 100644 --- a/src/pages/ManagePage/User/pages/UserModi.tsx +++ b/src/pages/ManagePage/User/pages/UserModi.tsx @@ -22,6 +22,7 @@ import EditModal from '../components/UserEditModal'; import SearchRoundedIcon from '@mui/icons-material/SearchRounded'; import EditRoundedIcon from '@mui/icons-material/EditRounded'; import DeleteForeverRoundedIcon from '@mui/icons-material/DeleteForeverRounded'; +import { useNotificationStore } from '../../../../hooks/notificationStore'; const columnHelper = createColumnHelper(); @@ -33,16 +34,18 @@ declare module '@tanstack/react-table' { const UserModi = () => { const queryClient = useQueryClient(); - + const showNotification = useNotificationStore( + (state) => state.showNotification + ); //승인API const ModifyMutation = useMutation({ mutationFn: (Modiuser: User) => userAPI.modifyUser(Modiuser), onSuccess: () => { - alert('수정 성공'); + showNotification('수정 성공!', { severity: 'success' }); queryClient.invalidateQueries({ queryKey: ['users'] }); }, onError: (error) => { - alert('수정에 실패했습니다. 다시 시도해주세요.'); + showNotification('수정에 실패했습니다.', { severity: 'error' }); console.error('approve Error:', error); }, }); @@ -50,11 +53,11 @@ const UserModi = () => { const DeleteMutation = useMutation({ mutationFn: (id: number) => userAPI.deleteUser(id), onSuccess: () => { - alert('삭제 성공'); + showNotification('삭제 성공!', { severity: 'success' }); queryClient.invalidateQueries({ queryKey: ['users'] }); }, onError: (error) => { - alert('삭제에 실패했습니다. 다시 시도해주세요.'); + showNotification('삭제에 실패했습니다.', { severity: 'error' }); console.error('approve Error:', error); }, }); diff --git a/src/pages/MapPage/components/comment/CommentCreateModal.tsx b/src/pages/MapPage/components/comment/CommentCreateModal.tsx index a87240d..92825bb 100644 --- a/src/pages/MapPage/components/comment/CommentCreateModal.tsx +++ b/src/pages/MapPage/components/comment/CommentCreateModal.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; +import { useNotificationStore } from '../../../../hooks/notificationStore'; interface CommentAddModalProps { isOpen: boolean; @@ -29,6 +30,10 @@ const CommentAddModal = ({ }: CommentAddModalProps) => { const [comment, setComment] = useState(''); const [images, setImages] = useState([]); + //전역 알람 + const showNotification = useNotificationStore( + (state) => state.showNotification + ); const isReactNativeWebView = typeof window !== 'undefined' && window.ReactNativeWebView != null; //모달이 열릴 때 초기상태로 복원 @@ -141,7 +146,7 @@ const CommentAddModal = ({ } } } catch (error) { - alert(`React Native에서 오는 메시지 수신 useEffect\n${error}`); + showNotification('Error:', { severity: 'error' }); console.error('모바일 메시지 처리 오류:', error); } }; diff --git a/src/pages/MapPage/components/comment/CommentUpdateModal.tsx b/src/pages/MapPage/components/comment/CommentUpdateModal.tsx index 3535af8..314074c 100644 --- a/src/pages/MapPage/components/comment/CommentUpdateModal.tsx +++ b/src/pages/MapPage/components/comment/CommentUpdateModal.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; +import { useNotificationStore } from '../../../../hooks/notificationStore'; interface CommentUpdateModalProps { isOpen: boolean; @@ -46,7 +47,9 @@ const CommentUpdateModal = ({ ); const isReactNativeWebView = typeof window !== 'undefined' && window.ReactNativeWebView != null; - + const showNotification = useNotificationStore( + (state) => state.showNotification + ); // 모달이 열릴 때마다 초기 상태로 재설정 useEffect(() => { if (isOpen) { @@ -213,7 +216,8 @@ const CommentUpdateModal = ({ } } } catch (error) { - alert(`React Native에서 오는 메시지 수신 useEffect\n${error}`); + showNotification('Error:', { severity: 'error' }); + console.error('모바일 메시지 처리 오류:', error); } }; diff --git a/src/pages/MapPage/components/map/DeleteIdModal.tsx b/src/pages/MapPage/components/map/DeleteIdModal.tsx index 0df88d3..7a3b468 100644 --- a/src/pages/MapPage/components/map/DeleteIdModal.tsx +++ b/src/pages/MapPage/components/map/DeleteIdModal.tsx @@ -1,6 +1,7 @@ import styled from 'styled-components'; import { useState, useEffect } from 'react'; import { userAPI } from '../../../../apis/User'; +import { useNotificationStore } from '../../../../hooks/notificationStore'; interface DeleteConfirmModalProps { isOpen: boolean; onClose: () => void; @@ -25,11 +26,16 @@ const DeleteIdModal = ({ isOpen, onClose }: DeleteConfirmModalProps) => { }; getUser(); }, []); - + const showNotification = useNotificationStore( + (state) => state.showNotification + ); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (inputValue === '회원탈퇴') { userAPI.selfDeleteUser(user!._id); + showNotification('회원탈퇴가 정상처리 되었습니다.', { + severity: 'success', + }); window.location.href = '/'; } }; diff --git a/src/pages/MapPage/components/map/LeftModal.tsx b/src/pages/MapPage/components/map/LeftModal.tsx index 65ae1e1..ee93168 100644 --- a/src/pages/MapPage/components/map/LeftModal.tsx +++ b/src/pages/MapPage/components/map/LeftModal.tsx @@ -5,6 +5,7 @@ import DeleteIdModal from './DeleteIdModal'; import { userAPI } from '../../../../apis/User'; import TermsofUseModal from './TermsofUseModal'; import ArrowBackIcon from '@mui/icons-material/ArrowBackIosNewRounded'; +import { useNotificationStore } from '../../../../hooks/notificationStore'; interface LeftModalProps { isOpen: boolean; onClose: () => void; @@ -16,7 +17,10 @@ const LeftModal = ({ isOpen, onClose }: LeftModalProps) => { const [isPrivacyPolicyOpen, setIsPrivacyPolicyOpen] = useState(false); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [isTermofUseOpen, setIsTermofUseOpen] = useState(false); - + //전역 알람 + const showNotification = useNotificationStore( + (state) => state.showNotification + ); useEffect(() => { if (isOpen) setTimeout(() => setAnimationOpen(true), 10); else { @@ -44,7 +48,7 @@ const LeftModal = ({ isOpen, onClose }: LeftModalProps) => { localStorage.removeItem('_id'); localStorage.removeItem('isAdmin'); onClose(); - alert('로그아웃 되었습니다.'); + showNotification('로그아웃 되었습니다.', { severity: 'success' }); window.location.href = '/'; }; @@ -131,7 +135,7 @@ const ModalOverlay = styled.div` `; const LeftModalContainer = styled.div<{ $animationOpen: boolean }>` - width: 80%; + width: 70%; height: 100%; background-color: #fff; position: absolute; diff --git a/src/pages/MapPage/components/map/PrivacyPolicyModal.tsx b/src/pages/MapPage/components/map/PrivacyPolicyModal.tsx index 70cc8a8..c67c5ac 100644 --- a/src/pages/MapPage/components/map/PrivacyPolicyModal.tsx +++ b/src/pages/MapPage/components/map/PrivacyPolicyModal.tsx @@ -402,6 +402,7 @@ const ModalHeader = styled.div` top: 0; background-color: white; z-index: 10; + margin-top: 35px; `; const HeaderTitle = styled.h1` diff --git a/src/pages/MapPage/components/map/TermsofUseModal.tsx b/src/pages/MapPage/components/map/TermsofUseModal.tsx index ac7e950..3b5ae11 100644 --- a/src/pages/MapPage/components/map/TermsofUseModal.tsx +++ b/src/pages/MapPage/components/map/TermsofUseModal.tsx @@ -363,6 +363,7 @@ const ModalHeader = styled.div` top: 0; background-color: white; z-index: 10; + margin-top: 35px; `; const HeaderTitle = styled.h1`
초기화
다운로드
{isDownloading ? '다운로드 중...' : '엑셀 다운로드'}