diff --git a/src/apis/index.ts b/src/apis/index.ts index 36cb943..62428f4 100644 --- a/src/apis/index.ts +++ b/src/apis/index.ts @@ -5,7 +5,7 @@ export const REFRESH_URL = BACKEND_URL + '/api/auth/refresh'; // login이 완료된 사람의 요청의 경우 axiosInstance를 사용하여 요청 export const axiosInstance = axios.create({ - baseURL: 'https://www.api.cotato-midpoint.site', + baseURL: BACKEND_URL, }); // accessToken, refreshToken 재발급하는 함수 diff --git a/src/components/common/shared/BannerMessage.tsx b/src/components/common/shared/BannerMessage.tsx index dc34af3..147027f 100644 --- a/src/components/common/shared/BannerMessage.tsx +++ b/src/components/common/shared/BannerMessage.tsx @@ -1,62 +1,95 @@ import { useState } from 'react'; -import styled from 'styled-components'; -import { motion, AnimatePresence } from 'framer-motion'; import CopyLogo from '@/assets/imgs/Navbar/copyLogo.svg?react'; +import ShareIcon from '@/assets/imgs/Location/sharepin.svg?react'; // 모달 상단의 아이콘을 추가 + +interface ModalProps { + url: string; + onClose: () => void; + onCopy: () => void; +} + +// HTTP 환경에서도 URL 복사 기능을 지원하는 함수 +const copyToClipboard = (text: string) => { + if (navigator.clipboard) { + navigator.clipboard.writeText(text).catch(() => { + fallbackCopyTextToClipboard(text); + }); + } else { + fallbackCopyTextToClipboard(text); + } +}; + +const fallbackCopyTextToClipboard = (text: string) => { + const textArea = document.createElement('textarea'); + textArea.value = text; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + try { + document.execCommand('copy'); + } catch (err) { + console.error('복사 실패', err); + } + document.body.removeChild(textArea); +}; export default function BannerMessage() { + const [isModalOpen, setIsModalOpen] = useState(false); const [copied, setCopied] = useState(false); - // url에 따라 안에 배너 메세지 수정하는 로직 추가 필요 - const handleCopy = () => { + + const handleOpenModal = () => { + setIsModalOpen(true); + }; + + const handleCloseModal = () => { + setIsModalOpen(false); + }; + + const handleCopyUrl = () => { const url = window.location.href; - navigator.clipboard.writeText(url).then(() => { - setCopied(true); - setTimeout(() => setCopied(false), 1000); - }); + copyToClipboard(url); + setCopied(true); + setTimeout(() => setCopied(false), 1000); }; return ( - +
- - {copied && ( - - 링크가 복사되었습니다 - - )} - 상대방에게{' '} - + 싱크스팟 링크 를 공유해보세요!
- + + {isModalOpen && ( + + )} +
); } -const Textbox = styled.div` - width: 80%; - background: ${(props) => props.theme.bgColor}; -`; - -const CopiedMessage = styled(motion.div)` - position: absolute; - bottom: 150%; - left: 20%; - transform: translateX(-50%); - background-color: ${(props) => props.theme.mainColor}; - color: white; - padding: 5px 10px; - border-radius: 5px; - font-size: 14px; - font-weight: 600; - display: flex; - justify-content: center; - width: max-content; -`; +const Modal: React.FC = ({ url, onClose, onCopy, copied }) => ( +
+
+
+ +
+

친구들에게 링크 공유하기

+

+ 링크를 공유하면 내가 입력한 위치에 대한 + 중간 지점 찾기 결과를 공유할 수 있어요! +

+
{url}
+
+ + +
+
+
+); diff --git a/src/pages/Home/home.tsx b/src/pages/Home/home.tsx index 0ed3563..e949140 100644 --- a/src/pages/Home/home.tsx +++ b/src/pages/Home/home.tsx @@ -12,9 +12,9 @@ import Map from '@/assets/imgs/Home/Map.svg?react'; import PinkVote from '@/assets/imgs/Home/PinkVote.svg?react'; import SpeechBubble from '@/assets/imgs/Home/speechBubble.svg?react'; import Target from '@/assets/imgs/Home/Target.svg?react'; -import NextArrow from '@/assets/imgs/Home/nextArrow.svg?react'; import Tower from '@/assets/imgs/Home/Tower.svg?react'; import MiniCar from '@/assets/imgs/Home/MiniCar.svg?react'; +import { ArrowLongRightIcon } from '@heroicons/react/24/solid'; interface DotProps { num: number; @@ -207,7 +207,7 @@ export default function Home() {
- 지금 바로 중간지점을 찾아보세요! + 지금 바로 중간지점을 찾아보세요! - -

+ +

여기를 눌러 싱크스팟을 사용해 보세요!

diff --git a/src/pages/Login/login.tsx b/src/pages/Login/login.tsx index f65e2f3..190177c 100644 --- a/src/pages/Login/login.tsx +++ b/src/pages/Login/login.tsx @@ -8,6 +8,8 @@ import { fetchLogin } from '@/apis/login'; import LoginLogo from '@/assets/imgs/loginLogo.svg?react'; import { yupResolver } from '@hookform/resolvers/yup'; import { schema } from '@/types/Login'; +import { toast, ToastContainer } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; import { FROM_ALONE_CREATE_VOTE_PLACE, FROM_ALONE_CREATE_VOTE_TIME, @@ -54,66 +56,75 @@ export default function Login() { const { mutate: userLogin } = useMutation({ mutationFn: fetchLogin, onSuccess: (data: any) => { - localStorage.setItem('accessToken', data.data.data.accessToken); - localStorage.setItem('refreshToken', data.data.data.refreshToken); - localStorage.setItem('roomId', roomId!); - setLoginState(true); - switch (from) { - case FROM_ALONE_RESULT: - navigate(`/page/a/results/${roomId}`); - break; - case FROM_EACH_RESULT: - navigate(`/page/e/results/${roomId}`); - break; - case FROM_ENTER_ALONE: - navigate(`/page/alone/${roomId}`); - break; - case FROM_ENTER_EACH: - navigate(`/page/each/${roomId}`); - break; - case FROM_ALONE_CREATE_VOTE_PLACE: - navigate(`/page/a/create/place-vote-room/${roomId}`); - break; - case FROM_ALONE_PLACE_VOTE: - navigate(`/page/a/place-vote/${roomId}`); - break; - case FROM_ALONE_PLACE_VOTE_RESULT: - navigate(`/page/a/place-vote/results/${roomId}`); - break; - case FROM_EACH_CREATE_VOTE_PLACE: - navigate(`/page/e/create/place-vote-room/${roomId}`); - break; - case FROM_EACH_PLACE_VOTE: - navigate(`/page/e/place-vote/${roomId}`); - break; - case FROM_EACH_PLACE_VOTE_RESULT: - navigate(`/page/e/place-vote/results/${roomId}`); - break; - case FROM_ALONE_CREATE_VOTE_TIME: - navigate(`/page/a/create/time-vote-room/${roomId}`); - break; - case FROM_ALONE_TIME_VOTE: - navigate(`/page/a/time-vote/${roomId}`); - break; - case FROM_ALONE_TIME_VOTE_RESULT: - navigate(`/page/a/time-vote/results/${roomId}`); - break; - case FROM_EACH_CREATE_VOTE_TIME: - navigate(`/page/e/create/time-vote-room/${roomId}`); - break; - case FROM_EACH_TIME_VOTE: - navigate(`/page/e/time-vote/${roomId}`); - break; - case FROM_EACH_TIME_VOTE_RESULT: - navigate(`/page/e/time-vote/results/${roomId}`); - break; - default: - navigate('/'); - break; + if (data.data.data) { + localStorage.setItem('accessToken', data.data.data.accessToken); + localStorage.setItem('refreshToken', data.data.data.refreshToken); + localStorage.setItem('roomId', roomId!); + setLoginState(true); + switch (from) { + case FROM_ALONE_RESULT: + navigate(`/page/a/results/${roomId}`); + break; + case FROM_EACH_RESULT: + navigate(`/page/e/results/${roomId}`); + break; + case FROM_ENTER_ALONE: + navigate(`/page/alone/${roomId}`); + break; + case FROM_ENTER_EACH: + navigate(`/page/each/${roomId}`); + break; + case FROM_ALONE_CREATE_VOTE_PLACE: + navigate(`/page/a/create/place-vote-room/${roomId}`); + break; + case FROM_ALONE_PLACE_VOTE: + navigate(`/page/a/place-vote/${roomId}`); + break; + case FROM_ALONE_PLACE_VOTE_RESULT: + navigate(`/page/a/place-vote/results/${roomId}`); + break; + case FROM_EACH_CREATE_VOTE_PLACE: + navigate(`/page/e/create/place-vote-room/${roomId}`); + break; + case FROM_EACH_PLACE_VOTE: + navigate(`/page/e/place-vote/${roomId}`); + break; + case FROM_EACH_PLACE_VOTE_RESULT: + navigate(`/page/e/place-vote/results/${roomId}`); + break; + case FROM_ALONE_CREATE_VOTE_TIME: + navigate(`/page/a/create/time-vote-room/${roomId}`); + break; + case FROM_ALONE_TIME_VOTE: + navigate(`/page/a/time-vote/${roomId}`); + break; + case FROM_ALONE_TIME_VOTE_RESULT: + navigate(`/page/a/time-vote/results/${roomId}`); + break; + case FROM_EACH_CREATE_VOTE_TIME: + navigate(`/page/e/create/time-vote-room/${roomId}`); + break; + case FROM_EACH_TIME_VOTE: + navigate(`/page/e/time-vote/${roomId}`); + break; + case FROM_EACH_TIME_VOTE_RESULT: + navigate(`/page/e/time-vote/results/${roomId}`); + break; + default: + navigate('/'); + break; + } + } else { + toast.error('로그인 정보가 올바르지 않습니다!', { + position: 'top-center', + }); // 로그인 실패 시 토스트 메시지 } }, onError: (error) => { console.log('로그인 과정 에러', error); + toast.error('로그인 중 문제가 발생했습니다!', { + position: 'top-center', + }); // 에러 발생 시 토스트 메시지 }, }); @@ -159,7 +170,6 @@ export default function Login() { className="w-full py-2 transition bg-gray-100 border-none outline-none focus:ring-2 ring-indigo-100 focus:outline-none" /> {errors.name && {errors.name.message}} - {errors.pw && {errors.pw.message}} -
); }