diff --git a/package-lock.json b/package-lock.json index b254e1a..ac0e3b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "browser-image-compression": "^2.0.2", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-intersection-observer": "^9.13.0", "react-kakao-maps-sdk": "^1.1.27", "react-query": "^3.39.3", "react-router-dom": "^6.24.0", @@ -16888,6 +16889,20 @@ "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", "dev": true }, + "node_modules/react-intersection-observer": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.13.0.tgz", + "integrity": "sha512-y0UvBfjDiXqC8h0EWccyaj4dVBWMxgEx0t5RGNzQsvkfvZwugnKwxpu70StY4ivzYuMajavwUDjH4LJyIki9Lw==", + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", diff --git a/package.json b/package.json index 1ab9cc8..38d2a1b 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "browser-image-compression": "^2.0.2", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-intersection-observer": "^9.13.0", "react-kakao-maps-sdk": "^1.1.27", "react-query": "^3.39.3", "react-router-dom": "^6.24.0", diff --git a/src/apis/editors/getEditorsData.ts b/src/apis/editors/getEditorsData.tsx similarity index 64% rename from src/apis/editors/getEditorsData.ts rename to src/apis/editors/getEditorsData.tsx index 024b45d..bd9a587 100644 --- a/src/apis/editors/getEditorsData.ts +++ b/src/apis/editors/getEditorsData.tsx @@ -1,12 +1,11 @@ -import instance from '../instance'; -import { EditorType } from '../../types/getEditors/EditorType'; -import { BaseResponse } from '../../types/BaseResponse'; +import instance from "../instance"; +import { EditorType } from "../../types/editors/EditorType"; +import { BaseResponse } from "../../types/BaseResponse"; export const getEditorDataWithToken = async () => { try { - const response = - await instance.get>('/home/editor'); - console.log('Data without Token:', response.data); + const response = await instance.get>('/home/editor'); + console.log('Data with Token:', response.data); return response.data.result || []; } catch (error) { console.error('Error fetching data without token:', error); diff --git a/src/apis/editors/fetchEditorsData.ts b/src/apis/editors/useGetEditorsData.tsx similarity index 70% rename from src/apis/editors/fetchEditorsData.ts rename to src/apis/editors/useGetEditorsData.tsx index ea7e7d9..d4d7cde 100644 --- a/src/apis/editors/fetchEditorsData.ts +++ b/src/apis/editors/useGetEditorsData.tsx @@ -1,8 +1,5 @@ -import { - getEditorDataWithToken, - getEditorDataWithoutToken, -} from './getEditorsData'; -import { EditorType } from '../../types/getEditors/EditorType'; +import { getEditorDataWithToken, getEditorDataWithoutToken } from "./getEditorsData"; +import { EditorType } from "../../types/editors/EditorType"; export const fetchEditorData = async ( token: string | undefined, diff --git a/src/apis/follow/getFollowing.tsx b/src/apis/follow/getFollowing.tsx new file mode 100644 index 0000000..2771238 --- /dev/null +++ b/src/apis/follow/getFollowing.tsx @@ -0,0 +1,14 @@ +import instance from "../instance"; +import { FollowingType } from "../../types/follow/FollowingType"; +import { BaseResponse } from "../../types/BaseResponse"; + +export const getFollowing = async () => { + try{ + const response = await instance.get>('following') + console.log('response', response) + return response.data.result || undefined + } catch (error) { + console.error('Error fetching data'); + return undefined; + } +} \ No newline at end of file diff --git a/src/apis/follow/postFollow.tsx b/src/apis/follow/postFollow.tsx new file mode 100644 index 0000000..b7546ba --- /dev/null +++ b/src/apis/follow/postFollow.tsx @@ -0,0 +1,6 @@ +import instance from "../instance"; +import { FollowType } from "../../types/follow/FollowType"; + +export const postFollow = async (newFollow: FollowType): Promise => { + return await instance.post('/follow', newFollow); +}; \ No newline at end of file diff --git a/src/apis/follow/useGetFollowing.tsx b/src/apis/follow/useGetFollowing.tsx new file mode 100644 index 0000000..636bd97 --- /dev/null +++ b/src/apis/follow/useGetFollowing.tsx @@ -0,0 +1,15 @@ +import { getFollowing } from "./getFollowing"; +import { FollowingType } from "../../types/follow/FollowingType"; + +export const fetchFollowing = async (token:string| undefined): Promise => { + try { + if(token) { + return await getFollowing(); + } else { + return undefined; + } + } catch (error) { + console.error('Error fetching data:', error); + throw new Error('정보를 불러올 수 없음.'); + } +} \ No newline at end of file diff --git a/src/apis/follow/usePostFollow.tsx b/src/apis/follow/usePostFollow.tsx new file mode 100644 index 0000000..d0a4e6e --- /dev/null +++ b/src/apis/follow/usePostFollow.tsx @@ -0,0 +1,23 @@ +// src/apis/follow/usePostFollow.ts +import { useMutation, useQueryClient } from 'react-query'; +import instance from '../instance'; +import { FollowType } from '../../types/follow/FollowType'; + +export const usePostFollow = () => { + const queryClient = useQueryClient(); + + const mutation = useMutation({ + mutationFn: async (followData: FollowType) => { + console.log('Sending request with data:', followData); + return await instance.post('/follow', followData); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['followData'] }); + }, + onError: (error: Error) => { + console.error('Follow request failed:', error); + }, + }); + + return mutation; +}; diff --git a/src/apis/keywords/fetchGetKeywords.tsx b/src/apis/keywords/fetchGetKeywords.tsx new file mode 100644 index 0000000..7c633b4 --- /dev/null +++ b/src/apis/keywords/fetchGetKeywords.tsx @@ -0,0 +1,64 @@ +import { useQuery } from 'react-query'; +import { getKeyword } from './getKeywords'; +import { KeywordType } from '../../types/keywords/KeywordType'; +import { useAllKeywordStore } from '../../stores/keywordStore'; +import { useKeywordStore } from '../../stores/keywordStore'; +import { fetchFollowing } from '../follow/useGetFollowing'; +import { useEffect } from 'react'; +import { useLocation } from 'react-router-dom'; + +export const useGetKeywords = (token: string | undefined) => { + const location = useLocation(); + const { setAllKeywordList, allKeywordList } = useAllKeywordStore(); + const {selectedList, setSelectedList} = useKeywordStore() + + const { data: followingData, isSuccess: isFollowingDataSuccess } = useQuery( + ['followingData', token], + () => fetchFollowing(token), + { + enabled: !!token, + refetchOnWindowFocus: false, + }, + ); + // 페이지 이동 시 재요청 방지를 위해 enabled를 false로 설정 + const { data, isSuccess, refetch } = useQuery( + 'keywordsData', + getKeyword, + { + refetchOnWindowFocus: false, + enabled: false, // 자동으로 데이터 요청 안 함 + }, + ); + + useEffect(() => { + if (allKeywordList.length === 0) { + refetch(); + } + }, [refetch, allKeywordList.length]); + + useEffect(() => { + if (isSuccess && data && allKeywordList.length === 0) { + let Keywords = data; + + if (!token && location.pathname === '/timeline') { + Keywords = data.map((keyword, index) => ({ + ...keyword, + selected: index < 2 ? true : keyword.selected, + })); + } else if (token &&followingData && followingData.users === undefined) { + Keywords = data.map((keyword, index) => ({ + ...keyword, + selected: index < 2 ? true : keyword.selected, + })); + } + + setAllKeywordList(Keywords); + + // Filter keywords with selected as true and set them to selectedList + const selectedKeywords = Keywords.filter(keyword => keyword.selected); + setSelectedList(selectedKeywords); + } + }, [isSuccess, data, setAllKeywordList, token, allKeywordList.length]); + + return { allKeywordList, refetch }; +}; diff --git a/src/apis/keywords/getKeywordMap.tsx b/src/apis/keywords/getKeywordMap.tsx new file mode 100644 index 0000000..497e536 --- /dev/null +++ b/src/apis/keywords/getKeywordMap.tsx @@ -0,0 +1,12 @@ +import { keywordMap } from "./keywordMap"; + +export const getKeywordMap = async (keyword: string) => { + try { + const response = await keywordMap(keyword); + console.log(response) + return response; + } catch (error) { + console.error('Error fetching keyword map:', error); + return undefined; + } +}; \ No newline at end of file diff --git a/src/apis/keywords/getKeywords.tsx b/src/apis/keywords/getKeywords.tsx new file mode 100644 index 0000000..5b35461 --- /dev/null +++ b/src/apis/keywords/getKeywords.tsx @@ -0,0 +1,20 @@ +import instance from "../instance"; +import { BaseResponse } from "../../types/BaseResponse"; + +export const getKeyword = async () => { + try { + const response = await instance.get>('home/keyword'); + console.log('keywords:',response.data) + const keywords = response.data.result || []; + + return keywords.map((title, index) => ({ + id: Math.random(), + title: title, + selected: false, + })) + + } catch (error) { + console.error('Error fetching data;',error); + return []; + } +} \ No newline at end of file diff --git a/src/apis/keywords/keywordMap.tsx b/src/apis/keywords/keywordMap.tsx new file mode 100644 index 0000000..14271aa --- /dev/null +++ b/src/apis/keywords/keywordMap.tsx @@ -0,0 +1,15 @@ +import instance from "../instance"; +import { APIKeywordMapType } from "../../types/keywords/APIKeywordMapType"; +import { BaseResponse } from "../../types/BaseResponse"; + +export const keywordMap = async (keyword:string) => { + try { + const response = await instance.get>(`home/map/keyword`, { + params: { keyword } + }) + return response.data.result; + }catch(error) { + console.error('Error fetching data:', error); + return undefined; + } +} \ No newline at end of file diff --git a/src/apis/mapData/exploreMap.tsx b/src/apis/mapData/exploreMap.tsx new file mode 100644 index 0000000..0b876f1 --- /dev/null +++ b/src/apis/mapData/exploreMap.tsx @@ -0,0 +1,36 @@ +import instance from "../instance"; +import { ExploreMapType } from "../../types/mapData/ExploreMapType"; +import { BaseResponse } from "../../types/BaseResponse"; +import { KeywordType } from "../../types/keywords/KeywordType"; +import { APIExploreMapType } from "../../types/mapData/APIExploreMapType "; + +export const exploreMap = async (searchType:string, size: number, page:number) => { + try { + const response = await instance.get> (`/map/search`, { + params: { + searchType, + size, + page + } + }) + + const results = response.data.result?.map((item) => { + const newKeyword : KeywordType[]= item.keyword.map((title: string, index) => ({ + id: index, + title: title, + selected: false, + })); + console.log('newKeyword:', newKeyword); + + return { + ...item, + keyword: newKeyword, + }; + }); + + return results; + } catch(error) { + console.error('Error fetchgin data:',error); + return undefined; + } +} \ No newline at end of file diff --git a/src/apis/mapData/followingMap.tsx b/src/apis/mapData/followingMap.tsx new file mode 100644 index 0000000..28b7a09 --- /dev/null +++ b/src/apis/mapData/followingMap.tsx @@ -0,0 +1,14 @@ +import instance from "../instance"; +import { FollowingMapType } from "../../types/mapData/FollowingMapType"; +import { BaseResponse } from "../../types/BaseResponse"; + +export const followingMap = async () => { + try { + const response = await instance.get>('/home/map') + console.log('followingmap:', response) + return response.data.result; + } catch (error) { + console.error('Error fetching data:',error); + return undefined; + } +} \ No newline at end of file diff --git a/src/apis/mapData/getExploreMap.tsx b/src/apis/mapData/getExploreMap.tsx new file mode 100644 index 0000000..857c034 --- /dev/null +++ b/src/apis/mapData/getExploreMap.tsx @@ -0,0 +1,12 @@ +import { exploreMap } from "./exploreMap"; + +export const getExploreMap = async(searchType:string, size: number, page:number) => { + try{ + const response = await exploreMap(searchType,size,page); + console.log(response) + return response; + } catch (error) { + console.error('Error fetching random map:',error); + return undefined; + } +} \ No newline at end of file diff --git a/src/apis/mapData/getFollowingMap.tsx b/src/apis/mapData/getFollowingMap.tsx new file mode 100644 index 0000000..f737ad7 --- /dev/null +++ b/src/apis/mapData/getFollowingMap.tsx @@ -0,0 +1,17 @@ +import { followingMap } from "./followingMap"; +import { FollowingMapType } from "../../types/mapData/FollowingMapType"; + +export const getFollowingMap = async (token: string | undefined): Promise => { + try { + if (!token) { + return undefined; + } + // 여기서 followingMap 함수가 token을 사용하여 데이터를 가져온다고 가정 + const response = await followingMap(); + return response; // 성공적으로 데이터를 반환 + } catch (error) { + console.error('Error fetching following map data:', error); + // 오류가 발생한 경우, undefined를 반환 + return undefined; + } +}; \ No newline at end of file diff --git a/src/components/explore/MapList.module.scss b/src/components/explore/MapList.module.scss index 0bf7374..5d0bbb6 100644 --- a/src/components/explore/MapList.module.scss +++ b/src/components/explore/MapList.module.scss @@ -33,8 +33,13 @@ } .editorImg { + display: flex; width: 60px; height: 60px; + justify-content: center; + align-items: center; + border-radius: 90.795px; + border: 0.908px solid var(--gray_100); } .editorInfo { diff --git a/src/components/explore/MapList.tsx b/src/components/explore/MapList.tsx index 4f38528..51684d1 100644 --- a/src/components/explore/MapList.tsx +++ b/src/components/explore/MapList.tsx @@ -1,80 +1,139 @@ import { MapType } from '../../types/MapType'; -import userImg from '../../assets/user.svg'; - +import user_default from '../../assets/img_user_default_profile.svg'; import styles from './MapList.module.scss'; -import { MapKeywordType } from '../../types/MapKeywordType'; -import MapKeywordCard from './MapKeywordCard'; import { useState } from 'react'; import { Link } from 'react-router-dom'; +import { ExploreMapType } from '../../types/mapData/ExploreMapType'; +import { KeywordType } from '../../types/keywords/KeywordType'; +import { MapsType } from '../../types/keywords/MapsType'; interface MapListProps { - map: MapType; - keyword: MapKeywordType[]; + map?: ExploreMapType; + keywordMap?: MapsType[]; + keyword: KeywordType[] | KeywordType; } -const MapList: React.FC = ({ map, keyword }) => { - const [selectedKeyword, setSelectedKeyword] = useState( - null, - ); +const MapList: React.FC = ({ map, keywordMap, keyword }) => { + const [selectedKeyword, setSelectedKeyword] = useState(''); - const handleSelectPills = (mapKeyword: MapKeywordType) => { - if (selectedKeyword?.keyword === mapKeyword.keyword) { - setSelectedKeyword(null); + const handleSelectPills = (mapKeyword: KeywordType) => { + if (selectedKeyword === mapKeyword.title) { + setSelectedKeyword(''); } else { - setSelectedKeyword(mapKeyword); + setSelectedKeyword(mapKeyword.title); } }; return (
-
- - Map - + {keywordMap !== undefined ? ( + keywordMap.map((mapItem: MapsType, index: number) => ( +
+
+ + Map + +
+ User +
+ {mapItem.nickname} + {mapItem.profileId} +
+
+
+ +
+
+
+ {mapItem.mapTitle} + + 지도 설명 API 추가 요청 + +
+ + 지도 설명 API 추가 요청 + +
-
- User -
- {map.owner?.name} - {map.owner?.userId} +
+ {/* {mapItem.keyword?.map((mapKeyword: KeywordType) => ( + + ))} */} +
+
-
-
+ )) + ) : ( +
+
+ + Map + -
-
-
- {map.name} - {map.address} +
+ User +
+ {map?.user.nickName} + {map?.user.profileId} +
+
- {map.discription} -
-
- {keyword?.map((mapKeyword: MapKeywordType, index: number) => ( - - ))} -
+
+
+
+ {map?.title} + {map?.region} +
+ {map?.description} +
-
- {selectedKeyword && ( - - )} +
+ {map?.keyword?.map((mapKeyword: KeywordType) => ( + + ))} +
+
-
+ )}
); }; diff --git a/src/components/explore/SearchPopUp.tsx b/src/components/explore/SearchPopUp.tsx index 11d83cc..758bd59 100644 --- a/src/components/explore/SearchPopUp.tsx +++ b/src/components/explore/SearchPopUp.tsx @@ -23,7 +23,7 @@ const SearchPopUp: React.FC = ({
); diff --git a/src/components/timeLine/Dimmed.module.scss b/src/components/timeLine/Dimmed.module.scss index 41e1f1c..a5efa09 100644 --- a/src/components/timeLine/Dimmed.module.scss +++ b/src/components/timeLine/Dimmed.module.scss @@ -2,8 +2,16 @@ position: fixed; top: 0; left: 0; - width: 100%; - height: 100%; + width: 100vw; + height: 100vh; background-color: rgba(0, 0, 0, 0.5); z-index: 1000; } + +.authContainer { + position: fixed; + z-index: 1100; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} diff --git a/src/components/timeLine/editorList/EditorList.tsx b/src/components/timeLine/editorList/EditorList.tsx index f8f49f3..87f1b8f 100644 --- a/src/components/timeLine/editorList/EditorList.tsx +++ b/src/components/timeLine/editorList/EditorList.tsx @@ -2,27 +2,27 @@ import React, { useEffect, useState } from 'react'; import { useQuery } from 'react-query'; import EditorProfileCard from './EditorProfileCard'; -import { fetchEditorData } from '../../../apis/editors/fetchEditorsData'; +import { fetchEditorData } from '../../../apis/editors/useGetEditorsData'; import useRegisterStore from '../../../stores/registerStore'; import styles from './EditorList.module.scss'; interface EditorListProps { className?: string; + isLog: boolean; + token: string|undefined; } -const EditorList: React.FC = ({ className }) => { - const token = useRegisterStore((state) => state.accessToken); +const EditorList: React.FC = ({ className, isLog, token }) => { - const { - data: editorData, - error, - isLoading, - refetch, - } = useQuery(['editorsData', token], () => fetchEditorData(token), { - enabled: true, - refetchOnWindowFocus: false, - }); + const { data: editorData, refetch } = useQuery( + ['editorsData', token], + () => fetchEditorData(token), + { + enabled: true, + refetchOnWindowFocus: false, + } + ); const handleRefreshClick = () => { refetch(); @@ -40,7 +40,7 @@ const EditorList: React.FC = ({ className }) => {
{editorData && editorData.map((editor) => ( - + ))}
diff --git a/src/components/timeLine/editorList/EditorProfileCard.module.scss b/src/components/timeLine/editorList/EditorProfileCard.module.scss index d2caf3c..c4f3486 100644 --- a/src/components/timeLine/editorList/EditorProfileCard.module.scss +++ b/src/components/timeLine/editorList/EditorProfileCard.module.scss @@ -51,3 +51,17 @@ line-height: 150%; color: var(--main); } + +.unfollowing { + display: flex; + padding: 2px 12px; + justify-content: center; + align-items: center; + border-radius: 4px; + background-color: #f2f5f8; + border: none; + font-size: 14px; + font-weight: 600; + line-height: 150%; + color: var(--main); +} diff --git a/src/components/timeLine/editorList/EditorProfileCard.tsx b/src/components/timeLine/editorList/EditorProfileCard.tsx index 4e61136..a84a205 100644 --- a/src/components/timeLine/editorList/EditorProfileCard.tsx +++ b/src/components/timeLine/editorList/EditorProfileCard.tsx @@ -1,31 +1,64 @@ -import React from 'react'; +import React, { useState } from 'react'; -import { EditorType } from '../../../types/getEditors/EditorType'; +import { EditorType } from '../../../types/editors/EditorType'; import useRegisterStore from '../../../stores/registerStore'; -import { RegisterStatus } from '../../../types/enum/RegisterStatus'; import styles from './EditorProfileCard.module.scss'; -import userImg from '../../../assets/img_user_default_profile.svg'; +import dimmedStyles from '../Dimmed.module.scss'; +import userImg from '../../../assets/img_user_default_profile.svg' +import AuthContainer from '../../login/AuthContainer'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { usePostFollow } from '../../../apis/follow/usePostFollow'; +import { FollowType } from '../../../types/follow/FollowType'; +import { FollowingType } from '../../../types/follow/FollowingType'; interface ProfileCardProps { Editor: EditorType; + token: string|undefined; + isLog: boolean; + } -const EditorProfileCard: React.FC = ({ Editor }) => { +const EditorProfileCard: React.FC = ({ Editor, token, isLog }) => { const { registerStatus, setLoginNeededStatus } = useRegisterStore(); + const [isOverlayVisible, setIsOverlayVisible] = useState(false); + const [pendingUser, setPendingUser] = useState(null); + const [isFollow, setIsFollow] = useState(false); + const navigate = useNavigate(); + const pathname = useLocation().pathname; + const mutation = usePostFollow(); - const isLoggedIn = () => { - return registerStatus === RegisterStatus.LOG_IN; - }; - const handleFollow = () => { - if (!isLoggedIn()) { + const handleFollow = (followingId:number) => { + if (!isLog) { + setPendingUser(followingId) setLoginNeededStatus(true); + setIsOverlayVisible(true); + } else { + const followData: FollowType = { + followingId: followingId, + } + mutation.mutate(followData); + setIsFollow(true); } }; + const handleClose = () => { + setLoginNeededStatus(false); + const prevUrl = pathname.split('?')[0]; + navigate(prevUrl); + setIsOverlayVisible(false); + }; + + return (
+ {isOverlayVisible && ( + <> +
+ + + )}
= ({ Editor }) => {
- +
); }; diff --git a/src/components/timeLine/keywordList/KeywordList.tsx b/src/components/timeLine/keywordList/KeywordList.tsx index fa6f6fa..75233c6 100644 --- a/src/components/timeLine/keywordList/KeywordList.tsx +++ b/src/components/timeLine/keywordList/KeywordList.tsx @@ -1,109 +1,67 @@ import React, { useEffect, useState } from 'react'; - -import { KeywordType } from '../../../types/KeywordType'; -import { - useKeywordStore, - useAllKeywordStore, -} from '../../../stores/keywordStore'; -import useRegisterStore from '../../../stores/registerStore'; -import { RegisterStatus } from '../../../types/enum/RegisterStatus'; - +import { KeywordType } from '../../../types/keywords/KeywordType'; +import { useKeywordStore, useAllKeywordStore } from '../../../stores/keywordStore'; +import { useLocation } from 'react-router-dom'; +import { useGetKeywords } from '../../../apis/keywords/fetchGetKeywords'; import styles from './KeywordList.module.scss'; - import ico_info from '../../../assets/ico_info_gray.svg'; -import { useLocation } from 'react-router-dom'; interface KeywordListProps { className?: string; + isLog: boolean; + token: string | undefined; } -const mockData: KeywordType[] = [ - { id: 1, title: '동대문시장 골목맛집', selected: false }, - { id: 2, title: '서울 타워 야경 관람', selected: false }, - { id: 3, title: '홍대 놀이터 스트리트 퍼포먼스', selected: false }, - { id: 4, title: '강남역 카페 투어', selected: false }, - { id: 5, title: '코엑스 쇼핑몰 쇼핑 추천', selected: false }, -]; - -const KeywordList: React.FC = ({ className }) => { - const { registerStatus } = useRegisterStore(); - const { selectedList, setSelectedList, removeSelectedList } = - useKeywordStore(); +const KeywordList: React.FC = ({ className, isLog, token }) => { + const { selectedList, setSelectedList } = useKeywordStore(); const { allKeywordList, setAllKeywordList } = useAllKeywordStore(); const [isRefresh, setIsRefresh] = useState(false); const [alert, setAlert] = useState(false); - const [isLog, setIsLog] = useState(false); const location = useLocation(); + const { refetch } = useGetKeywords(token); - const fetchKeywordData = async () => { - try { - // TODO: 실제 API 호출로 데이터 받아오기 - setAllKeywordList(mockData); - } catch (error) { - console.error('정보를 불러올 수 없음.'); - } - }; - useEffect(() => { - fetchKeywordData(); - }, [location.pathname]); + // useEffect(() => { + // console.log('allKeywordList:', allKeywordList) + // console.log('isLog',isLog); + // console.log('selected',selectedList) + // console.log(typeof token); + // console.log("followingData:",followingData?.users) + // },[allKeywordList, isLog, followingData]) - useEffect(() => { - if (registerStatus === RegisterStatus.LOG_IN) { - setIsLog(true); - } else { - setIsLog(false); - } - }, [registerStatus]); - - useEffect(() => { - if ( - !isLog && - selectedList.length === 0 && - location.pathname === '/timeline' - ) { - const selectedInit = allKeywordList - .slice(0, 2) - .map((item: KeywordType) => { - item.selected = !item.selected; - return item; - }); - setSelectedList(selectedInit); - } - }, [isLog, allKeywordList]); useEffect(() => { if (isRefresh) { - const falseKeyword = allKeywordList.filter( - (keyword: KeywordType) => !keyword.selected, - ); - - const refreshDatas = allKeywordList.filter( - (keyword: KeywordType) => - !falseKeyword.some( - (falseKeywordItem: KeywordType) => - falseKeywordItem.id === keyword.id, - ), + const refreshKeyword = allKeywordList.filter((refresh) => + !selectedList.some((select) => refresh.title === select.title) ); - const updatedKeywordList = [...selectedList, ...falseKeyword]; - - if ( - JSON.stringify(allKeywordList) !== JSON.stringify(updatedKeywordList) - ) { + if(refreshKeyword.length > 5-selectedList.length) { + const newKeyword = refreshKeyword.slice(0,5-selectedList.length); + const updatedKeywordList = [...selectedList, ...newKeyword]; setAllKeywordList(updatedKeywordList); + } else { + setAllKeywordList([...selectedList, ...refreshKeyword]) } - setIsRefresh(false); } }, [isRefresh]); - const handleRefreshClick = () => { + const handleRefreshClick = async () => { if (selectedList.length === 5) { setAlert(true); } else { - setIsRefresh(true); + try { + const { data } = await refetch(); + if(data) { + setAllKeywordList(data); + } + + setIsRefresh(true); + } catch (error) { + console.error('Error fetching keywords:', error); + } } }; @@ -119,9 +77,11 @@ const KeywordList: React.FC = ({ className }) => { setSelectedList(updatedList.filter((keyword) => keyword.selected)); } else { selectedKeyword.selected = !selectedKeyword.selected; - const updatedList = allKeywordList.filter((item) => item.selected); - setAllKeywordList(allKeywordList); - setSelectedList(updatedList); + const updatedList = allKeywordList.map((item) => + item.id === selectedKeyword.id ? selectedKeyword : item + ); + setAllKeywordList(updatedList); + setSelectedList(updatedList.filter((item) => item.selected)); if (updatedList.length < 5) { setAlert(false); @@ -133,7 +93,7 @@ const KeywordList: React.FC = ({ className }) => {
추천 키워드
-
diff --git a/src/components/timeLine/leftBar/LeftBar.tsx b/src/components/timeLine/leftBar/LeftBar.tsx index 19ab187..2a83ad8 100644 --- a/src/components/timeLine/leftBar/LeftBar.tsx +++ b/src/components/timeLine/leftBar/LeftBar.tsx @@ -5,13 +5,18 @@ import KeywordList from '../keywordList/KeywordList'; import styles from './LeftBar.module.scss'; -const LeftBar: React.FC = () => { +interface LeftBarProps { + token: string|undefined; + isLog: boolean; +} + +const LeftBar: React.FC = ({token, isLog}) => { return ( <>
- +
- +
); diff --git a/src/components/timeLine/mapCard/MapCard.module.scss b/src/components/timeLine/mapCard/MapCard.module.scss index 6300108..edeb96c 100644 --- a/src/components/timeLine/mapCard/MapCard.module.scss +++ b/src/components/timeLine/mapCard/MapCard.module.scss @@ -2,11 +2,11 @@ display: flex; flex-direction: column; width: 1408px; - height: auto; + height: 393px; margin-bottom: 36px; - // overflow: hidden; + // overflow-x: hidden; max-width: 100%; } @@ -25,6 +25,7 @@ gap: 16px; margin-top: 16px; flex-wrap: nowrap; + overflow: hidden; } .map { diff --git a/src/components/timeLine/mapCard/MapCard.tsx b/src/components/timeLine/mapCard/MapCard.tsx index a45886a..0d57d8b 100644 --- a/src/components/timeLine/mapCard/MapCard.tsx +++ b/src/components/timeLine/mapCard/MapCard.tsx @@ -4,22 +4,34 @@ import { MapType } from '../../../types/MapType'; import styles from './MapCard.module.scss'; -import userImg from '../../../assets/user.svg'; +import user_default from '../../../assets/img_user_default_profile.svg'; import ico_carousel_backward from '../../../assets/ico_carousel_backward.svg'; import ico_carousel_forward from '../../../assets/ico_carousel_forward.svg'; import { Link } from 'react-router-dom'; +import { MapsType } from '../../../types/mapData/MapsType'; +import { MapsType as keywordMapsType} from '../../../types/keywords/MapsType'; +import { FollowingMapType } from '../../../types/mapData/FollowingMapType'; interface MapCardProps { - mapData: MapType[]; - isLog: Boolean; + mapData?: keywordMapsType[]; keyword: string; + followingMap?: MapsType[]; + userInfo?: FollowingMapType; } -const MapCard: React.FC = ({ mapData, isLog, keyword }) => { +const MapCard: React.FC = ({ + mapData, + keyword, + userInfo, + followingMap, +}) => { const INIT_RENDER: number = 3; const MAP_PER_PAGE: number = 2; const [renderMap, setRenderMap] = useState(INIT_RENDER); + useEffect(() => { + console.log('MapCard followingMap:', followingMap); + }, [followingMap]); const handleForward = () => { setRenderMap((preVisibleItems) => preVisibleItems - MAP_PER_PAGE); }; @@ -42,39 +54,66 @@ const MapCard: React.FC = ({ mapData, isLog, keyword }) => {
- {mapData.slice(renderMap - INIT_RENDER, renderMap).map((map) => ( - -
+ {followingMap + ?.slice(renderMap - INIT_RENDER, renderMap) + .map((map, index) => ( + +
+ {`${map.title}`} + +
+
+
{map.title}
+
{map.address}
+
+ +
+ +
+
+
+ + ))} + + {mapData?.slice(renderMap - INIT_RENDER, renderMap).map((map) => ( + +
{`${map.name}`}
-
{map.name}
-
{map.address}
+
{map.mapTitle}
+ {/*
{map.address}
*/}
- {map.editors.map((editor, index) => { - const offset = index * 15; - return ( {`${editor.name} - ); - })}
diff --git a/src/pages/Explore/Explore.module.scss b/src/pages/Explore/Explore.module.scss index 6cdb597..1e69fd1 100644 --- a/src/pages/Explore/Explore.module.scss +++ b/src/pages/Explore/Explore.module.scss @@ -1,20 +1,30 @@ .root { display: flex; + width: 100vw; + height: 100vh; + // overflow-y: hidden; + // overflow-x: hidden; } -.authContainer { - position: absolute; - z-index: 1100; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} +// .authContainer { +// position: absolute; +// z-index: 1100; +// top: 50%; +// left: 50%; +// transform: translate(-50%, -50%); +// } .leftBarWrapper { display: flex; flex-direction: row; } +.pageMain { + display: flex; + flex-direction: column; + align-items: flex-start; +} + .btnTitle { position: relative; display: flex; @@ -56,4 +66,6 @@ padding-left: 20px; padding-right: 12px; gap: 28px; + width: 100vw; + height: 100vh; } diff --git a/src/pages/Explore/Explore.tsx b/src/pages/Explore/Explore.tsx index 3b55a7c..f8c5cd8 100644 --- a/src/pages/Explore/Explore.tsx +++ b/src/pages/Explore/Explore.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useRef } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useInView } from 'react-intersection-observer'; import SideBar from '../../components/global/GlobalNavigationBar'; import HeaderNavigation from '../../components/timeLine/headerNavigation/HeaderNavigation'; @@ -7,45 +7,46 @@ import LeftBar from '../../components/timeLine/leftBar/LeftBar'; import SearchBar from '../../components/explore/SearchBar'; import SearchPopUp from '../../components/explore/SearchPopUp'; import useRegisterStore from '../../stores/registerStore'; -import AuthContainer from '../../components/login/AuthContainer'; +import { getExploreMap } from '../../apis/mapData/getExploreMap'; import MapList from '../../components/explore/MapList'; import ErrorPage from '../../components/explore/ErrorPage'; -import { RegisterStatus } from '../../types/enum/RegisterStatus'; import { useAllKeywordStore, useKeywordStore } from '../../stores/keywordStore'; import mockData from '../../components/timeLine/mapCard/MapModel'; import styles from './Explore.module.scss'; -import dimmedStyles from '../../components/timeLine/Dimmed.module.scss'; import ico_title_arrow_down from '../../assets/ico_title_arrow_down.svg'; import { MapType } from '../../types/MapType'; -import { KeywordType } from '../../types/KeywordType'; +import { KeywordType } from '../../types/keywords/KeywordType'; +import { useQuery } from 'react-query'; +import { KeywordMapType } from '../../types/keywords/KeywordMapType'; +import { getKeywordMap } from '../../apis/keywords/getKeywordMap'; +import { APIKeywordMapType } from '../../types/keywords/APIKeywordMapType'; const Explore: React.FC = () => { + const token = useRegisterStore((state) => state.accessToken); const [isCheck, setIsCheck] = useState('random'); const [text, setText] = useState(''); const [isPopup, setIsPopup] = useState(false); const [mapData, setMapData] = useState([]); + const [ref, inView] = useInView(); + const [page, setPage] = useState(0); + const [size, setSize] = useState(4); + const [isLog, setIsLog] = useState(false); + const [keywordMap, setKeywordMap] = useState( + undefined, + ); - const { selectedList, setSelectedList, removeSelectedList } = - useKeywordStore(); + const { selectedList, setSelectedList } = useKeywordStore(); const outside = useRef(null); - const navigate = useNavigate(); - const pathname = useLocation().pathname; - const { loginNeeded, registerStatus, setLoginNeededStatus } = - useRegisterStore(); const { allKeywordList, setAllKeywordList } = useAllKeywordStore(); - const [isOverlayVisible, setIsOverlayVisible] = useState(false); - const fetchMapData = async () => { - try { - setMapData(mockData); - } catch { - console.error('error'); - } - }; + const { data: ExploreMapData, refetch } = useQuery( + ['exploreMapData', isCheck, size, page], + () => getExploreMap(text, size, page), + ); const fetchKeywordSearch = async (keyword: KeywordType) => { try { @@ -62,7 +63,7 @@ const Explore: React.FC = () => { }; const handleRecentBtn = () => { - setIsCheck('recent'); + setIsCheck('date'); }; const handleMenuBtn = () => { @@ -75,22 +76,10 @@ const Explore: React.FC = () => { }, []); useEffect(() => { - console.log(selectedList); - }, [selectedList]); - - useEffect(() => { - if (registerStatus !== RegisterStatus.LOG_IN && loginNeeded) { - setIsOverlayVisible(true); - } else { - setIsOverlayVisible(false); + if (inView) { + setPage((num) => num + 1); } - }, [loginNeeded, registerStatus]); - - const handleClose = () => { - setLoginNeededStatus(false); - const prevUrl = pathname.split('?')[0]; - navigate(prevUrl); - }; + }, [page]); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -111,7 +100,6 @@ const Explore: React.FC = () => { }, [isPopup]); useEffect(() => { - fetchMapData(); setSelectedList([]); if (allKeywordList) { const keywords: KeywordType[] = allKeywordList.map((item) => { @@ -122,70 +110,98 @@ const Explore: React.FC = () => { }, []); useEffect(() => { - if (selectedList !== null && selectedList.length !== 0) { - const keyword = selectedList[0]; - setText(keyword.title); - fetchKeywordSearch(keyword); - } else { - setText(''); - fetchMapData(); - } + const fetchData = async () => { + if (selectedList !== null && selectedList.length !== 0) { + const keyword = selectedList[0].title; + setText(keyword); + const result = await getKeywordMap(keyword); + + if (result) { + const newResults: KeywordMapType[] = result.map( + (map: APIKeywordMapType) => { + const newKeyword: KeywordType = { + id: Math.random(), + title: map.keyword, + selected: false, + }; + + return { + ...map, + keyword: newKeyword, + }; + }, + ); + setKeywordMap(newResults); + } + } else { + setText(''); + setKeywordMap(undefined); + } + }; + + fetchData(); }, [selectedList]); return (
- {isOverlayVisible && ( - <> -
- - - )} -
- -
+ +
-
- {isCheck === 'random' && ( - 랜덤순 탐색 - )} - {isCheck === 'recent' && ( - 날짜순 탐색 - )} - - {isPopup && ( -
- -
- )} -
- -
- {mapData !== null && mapData.length !== 0 ? ( - mapData.map((map: MapType) => ( - - )) - ) : ( - - )} +
+
+ {isCheck === 'random' && ( + 랜덤순 탐색 + )} + {isCheck === 'date' && ( + 날짜순 탐색 + )} + + {isPopup && ( +
+ +
+ )} +
+ +
+ {ExploreMapData !== undefined && selectedList.length === 0 ? ( + ExploreMapData.map((item, index) => ( +
+ +
+ )) + ) : ( + + )} + + {selectedList.length !== 0 && keywordMap !== undefined ? ( + keywordMap.map((map: KeywordMapType) => { + const keyword = map.keyword; + const data = map.maps; + return ; + }) + ) : ( + + )} +
- {/* */}
diff --git a/src/pages/TimeLine/TimeLine.module.scss b/src/pages/TimeLine/TimeLine.module.scss index 14f046c..9705373 100644 --- a/src/pages/TimeLine/TimeLine.module.scss +++ b/src/pages/TimeLine/TimeLine.module.scss @@ -1,9 +1,10 @@ .timeLineContainer { display: flex; - width: 1920px; - height: 1080px; + width: 100vw; + height: 100vh; position: relative; + overflow-x: hidden; } .main { diff --git a/src/pages/TimeLine/TimeLine.tsx b/src/pages/TimeLine/TimeLine.tsx index ead74a9..47cead6 100644 --- a/src/pages/TimeLine/TimeLine.tsx +++ b/src/pages/TimeLine/TimeLine.tsx @@ -1,30 +1,34 @@ import React, { useEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; +import { useQuery } from 'react-query'; import HeaderNavigation from '../../components/timeLine/headerNavigation/HeaderNavigation'; import LeftBar from '../../components/timeLine/leftBar/LeftBar'; import { useKeywordStore } from '../../stores/keywordStore'; import useRegisterStore from '../../stores/registerStore'; -import { RegisterStatus } from '../../types/enum/RegisterStatus'; -import AuthContainer from '../../components/login/AuthContainer'; import MapCard from '../../components/timeLine/mapCard/MapCard'; import { MapType } from '../../types/MapType'; +import { KeywordMapType } from '../../types/keywords/KeywordMapType'; +import { getFollowingMap } from '../../apis/mapData/getFollowingMap'; +import { getKeywordMap } from '../../apis/keywords/getKeywordMap'; import mockData from '../../components/timeLine/mapCard/MapModel'; import styles from './TimeLine.module.scss'; -import dimmedStyles from '../../components/timeLine/Dimmed.module.scss'; import GlobalNavigationBar from '../../components/global/GlobalNavigationBar'; +import { keywordMap } from '../../apis/keywords/keywordMap'; +import { KeywordType } from '../../types/keywords/KeywordType'; +import { MapsType } from '../../types/mapData/MapsType'; +import { APIKeywordMapType } from '../../types/keywords/APIKeywordMapType'; const TimeLine: React.FC = () => { - const [mapData, setMapData] = useState<{ [key: string]: MapType[] }>({}); + const [keywordMap, setKeywordMap] = useState(undefined); const [isLog, setIsLog] = useState(false); const { selectedList } = useKeywordStore(); - const navigate = useNavigate(); - const pathname = useLocation().pathname; + const token = useRegisterStore((state) => state.accessToken); - const { loginNeeded, registerStatus, setLoginNeededStatus } = - useRegisterStore(); - const [isOverlayVisible, setIsOverlayVisible] = useState(false); + const { data: followingMapData } = useQuery(['followingMapData', token], () => + getFollowingMap(token), + ); useEffect(() => { const titleElement = document.getElementsByTagName('title')[0]; @@ -32,65 +36,70 @@ const TimeLine: React.FC = () => { }, []); useEffect(() => { - if (registerStatus !== RegisterStatus.LOG_IN && loginNeeded) { - setIsOverlayVisible(true); - setIsLog(false); - console.log('setDimmed(true)'); - } else { - setIsLog(true); - setIsOverlayVisible(false); - } - }, [loginNeeded, registerStatus]); - - const fetchMapData = async (keyword: string) => { - try { - const data = mockData.filter((map) => map.keywords.includes(keyword)); // mockData에서 필터링 - setMapData((prevState) => ({ ...prevState, [keyword]: data })); - } catch { - console.error(`Failed to fetch map data for keyword: ${keyword}`); - } - }; + setIsLog(!!token); + console.log('timeline followingmap', followingMapData); + }, [token]); useEffect(() => { - selectedList.forEach((keyword) => { - fetchMapData(keyword.title); - }); + const fetchData = async () => { + const allResults: APIKeywordMapType[] = []; + + for (const keyword of selectedList) { + const result = await getKeywordMap(keyword.title); + if (result !== undefined) { + allResults.push(...result); + } + } + + setKeywordMap(allResults !== undefined ? allResults : undefined); // 최종적으로 모인 데이터를 상태로 설정합니다. + }; + + fetchData(); }, [selectedList]); - const handleClose = () => { - setLoginNeededStatus(false); - const prevUrl = pathname.split('?')[0]; - navigate(prevUrl); - }; + useEffect(() => { + console.log(keywordMap) + },[keywordMap]) + return (
- {isOverlayVisible && ( - <> -
- - - )} -
- +
- {selectedList.map((keyword) => { - const data = mapData[keyword.title] || []; + {followingMapData ? ( + followingMapData.map((item) => { + const nickname = item.nickname; + const data = item.maps; + + return data.length > 0 ? ( + + ) : null; + }) + ) : ( + null + )} + + {keywordMap ? (keywordMap.map((item:APIKeywordMapType) => { + const keyword = item.keyword; + const data = item.maps; return data.length > 0 ? ( - - ) : null; - })} + /> + ) : null + })) : null}
- {/*
*/}
diff --git a/src/stores/keywordStore.ts b/src/stores/keywordStore.ts index 210460c..2e6701e 100644 --- a/src/stores/keywordStore.ts +++ b/src/stores/keywordStore.ts @@ -1,7 +1,7 @@ import { create } from 'zustand'; import { StateStorage, persist, createJSONStorage } from 'zustand/middleware'; import { getCookie, setCookie, removeCookie } from 'typescript-cookie'; -import { KeywordType } from '../types/KeywordType'; +import { KeywordType } from '../types/keywords/KeywordType'; const cookieStorage: StateStorage = { getItem: (name: string) => { diff --git a/src/types/BaseResponse.ts b/src/types/BaseResponse.ts index 3fa23fa..14e5cab 100644 --- a/src/types/BaseResponse.ts +++ b/src/types/BaseResponse.ts @@ -3,5 +3,5 @@ export type BaseResponse = { status: number; message: string; result: T; - timestamp: string; + timestamp?: string; }; diff --git a/src/types/getEditors/EditorType.ts b/src/types/editors/EditorType.ts similarity index 100% rename from src/types/getEditors/EditorType.ts rename to src/types/editors/EditorType.ts diff --git a/src/types/follow/FollowType.ts b/src/types/follow/FollowType.ts new file mode 100644 index 0000000..50c9f13 --- /dev/null +++ b/src/types/follow/FollowType.ts @@ -0,0 +1,3 @@ +export interface FollowType { + followingId: number; +} \ No newline at end of file diff --git a/src/types/follow/FollowingType.ts b/src/types/follow/FollowingType.ts new file mode 100644 index 0000000..67ebf18 --- /dev/null +++ b/src/types/follow/FollowingType.ts @@ -0,0 +1,6 @@ +import { UserType } from "./UserType" + +export interface FollowingType { + userId: number; + users : UserType[]; +} \ No newline at end of file diff --git a/src/types/follow/UserType.ts b/src/types/follow/UserType.ts new file mode 100644 index 0000000..4ce99fd --- /dev/null +++ b/src/types/follow/UserType.ts @@ -0,0 +1,5 @@ +export interface UserType { + userId: number; + nickname: string; + profileImageUrl : string | null; +} \ No newline at end of file diff --git a/src/types/keywords/APIKeywordMapType.ts b/src/types/keywords/APIKeywordMapType.ts new file mode 100644 index 0000000..ce2a35c --- /dev/null +++ b/src/types/keywords/APIKeywordMapType.ts @@ -0,0 +1,6 @@ +import { MapsType } from "./MapsType"; + +export interface APIKeywordMapType { + keyword: string; + maps: MapsType[]; +} diff --git a/src/types/keywords/APIKeywordType.ts b/src/types/keywords/APIKeywordType.ts new file mode 100644 index 0000000..5c99d33 --- /dev/null +++ b/src/types/keywords/APIKeywordType.ts @@ -0,0 +1,6 @@ +import { MapType } from "./MapType"; + +export interface KeywordTypes { + keyword : string; + maps: MapType[]; +} \ No newline at end of file diff --git a/src/types/keywords/KeywordMapType.ts b/src/types/keywords/KeywordMapType.ts new file mode 100644 index 0000000..dce7854 --- /dev/null +++ b/src/types/keywords/KeywordMapType.ts @@ -0,0 +1,7 @@ +import { KeywordType } from "./KeywordType"; +import { MapsType } from "./MapsType"; + +export interface KeywordMapType { + keyword: KeywordType; + maps: MapsType[]; +} diff --git a/src/types/KeywordType.ts b/src/types/keywords/KeywordType.ts similarity index 100% rename from src/types/KeywordType.ts rename to src/types/keywords/KeywordType.ts diff --git a/src/types/keywords/MapType.ts b/src/types/keywords/MapType.ts new file mode 100644 index 0000000..2110016 --- /dev/null +++ b/src/types/keywords/MapType.ts @@ -0,0 +1,7 @@ +export interface MapType { + nickname: string; + profileId: string; + userImage: string; + mapTitle: string; + mapImage: string; +} diff --git a/src/types/keywords/MapsType.ts b/src/types/keywords/MapsType.ts new file mode 100644 index 0000000..b59cdad --- /dev/null +++ b/src/types/keywords/MapsType.ts @@ -0,0 +1,7 @@ +export interface MapsType { + nickname: string; + profileId: string; + userImage: string; + mapTitle: string; + mapImage: string; +} diff --git a/src/types/mapData/APIExploreMapType .ts b/src/types/mapData/APIExploreMapType .ts new file mode 100644 index 0000000..ecb42a8 --- /dev/null +++ b/src/types/mapData/APIExploreMapType .ts @@ -0,0 +1,12 @@ +import { UserType } from "./UserType"; +import { KeywordType } from "../keywords/KeywordType"; + +export interface APIExploreMapType { + mapId: number; + imageUrl: string; + title: string; + region : string; + description: string; + user : UserType; + keyword:string[]; +} diff --git a/src/types/mapData/ExploreMapType.ts b/src/types/mapData/ExploreMapType.ts new file mode 100644 index 0000000..d4934d3 --- /dev/null +++ b/src/types/mapData/ExploreMapType.ts @@ -0,0 +1,12 @@ +import { UserType } from "./UserType"; +import { KeywordType } from "../keywords/KeywordType"; + +export interface ExploreMapType { + mapId: number; + imageUrl: string; + title: string; + region : string; + description: string; + user : UserType; + keyword: KeywordType[]; +} diff --git a/src/types/mapData/FollowingMapType.ts b/src/types/mapData/FollowingMapType.ts new file mode 100644 index 0000000..456aeb7 --- /dev/null +++ b/src/types/mapData/FollowingMapType.ts @@ -0,0 +1,8 @@ +import { MapsType } from "./MapsType"; + +export interface FollowingMapType { + nickname: string; + profileId: string; + userImages: string; + maps: MapsType[]; +} \ No newline at end of file diff --git a/src/types/mapData/MapsType.ts b/src/types/mapData/MapsType.ts new file mode 100644 index 0000000..dbd7809 --- /dev/null +++ b/src/types/mapData/MapsType.ts @@ -0,0 +1,5 @@ +export interface MapsType { + title:string; + address: string; + imageUrl: string; +} diff --git a/src/types/mapData/UserType.ts b/src/types/mapData/UserType.ts new file mode 100644 index 0000000..9cbc871 --- /dev/null +++ b/src/types/mapData/UserType.ts @@ -0,0 +1,5 @@ +export interface UserType { + imageUrl: string | null; + nickName: string; + profileId: string; +}