diff --git a/frontend/src/apiHooks/useDelete.ts b/frontend/src/apiHooks/useDelete.ts new file mode 100644 index 00000000..38f61d67 --- /dev/null +++ b/frontend/src/apiHooks/useDelete.ts @@ -0,0 +1,32 @@ +import { deleteApi } from '../apis/deleteApi'; +import useToast from '../hooks/useToast'; +import { ContentTypeType } from '../types/Api'; + +interface fetchDeleteProps { + url: string; + contentType?: ContentTypeType; +} + +const useDelete = () => { + const { showToast } = useToast(); + + const fetchDelete = async ( + { url, contentType }: fetchDeleteProps, + errorMessage: string, + onSuccess: () => void, + ) => { + try { + await deleteApi(url, contentType); + + if (onSuccess) { + onSuccess(); + } + } catch (e) { + showToast('error', errorMessage); + } + }; + + return { fetchDelete }; +}; + +export default useDelete; diff --git a/frontend/src/apiHooks/useGet.ts b/frontend/src/apiHooks/useGet.ts new file mode 100644 index 00000000..f55b4cc0 --- /dev/null +++ b/frontend/src/apiHooks/useGet.ts @@ -0,0 +1,23 @@ +import { getApi } from '../apis/getApi'; +import useToast from '../hooks/useToast'; + +const useGet = () => { + const { showToast } = useToast(); + + const fetchGet = async ( + url: string, + errorMessage: string, + onSuccess: (responseData: T) => void, + ) => { + try { + const responseData = await getApi(url); + onSuccess(responseData); + } catch (e) { + showToast('error', errorMessage); + } + }; + + return { fetchGet }; +}; + +export default useGet; diff --git a/frontend/src/apiHooks/usePost.ts b/frontend/src/apiHooks/usePost.ts new file mode 100644 index 00000000..df63216a --- /dev/null +++ b/frontend/src/apiHooks/usePost.ts @@ -0,0 +1,35 @@ +import { postApi } from '../apis/postApi'; +import useToast from '../hooks/useToast'; +import { ContentTypeType } from '../types/Api'; + +interface fetchPostProps { + url: string; + payload: {}; + contentType?: ContentTypeType; +} + +const usePost = () => { + const { showToast } = useToast(); + + const fetchPost = async ( + { url, payload, contentType }: fetchPostProps, + errorMessage: string, + onSuccess?: () => void, + ) => { + try { + const responseData = await postApi(url, payload, contentType); + + if (onSuccess) { + onSuccess(); + } + + return responseData; + } catch (e) { + showToast('error', errorMessage); + } + }; + + return { fetchPost }; +}; + +export default usePost; diff --git a/frontend/src/apiHooks/usePut.ts b/frontend/src/apiHooks/usePut.ts new file mode 100644 index 00000000..5316a6b6 --- /dev/null +++ b/frontend/src/apiHooks/usePut.ts @@ -0,0 +1,35 @@ +import { putApi } from '../apis/putApi'; +import useToast from '../hooks/useToast'; +import { ContentTypeType } from '../types/Api'; + +interface fetchPutProps { + url: string; + payload: {}; + contentType?: ContentTypeType; +} + +const usePut = () => { + const { showToast } = useToast(); + + const fetchPut = async ( + { url, payload, contentType }: fetchPutProps, + errorMessage: string, + onSuccess?: () => void, + ) => { + try { + const responseData = await putApi(url, payload, contentType); + + if (onSuccess) { + onSuccess(); + } + + return responseData; + } catch (e) { + showToast('error', errorMessage); + } + }; + + return { fetchPut }; +}; + +export default usePut; diff --git a/frontend/src/apis/deleteApi.ts b/frontend/src/apis/deleteApi.ts index 0cf943fb..7707d046 100644 --- a/frontend/src/apis/deleteApi.ts +++ b/frontend/src/apis/deleteApi.ts @@ -4,13 +4,34 @@ // : process.env.REACT_APP_API_DEFAULT_DEV; import { DEFAULT_PROD_URL } from '../constants'; +import { ContentTypeType } from '../types/Api'; -export const deleteApi = async (url: string, contentType?: string) => { - await fetch(`${DEFAULT_PROD_URL + url}`, { +interface Headers { + 'content-type': string; + [key: string]: string; +} + +export const deleteApi = async (url: string, contentType?: ContentTypeType) => { + const apiUrl = `${DEFAULT_PROD_URL + url}`; + const userToken = localStorage.getItem('userToken'); + const headers: Headers = { + 'content-type': 'application/json', + }; + + if (userToken) { + headers['Authorization'] = `Bearer ${userToken}`; + } + + if (contentType) { + headers['content-type'] = contentType; + } + + const response = await fetch(apiUrl, { method: 'DELETE', - headers: { - Authorization: `Bearer ${localStorage.getItem('userToken') || ''}`, - 'Content-Type': contentType || 'application/json', - }, + headers, }); + + if (response.status >= 400) { + throw new Error('[SERVER] DELETE 요청에 실패했습니다.'); + } }; diff --git a/frontend/src/apis/getApi.ts b/frontend/src/apis/getApi.ts index 804e138e..08df249b 100644 --- a/frontend/src/apis/getApi.ts +++ b/frontend/src/apis/getApi.ts @@ -6,31 +6,31 @@ import { DEFAULT_PROD_URL } from '../constants'; interface Headers { - 'Content-Type': string; + 'content-type': string; [key: string]: string; } -export const getApi = async ( - type: 'tMap' | 'default' | 'login', - url: string, -): Promise => { - const apiUrl = - type === 'tMap' || type === 'login' ? url : `${DEFAULT_PROD_URL + url}`; +export const getApi = async (url: string) => { + const apiUrl = `${DEFAULT_PROD_URL + url}`; const userToken = localStorage.getItem('userToken'); const headers: Headers = { - 'Content-Type': 'application/json', + 'content-type': 'application/json', }; + if (userToken) { headers['Authorization'] = `Bearer ${userToken}`; } + const response = await fetch(apiUrl, { method: 'GET', - headers: headers, + headers, }); - const responseData: T = await response.json(); + if (response.status >= 400) { - //todo: status 상태별로 로그인 토큰 유효 검증 - throw new Error('API 요청에 실패했습니다.'); + throw new Error('[SERVER] GET 요청에 실패했습니다.'); } + + const responseData: T = await response.json(); + return responseData; }; diff --git a/frontend/src/apis/getLoginApi.ts b/frontend/src/apis/getLoginApi.ts new file mode 100644 index 00000000..2466e95a --- /dev/null +++ b/frontend/src/apis/getLoginApi.ts @@ -0,0 +1,16 @@ +export const getLoginApi = async (url: string) => { + const response = await fetch(url, { + method: 'GET', + headers: { + 'content-type': 'application/json', + }, + }); + + if (response.status >= 400) { + throw new Error('[KAKAO] GET 요청에 실패했습니다.'); + } + + const responseData: T = await response.json(); + + return responseData; +}; diff --git a/frontend/src/apis/getMapApi.ts b/frontend/src/apis/getMapApi.ts index 5e552b9d..c6883278 100644 --- a/frontend/src/apis/getMapApi.ts +++ b/frontend/src/apis/getMapApi.ts @@ -1,13 +1,16 @@ -export const getMapApi = (url: string) => - fetch(url, { +export const getMapApi = async (url: string) => { + const response = await fetch(url, { method: 'GET', headers: { - 'Content-type': 'application/json', + 'content-type': 'application/json', }, - }) - .then((data) => { - return data.json(); - }) - .catch((error) => { - throw new Error(`${error.message}`); - }); + }); + + if (response.status >= 400) { + throw new Error('[MAP] GET 요청에 실패했습니다.'); + } + + const responseData: T = await response.json(); + + return responseData; +}; diff --git a/frontend/src/apis/postApi.ts b/frontend/src/apis/postApi.ts index 0b4284b8..44009be9 100644 --- a/frontend/src/apis/postApi.ts +++ b/frontend/src/apis/postApi.ts @@ -4,28 +4,41 @@ // : process.env.REACT_APP_API_DEFAULT_DEV; import { DEFAULT_PROD_URL } from '../constants'; +import { ContentTypeType } from '../types/Api'; interface Headers { - 'Content-Type': string; + 'content-type': string; [key: string]: string; } -export const postApi = async (url: string, data?: {}, contentType?: string) => { + +export const postApi = async ( + url: string, + payload: {}, + contentType?: ContentTypeType, +) => { + const apiUrl = `${DEFAULT_PROD_URL + url}`; const userToken = localStorage.getItem('userToken'); const headers: Headers = { - 'Content-Type': `${contentType || 'application/json'}`, + 'content-type': 'application/json', }; + if (userToken) { headers['Authorization'] = `Bearer ${userToken}`; } - const response = await fetch(`${DEFAULT_PROD_URL + url}`, { + if (contentType) { + headers['content-type'] = contentType; + } + + const response = await fetch(apiUrl, { method: 'POST', - headers: headers, - body: JSON.stringify(data), + headers, + body: JSON.stringify(payload), }); + if (response.status >= 400) { - //todo: status 상태별로 로그인 토큰 유효 검증 - throw new Error('API 요청에 실패했습니다.'); + throw new Error('[SERVER] POST 요청에 실패했습니다.'); } + return response; }; diff --git a/frontend/src/apis/putApi.ts b/frontend/src/apis/putApi.ts index 5fffb872..13d87f61 100644 --- a/frontend/src/apis/putApi.ts +++ b/frontend/src/apis/putApi.ts @@ -4,30 +4,41 @@ // : process.env.REACT_APP_API_DEFAULT_DEV; import { DEFAULT_PROD_URL } from '../constants'; +import { ContentTypeType } from '../types/Api'; interface Headers { - 'Content-Type': string; + 'content-type': string; [key: string]: string; } + export const putApi = async ( url: string, - data: { name: string; images: string[]; description: string }, + data: {}, + contentType?: ContentTypeType, ) => { + const apiUrl = `${DEFAULT_PROD_URL + url}`; const userToken = localStorage.getItem('userToken'); const headers: Headers = { - 'Content-Type': 'application/json', + 'content-type': 'application/json', }; + if (userToken) { headers['Authorization'] = `Bearer ${userToken}`; } - const response = await fetch(`${DEFAULT_PROD_URL + url}`, { + if (contentType) { + headers['content-type'] = contentType; + } + + const response = await fetch(apiUrl, { method: 'PUT', - headers: headers, + headers, body: JSON.stringify(data), }); + if (response.status >= 400) { - throw new Error('API 요청에 실패했습니다.'); + throw new Error('[SERVER] PUT 요청에 실패했습니다.'); } + return response; }; diff --git a/frontend/src/assets/ModifyMyInfoIcon.svg b/frontend/src/assets/ModifyMyInfoIcon.svg deleted file mode 100644 index dbc9b1b9..00000000 --- a/frontend/src/assets/ModifyMyInfoIcon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/src/assets/My.svg b/frontend/src/assets/My.svg deleted file mode 100644 index 509ca1a2..00000000 --- a/frontend/src/assets/My.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/src/assets/InfoDefalutImg.svg b/frontend/src/assets/profile_defaultImage.svg similarity index 100% rename from frontend/src/assets/InfoDefalutImg.svg rename to frontend/src/assets/profile_defaultImage.svg diff --git a/frontend/src/components/AddFavorite/index.tsx b/frontend/src/components/AddFavorite/index.tsx index 5fc7dc60..fe46e1c3 100644 --- a/frontend/src/components/AddFavorite/index.tsx +++ b/frontend/src/components/AddFavorite/index.tsx @@ -6,14 +6,14 @@ import { deleteApi } from '../../apis/deleteApi'; interface AddFavoriteProps { id: number; isBookmarked: boolean; - setTopicsFromServer: () => void; + getTopicsFromServer: () => void; children: React.ReactNode; } const AddFavorite = ({ id, isBookmarked, - setTopicsFromServer, + getTopicsFromServer, children, }: AddFavoriteProps) => { const { showToast } = useToast(); @@ -24,7 +24,7 @@ const AddFavorite = ({ try { await postApi(`/bookmarks/topics?id=${id}`, {}, 'x-www-form-urlencoded'); - setTopicsFromServer(); + getTopicsFromServer(); showToast('info', '즐겨찾기에 추가되었습니다.'); } catch { @@ -38,18 +38,12 @@ const AddFavorite = ({ try { await deleteApi(`/bookmarks/topics?id=${id}`, 'x-www-form-urlencoded'); - setTopicsFromServer(); + getTopicsFromServer(); showToast('info', '해당 지도를 즐겨찾기에서 제외했습니다.'); } catch { showToast('error', '로그인 후 사용해주세요.'); } - - await deleteApi(`/bookmarks/topics?id=${id}`, 'x-www-form-urlencoded'); - - setTopicsFromServer(); - - showToast('info', '해당 지도를 즐겨찾기에서 제외했습니다.'); }; return ( diff --git a/frontend/src/components/AddSeeTogether/index.tsx b/frontend/src/components/AddSeeTogether/index.tsx index 401c3e40..9acfed43 100644 --- a/frontend/src/components/AddSeeTogether/index.tsx +++ b/frontend/src/components/AddSeeTogether/index.tsx @@ -3,7 +3,7 @@ import { postApi } from '../../apis/postApi'; import useToast from '../../hooks/useToast'; import { useContext } from 'react'; import { getApi } from '../../apis/getApi'; -import { TopicType } from '../../types/Topic'; +import { TopicCardProps } from '../../types/Topic'; import { SeeTogetherContext } from '../../context/SeeTogetherContext'; import { deleteApi } from '../../apis/deleteApi'; @@ -11,14 +11,14 @@ interface AddSeeTogetherProps { isInAtlas: boolean; id: number; children: React.ReactNode; - setTopicsFromServer: () => void; + getTopicsFromServer: () => void; } const AddSeeTogether = ({ isInAtlas, id, children, - setTopicsFromServer, + getTopicsFromServer, }: AddSeeTogetherProps) => { const { showToast } = useToast(); const { seeTogetherTopics, setSeeTogetherTopics } = @@ -35,12 +35,12 @@ const AddSeeTogether = ({ await postApi(`/atlas/topics?id=${id}`, {}, 'x-www-form-urlencoded'); - const topics = await getApi('default', '/members/my/atlas'); + const topics = await getApi('/members/my/atlas'); setSeeTogetherTopics(topics); // TODO: 모아보기 페이지에서는 GET /members/my/atlas 두 번 됨 - setTopicsFromServer(); + getTopicsFromServer(); showToast('info', '모아보기에 추가했습니다.'); } catch { @@ -54,12 +54,12 @@ const AddSeeTogether = ({ try { await deleteApi(`/atlas/topics?id=${id}`, 'x-www-form-urlencoded'); - const topics = await getApi('default', '/members/my/atlas'); + const topics = await getApi('/members/my/atlas'); setSeeTogetherTopics(topics); // TODO: 모아보기 페이지에서는 GET /members/my/atlas 두 번 됨 - setTopicsFromServer(); + getTopicsFromServer(); showToast('info', '해당 지도를 모아보기에서 제외했습니다.'); } catch { diff --git a/frontend/src/components/BookmarksList/index.tsx b/frontend/src/components/BookmarksList/index.tsx deleted file mode 100644 index aea2420c..00000000 --- a/frontend/src/components/BookmarksList/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Fragment, useEffect, useState } from 'react'; -import { styled } from 'styled-components'; -import { getApi } from '../../apis/getApi'; -import TopicCard from '../TopicCard'; -import { TopicType } from '../../types/Topic'; -import useToast from '../../hooks/useToast'; - -interface BookmarksListProps { - bookmarks: TopicType[]; - setTopicsFromServer: () => void; -} - -const BookmarksList = ({ - bookmarks, - setTopicsFromServer, -}: BookmarksListProps) => { - return ( - - {bookmarks.map((topic) => ( - - - - ))} - - ); -}; - -const Wrapper = styled.ul` - display: flex; - flex-wrap: wrap; - gap: 20px; -`; - -export default BookmarksList; diff --git a/frontend/src/components/Layout/index.tsx b/frontend/src/components/Layout/index.tsx index 5e5d36fb..666ca050 100644 --- a/frontend/src/components/Layout/index.tsx +++ b/frontend/src/components/Layout/index.tsx @@ -44,7 +44,7 @@ const Layout = ({ children }: LayoutProps) => { const map = new Tmapv3.Map(mapContainer.current, { center: new Tmapv3.LatLng(37.5154, 127.1029), }); - map.setZoomLimit(7, 17); + map.setZoomLimit(7, 18); setMap(map); return () => { map.destroy(); diff --git a/frontend/src/components/Loader/index.tsx b/frontend/src/components/Loader/index.tsx index 90f7ed3c..43066893 100644 --- a/frontend/src/components/Loader/index.tsx +++ b/frontend/src/components/Loader/index.tsx @@ -9,7 +9,7 @@ const Loader = () => { }; const Rotate = keyframes` - 0% { + 0% { transform: rotate(0deg); } 100% { diff --git a/frontend/src/components/ModalMyTopicList/addToMyTopicList.tsx b/frontend/src/components/ModalMyTopicList/addToMyTopicList.tsx index d9f1ed80..d3ed6c4f 100644 --- a/frontend/src/components/ModalMyTopicList/addToMyTopicList.tsx +++ b/frontend/src/components/ModalMyTopicList/addToMyTopicList.tsx @@ -1,61 +1,81 @@ import { Fragment, useContext, useEffect, useState } from 'react'; -import { ModalMyTopicType } from '../../types/Topic'; -import { getApi } from '../../apis/getApi'; +import { TopicCardProps } from '../../types/Topic'; import { styled } from 'styled-components'; -import ModalTopicCard from '../ModalTopicCard'; +import TopicCard from '../TopicCard'; import { ModalContext } from '../../context/ModalContext'; -import { postApi } from '../../apis/postApi'; import useToast from '../../hooks/useToast'; +import useGet from '../../apiHooks/useGet'; +import usePost from '../../apiHooks/usePost'; + +interface OnClickDesignatedProps { + topicId: number; + topicName: string; +} const AddToMyTopicList = ({ pin }: any) => { - const [myTopics, setMyTopics] = useState([]); + const [myTopics, setMyTopics] = useState(null); const { closeModal } = useContext(ModalContext); + const { fetchGet } = useGet(); + const { fetchPost } = usePost(); const { showToast } = useToast(); - const getMyTopicFromServer = async () => { - const serverMyTopic = await getApi( - 'default', + const getMyTopicsFromServer = async () => { + fetchGet( '/members/my/topics', + '내가 만든 지도를 가져오는데 실패했습니다. 잠시 후 다시 시도해주세요.', + (response) => { + setMyTopics(response); + }, ); - setMyTopics(serverMyTopic); }; useEffect(() => { - getMyTopicFromServer(); + getMyTopicsFromServer(); }, []); - const addPinToTopic = async (topicId: any) => { - try { - await postApi(`/pins`, { - topicId: topicId.topicId, - name: pin.name, - description: pin.description, - address: pin.address, - latitude: pin.latitude, - longitude: pin.longitude, - legalDongCode: '', - }); - closeModal('addToMyTopicList'); - showToast('info', '내 지도에 핀이 추가되었습니다.'); - } catch (error) { - showToast('error', '내 지도에 핀 추가를 실패했습니다.'); - } + const addPinToTopic = async (topic: OnClickDesignatedProps) => { + const url = '/pins'; + const payload = { + topicId: topic.topicId, + name: pin.name, + description: pin.description, + address: pin.address, + latitude: pin.latitude, + longitude: pin.longitude, + legalDongCode: '', + }; + + fetchPost( + { + url, + payload, + }, + '내 지도에 핀 추가를 실패하였습니다. 잠시 후 다시 시도해주세요.', + () => { + closeModal('addToMyTopicList'); + showToast('info', '내 지도에 핀이 추가되었습니다.'); + }, + ); }; + if (!myTopics) return <>; return ( {myTopics.map((topic) => ( - ))} diff --git a/frontend/src/components/ModalMyTopicList/index.tsx b/frontend/src/components/ModalMyTopicList/index.tsx index a5e90e91..79981091 100644 --- a/frontend/src/components/ModalMyTopicList/index.tsx +++ b/frontend/src/components/ModalMyTopicList/index.tsx @@ -1,33 +1,39 @@ -import React, { Fragment, useEffect, useState } from 'react'; +import { Fragment, useEffect, useState } from 'react'; import { styled } from 'styled-components'; -import { getApi } from '../../apis/getApi'; -import { ModalMyTopicType } from '../../types/Topic'; -import ModalTopicCard from '../ModalTopicCard'; +import { TopicCardProps } from '../../types/Topic'; +import TopicCard from '../TopicCard'; import Space from '../common/Space'; +import useGet from '../../apiHooks/useGet'; -interface ModalMyTopicList { +interface ModalMyTopicListProps { topicId: string; topicClick: any; } -const ModalMyTopicList = ({ topicId, topicClick }: ModalMyTopicList) => { - const [myTopics, setMyTopics] = useState([]); +const ModalMyTopicList = ({ topicId, topicClick }: ModalMyTopicListProps) => { + const [myTopics, setMyTopics] = useState(null); + const { fetchGet } = useGet(); const getMyTopicFromServer = async () => { if (topicId && topicId.split(',').length > 1) { - const topics = await getApi( - 'default', + fetchGet( `/topics/ids?ids=${topicId}`, + '모아보기로 선택한 지도 목록을 조회하는데 실패했습니다.', + (response) => { + setMyTopics(response); + }, ); - setMyTopics(topics); return; } - const serverMyTopic = await getApi( - 'default', + + fetchGet( '/members/my/topics', + '나의 지도 목록을 조회하는데 실패했습니다.', + (response) => { + setMyTopics(response); + }, ); - setMyTopics(serverMyTopic); }; useEffect(() => { @@ -41,15 +47,18 @@ const ModalMyTopicList = ({ topicId, topicClick }: ModalMyTopicList) => { {myTopics.map((topic) => ( - ))} diff --git a/frontend/src/components/ModalTopicCard/index.tsx b/frontend/src/components/ModalTopicCard/index.tsx deleted file mode 100644 index 9bd69333..00000000 --- a/frontend/src/components/ModalTopicCard/index.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { styled } from 'styled-components'; -import Text from '../common/Text'; -import useNavigator from '../../hooks/useNavigator'; -import Box from '../common/Box'; -import Image from '../common/Image'; -import { SyntheticEvent, useContext } from 'react'; -import Space from '../common/Space'; -import Flex from '../common/Flex'; -import SmallTopicPin from '../../assets/smallTopicPin.svg'; -import SmallTopicStar from '../../assets/smallTopicStar.svg'; -import { DEFAULT_TOPIC_IMAGE } from '../../constants'; -import { ModalContext } from '../../context/ModalContext'; - -const FAVORITE_COUNT = 10; - -export interface ModalTopicCardProps { - topicId: number; - topicImage: string; - topicTitle: string; - topicCreator: string; - topicUpdatedAt: string; - topicPinCount: number; - topicClick: any; - topicBookmarkCount: number; -} - -const ModalTopicCard = ({ - topicId, - topicImage, - topicTitle, - topicCreator, - topicUpdatedAt, - topicPinCount, - topicClick, - topicBookmarkCount, -}: ModalTopicCardProps) => { - const { routePage } = useNavigator(); - const { closeModal } = useContext(ModalContext); - const goToSelectedTopic = (topic: any, type: 'newPin' | 'addToTopic') => { - if (type === 'newPin') { - topicClick(topic); - closeModal('newPin'); - } - if (type === 'addToTopic') { - topicClick(topic); - } - }; - - return ( - { - goToSelectedTopic({ topicId, topicTitle }, 'newPin'); - }} - > - - ) => { - e.currentTarget.src = DEFAULT_TOPIC_IMAGE; - }} - /> - - - - - {topicTitle} - - - - - {topicCreator} - - - - - - {topicUpdatedAt.split('T')[0].replaceAll('-', '.')} 업데이트 - - - - - - - - - - {topicPinCount > 999 ? '+999' : topicPinCount}개 - - - - - - - {topicBookmarkCount > 999 ? '+999' : topicBookmarkCount}명 - - - - - - - ); -}; - -const Wrapper = styled.li` - width: 332px; - height: 140px; - cursor: pointer; - border: 1px solid ${({ theme }) => theme.color.gray}; - border-radius: ${({ theme }) => theme.radius.small}; -`; - -const TopicImage = styled(Image)` - border-radius: ${({ theme }) => theme.radius.small}; -`; - -export default ModalTopicCard; diff --git a/frontend/src/components/MyInfo/UpdateMyInfo.tsx b/frontend/src/components/MyInfo/UpdateMyInfo.tsx index 04441b4d..7268ca82 100644 --- a/frontend/src/components/MyInfo/UpdateMyInfo.tsx +++ b/frontend/src/components/MyInfo/UpdateMyInfo.tsx @@ -1,19 +1,16 @@ import { styled } from 'styled-components'; import Flex from '../common/Flex'; -import InfoDefalutImg from '../../assets/InfoDefalutImg.svg'; -import ModifyMyInfoIcon from '../../assets/ModifyMyInfoIcon.svg'; +import ProfileDefaultImage from '../../assets/profile_defaultImage.svg'; import Box from '../common/Box'; -import Text from '../common/Text'; import Space from '../common/Space'; -import { useEffect, useState } from 'react'; -import { MyInfoType } from '../../types/MyInfo'; +import { ProfileProps } from '../../types/Profile'; import Button from '../common/Button'; interface UpdateMyInfoProps { isThereImg: boolean; - myInfoNameAndEmail: MyInfoType; + myInfoNameAndEmail: ProfileProps; setIsModifyMyInfo: React.Dispatch>; - setMyInfoNameAndEmail: React.Dispatch>; + setMyInfoNameAndEmail: React.Dispatch>; } const UpdateMyInfo = ({ @@ -23,12 +20,12 @@ const UpdateMyInfo = ({ setMyInfoNameAndEmail, }: UpdateMyInfoProps) => { const onChangeMyInfoName = (e: React.ChangeEvent) => { - if(e.target.value.length >= 20) return; + if (e.target.value.length >= 20) return; setMyInfoNameAndEmail({ ...myInfoNameAndEmail, name: e.target.value }); }; const onChangeMyInfoEmail = (e: React.ChangeEvent) => { - if(e.target.value.length >= 35) return; + if (e.target.value.length >= 35) return; setMyInfoNameAndEmail({ ...myInfoNameAndEmail, email: e.target.value }); }; @@ -47,7 +44,7 @@ const UpdateMyInfo = ({ {isThereImg ? ( ) : ( - + )} diff --git a/frontend/src/components/MyInfo/index.tsx b/frontend/src/components/MyInfo/index.tsx index 1d2706e0..b2aa0fd8 100644 --- a/frontend/src/components/MyInfo/index.tsx +++ b/frontend/src/components/MyInfo/index.tsx @@ -4,7 +4,7 @@ import Box from '../common/Box'; import Text from '../common/Text'; import Space from '../common/Space'; import { useState } from 'react'; -import { MyInfoType } from '../../types/MyInfo'; +import { ProfileProps } from '../../types/Profile'; import UpdateMyInfo from './UpdateMyInfo'; const user = JSON.parse(localStorage.getItem('user') || '{}'); @@ -12,7 +12,7 @@ const user = JSON.parse(localStorage.getItem('user') || '{}'); const MyInfo = () => { const [isThereImg, setIsThereImg] = useState(true); const [isModifyMyInfo, setIsModifyMyInfo] = useState(false); - const [myInfoNameAndEmail, setMyInfoNameAndEmail] = useState({ + const [myInfoNameAndEmail, setMyInfoNameAndEmail] = useState({ name: user.nickName, email: user.email, }); @@ -39,7 +39,7 @@ const MyInfo = () => { - + {user.nickName} diff --git a/frontend/src/components/MyInfoContainer/MyInfoList/index.tsx b/frontend/src/components/MyInfoContainer/MyInfoList/index.tsx deleted file mode 100644 index 94a05e42..00000000 --- a/frontend/src/components/MyInfoContainer/MyInfoList/index.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Fragment, useEffect, useState } from 'react'; -import { styled } from 'styled-components'; -import { getApi } from '../../../apis/getApi'; -import PinCard from '../../PinCard'; -import TopicCard from '../../TopicCard'; -import { TopicType } from '../../../types/Topic'; -import useToast from '../../../hooks/useToast'; - -const MyInfoList = () => { - const [myInfoTopics, setMyInfoTopics] = useState([]); - const { showToast } = useToast(); - - const getMyInfoListFromServer = async () => { - try { - const serverMyInfoTopics = await getApi( - 'default', - '/members/my/topics', - ); - - setMyInfoTopics(serverMyInfoTopics); - } catch { - showToast('error', '로그인 후 이용해주세요.'); - } - }; - - useEffect(() => { - getMyInfoListFromServer(); - }, []); - - if (!myInfoTopics) return <>; - - return ( - - {myInfoTopics.map((topic, index) => { - return ( - - - - ); - })} - - ); -}; - -const MyInfoListWrapper = styled.ul` - display: flex; - flex-wrap: wrap; - gap: 20px; -`; - -export default MyInfoList; diff --git a/frontend/src/components/MyInfoContainer/index.tsx b/frontend/src/components/MyInfoContainer/index.tsx deleted file mode 100644 index b2e38cfd..00000000 --- a/frontend/src/components/MyInfoContainer/index.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { styled } from 'styled-components'; -import Flex from '../common/Flex'; -import Text from '../common/Text'; -import Box from '../common/Box'; -import Space from '../common/Space'; -import { lazy, Suspense } from 'react'; -import TopicCardListSkeleton from '../TopicCardList/TopicCardListSkeleton'; -import Button from '../common/Button'; - -const MyInfoList = lazy(() => import('./MyInfoList')); - -interface MyInfoContainerProps { - containerTitle: string; - containerDescription: string; -} - -const MyInfoContainer = ({ - containerTitle, - containerDescription, -}: MyInfoContainerProps) => ( -
- - - - {containerTitle} - - - - {containerDescription} - - - - - - - }> - - -
-); - -const SeeAllButton = styled(Button)` - cursor: pointer; -`; - -export default MyInfoContainer; diff --git a/frontend/src/components/PinsOfTopic/index.tsx b/frontend/src/components/PinsOfTopic/index.tsx index da2aae33..fc3ee771 100644 --- a/frontend/src/components/PinsOfTopic/index.tsx +++ b/frontend/src/components/PinsOfTopic/index.tsx @@ -1,12 +1,12 @@ import { DEFAULT_TOPIC_IMAGE } from '../../constants'; -import { TopicDetailType } from '../../types/Topic'; +import { TopicDetailProps } from '../../types/Topic'; import PinPreview from '../PinPreview'; import TopicInfo from '../TopicInfo'; interface PinsOfTopicProps { topicId: string; idx: number; - topicDetail: TopicDetailType; + topicDetail: TopicDetailProps; setSelectedPinId: React.Dispatch>; setIsEditPinDetail: React.Dispatch>; setTopicsFromServer: () => void; diff --git a/frontend/src/components/SeeAllCardList/SeeAllCardListSkeleton.tsx b/frontend/src/components/SeeAllCardList/SeeAllCardListSkeleton.tsx deleted file mode 100644 index d78b972e..00000000 --- a/frontend/src/components/SeeAllCardList/SeeAllCardListSkeleton.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import Flex from '../common/Flex'; -import Space from '../common/Space'; -import TopicCardSkeleton from '../TopicCardSkeleton'; - -const SeeAllCardListSkeleton = () => { - return ( - - - - - - - - - - ); -}; - -export default SeeAllCardListSkeleton; diff --git a/frontend/src/components/SeeAllCardList/index.tsx b/frontend/src/components/SeeAllCardList/index.tsx deleted file mode 100644 index 78c22864..00000000 --- a/frontend/src/components/SeeAllCardList/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { Fragment, useEffect, useState } from 'react'; -import { TopicType } from '../../types/Topic'; -import { getApi } from '../../apis/getApi'; -import TopicCard from '../TopicCard'; -import Flex from '../common/Flex'; -import { styled } from 'styled-components'; - -interface SeeAllCardListProps { - url: string; -} - -const SeeAllCardList = ({ url }: SeeAllCardListProps) => { - const [topics, setTopics] = useState([]); - - const getAndSetDataFromServer = async () => { - const topics = await getApi('default', url); - setTopics(topics); - }; - - useEffect(() => { - getAndSetDataFromServer(); - }, []); - - return ( - - {topics && - topics.map((topic) => ( - - - - ))} - - ); -}; - -const Wrapper = styled.ul` - display: flex; - flex-wrap: wrap; - gap: 20px; -`; - -export default SeeAllCardList; diff --git a/frontend/src/components/SeeTogetherCounter/index.tsx b/frontend/src/components/SeeTogetherCounter/index.tsx index ef720be8..4c372916 100644 --- a/frontend/src/components/SeeTogetherCounter/index.tsx +++ b/frontend/src/components/SeeTogetherCounter/index.tsx @@ -2,7 +2,7 @@ import { useContext, useEffect } from 'react'; import { SeeTogetherContext } from '../../context/SeeTogetherContext'; import { keyframes, styled } from 'styled-components'; import { getApi } from '../../apis/getApi'; -import { TopicType } from '../../types/Topic'; +import { TopicCardProps } from '../../types/Topic'; import useToast from '../../hooks/useToast'; const SeeTogetherCounter = () => { @@ -15,7 +15,7 @@ const SeeTogetherCounter = () => { try { if (!userToken) return; - const topics = await getApi('default', '/members/my/atlas'); + const topics = await getApi('/members/my/atlas'); setSeeTogetherTopics(topics); } catch { showToast( diff --git a/frontend/src/components/PinPreviewSkeleton/index.tsx b/frontend/src/components/Skeletons/PinPreviewSkeleton.tsx similarity index 100% rename from frontend/src/components/PinPreviewSkeleton/index.tsx rename to frontend/src/components/Skeletons/PinPreviewSkeleton.tsx diff --git a/frontend/src/components/PinsOfTopic/PinsOfTopicSkeleton.tsx b/frontend/src/components/Skeletons/PinsOfTopicSkeleton.tsx similarity index 79% rename from frontend/src/components/PinsOfTopic/PinsOfTopicSkeleton.tsx rename to frontend/src/components/Skeletons/PinsOfTopicSkeleton.tsx index 0d2c5b19..ffe99d46 100644 --- a/frontend/src/components/PinsOfTopic/PinsOfTopicSkeleton.tsx +++ b/frontend/src/components/Skeletons/PinsOfTopicSkeleton.tsx @@ -1,7 +1,7 @@ import Flex from '../common/Flex'; -import PinPreviewSkeleton from '../PinPreviewSkeleton'; +import PinPreviewSkeleton from './PinPreviewSkeleton'; import Space from '../common/Space'; -import TopicInfoSkeleton from '../TopicInfoSkeleton'; +import TopicInfoSkeleton from './TopicInfoSkeleton'; const PinsOfTopicSkeleton = () => { return ( diff --git a/frontend/src/components/TopicCardSkeleton/index.tsx b/frontend/src/components/Skeletons/TopicCardSkeleton.tsx similarity index 100% rename from frontend/src/components/TopicCardSkeleton/index.tsx rename to frontend/src/components/Skeletons/TopicCardSkeleton.tsx diff --git a/frontend/src/components/TopicInfoSkeleton/index.tsx b/frontend/src/components/Skeletons/TopicInfoSkeleton.tsx similarity index 100% rename from frontend/src/components/TopicInfoSkeleton/index.tsx rename to frontend/src/components/Skeletons/TopicInfoSkeleton.tsx diff --git a/frontend/src/components/TopicCardList/TopicCardListSkeleton.tsx b/frontend/src/components/Skeletons/TopicListSkeleton.tsx similarity index 73% rename from frontend/src/components/TopicCardList/TopicCardListSkeleton.tsx rename to frontend/src/components/Skeletons/TopicListSkeleton.tsx index d274e085..5c30b815 100644 --- a/frontend/src/components/TopicCardList/TopicCardListSkeleton.tsx +++ b/frontend/src/components/Skeletons/TopicListSkeleton.tsx @@ -1,7 +1,7 @@ import { styled } from 'styled-components'; -import TopicCardSkeleton from '../TopicCardSkeleton'; +import TopicCardSkeleton from './TopicCardSkeleton'; -const TopicCardListSkeleton = () => { +const TopicCardContainerSkeleton = () => { return ( @@ -22,4 +22,4 @@ const Wrapper = styled.section` height: 300px; `; -export default TopicCardListSkeleton; +export default TopicCardContainerSkeleton; diff --git a/frontend/src/components/TopicCard/index.tsx b/frontend/src/components/TopicCard/index.tsx index 19038b31..86953c29 100644 --- a/frontend/src/components/TopicCard/index.tsx +++ b/frontend/src/components/TopicCard/index.tsx @@ -3,7 +3,7 @@ import Text from '../common/Text'; import useNavigator from '../../hooks/useNavigator'; import Box from '../common/Box'; import Image from '../common/Image'; -import { SyntheticEvent } from 'react'; +import { SyntheticEvent, useContext } from 'react'; import Space from '../common/Space'; import Flex from '../common/Flex'; import FavoriteSVG from '../../assets/favoriteBtn_filled.svg'; @@ -15,14 +15,23 @@ import SmallTopicStar from '../../assets/smallTopicStar.svg'; import { DEFAULT_TOPIC_IMAGE } from '../../constants'; import AddSeeTogether from '../AddSeeTogether'; import AddFavorite from '../AddFavorite'; -import { TopicType } from '../../types/Topic'; +import { TopicCardProps } from '../../types/Topic'; import useKeyDown from '../../hooks/useKeyDown'; +import { ModalContext } from '../../context/ModalContext'; -interface TopicCardProps extends TopicType { - setTopicsFromServer: () => void; +interface OnClickDesignatedProps { + topicId: number; + topicName: string; +} + +interface TopicCardExtendedProps extends TopicCardProps { + cardType: 'default' | 'modal'; + onClickDesignated?: ({ topicId, topicName }: OnClickDesignatedProps) => void; + getTopicsFromServer?: () => void; } const TopicCard = ({ + cardType, id, image, creator, @@ -32,19 +41,29 @@ const TopicCard = ({ bookmarkCount, isInAtlas, isBookmarked, - setTopicsFromServer, -}: TopicCardProps) => { + onClickDesignated, + getTopicsFromServer, +}: TopicCardExtendedProps) => { const { routePage } = useNavigator(); + const { closeModal } = useContext(ModalContext); const { elementRef, onElementKeyDown } = useKeyDown(); const goToSelectedTopic = () => { routePage(`/topics/${id}`, [id]); }; + const addPinToThisTopic = () => { + if (onClickDesignated) { + onClickDesignated({ topicId: id, topicName: name }); + } + + closeModal('newPin'); + }; + return ( @@ -116,22 +135,24 @@ const TopicCard = ({ - - - {isInAtlas ? : } - - - {isBookmarked ? : } - - + {cardType === 'default' && getTopicsFromServer && ( + + + {isInAtlas ? : } + + + {isBookmarked ? : } + + + )}
diff --git a/frontend/src/components/TopicCardContainer/index.tsx b/frontend/src/components/TopicCardContainer/index.tsx new file mode 100644 index 00000000..a8d0631a --- /dev/null +++ b/frontend/src/components/TopicCardContainer/index.tsx @@ -0,0 +1,122 @@ +import { styled } from 'styled-components'; +import Flex from '../common/Flex'; +import Text from '../common/Text'; +import Box from '../common/Box'; +import Space from '../common/Space'; +import { Fragment, useEffect, useState } from 'react'; +import { TopicCardProps } from '../../types/Topic'; +import useKeyDown from '../../hooks/useKeyDown'; +import TopicCard from '../TopicCard'; +import useGet from '../../apiHooks/useGet'; + +interface TopicCardContainerProps { + url: string; + containerTitle: string; + containerDescription: string; + routeWhenSeeAll: () => void; +} + +const TopicCardContainer = ({ + url, + containerTitle, + containerDescription, + routeWhenSeeAll, +}: TopicCardContainerProps) => { + const [topics, setTopics] = useState(null); + const { elementRef, onElementKeyDown } = useKeyDown(); + const { fetchGet } = useGet(); + + const setTopicsFromServer = async () => { + await fetchGet( + url, + '지도를 가져오는데 실패했습니다. 잠시 후 다시 시도해주세요.', + (response) => { + setTopics(response); + }, + ); + }; + + useEffect(() => { + setTopicsFromServer(); + }, []); + + return ( +
+ + + + {containerTitle} + + + + {containerDescription} + + + + + 전체 보기 + + + + + + + {topics && + topics.map((topic, index) => { + return ( + index < 6 && ( + + + + ) + ); + })} + +
+ ); +}; + +const PointerText = styled(Text)` + cursor: pointer; +`; + +const TopicsWrapper = styled.ul` + display: flex; + flex-wrap: wrap; + gap: 20px; + height: 300px; + overflow: hidden; +`; + +export default TopicCardContainer; diff --git a/frontend/src/components/TopicCardList/index.tsx b/frontend/src/components/TopicCardList/index.tsx index 48fec5d7..8b1df9c2 100644 --- a/frontend/src/components/TopicCardList/index.tsx +++ b/frontend/src/components/TopicCardList/index.tsx @@ -1,53 +1,99 @@ -import { Fragment, useContext, useEffect, useState } from 'react'; -import { getApi } from '../../apis/getApi'; -import { TopicType } from '../../types/Topic'; +import { Fragment, useEffect, useState } from 'react'; +import { styled } from 'styled-components'; import TopicCard from '../TopicCard'; -import { MarkerContext } from '../../context/MarkerContext'; +import { TopicCardProps } from '../../types/Topic'; +import useGet from '../../apiHooks/useGet'; import Flex from '../common/Flex'; +import Space from '../common/Space'; +import Text from '../common/Text'; +import Button from '../common/Button'; -interface TopicCardList { - topics: TopicType[]; - setTopicsFromServer: () => void; +interface TopicCardListProps { + url: string; + errorMessage: string; + commentWhenEmpty: string; + pageCommentWhenEmpty: string; + routePage: () => void; + children?: React.ReactNode; } -const TopicCardList = ({ topics, setTopicsFromServer }: TopicCardList) => { - const { markers, removeMarkers, removeInfowindows } = - useContext(MarkerContext); +const TopicCardList = ({ + url, + errorMessage, + commentWhenEmpty, + pageCommentWhenEmpty, + routePage, + children, +}: TopicCardListProps) => { + const [topics, setTopics] = useState(null); + const { fetchGet } = useGet(); + + const getTopicsFromServer = async () => { + fetchGet(url, errorMessage, (response) => { + setTopics(response); + }); + }; useEffect(() => { - if (markers.length > 0) { - removeMarkers(); - removeInfowindows(); - } + getTopicsFromServer(); }, []); + if (!topics) return <>; + + if (topics.length === 0) { + return ( + + + {children} + + + {commentWhenEmpty} + + + + + + + ); + } + return ( -
    - - {topics && - topics.map((topic, index) => { - return ( - index < 6 && ( - - - - ) - ); - })} - -
+ + {topics.map((topic) => ( + + + + ))} + ); }; +const EmptyWrapper = styled.section` + height: 240px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +`; + +const Wrapper = styled.ul` + display: flex; + flex-wrap: wrap; + gap: 20px; +`; + export default TopicCardList; diff --git a/frontend/src/components/TopicListContainer/index.tsx b/frontend/src/components/TopicListContainer/index.tsx deleted file mode 100644 index 54f4a4c6..00000000 --- a/frontend/src/components/TopicListContainer/index.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { styled } from 'styled-components'; -import Flex from '../common/Flex'; -import Text from '../common/Text'; -import Box from '../common/Box'; -import Space from '../common/Space'; -import { lazy, Suspense } from 'react'; -import TopicCardListSkeleton from '../TopicCardList/TopicCardListSkeleton'; -import { TopicType } from '../../types/Topic'; -import useKeyDown from '../../hooks/useKeyDown'; - -const TopicCardList = lazy(() => import('../TopicCardList')); - -interface TopicListContainerProps { - containerTitle: string; - containerDescription: string; - routeWhenSeeAll: () => void; - topics: TopicType[]; - setTopicsFromServer: () => void; -} - -const TopicListContainer = ({ - containerTitle, - containerDescription, - routeWhenSeeAll, - topics, - setTopicsFromServer, -}: TopicListContainerProps) => { - const { elementRef, onElementKeyDown } = useKeyDown(); - - return ( -
- - - - {containerTitle} - - - - {containerDescription} - - - - - 전체 보기 - - - - - - }> - - -
- ); -}; - -const PointerText = styled(Text)` - cursor: pointer; -`; - -export default TopicListContainer; diff --git a/frontend/src/context/SeeTogetherContext.tsx b/frontend/src/context/SeeTogetherContext.tsx index 311fc7cf..81af69af 100644 --- a/frontend/src/context/SeeTogetherContext.tsx +++ b/frontend/src/context/SeeTogetherContext.tsx @@ -5,11 +5,11 @@ import { createContext, useState, } from 'react'; -import { TopicType } from '../types/Topic'; +import { TopicCardProps } from '../types/Topic'; interface SeeTogetherContextProps { - seeTogetherTopics: TopicType[] | null; - setSeeTogetherTopics: Dispatch>; + seeTogetherTopics: TopicCardProps[] | null; + setSeeTogetherTopics: Dispatch>; } interface SeeTogetherProviderProps { @@ -23,7 +23,7 @@ export const SeeTogetherContext = createContext({ const SeeTogetherProvider = ({ children }: SeeTogetherProviderProps) => { const [seeTogetherTopics, setSeeTogetherTopics] = useState< - TopicType[] | null + TopicCardProps[] | null >(null); return ( diff --git a/frontend/src/hooks/useMapClick.ts b/frontend/src/hooks/useMapClick.ts index 4a7f9bc0..c0f67dfd 100644 --- a/frontend/src/hooks/useMapClick.ts +++ b/frontend/src/hooks/useMapClick.ts @@ -7,24 +7,26 @@ export default function useMapClick(map: any) { const { setClickedCoordinate } = useContext(CoordinatesContext); const { showToast } = useToast(); - useEffect(() => { - if (!map) return; - const clickHandler = async (evt: any) => { + const clickHandler = async (evt: any) => { + try { const roadName = await getAddressFromServer( evt.data.lngLat._lat, evt.data.lngLat._lng, ); - if (roadName.id) { - showToast('error', `제공되지 않는 주소 범위입니다.`); - } - setClickedCoordinate({ latitude: evt.data.lngLat._lat, longitude: evt.data.lngLat._lng, address: roadName, }); - }; + } catch (e) { + showToast('error', `제공되지 않는 주소 범위입니다.`); + } + }; + + useEffect(() => { + if (!map) return; + map.on('Click', clickHandler); return () => { diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index d5d0e6e6..314ac3d8 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -4,7 +4,7 @@ import { ThemeProvider } from 'styled-components'; import theme from './themes'; import GlobalStyle from './GlobalStyle'; import ErrorBoundary from './components/ErrorBoundary'; -import NotFound from './components/NotFound'; +import NotFound from './pages/NotFound'; const rootElement = document.getElementById('root'); if (!rootElement) throw new Error('Failed to find the root element'); diff --git a/frontend/src/lib/getAddressFromServer.ts b/frontend/src/lib/getAddressFromServer.ts index 42ad45e2..3703a9b2 100644 --- a/frontend/src/lib/getAddressFromServer.ts +++ b/frontend/src/lib/getAddressFromServer.ts @@ -1,19 +1,17 @@ import { getMapApi } from '../apis/getMapApi'; +import { MapAddressProps } from '../types/Map'; -const getAddressFromServer = async (lat: any, lng: any) => { +const getAddressFromServer = async (lat: number, lng: number) => { const version = '1'; const coordType = 'WGS84GEO'; const addressType = 'A10'; const callback = 'result'; - const addressData = await getMapApi( + + const addressData = await getMapApi( `https://apis.openapi.sk.com/tmap/geo/reversegeocoding?version=${version}&lat=${lat}&lon=${lng}&coordType=${coordType}&addressType=${addressType}&callback=${callback}&appKey=P2MX6F1aaf428AbAyahIl9L8GsIlES04aXS9hgxo `, ); - if (addressData.error) { - return addressData.error; - } - const addressResult = addressData.addressInfo.fullAddress.split(','); return addressResult[2]; }; diff --git a/frontend/src/pages/LoginError.tsx b/frontend/src/pages/AskLogin.tsx similarity index 96% rename from frontend/src/pages/LoginError.tsx rename to frontend/src/pages/AskLogin.tsx index e6d11fe9..507a793f 100644 --- a/frontend/src/pages/LoginError.tsx +++ b/frontend/src/pages/AskLogin.tsx @@ -7,7 +7,7 @@ import Text from '../components/common/Text'; import useSetLayoutWidth from '../hooks/useSetLayoutWidth'; import { DEFAULT_PROD_URL, FULLSCREEN } from '../constants'; -const LoginError = () => { +const AskLogin = () => { const { width } = useSetLayoutWidth(FULLSCREEN); const loginButtonClick = () => { @@ -53,4 +53,4 @@ const NotFoundButton = styled(Button)` border: 1px solid ${({ theme }) => theme.color.white}; `; -export default LoginError; +export default AskLogin; diff --git a/frontend/src/pages/AskLoginPage.tsx b/frontend/src/pages/AskLoginPage.tsx deleted file mode 100644 index bae9f958..00000000 --- a/frontend/src/pages/AskLoginPage.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { keyframes, styled } from 'styled-components'; -import Image from '../components/common/Image'; -import Text from '../components/common/Text'; -import Space from '../components/common/Space'; -import { DEFAULT_PROD_URL } from '../constants'; - -export default function AskLoginPage() { - const loginButtonClick = () => { - window.location.href = `${DEFAULT_PROD_URL}/oauth/kakao`; - }; - - return ( - - - 로그인해서 쓰면 더 재밌지롱~ - - - - - - - ); -} - -const ErrorContainer = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: 100%; - height: 100vh; - text-align: center; -`; - -const pulseAnimation = keyframes` - 0% { - transform: scale(1); - } - 50% { - transform: scale(1.2); - } - 100% { - transform: scale(1); - } -`; - -const RetryButton = styled.button` - display: flex; - justify-content: center; - align-items: center; - width: 200px; - height: 200px; - border: none; - border-radius: 50%; - background-color: #fee500; - font-size: 16px; - font-weight: 700; - color: black; - cursor: pointer; - animation: ${pulseAnimation} 1.5s infinite; -`; diff --git a/frontend/src/pages/Bookmark.tsx b/frontend/src/pages/Bookmark.tsx index ff62111d..d4ba4d01 100644 --- a/frontend/src/pages/Bookmark.tsx +++ b/frontend/src/pages/Bookmark.tsx @@ -6,118 +6,65 @@ import useSetLayoutWidth from '../hooks/useSetLayoutWidth'; import useSetNavbarHighlight from '../hooks/useSetNavbarHighlight'; import Flex from '../components/common/Flex'; import Space from '../components/common/Space'; -import { Suspense, lazy, useEffect, useState } from 'react'; -import FavoriteNotFilledSVG from '../assets/favoriteBtn_notFilled.svg'; -import TopicCardListSkeleton from '../components/TopicCardList/TopicCardListSkeleton'; -import useToast from '../hooks/useToast'; -import { TopicType } from '../types/Topic'; -import { getApi } from '../apis/getApi'; -import Button from '../components/common/Button'; +import { Suspense, lazy } from 'react'; +import TopicCardContainerSkeleton from '../components/Skeletons/TopicListSkeleton'; import useNavigator from '../hooks/useNavigator'; +import FavoriteNotFilledSVG from '../assets/favoriteBtn_notFilled.svg'; -const BookmarksList = lazy(() => import('../components/BookmarksList')); +const TopicCardList = lazy(() => import('../components/TopicCardList')); const Bookmark = () => { - const { width } = useSetLayoutWidth(FULLSCREEN); - const { navbarHighlights: __ } = useSetNavbarHighlight('favorite'); - const [bookmarks, setBookmarks] = useState(null); - const { showToast } = useToast(); const { routePage } = useNavigator(); - - const getBookmarksFromServer = async () => { - try { - const serverBookmarks = await getApi( - 'default', - '/members/my/bookmarks', - ); - setBookmarks(serverBookmarks); - } catch { - showToast('error', '로그인 후 이용해주세요.'); - } - }; + useSetLayoutWidth(FULLSCREEN); + useSetNavbarHighlight('favorite'); const goToHome = () => { routePage('/'); }; - useEffect(() => { - getBookmarksFromServer(); - }, []); - - if (!bookmarks) return <>; - - if (bookmarks.length === 0) { - return ( - - - - - - 버튼을 눌러 지도를 추가해보세요. - - - - - - - ); - } - return ( - -
- - - - - 즐겨찾기 - - - - 즐겨찾기한 지도들을 한 눈에 보세요. - - - + + + + + + 즐겨찾기 + + + + 즐겨찾기한 지도들을 한 눈에 보세요. + + + - + - }> - - -
-
+ }> + + + + + ); }; -const WrapperWhenEmpty = styled.section<{ width: '372px' | '100vw' }>` - width: ${({ width }) => `calc(${width} - 40px)`}; - height: 100%; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -`; - -const PointerText = styled(Text)` - cursor: pointer; -`; - -const BookMarksWrapper = styled(Box)` +const Wrapper = styled.article` width: 1036px; margin: 0 auto; `; diff --git a/frontend/src/pages/ErrorPage.tsx b/frontend/src/pages/ErrorPage.tsx deleted file mode 100644 index 24c7fe9a..00000000 --- a/frontend/src/pages/ErrorPage.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { useRouteError } from 'react-router-dom'; -import { keyframes, styled } from 'styled-components'; -import useNavigator from '../hooks/useNavigator'; - -interface Error { - statusText: string; - status: number; -} - -export default function RootErrorPage() { - const { routePage } = useNavigator(); - - const error: Error = useRouteError() as Error; - - return ( - -

Oops!

-

Sorry, an unexpected error has occurred.

- routePage('/')}> - {error.status || error.statusText} - -
- ); -} - -const ErrorContainer = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: 100%; - height: 100vh; - text-align: center; -`; - -const pulseAnimation = keyframes` - 0% { - transform: scale(1); - } - 50% { - transform: scale(1.2); - } - 100% { - transform: scale(1); - } -`; - -const RetryButton = styled.button` - display: flex; - justify-content: center; - align-items: center; - width: 150px; - height: 150px; - border: none; - border-radius: 50%; - background-color: #ff3b3b; - font-size: 16px; - font-weight: 700; - color: #ffffff; - cursor: pointer; - animation: ${pulseAnimation} 1.5s infinite; -`; diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index d5122253..5fa693c5 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -1,25 +1,24 @@ import Space from '../components/common/Space'; import Box from '../components/common/Box'; import useNavigator from '../hooks/useNavigator'; -import TopicListContainer from '../components/TopicListContainer'; import { styled } from 'styled-components'; import useSetLayoutWidth from '../hooks/useSetLayoutWidth'; import { FULLSCREEN } from '../constants'; import useSetNavbarHighlight from '../hooks/useSetNavbarHighlight'; -import useToast from '../hooks/useToast'; -import { TopicType } from '../types/Topic'; -import { getApi } from '../apis/getApi'; -import { useEffect, useState } from 'react'; -import Text from '../components/common/Text'; +import { Suspense, lazy, useContext, useEffect } from 'react'; +import { MarkerContext } from '../context/MarkerContext'; +import TopicCardContainerSkeleton from '../components/Skeletons/TopicListSkeleton'; + +const TopicListContainer = lazy( + () => import('../components/TopicCardContainer'), +); const Home = () => { - const [popularTopics, setPopularTopics] = useState(null); - const [nearTopics, setNearTopics] = useState(null); - const [newestTopics, setNewestTopics] = useState(null); const { routePage } = useNavigator(); - const { width: _ } = useSetLayoutWidth(FULLSCREEN); - const { navbarHighlights: __ } = useSetNavbarHighlight('home'); - const { showToast } = useToast(); + const { markers, removeMarkers, removeInfowindows } = + useContext(MarkerContext); + useSetLayoutWidth(FULLSCREEN); + useSetNavbarHighlight('home'); const goToPopularTopics = () => { routePage('see-all/popularity'); @@ -33,124 +32,57 @@ const Home = () => { routePage('see-all/latest'); }; - const getNearTopicsFromServer = async () => { - try { - const topics = await getApi('default', `/topics`); - setNearTopics(topics); - } catch { - showToast( - 'error', - '로그인 정보가 만료되었습니다. 로그아웃 후 다시 로그인 해주세요.', - ); - } - }; - - const getNewestTopicsFromServer = async () => { - try { - const topics = await getApi('default', '/topics/newest'); - setNewestTopics(topics); - } catch { - showToast( - 'error', - '로그인 정보가 만료되었습니다. 로그아웃 후 다시 로그인 해주세요.', - ); - } - }; - - const getPopularTopicsFromServer = async () => { - try { - const topics = await getApi('default', '/topics/bests'); - setPopularTopics(topics); - } catch { - showToast( - 'error', - '로그인 정보가 만료되었습니다. 로그아웃 후 다시 로그인 해주세요.', - ); - } - }; - - const topicsFetchingFromServer = async () => { - await getPopularTopicsFromServer(); - await getNearTopicsFromServer(); - await getNewestTopicsFromServer(); - }; - useEffect(() => { - topicsFetchingFromServer(); + if (markers.length > 0) { + removeMarkers(); + removeInfowindows(); + } }, []); - if (!(popularTopics && nearTopics && newestTopics)) return <>; - - if ( - popularTopics.length === 0 && - nearTopics.length === 0 && - newestTopics.length === 0 - ) { - return ( - - - 추가하기 버튼을 눌러 토픽을 추가해보세요! - - - - 토픽이 없습니다. - - - ); - } - return ( <> - + }> + + + - + + }> + + + - + + }> + + + ); }; -const EmptyWrapper = styled.section` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: 1036px; - height: 100vh; - margin: 0 auto; -`; - const Wrapper = styled(Box)` width: 1036px; margin: 0 auto; `; -const ModalWrapper = styled.div` - width: 300px; - height: 300px; - background-color: white; -`; - export default Home; diff --git a/frontend/src/pages/KaKaoRedirectPage.tsx b/frontend/src/pages/KaKaoRedirectPage.tsx index 9c4fbbcb..03e30e0c 100644 --- a/frontend/src/pages/KaKaoRedirectPage.tsx +++ b/frontend/src/pages/KaKaoRedirectPage.tsx @@ -1,9 +1,9 @@ import { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; -import { getApi } from '../apis/getApi'; import { keyframes, styled } from 'styled-components'; import useNavigator from '../hooks/useNavigator'; import { DEFAULT_PROD_URL } from '../constants'; +import { getLoginApi } from '../apis/getLoginApi'; // const API_URL = // process.env.NODE_ENV === 'production' @@ -13,7 +13,7 @@ import { DEFAULT_PROD_URL } from '../constants'; export const handleOAuthKakao = async (code: string) => { try { const url = `${DEFAULT_PROD_URL}/oauth/login/kakao?code=${code}`; - const data = await getApi('login', url); + const data = await getLoginApi(url); localStorage.setItem('userToken', data.accessToken); localStorage.setItem('user', JSON.stringify(data.member)); diff --git a/frontend/src/pages/NewPin.tsx b/frontend/src/pages/NewPin.tsx index 73af429e..643123a0 100644 --- a/frontend/src/pages/NewPin.tsx +++ b/frontend/src/pages/NewPin.tsx @@ -6,7 +6,7 @@ import Button from '../components/common/Button'; import { postApi } from '../apis/postApi'; import { FormEvent, useContext, useEffect, useState } from 'react'; import { getApi } from '../apis/getApi'; -import { TopicType } from '../types/Topic'; +import { TopicCardProps } from '../types/Topic'; import useNavigator from '../hooks/useNavigator'; import { NewPinFormProps } from '../types/FormValues'; import useFormValues from '../hooks/useFormValues'; @@ -23,6 +23,7 @@ import { ModalContext } from '../context/ModalContext'; import Modal from '../components/Modal'; import { styled } from 'styled-components'; import ModalMyTopicList from '../components/ModalMyTopicList'; +import { getMapApi } from '../apis/getMapApi'; type NewPinFormValueType = Pick< NewPinFormProps, @@ -116,7 +117,7 @@ const NewPin = () => { if (!topic) { //토픽이 없으면 selectedTopic을 통해 토픽을 생성한다. postTopicId = selectedTopic?.topicId; - postName = selectedTopic?.topicTitle; + postName = selectedTopic?.topicName; } if (postTopicId) routePage(`/topics/${postTopicId}`, [postTopicId]); @@ -144,8 +145,7 @@ const NewPin = () => { const addr = data.roadAddress; // 주소 변수 //data를 통해 받아온 값을 Tmap api를 통해 위도와 경도를 구한다. - const { ConvertAdd } = await getApi( - 'tMap', + const { ConvertAdd } = await getMapApi( `https://apis.openapi.sk.com/tmap/geo/convertAddress?version=1&format=json&callback=result&searchTypCd=NtoO&appKey=P2MX6F1aaf428AbAyahIl9L8GsIlES04aXS9hgxo&coordType=WGS84GEO&reqAdd=${addr}`, ); const lat = ConvertAdd.oldLat; @@ -165,16 +165,13 @@ const NewPin = () => { useEffect(() => { const getTopicId = async () => { if (topicId && topicId.split(',').length === 1) { - const data = await getApi('default', `/topics/${topicId}`); + const data = await getApi(`/topics/${topicId}`); setTopic(data); } if (topicId && topicId.split(',').length > 1) { - const topics = await getApi( - 'default', - `/topics/ids?ids=${topicId}`, - ); + const topics = await getApi(`/topics/ids?ids=${topicId}`); setTopic(topics); } @@ -218,8 +215,8 @@ const NewPin = () => { > {topic?.name ? topic?.name - : selectedTopic?.topicTitle - ? selectedTopic?.topicTitle + : selectedTopic?.topicName + ? selectedTopic?.topicName : '지도를 선택해주세요.'} @@ -325,9 +322,9 @@ const ModalContentsWrapper = styled.div` width: 100%; height: 100%; background-color: white; - - text-align: center; - + display: flex; + flex-direction: column; + align-items: center; overflow: scroll; `; diff --git a/frontend/src/pages/NewTopic.tsx b/frontend/src/pages/NewTopic.tsx index 6793b862..5acb370e 100644 --- a/frontend/src/pages/NewTopic.tsx +++ b/frontend/src/pages/NewTopic.tsx @@ -18,7 +18,7 @@ import Modal from '../components/Modal'; import { styled } from 'styled-components'; import { ModalContext } from '../context/ModalContext'; import { getApi } from '../apis/getApi'; -import { Member } from '../types/Login'; +import { MemberProps } from '../types/Login'; import Checkbox from '../components/common/CheckBox'; import { TagContext } from '../context/TagContext'; @@ -41,11 +41,11 @@ const NewTopic = () => { const { navbarHighlights: _ } = useSetNavbarHighlight('addMapOrPin'); const { setTags } = useContext(TagContext); - const [members, setMembers] = useState([]); + const [members, setMembers] = useState([]); useEffect(() => { const getMemberData = async () => { - const memberData = await getApi('default', `/members`); + const memberData = await getApi(`/members`); setMembers(memberData); }; @@ -57,7 +57,7 @@ const NewTopic = () => { const [checkedMemberIds, setCheckedMemberIds] = useState([]); const handleChecked = (isChecked: boolean, id: number) => - setCheckedMemberIds((prev: Member['id'][]) => + setCheckedMemberIds((prev: MemberProps['id'][]) => isChecked ? [...prev, id] : prev.filter((n: number) => n !== id), ); diff --git a/frontend/src/components/NotFound/index.tsx b/frontend/src/pages/NotFound.tsx similarity index 82% rename from frontend/src/components/NotFound/index.tsx rename to frontend/src/pages/NotFound.tsx index 706c5148..4cca6c17 100644 --- a/frontend/src/components/NotFound/index.tsx +++ b/frontend/src/pages/NotFound.tsx @@ -1,10 +1,10 @@ import { styled } from 'styled-components'; -import NotFoundIcon from '../../assets/NotFoundIcon.svg'; -import useNavigator from '../../hooks/useNavigator'; -import Button from '../common/Button'; -import Flex from '../common/Flex'; -import Space from '../common/Space'; -import Text from '../common/Text'; +import NotFoundIcon from '../assets/NotFoundIcon.svg'; +import useNavigator from '../hooks/useNavigator'; +import Button from '../components/common/Button'; +import Flex from '../components/common/Flex'; +import Space from '../components/common/Space'; +import Text from '../components/common/Text'; const NotFound = () => { const { routePage } = useNavigator(); diff --git a/frontend/src/pages/PinDetail.tsx b/frontend/src/pages/PinDetail.tsx index 11cac986..30ef43a1 100644 --- a/frontend/src/pages/PinDetail.tsx +++ b/frontend/src/pages/PinDetail.tsx @@ -2,7 +2,7 @@ import Flex from '../components/common/Flex'; import Space from '../components/common/Space'; import Text from '../components/common/Text'; import { useContext, useEffect, useState } from 'react'; -import { PinType } from '../types/Pin'; +import { PinProps } from '../types/Pin'; import { getApi } from '../apis/getApi'; import { useSearchParams } from 'react-router-dom'; import Box from '../components/common/Box'; @@ -32,7 +32,7 @@ const PinDetail = ({ setIsEditPinDetail, }: PinDetailProps) => { const [searchParams, setSearchParams] = useSearchParams(); - const [pin, setPin] = useState(null); + const [pin, setPin] = useState(null); const [selectedTopic, setSelectedTopic] = useState(null); //토픽이 없을 때 사용하는 변수 const { showToast } = useToast(); const { @@ -59,7 +59,7 @@ const PinDetail = ({ useEffect(() => { const getPinData = async () => { - const pinData = await getApi('default', `/pins/${pinId}`); + const pinData = await getApi(`/pins/${pinId}`); setPin(pinData); setFormValues({ name: pinData.name, @@ -238,10 +238,10 @@ const ShareButton = styled(Button)` const ModalContentsWrapper = styled.div` width: 100%; height: 100%; + display: flex; + flex-direction: column; + align-items: center; background-color: white; - - text-align: center; - overflow: scroll; `; diff --git a/frontend/src/pages/Profile.tsx b/frontend/src/pages/Profile.tsx index c6759446..c2a76b89 100644 --- a/frontend/src/pages/Profile.tsx +++ b/frontend/src/pages/Profile.tsx @@ -1,39 +1,73 @@ -import { useState } from 'react'; import { styled } from 'styled-components'; import Box from '../components/common/Box'; import Flex from '../components/common/Flex'; import Space from '../components/common/Space'; import MyInfo from '../components/MyInfo'; -import MyInfoContainer from '../components/MyInfoContainer'; -import useNavigator from '../hooks/useNavigator'; import useSetNavbarHighlight from '../hooks/useSetNavbarHighlight'; import useSetLayoutWidth from '../hooks/useSetLayoutWidth'; import { FULLSCREEN } from '../constants'; +import TopicCardContainerSkeleton from '../components/Skeletons/TopicListSkeleton'; +import { Suspense, lazy } from 'react'; +import Text from '../components/common/Text'; +import useNavigator from '../hooks/useNavigator'; + +const TopicCardList = lazy(() => import('../components/TopicCardList')); const Profile = () => { const { routePage } = useNavigator(); - const { width: _ } = useSetLayoutWidth(FULLSCREEN); - const { navbarHighlights: __ } = useSetNavbarHighlight('profile'); + useSetLayoutWidth(FULLSCREEN); + useSetNavbarHighlight('profile'); - const goToPopularTopics = () => { - routePage('/see-all/popularity'); + const goToNewTopic = () => { + routePage('/new-topic'); }; return ( - + + - - + + + + + 나의 지도 + + + + 내가 만든 지도를 확인해보세요. + + + + + + + }> + + + ); }; -const ProfileWrapper = styled(Box)` +const Wrapper = styled(Box)` width: 1036px; margin: 0 auto; `; diff --git a/frontend/src/pages/SeeAllLatestTopics.tsx b/frontend/src/pages/SeeAllLatestTopics.tsx index 91b41a4e..4cd33932 100644 --- a/frontend/src/pages/SeeAllLatestTopics.tsx +++ b/frontend/src/pages/SeeAllLatestTopics.tsx @@ -6,15 +6,19 @@ import useSetLayoutWidth from '../hooks/useSetLayoutWidth'; import useSetNavbarHighlight from '../hooks/useSetNavbarHighlight'; import Box from '../components/common/Box'; import { Suspense, lazy } from 'react'; -import TopicCardListSkeleton from '../components/TopicCardList/TopicCardListSkeleton'; +import TopicCardContainerSkeleton from '../components/Skeletons/TopicListSkeleton'; +import useNavigator from '../hooks/useNavigator'; -const SeeAllCardList = lazy(() => import('../components/SeeAllCardList')); - -const url = '/topics/newest'; +const TopicCardList = lazy(() => import('../components/TopicCardList')); const SeeAllLatestTopics = () => { - const { width: _ } = useSetLayoutWidth(FULLSCREEN); - const { navbarHighlights: __ } = useSetNavbarHighlight('home'); + const { routePage } = useNavigator(); + useSetLayoutWidth(FULLSCREEN); + useSetNavbarHighlight('home'); + + const goToHome = () => { + routePage('/'); + }; return ( @@ -25,8 +29,14 @@ const SeeAllLatestTopics = () => { - }> - + }> + ); diff --git a/frontend/src/pages/SeeAllNearTopics.tsx b/frontend/src/pages/SeeAllNearTopics.tsx index 9ffc0415..510b2f8e 100644 --- a/frontend/src/pages/SeeAllNearTopics.tsx +++ b/frontend/src/pages/SeeAllNearTopics.tsx @@ -5,16 +5,20 @@ import useSetNavbarHighlight from '../hooks/useSetNavbarHighlight'; import Box from '../components/common/Box'; import Space from '../components/common/Space'; import Text from '../components/common/Text'; -import TopicCardListSkeleton from '../components/TopicCardList/TopicCardListSkeleton'; +import TopicCardContainerSkeleton from '../components/Skeletons/TopicListSkeleton'; import { Suspense, lazy } from 'react'; +import useNavigator from '../hooks/useNavigator'; -const SeeAllCardList = lazy(() => import('../components/SeeAllCardList')); - -const url = '/topics'; +const TopicCardList = lazy(() => import('../components/TopicCardList')); const SeeAllNearTopics = () => { - const { width: _ } = useSetLayoutWidth(FULLSCREEN); - const { navbarHighlights: __ } = useSetNavbarHighlight('home'); + const { routePage } = useNavigator(); + useSetLayoutWidth(FULLSCREEN); + useSetNavbarHighlight('home'); + + const goToHome = () => { + routePage('/new-topic'); + }; return ( @@ -25,8 +29,14 @@ const SeeAllNearTopics = () => { - }> - + }> + ); diff --git a/frontend/src/pages/SeeAllPopularTopics.tsx b/frontend/src/pages/SeeAllPopularTopics.tsx index 882f13b4..0f1a717d 100644 --- a/frontend/src/pages/SeeAllPopularTopics.tsx +++ b/frontend/src/pages/SeeAllPopularTopics.tsx @@ -6,15 +6,19 @@ import useSetLayoutWidth from '../hooks/useSetLayoutWidth'; import { FULLSCREEN } from '../constants'; import useSetNavbarHighlight from '../hooks/useSetNavbarHighlight'; import { Suspense, lazy } from 'react'; -import TopicCardListSkeleton from '../components/TopicCardList/TopicCardListSkeleton'; +import TopicCardContainerSkeleton from '../components/Skeletons/TopicListSkeleton'; +import useNavigator from '../hooks/useNavigator'; -const SeeAllCardList = lazy(() => import('../components/SeeAllCardList')); - -const url = '/topics/bests'; +const TopicCardList = lazy(() => import('../components/TopicCardList')); const SeeAllTopics = () => { - const { width: _ } = useSetLayoutWidth(FULLSCREEN); - const { navbarHighlights: __ } = useSetNavbarHighlight('home'); + const { routePage } = useNavigator(); + useSetLayoutWidth(FULLSCREEN); + useSetNavbarHighlight('home'); + + const goToHome = () => { + routePage('/'); + }; return ( @@ -25,8 +29,14 @@ const SeeAllTopics = () => { - }> - + }> + ); diff --git a/frontend/src/pages/SeeAllTopics.tsx b/frontend/src/pages/SeeAllTopics.tsx deleted file mode 100644 index 14674343..00000000 --- a/frontend/src/pages/SeeAllTopics.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useLocation } from 'react-router-dom'; -import Text from '../components/common/Text'; -import { lazy, Suspense } from 'react'; -import SeeAllCardListSkeleton from '../components/SeeAllCardList/SeeAllCardListSkeleton'; - -const SeeAllCardList = lazy(() => import('../components/SeeAllCardList')); - -const SeeAllTopics = () => { - const { state } = useLocation(); - const url = state.split('|')[0]; - const title = state.split('|')[1]; - - return ( - <> - - {title} - - }> - - - - ); -}; - -export default SeeAllTopics; diff --git a/frontend/src/pages/SeeTogetherTopics.tsx b/frontend/src/pages/SeeTogetherTopics.tsx index 38d0d9b1..c949b83c 100644 --- a/frontend/src/pages/SeeTogetherTopics.tsx +++ b/frontend/src/pages/SeeTogetherTopics.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from 'react'; +import { useContext } from 'react'; import { SIDEBAR } from '../constants'; import useNavigator from '../hooks/useNavigator'; import useSetLayoutWidth from '../hooks/useSetLayoutWidth'; @@ -14,15 +14,15 @@ import useSetNavbarHighlight from '../hooks/useSetNavbarHighlight'; import { deleteApi } from '../apis/deleteApi'; import useToast from '../hooks/useToast'; import { getApi } from '../apis/getApi'; -import { TopicType } from '../types/Topic'; +import { TopicCardProps } from '../types/Topic'; const SeeTogetherTopics = () => { const { routePage } = useNavigator(); const { width } = useSetLayoutWidth(SIDEBAR); - const { navbarHighlights } = useSetNavbarHighlight('seeTogether'); const { seeTogetherTopics, setSeeTogetherTopics } = useContext(SeeTogetherContext); const { showToast } = useToast(); + useSetNavbarHighlight('seeTogether'); const goToHome = () => { routePage('/'); @@ -30,7 +30,7 @@ const SeeTogetherTopics = () => { const setTopicsFromServer = async () => { try { - const topics = await getApi('default', '/members/my/atlas'); + const topics = await getApi('/members/my/atlas'); setSeeTogetherTopics(topics); } catch { @@ -93,6 +93,7 @@ const SeeTogetherTopics = () => { {seeTogetherTopics.map((topic, idx) => (
    { updatedAt={topic.updatedAt} isInAtlas={topic.isInAtlas} isBookmarked={topic.isBookmarked} - setTopicsFromServer={setTopicsFromServer} + getTopicsFromServer={setTopicsFromServer} /> {idx !== seeTogetherTopics.length - 1 ? : <>}
diff --git a/frontend/src/pages/SelectedTopic.tsx b/frontend/src/pages/SelectedTopic.tsx index ac0723ad..ec63bb2a 100644 --- a/frontend/src/pages/SelectedTopic.tsx +++ b/frontend/src/pages/SelectedTopic.tsx @@ -9,7 +9,7 @@ import { import { styled } from 'styled-components'; import Space from '../components/common/Space'; import Flex from '../components/common/Flex'; -import { TopicDetailType } from '../types/Topic'; +import { TopicDetailProps } from '../types/Topic'; import { useParams, useSearchParams } from 'react-router-dom'; import theme from '../themes'; import PinDetail from './PinDetail'; @@ -20,16 +20,16 @@ import useNavigator from '../hooks/useNavigator'; import useSetLayoutWidth from '../hooks/useSetLayoutWidth'; import { LAYOUT_PADDING, SIDEBAR } from '../constants'; import useSetNavbarHighlight from '../hooks/useSetNavbarHighlight'; -import PinsOfTopicSkeleton from '../components/PinsOfTopic/PinsOfTopicSkeleton'; +import PinsOfTopicSkeleton from '../components/Skeletons/PinsOfTopicSkeleton'; import { TagContext } from '../context/TagContext'; -import { PinType } from '../types/Pin'; +import { PinProps } from '../types/Pin'; const PinsOfTopic = lazy(() => import('../components/PinsOfTopic')); const SelectedTopic = () => { const { topicId } = useParams(); const [searchParams, _] = useSearchParams(); - const [topicDetails, setTopicDetails] = useState( + const [topicDetails, setTopicDetails] = useState( null, ); const [selectedPinId, setSelectedPinId] = useState(null); @@ -42,10 +42,7 @@ const SelectedTopic = () => { const { navbarHighlights: __ } = useSetNavbarHighlight(''); const getAndSetDataFromServer = async () => { - const data = await getApi( - 'default', - `/topics/ids?ids=${topicId}`, - ); + const data = await getApi(`/topics/ids?ids=${topicId}`); const topicHashmap = new Map([]); @@ -54,8 +51,8 @@ const SelectedTopic = () => { // 각 topic의 pin들의 좌표를 가져옴 const newCoordinates: any = []; - data.forEach((topic: TopicDetailType) => { - topic.pins.forEach((pin: PinType) => { + data.forEach((topic: TopicDetailProps) => { + topic.pins.forEach((pin: PinProps) => { newCoordinates.push({ id: pin.id, topicId: topic.id, @@ -68,13 +65,13 @@ const SelectedTopic = () => { setCoordinates(newCoordinates); - data.forEach((topicDetailFromData: TopicDetailType) => + data.forEach((topicDetailFromData: TopicDetailProps) => topicHashmap.set(`${topicDetailFromData.id}`, topicDetailFromData), ); const topicDetailFromData = topicId ?.split(',') - .map((number) => topicHashmap.get(number)) as TopicDetailType[]; + .map((number) => topicHashmap.get(number)) as TopicDetailProps[]; if (!topicDetailFromData) return; diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index 21aea0a5..ca5a572b 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -7,13 +7,13 @@ import SelectedTopic from './pages/SelectedTopic'; import SeeAllPopularTopics from './pages/SeeAllPopularTopics'; import SeeAllNearTopics from './pages/SeeAllNearTopics'; import SeeAllLatestTopics from './pages/SeeAllLatestTopics'; -import KakaoRedirectPage from './pages/KaKaoRedirectPage'; +import KakaoRedirectPage from './pages/KakaoRedirectPage'; import { ReactNode } from 'react'; import AuthLayout from './components/Layout/AuthLayout'; -import NotFound from './components/NotFound'; +import NotFound from './pages/NotFound'; import SeeTogetherTopics from './pages/SeeTogetherTopics'; import Profile from './pages/Profile'; -import LoginError from './pages/LoginError'; +import AskLogin from './pages/AskLogin'; import Bookmark from './pages/Bookmark'; interface routeElement { @@ -85,7 +85,7 @@ const routes: routeElement[] = [ }, { path: '/askLogin', - element: , + element: , withAuth: false, }, { diff --git a/frontend/src/types/Api.ts b/frontend/src/types/Api.ts new file mode 100644 index 00000000..9aea7dea --- /dev/null +++ b/frontend/src/types/Api.ts @@ -0,0 +1 @@ +export type ContentTypeType = 'application/json' | 'x-www-form-urlencoded'; diff --git a/frontend/src/types/Bookmarks.ts b/frontend/src/types/Bookmarks.ts index e0f180d0..d9f75def 100644 --- a/frontend/src/types/Bookmarks.ts +++ b/frontend/src/types/Bookmarks.ts @@ -1,4 +1,4 @@ -export interface BookmarksType { +export interface BookmarksProps { id: number; name: string; image: string; diff --git a/frontend/src/types/Login.ts b/frontend/src/types/Login.ts index a79f2a6c..23d871ab 100644 --- a/frontend/src/types/Login.ts +++ b/frontend/src/types/Login.ts @@ -1,4 +1,4 @@ -export interface Member { +export interface MemberProps { id: number; nickName: string; email: string; @@ -6,7 +6,7 @@ export interface Member { updatedAt: string; } -export interface LoginResponse { +export interface LoginResponseProps { accessToken: string; - member: Member; + member: MemberProps; } diff --git a/frontend/src/types/Map.ts b/frontend/src/types/Map.ts new file mode 100644 index 00000000..ba5b370a --- /dev/null +++ b/frontend/src/types/Map.ts @@ -0,0 +1,65 @@ +export interface MapAddressProps { + addressInfo: AddressInfoProps; +} + +export interface AddressInfoProps { + addressType: string; + adminDong: string; + adminDongCode: string; + buildingIndex: string; + buildingName: string; + bunji: string; + city_do: string; + eup_myun: string; + fullAddress: string; + gu_gun: string; + legalDong: string; + legalDongCode: string; + mappingDistance: string; + ri: string; + roadCode: string; + roadName: string; +} + +export interface MapProps { + isMobile: boolean; + mouseClickFlag: boolean; + name: string; + _data: MapDataProps; + _object_: MapObjectProps; + _status: MapStatusProps; +} + +export interface MapDataProps { + mapType: number; + maxBounds: {}; + target: string; + container: {}; + vsmMap: {}; + vsmOptions: {}; + minZoomLimit: number; + maxZoomLimit: number; + options: MapOptionsProps; +} + +export interface MapObjectProps { + eventListeners: {}; + getHandlers: string; + fireEvent: string; +} + +export interface MapStatusProps { + zoom: number; + center: {}; + width: number; + height: number; +} + +export interface MapOptionsProps { + draggable: boolean; + measureControl: boolean; + naviControl: boolean; + pinchZoom: boolean; + scaleBar: boolean; + scrollwheel: boolean; +} diff --git a/frontend/src/types/MyInfo.ts b/frontend/src/types/MyInfo.ts deleted file mode 100644 index 8e3b8619..00000000 --- a/frontend/src/types/MyInfo.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface MyInfoType { - name: string; - email: string; -} - -export interface MyInfoTopicType { - id: number; - name: string; - image: string; - pinCount: number; - bookmarkCount: number; - isBookmarked: boolean; - updatedAt: string; -} - -export interface MyInfoPinType { - id: number; - name: string; - address: string; - description: string; - latitude: number; - longitude: number; -} diff --git a/frontend/src/types/Pin.ts b/frontend/src/types/Pin.ts index 1765fb41..8dda38fb 100644 --- a/frontend/src/types/Pin.ts +++ b/frontend/src/types/Pin.ts @@ -1,6 +1,7 @@ -export interface PinType { +export interface PinProps { id: number; name: string; + creator: string; address: string; description: string; latitude: number; diff --git a/frontend/src/types/Profile.ts b/frontend/src/types/Profile.ts new file mode 100644 index 00000000..16f579c8 --- /dev/null +++ b/frontend/src/types/Profile.ts @@ -0,0 +1,4 @@ +export interface ProfileProps { + name: string; + email: string; +} diff --git a/frontend/src/types/Topic.ts b/frontend/src/types/Topic.ts index 8a9dd25b..55759ed7 100644 --- a/frontend/src/types/Topic.ts +++ b/frontend/src/types/Topic.ts @@ -1,6 +1,6 @@ -import { PinType } from './Pin'; +import { PinProps } from './Pin'; -export interface TopicType { +export interface TopicCardProps { id: number; name: string; image: string; @@ -8,11 +8,11 @@ export interface TopicType { pinCount: number; bookmarkCount: number; updatedAt: string; - isInAtlas: false; - isBookmarked: false; + isInAtlas: boolean; + isBookmarked: boolean; } -export interface TopicDetailType { +export interface TopicDetailProps { id: number; image: string; name: string; @@ -21,14 +21,15 @@ export interface TopicDetailType { pinCount: number; bookmarkCount: number; updatedAt: string; - isInAtlas: false; - isBookmarked: false; - pins: PinType[]; + isInAtlas: boolean; + isBookmarked: boolean; + pins: PinProps[]; } -export interface ModalMyTopicType { +export interface ModalTopicCardProps { id: number; name: string; + creator: string; image: string; pinCount: number; bookmarkCount: number;