From fdc69117716db37858c83df86dffddfd0d5e602e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EC=84=9C=EB=A6=AC=ED=8B=B0=EC=BD=94=EB=8D=94?= <85999976+jisung24@users.noreply.github.com> Date: Wed, 29 Nov 2023 00:37:13 +0900 Subject: [PATCH 01/21] =?UTF-8?q?fix:=20scroll=20=EC=97=86=EC=95=A0?= =?UTF-8?q?=EA=B3=A0,=20=EC=A0=84=EC=B2=B4=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detailSchedule/DetailSchedulePage.tsx | 4 +-- src/pages/calendar/data.ts | 29 ------------------- 2 files changed, 2 insertions(+), 31 deletions(-) delete mode 100644 src/pages/calendar/data.ts diff --git a/src/pages/academy/detailSchedule/DetailSchedulePage.tsx b/src/pages/academy/detailSchedule/DetailSchedulePage.tsx index cf04596f..49db2fbe 100644 --- a/src/pages/academy/detailSchedule/DetailSchedulePage.tsx +++ b/src/pages/academy/detailSchedule/DetailSchedulePage.tsx @@ -70,8 +70,8 @@ const DetailSchedulePage = () => {
- -
+ +
{data?.academyInfo.address}
diff --git a/src/pages/calendar/data.ts b/src/pages/calendar/data.ts deleted file mode 100644 index da6f9d24..00000000 --- a/src/pages/calendar/data.ts +++ /dev/null @@ -1,29 +0,0 @@ -export const DATA = { - date: '2023-10-20 목요일', - academyInfo: { - academyName: '닥스 어학원', - address: '서울 강동구 상암로 12, 202호' - }, - lessonInfo: { - lessonName: '체르니 100 타파반', - capacity: '50명', - total_fee: '300000', - lessonTimes: [ - { - startTime: '', - endTime: '', - dayOfWeek: '' - }, - { - startTime: '', - endTime: '', - dayOfWeek: '' - } - ], - periodicity: '' - }, - childrenInfos: [ - { childId: '1L', childName: '', memo: '', imageUrl: '', dashBoardId: '' }, - { childId: '2L', childName: '', memo: '', imageUrl: '', dashBoardId: '' } - ] -} From a64dd0ff8d9febf9bfdc06b4492bcbaa48e9a241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=A0=EC=84=9C=EB=A6=AC=ED=8B=B0=EC=BD=94=EB=8D=94?= <85999976+jisung24@users.noreply.github.com> Date: Wed, 29 Nov 2023 00:45:40 +0900 Subject: [PATCH 02/21] =?UTF-8?q?fix:=20=EC=95=84=EC=BD=94=EB=94=94?= =?UTF-8?q?=EC=96=B8=20=EA=B0=80=EC=9A=B4=EB=8D=B0=EC=A0=95=EB=A0=AC,=20?= =?UTF-8?q?=EB=A9=94=EB=AA=A8=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detailSchedule/DetailSchedulePage.tsx | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/src/pages/academy/detailSchedule/DetailSchedulePage.tsx b/src/pages/academy/detailSchedule/DetailSchedulePage.tsx index 49db2fbe..df868d81 100644 --- a/src/pages/academy/detailSchedule/DetailSchedulePage.tsx +++ b/src/pages/academy/detailSchedule/DetailSchedulePage.tsx @@ -76,32 +76,35 @@ const DetailSchedulePage = () => {
-
- } - contentHeight={100} - content={ - <> - {'정원'}} - rightElement={ - - {data?.lessonInfo.capacity + '명 정원'} - - } - /> - {'금액'}} - rightElement={ - - {data?.lessonInfo.totalFee + '원'} - - } - /> - - } - /> +
+
+ } + contentHeight={100} + content={ + <> + {'정원'}} + rightElement={ + + {data?.lessonInfo.capacity + '명 정원'} + + } + /> + {'금액'}} + rightElement={ + + {data?.lessonInfo.totalFee + '원'} + + } + /> + + } + /> +

{'일정 수행중인 아이'}

@@ -126,13 +129,12 @@ const DetailSchedulePage = () => {

{'메모'}

    {data?.childrenInfos.map(({ memo, childId }) => ( -
  • - +
  • {memo}
  • ))}
-
+
- {'시설이 좋아요'} + {'시설이 좋아요 🏫'}
- {'가격이 착해요'} + {'교육비가 저렴해요 💰'}
- {'학습 관리가 꼼꼼해요'} + {'교육 관리가 철저해요 📝'}
- {'선생님이 좋아요'} + {'학생에 대한 애정 가득 💓'} +
+ +
+
+
+
+ {'등하원이 편리해요 🚌'}
diff --git a/src/components/BottomSheet/BottomSheetHeader.tsx b/src/components/BottomSheet/BottomSheetHeader.tsx index ded7c85d..dd113c72 100644 --- a/src/components/BottomSheet/BottomSheetHeader.tsx +++ b/src/components/BottomSheet/BottomSheetHeader.tsx @@ -1,26 +1,38 @@ import { useState } from 'react' +import { useMutation } from '@tanstack/react-query' import { LikeBlank, LikeFilled } from '@/assets/icon' +import { postLike } from '@/libs/api/mapapi/mapApi.ts' const BottomSheetHeader = ({ title, - isLike + isLike, + academyId }: { title: string isLike: boolean + academyId: number }) => { + const likeMutation = useMutation({ + mutationFn: (academyId: number) => postLike({ academyId: academyId }), + onSuccess: () => { + setLiked(!liked) + } + }) + const [liked, setLiked] = useState(isLike) //TODO: 좋아요 API 로직 추가 + return (

{title}

{liked ? ( setLiked(!liked)} + onClick={() => likeMutation.mutate(academyId)} /> ) : ( setLiked(!liked)} + onClick={() => likeMutation.mutate(academyId)} /> )}
diff --git a/src/components/common/bottomsheet/BottomSheet.tsx b/src/components/common/bottomsheet/BottomSheet.tsx index 3112043e..dcfcefc5 100644 --- a/src/components/common/bottomsheet/BottomSheet.tsx +++ b/src/components/common/bottomsheet/BottomSheet.tsx @@ -1,8 +1,10 @@ import { useState } from 'react' +import { useQuery } from '@tanstack/react-query' import BottomSheetContent from '@/components/BottomSheet/BottomSheetContent' import BottomSheetHeader from '@/components/BottomSheet/BottomSheetHeader' +import Loading from '@/components/Loading/Loading.tsx' import Spacing from '@/components/common/spacing/Spacing.tsx' -import { DetailAcademyResponse } from '@/libs/api/mapapi/mapApiType.ts' +import { getAcademyDetail } from '@/libs/api/mapapi/mapApi.ts' /** * @param title BottomSheet에 들어갈 Title을 입력합니다. @@ -13,15 +15,24 @@ interface BottomSheetProps { title: string address: string number: string - detailInfo: DetailAcademyResponse + academyId: number } const BottomSheet = ({ title = '학원명 입력', address, number, - detailInfo + academyId }: BottomSheetProps) => { const [expanded, setExpanded] = useState(false) + const { data: detailAcademy, isLoading } = useQuery({ + queryKey: ['academy', academyId], + queryFn: () => + getAcademyDetail({ + academyId: academyId + }), + enabled: academyId > -1 + }) + return ( <>
-
- - -
+ {isLoading && } + {detailAcademy && ( +
+ + +
+ )} ) From 91779994789542bbb048bd2aaa83d76f9de9e120 Mon Sep 17 00:00:00 2001 From: kimheeseok Date: Wed, 29 Nov 2023 11:14:58 +0900 Subject: [PATCH 05/21] =?UTF-8?q?hotfix:=20=EC=A2=8B=EC=95=84=EC=9A=94=20a?= =?UTF-8?q?pi=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20ReviewPercent=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libs/api/mapapi/mapApi.ts | 12 ++++++++++++ src/libs/api/mapapi/mapApiType.ts | 9 ++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/libs/api/mapapi/mapApi.ts b/src/libs/api/mapapi/mapApi.ts index 80b04c2f..b1bfbd4c 100644 --- a/src/libs/api/mapapi/mapApi.ts +++ b/src/libs/api/mapapi/mapApi.ts @@ -7,6 +7,7 @@ import { GetAcademysParams, GetLocationParam, GetTownParam, + LikeResponse, LocationResponse, ProvinceResponse, TownResponse @@ -64,3 +65,14 @@ export const getAcademyDetail = async ({ return res.data } + +export const postLike = async ({ + academyId +}: { + academyId: number +}): Promise => { + const res = await request.post(`/likes`, { + academyId: academyId + }) + return res.data +} diff --git a/src/libs/api/mapapi/mapApiType.ts b/src/libs/api/mapapi/mapApiType.ts index 65bcf8e4..06a30e39 100644 --- a/src/libs/api/mapapi/mapApiType.ts +++ b/src/libs/api/mapapi/mapApiType.ts @@ -71,6 +71,7 @@ interface ReviewPercent { cheapFeePercent: number goodManagementPercent: number lovelyTeachingPercent: number + shuttleAvailabilityCount: number } export interface DetailAcademyResponse { @@ -80,7 +81,7 @@ export interface DetailAcademyResponse { shuttleAvailability: string expectedFee: number updatedDate: string - areaOfExpertise: string + categories: string[] lessonGetResponses: { lessons: Lesson[] } @@ -108,3 +109,9 @@ export interface InfiniteScrollPage { paged: boolean unpaged: boolean } + +export interface LikeResponse { + likeId: number + memberId: number + academyId: number +} From 93c09e4090db3b60e8b2263adbca3ae4a5bad8b7 Mon Sep 17 00:00:00 2001 From: kimheeseok Date: Wed, 29 Nov 2023 11:15:45 +0900 Subject: [PATCH 06/21] =?UTF-8?q?hotfix:=20=EB=A7=B5=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EA=B2=80=EC=83=89=EB=B0=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EC=A7=80=EB=8F=84=EA=B0=80=20?= =?UTF-8?q?=EB=B3=B4=EC=9D=B4=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/map/MapSearchBar.tsx | 126 +++++++++++++++++++ src/components/map/NaverMap.tsx | 124 ++++++++----------- src/pages/map/MapPage.tsx | 182 ++++------------------------ 3 files changed, 199 insertions(+), 233 deletions(-) create mode 100644 src/components/map/MapSearchBar.tsx diff --git a/src/components/map/MapSearchBar.tsx b/src/components/map/MapSearchBar.tsx new file mode 100644 index 00000000..95d3b728 --- /dev/null +++ b/src/components/map/MapSearchBar.tsx @@ -0,0 +1,126 @@ +import { useEffect, useState } from 'react' +import { useInView } from 'react-intersection-observer' +import { useNavigate } from 'react-router-dom' +import { useQuery } from '@tanstack/react-query' +import Icon from '@/components/common/icon/Icon.tsx' +import Input from '@/components/common/inputbox/input/Input.tsx' +import { getAcademiesSearchResult } from '@/libs/api/academy/AcademyApi.ts' +import { SearchAcademiesResponse } from '@/libs/api/mapapi/mapApiType.ts' +import { useDebounce } from '@/libs/hooks/useDebounce.ts' + +const MapSearchBar = () => { + const navigate = useNavigate() + const [searchValue, setSearchValue] = useState('') + const [page, setPage] = useState(0) + const debounceValue = useDebounce(searchValue, 300) + const [searchList, setSearchList] = useState([]) + + const { ref, inView } = useInView({ + threshold: 1 + }) + + const { data: searchData } = useQuery({ + queryKey: ['searchData', debounceValue, page], + queryFn: () => getAcademiesSearchResult(debounceValue, page), + enabled: debounceValue !== '' + }) + + useEffect(() => { + if (searchData && page === 0) { + // 검색어만 바뀌었을때 + setSearchList([...searchData.content]) + } else if (searchData && page > 0) { + // 페이지가 바뀌었을때 + setSearchList((prevList) => [...prevList, ...searchData.content]) + } else { + // debounceValue가 ''일때 + setSearchList([]) + } + }, [searchData, page, debounceValue]) + + const isLast = searchData?.last || false + const updatePage = () => { + setPage(page + 1) + } + + //검색어가 바뀔때마다 페이지도 0으로 맞춰 api를 호출 + const updateSearchValue = (value: string) => { + setSearchValue(value) + setPage(0) + } + + const observer = ( +
+ ) + + useEffect(() => { + if (isLast) { + return + } else if (inView) { + updatePage() + } + }, [inView]) + + return ( +
+
+ { + updateSearchValue(e.target.value) + }} + /> +
0 + ? 'rounded-lg border-blue-350 border mb-4 mt-2' + : '' + }`}> + {searchList.length > 0 && ( +
+ {searchList.map((data, index) => ( +
{ + console.log(data) + }}> + +
+
+ {data.academyName} +
+
+ {data.address} +
+
+
+ ))} + {observer} +
+ )} +
+
+
navigate('/map/filter')}> + + {'필터'} +
+
+ ) +} + +export default MapSearchBar diff --git a/src/components/map/NaverMap.tsx b/src/components/map/NaverMap.tsx index 85fde460..cb848f73 100644 --- a/src/components/map/NaverMap.tsx +++ b/src/components/map/NaverMap.tsx @@ -1,17 +1,14 @@ import { useCallback, useEffect, useRef, useState } from 'react' -import { useQuery } from '@tanstack/react-query' +import { useNavigate } from 'react-router-dom' import { useAtom } from 'jotai/index' -import { SetLocationProps } from '../../types/mapPage.ts' -import BottomSheet from '@/components/common/bottomsheet/BottomSheet.tsx' import Icon from '@/components/common/icon/Icon.tsx' import { DefaultMapOption, initSelectAcademy, Marker } from '@/components/map/constants.ts' -import { getAcademyDetail } from '@/libs/api/mapapi/mapApi.ts' import { Academy } from '@/libs/api/mapapi/mapApiType.ts' -import { selectAcademyAtom } from '@/libs/store/mapInfoAtom.ts' +import { mapInfoAtom, selectAcademyAtom } from '@/libs/store/mapInfoAtom.ts' import throttle from '@/libs/utils/throttle.ts' /** @@ -19,45 +16,16 @@ import throttle from '@/libs/utils/throttle.ts' * * **/ interface NaverMapProps { - latitude: number - longitude: number academyList: Academy[] - setLocation: ({ latitude, longitude }: SetLocationProps) => void - searchAcademy: number //나중에 제외해야합니다. } -const NaverMap = ({ - latitude, - longitude, - academyList, - setLocation, - searchAcademy -}: NaverMapProps) => { +const NaverMap = ({ academyList }: NaverMapProps) => { const mapRef = useRef(null) + const markerRef = useRef([]) + const [mapInfo, setMapInfo] = useAtom(mapInfoAtom) const [selectAcademy, setSelectAcademy] = useAtom(selectAcademyAtom) const [isNewLocation, setIsNewLocation] = useState(false) - - useEffect(() => { - if (searchAcademy > -1) { - setSelectAcademy((prev) => ({ - ...prev, - isBottomSheet: true - })) - console.log(selectAcademy) - } - }, [searchAcademy]) - - const { data: detailAcademy } = useQuery({ - queryKey: ['academy', selectAcademy], - queryFn: () => - getAcademyDetail({ - academyId: - selectAcademy.academy.academyId > -1 - ? selectAcademy.academy.academyId - : searchAcademy - }), - enabled: selectAcademy.academy.academyId > -1 || searchAcademy > -1 - }) + const navigate = useNavigate() const currentLocation = useCallback(() => { if (!navigator.geolocation || !mapRef.current) { @@ -68,13 +36,12 @@ const NaverMap = ({ (position) => { const { latitude, longitude } = position.coords const center = new naver.maps.LatLng(latitude, longitude) - console.log(center) mapRef.current?.panTo(center, { duration: 500, easing: 'easeOutCubic' }) }, - () => console.log('test'), + () => navigate('/'), { enableHighAccuracy: false, timeout: 1000 @@ -84,17 +51,54 @@ const NaverMap = ({ const updateLocation = useCallback(() => { const location = mapRef.current?.getCenter() - setLocation({ + createMap({ latitude: location?.y as number, longitude: location?.x as number }) + + setMapInfo((prev) => ({ + ...prev, + latitude: location?.y as number, + longitude: location?.x as number + })) + setIsNewLocation(false) }, []) + const createMap = ({ + latitude, + longitude + }: { + latitude: number + longitude: number + }) => { + const center: naver.maps.LatLng = new naver.maps.LatLng(latitude, longitude) + const naverMapOption = { + center: center, + ...DefaultMapOption + } + + mapRef.current = new naver.maps.Map('map', naverMapOption) + + naver.maps.Event.addListener(mapRef.current, 'click', () => + setSelectAcademy({ + isBottomSheet: false, + academy: initSelectAcademy.academy + }) + ) + + naver.maps.Event.addListener( + mapRef.current, + 'dragend', + throttle(() => { + setIsNewLocation(true) + }, 100) + ) + } + useEffect(() => { if (mapRef.current) { academyList.map((data) => { - // console.log(Marker({ value: data.academyName, select: false })) const isSelected = data.academyId === selectAcademy.academy.academyId const marker = new naver.maps.Marker({ position: new naver.maps.LatLng(data.latitude, data.longitude), @@ -103,7 +107,7 @@ const NaverMap = ({ content: Marker({ value: data.academyName, select: isSelected }) } }) - + markerRef.current.push(marker) naver.maps.Event.addListener(marker, 'click', () => { setSelectAcademy((prev) => ({ isBottomSheet: !isSelected, @@ -118,30 +122,8 @@ const NaverMap = ({ }, [academyList, selectAcademy]) useEffect(() => { - const center: naver.maps.LatLng = new naver.maps.LatLng(latitude, longitude) - const naverMapOption = { - center: center, - ...DefaultMapOption - } - - if (!mapRef.current) { - mapRef.current = new naver.maps.Map('map', naverMapOption) - } - - naver.maps.Event.addListener(mapRef.current, 'click', () => - setSelectAcademy({ - isBottomSheet: false, - academy: initSelectAcademy.academy - }) - ) - - naver.maps.Event.addListener( - mapRef.current, - 'dragend', - throttle(() => { - setIsNewLocation(true) - }, 100) - ) + const { latitude, longitude } = mapInfo + createMap({ latitude: latitude, longitude: longitude }) }, []) return ( @@ -164,14 +146,6 @@ const NaverMap = ({ )} - {selectAcademy.isBottomSheet && detailAcademy && ( - - )}
) } diff --git a/src/pages/map/MapPage.tsx b/src/pages/map/MapPage.tsx index f6ad993b..38f5b3c8 100644 --- a/src/pages/map/MapPage.tsx +++ b/src/pages/map/MapPage.tsx @@ -1,84 +1,20 @@ -import { useEffect, useState } from 'react' -import { useInView } from 'react-intersection-observer' -import { useLocation, useNavigate } from 'react-router-dom' +import { useLocation } from 'react-router-dom' import { useQuery } from '@tanstack/react-query' import { useAtom } from 'jotai/index' -import { SetLocationProps } from '../../types/mapPage.ts' -import SettingPage from '../setting/SettingPage.tsx' -import Icon from '@/components/common/icon/Icon.tsx' -import Input from '@/components/common/inputbox/input/Input.tsx' +import BottomSheet from '@/components/common/bottomsheet/BottomSheet.tsx' import Spacing from '@/components/common/spacing/Spacing.tsx' +import MapSearchBar from '@/components/map/MapSearchBar.tsx' import NaverMap from '@/components/map/NaverMap.tsx' -import { getAcademiesSearchResult } from '@/libs/api/academy/AcademyApi.ts' import { getAcademyFilter } from '@/libs/api/filter/filterApi.ts' import { getAcademyList } from '@/libs/api/mapapi/mapApi.ts' -import { SearchAcademiesResponse } from '@/libs/api/mapapi/mapApiType.ts' -import { useDebounce } from '@/libs/hooks' -import useSidebar from '@/libs/hooks/useSidebar.tsx' -import { mapInfoAtom } from '@/libs/store/mapInfoAtom.ts' +import { mapInfoAtom, selectAcademyAtom } from '@/libs/store/mapInfoAtom.ts' const MapPage = () => { - const { ref, inView } = useInView({ - threshold: 1 - }) - const navigate = useNavigate() + const [mapInfo] = useAtom(mapInfoAtom) + const [selectAcademy] = useAtom(selectAcademyAtom) const location = useLocation() - const [searchValue, setSearchValue] = useState('') const queryString = location.search - const debounceValue = useDebounce(searchValue, 300) - const [lastPage, setLastPage] = useState(false) - const [academiesData, setAcademiesData] = useState( - [] - ) - const [page, setPage] = useState(0) - const [isinitial, setInitial] = useState(true) - const [searchAcademy, setSearchAcademy] = useState(-1) - const { toggleOpen } = useSidebar() - useEffect(() => { - if (lastPage) { - return - } else if (inView) { - fetchSearchInfiniteScroll(debounceValue, page) - } - }, [inView]) - - const fetchSearchInfiniteScroll = async ( - searchKeyword: string, - page: number - ) => { - const data = await getAcademiesSearchResult(searchKeyword, page) - setAcademiesData([...academiesData, ...data.content]) - setPage(data.number + 1) - setInitial(data.first) - setLastPage(data.last) - } - const fetchSearchResult = async (searchKeyword: string, page: number) => { - const data = await getAcademiesSearchResult(searchKeyword, page) - setAcademiesData([...data.content]) - setPage(data.number + 1) - setInitial(data.first) - setLastPage(data.last) - } - - useEffect(() => { - if (isinitial) { - fetchSearchResult(debounceValue, 0) - } - }, [debounceValue]) - - const observer = ( -
- ) - useEffect(() => { - console.log(searchAcademy) - }) - - const [mapInfo, setMapInfo] = useAtom(mapInfoAtom) const { data: academyList } = useQuery({ queryKey: ['academyList', mapInfo.latitude, mapInfo.longitude], queryFn: () => @@ -88,102 +24,32 @@ const MapPage = () => { }), enabled: !queryString }) + const { data: academyFilterList } = useQuery({ queryKey: ['academyFilterList', queryString], queryFn: () => getAcademyFilter({ queryString: queryString }), enabled: queryString.length > 0 }) - const moveFilter = () => { - navigate('/map/filter') - } - - const setLocation = ({ latitude, longitude }: SetLocationProps) => { - setMapInfo((prev) => ({ - ...prev, - latitude, - longitude - })) - } - return ( -
- -
- -
-
- { - setSearchValue(e.target.value) - }} - /> -
0 - ? 'rounded-lg border-blue-350 border mb-4 mt-2' - : '' - }`}> - {academiesData.length > 0 && ( -
- {academiesData.map((data, index) => ( -
{ - setSearchAcademy(data.academyId) - console.log(data) - }}> - -
{ - setAcademiesData([]) - }}> -
- {data.academyName} -
-
- {data.address} -
-
-
- ))} - {observer} -
- )} -
-
-
- - {'필터'} -
-
- + + + + {selectAcademy.isBottomSheet && ( + -
+ )}
) } From cce1582dd598b40376938dbbcfffc356d502bb70 Mon Sep 17 00:00:00 2001 From: kimheeseok Date: Wed, 29 Nov 2023 11:17:43 +0900 Subject: [PATCH 07/21] =?UTF-8?q?hotfix:=20=EC=8A=A4=ED=86=A0=EB=A6=AC?= =?UTF-8?q?=EB=B6=81=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bottomsheet/BottomSheet.stories.tsx | 29 +------------------ 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/components/common/bottomsheet/BottomSheet.stories.tsx b/src/components/common/bottomsheet/BottomSheet.stories.tsx index 58266fea..c3b4a455 100644 --- a/src/components/common/bottomsheet/BottomSheet.stories.tsx +++ b/src/components/common/bottomsheet/BottomSheet.stories.tsx @@ -23,34 +23,7 @@ export const Default: Story = { title={args.title} address={'주소'} number={'전화번호'} - detailInfo={{ - academyName: 'test', - contact: 'test', - address: 'test', - shuttleAvailability: 'test', - expectedFee: 0, - updatedDate: '2023-11-16', - areaOfExpertise: 'test', - lessonGetResponses: { - lessons: [ - { - lessonId: 0, - subject: 'test', - capacity: 0, - duration: 'test', - totalFee: 0 - } - ] - }, - reviewPercentGetResponse: { - kindnessPercent: 0, - goodFacilityPercent: 0, - cheapFeePercent: 0, - goodManagementPercent: 0, - lovelyTeachingPercent: 0 - }, - isLiked: false - }} + academyId={0} /> ) } From 375165692e3afd8b69878505a84714781d452e2c Mon Sep 17 00:00:00 2001 From: kimheeseok Date: Wed, 29 Nov 2023 12:14:11 +0900 Subject: [PATCH 08/21] =?UTF-8?q?hotfix:=20=EA=B2=80=EC=83=89=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=ED=81=B4=EB=A6=AD=EC=8B=9C=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=ED=9B=84=20=EB=A7=88=EC=BB=A4=20=ED=91=9C=EC=8B=9C=EB=90=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/map/MapSearchBar.tsx | 12 ++++-- src/components/map/NaverMap.tsx | 59 ++++++++++++++++++++++++++++- src/libs/api/mapapi/mapApiType.ts | 9 +++++ src/libs/store/mapInfoAtom.ts | 9 +++++ src/pages/map/MapPage.tsx | 14 ++++++- 5 files changed, 96 insertions(+), 7 deletions(-) diff --git a/src/components/map/MapSearchBar.tsx b/src/components/map/MapSearchBar.tsx index 95d3b728..f9060d50 100644 --- a/src/components/map/MapSearchBar.tsx +++ b/src/components/map/MapSearchBar.tsx @@ -2,11 +2,13 @@ import { useEffect, useState } from 'react' import { useInView } from 'react-intersection-observer' import { useNavigate } from 'react-router-dom' import { useQuery } from '@tanstack/react-query' +import { useAtom } from 'jotai' import Icon from '@/components/common/icon/Icon.tsx' import Input from '@/components/common/inputbox/input/Input.tsx' import { getAcademiesSearchResult } from '@/libs/api/academy/AcademyApi.ts' import { SearchAcademiesResponse } from '@/libs/api/mapapi/mapApiType.ts' import { useDebounce } from '@/libs/hooks/useDebounce.ts' +import { selectSearchAcademyAtom } from '@/libs/store/mapInfoAtom.ts' const MapSearchBar = () => { const navigate = useNavigate() @@ -14,6 +16,8 @@ const MapSearchBar = () => { const [page, setPage] = useState(0) const debounceValue = useDebounce(searchValue, 300) const [searchList, setSearchList] = useState([]) + const [_, setSelectValue] = useAtom(selectSearchAcademyAtom) + const [isHidden, setHidden] = useState(false) const { ref, inView } = useInView({ threshold: 1 @@ -46,6 +50,7 @@ const MapSearchBar = () => { //검색어가 바뀔때마다 페이지도 0으로 맞춰 api를 호출 const updateSearchValue = (value: string) => { setSearchValue(value) + setHidden(true) setPage(0) } @@ -78,11 +83,11 @@ const MapSearchBar = () => { />
0 + searchList.length > 0 && isHidden ? 'rounded-lg border-blue-350 border mb-4 mt-2' : '' }`}> - {searchList.length > 0 && ( + {isHidden && searchList.length > 0 && (
{searchList.map((data, index) => (
{ } key={index} onClick={() => { - console.log(data) + setSelectValue(data) + setHidden(false) }}>
{ const markerRef = useRef([]) const [mapInfo, setMapInfo] = useAtom(mapInfoAtom) const [selectAcademy, setSelectAcademy] = useAtom(selectAcademyAtom) + const [selectValue, _] = useAtom(selectSearchAcademyAtom) const [isNewLocation, setIsNewLocation] = useState(false) const navigate = useNavigate() @@ -124,7 +129,57 @@ const NaverMap = ({ academyList }: NaverMapProps) => { useEffect(() => { const { latitude, longitude } = mapInfo createMap({ latitude: latitude, longitude: longitude }) - }, []) + if (selectValue.academyId > -1) { + const marker = new naver.maps.Marker({ + position: new naver.maps.LatLng( + selectValue.latitude, + selectValue.longitude + ), + map: mapRef.current as naver.maps.Map, + icon: { + content: Marker({ value: selectValue.academyName, select: true }) + } + }) + markerRef.current = [marker] + naver.maps.Event.addListener(marker, 'click', () => { + setSelectAcademy(() => ({ + isBottomSheet: true, + academy: { + academyId: selectValue.academyId, + academyName: selectValue.academyName, + address: selectValue.address, + contact: '', + areaOfExpertise: '', + latitude: selectValue.latitude, + longitude: selectValue.longitude + } + })) + }) + + setSelectAcademy(() => ({ + isBottomSheet: true, + academy: { + academyId: selectValue.academyId, + academyName: selectValue.academyName, + address: selectValue.address, + contact: '', + areaOfExpertise: '', + latitude: selectValue.latitude, + longitude: selectValue.longitude + } + })) + } + }, [mapInfo]) + + useEffect(() => { + if (selectValue.academyId > -1) { + setMapInfo((prev) => ({ + ...prev, + longitude: selectValue.longitude, + latitude: selectValue.latitude + })) + } + }, [selectValue]) return (
diff --git a/src/libs/api/mapapi/mapApiType.ts b/src/libs/api/mapapi/mapApiType.ts index 06a30e39..d82e2096 100644 --- a/src/libs/api/mapapi/mapApiType.ts +++ b/src/libs/api/mapapi/mapApiType.ts @@ -49,6 +49,15 @@ export interface Academy { longitude: number } +export interface SearchAcademy { + academyId: number + academyName: string + address: string + areaOfExpertise: string + latitude: number + longitude: number +} + export interface AcademyResponse { academiesByLocationResponse: Academy[] } diff --git a/src/libs/store/mapInfoAtom.ts b/src/libs/store/mapInfoAtom.ts index 60bd0a30..478cfdd6 100644 --- a/src/libs/store/mapInfoAtom.ts +++ b/src/libs/store/mapInfoAtom.ts @@ -1,6 +1,7 @@ import { atom } from 'jotai' import { MapInfoAtomType } from '../../types/selectcity.ts' import { InitSelectAcademyType } from '@/components/map/naverMapType.ts' +import { SearchAcademiesResponse } from '@/libs/api/mapapi/mapApiType.ts' export const mapInfoAtom = atom({ selectProvince: '', @@ -22,3 +23,11 @@ export const selectAcademyAtom = atom({ longitude: -1 } }) + +export const selectSearchAcademyAtom = atom({ + academyId: -1, + academyName: '', + address: '', + latitude: -1, + longitude: -1 +}) diff --git a/src/pages/map/MapPage.tsx b/src/pages/map/MapPage.tsx index 38f5b3c8..6efbc167 100644 --- a/src/pages/map/MapPage.tsx +++ b/src/pages/map/MapPage.tsx @@ -1,3 +1,4 @@ +import { useEffect } from 'react' import { useLocation } from 'react-router-dom' import { useQuery } from '@tanstack/react-query' import { useAtom } from 'jotai/index' @@ -7,11 +8,16 @@ import MapSearchBar from '@/components/map/MapSearchBar.tsx' import NaverMap from '@/components/map/NaverMap.tsx' import { getAcademyFilter } from '@/libs/api/filter/filterApi.ts' import { getAcademyList } from '@/libs/api/mapapi/mapApi.ts' -import { mapInfoAtom, selectAcademyAtom } from '@/libs/store/mapInfoAtom.ts' +import { + mapInfoAtom, + selectAcademyAtom, + selectSearchAcademyAtom +} from '@/libs/store/mapInfoAtom.ts' const MapPage = () => { const [mapInfo] = useAtom(mapInfoAtom) const [selectAcademy] = useAtom(selectAcademyAtom) + const [selectValue] = useAtom(selectSearchAcademyAtom) const location = useLocation() const queryString = location.search @@ -22,7 +28,7 @@ const MapPage = () => { latitude: mapInfo.latitude, longitude: mapInfo.longitude }), - enabled: !queryString + enabled: !queryString && selectValue.academyId === -1 }) const { data: academyFilterList } = useQuery({ @@ -31,6 +37,10 @@ const MapPage = () => { enabled: queryString.length > 0 }) + useEffect(() => { + console.log(selectAcademy) + }, [selectAcademy]) + return (
From 612877a08e05b94edfbf7927d6c59f1c88b3b151 Mon Sep 17 00:00:00 2001 From: kimheeseok Date: Wed, 29 Nov 2023 14:42:01 +0900 Subject: [PATCH 09/21] =?UTF-8?q?hotfix:=20=ED=95=84=ED=84=B0=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/slider/Slider.stories.tsx | 2 +- src/components/common/slider/Slider.tsx | 3 ++- src/libs/store/mapFilterAtom.ts | 19 ++++++++------- src/pages/filter/FilterPage.tsx | 23 ++++++++++++------- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/components/common/slider/Slider.stories.tsx b/src/components/common/slider/Slider.stories.tsx index 188055bd..3758d8af 100644 --- a/src/components/common/slider/Slider.stories.tsx +++ b/src/components/common/slider/Slider.stories.tsx @@ -12,7 +12,7 @@ const meta: Meta = { render: function Render() { return (
- + console.log('')} />
) } diff --git a/src/components/common/slider/Slider.tsx b/src/components/common/slider/Slider.tsx index a69712ec..ed7ee524 100644 --- a/src/components/common/slider/Slider.tsx +++ b/src/components/common/slider/Slider.tsx @@ -31,7 +31,7 @@ const Silder = ({ onChange }: { onChange: () => void }) => { } } return ( -
+
{parseAcademyFee(value)}
@@ -107,6 +107,7 @@ const Silder = ({ onChange }: { onChange: () => void }) => { } onChange={(e) => { const newValue = Number.parseInt(e.target.value, 10) + onChange() setValue(newValue * 1000) }} /> diff --git a/src/libs/store/mapFilterAtom.ts b/src/libs/store/mapFilterAtom.ts index 45d6e8f8..bf7f95b1 100644 --- a/src/libs/store/mapFilterAtom.ts +++ b/src/libs/store/mapFilterAtom.ts @@ -2,16 +2,15 @@ import { atom } from 'jotai' const initMapFilter: SubjectList = { subjectList: [ - { title: '예능', filter: '예능(대)', color: 'default' }, - { title: '국제화', filter: '국제화', color: 'default' }, - { title: '입시', filter: '입시, 검정 및 보습', color: 'default' }, - { title: '직업기술', filter: '직업기술', color: 'default' }, - { title: '종합', filter: '종합(대)', color: 'default' }, - { title: '독서실', filter: '독서실', color: 'default' }, - { title: '기예', filter: '기예(대)', color: 'default' }, - { title: '기타', filter: '기타(대)', color: 'default' }, - { title: '인문사회', filter: '인문사회(대)', color: 'default' }, - { title: '정보', filter: '정보', color: 'default' } + { title: '수학', filter: '수학', color: 'default' }, + { title: '과학', filter: '과학', color: 'default' }, + { title: '국어', filter: '국어', color: 'default' }, + { title: '영어', filter: '영어', color: 'default' }, + { title: '컴퓨터', filter: '컴퓨터', color: 'default' }, + { title: '예체능', filter: '예체능', color: 'default' }, + { title: '외국어', filter: '외국어', color: 'default' }, + { title: '보습', filter: '보습', color: 'default' }, + { title: '기타', filter: '기타', color: 'default' } ], maxMoney: 500_000, minMoney: 1 diff --git a/src/pages/filter/FilterPage.tsx b/src/pages/filter/FilterPage.tsx index 1715f26b..f41f1313 100644 --- a/src/pages/filter/FilterPage.tsx +++ b/src/pages/filter/FilterPage.tsx @@ -3,6 +3,7 @@ import { useAtom } from 'jotai' import Button from '@/components/common/button/Button.tsx' import Label from '@/components/common/label/Label.tsx' import { LabelColorType } from '@/components/common/label/LabelType.ts' +import Slider from '@/components/common/slider/Slider.tsx' import Spacing from '@/components/common/spacing/Spacing.tsx' import { mapFilterState } from '@/libs/store/mapFilterAtom.ts' import { mapInfoAtom } from '@/libs/store/mapInfoAtom.ts' @@ -23,7 +24,7 @@ const FilterPage = () => { console.log(subjectList) let url = `/map?lat=${mapInfo.latitude}&lng=${ mapInfo.longitude - }&areaOfExpertises=${subjectList.join(',')}` + }&categories=${subjectList.join(',')}` console.log(url) if (mapFilter.minMoney > 1) { @@ -106,15 +107,21 @@ const FilterPage = () => { className={ 'flex flex-col w-full h-[260px] bg-white-0 shadow-[0_4px_4px_0_rgba(0,0,0,0.25)] mb-[10px] justify-center items-center' }> - + {'희망 금액(추정치)'} -
-
-
+
+
{

{'일정 수행중인 아이'}

- {data?.childrenInfos.map( - ({ childId, childName, imageUrl, dashBoardId }) => ( -
  • navigate(`academies/${dashBoardId}/edit`)}> - -
  • - ) - )} +
    + navigate(`academies/${data?.childrenInfo.dashBoardId}/edit`) + }> + +

    {'메모'}

      - {data?.childrenInfos.map(({ memo, childId }) => ( -
    • - {memo} -
    • - ))} +
      + {data?.childrenInfo.memo} +
    diff --git a/src/components/BottomSheet/BottomSheetHeader.tsx b/src/components/BottomSheet/BottomSheetHeader.tsx index ded7c85d..16a30a8d 100644 --- a/src/components/BottomSheet/BottomSheetHeader.tsx +++ b/src/components/BottomSheet/BottomSheetHeader.tsx @@ -1,26 +1,39 @@ import { useState } from 'react' +import { useMutation } from '@tanstack/react-query' import { LikeBlank, LikeFilled } from '@/assets/icon' +import { postLike } from '@/libs/api/mapapi/mapApi.ts' const BottomSheetHeader = ({ title, - isLike + isLike, + academyId }: { title: string isLike: boolean + academyId: number }) => { + const likeMutation = useMutation({ + mutationFn: (academyId: number) => postLike({ academyId: academyId }), + onSuccess: () => {}, + onSettled: () => { + setLiked(!liked) + } + }) + const [liked, setLiked] = useState(isLike) //TODO: 좋아요 API 로직 추가 + return (

    {title}

    {liked ? ( setLiked(!liked)} + onClick={() => likeMutation.mutate(academyId)} /> ) : ( setLiked(!liked)} + onClick={() => likeMutation.mutate(academyId)} /> )}
    diff --git a/src/components/common/bottomsheet/BottomSheet.stories.tsx b/src/components/common/bottomsheet/BottomSheet.stories.tsx deleted file mode 100644 index 58266fea..00000000 --- a/src/components/common/bottomsheet/BottomSheet.stories.tsx +++ /dev/null @@ -1,56 +0,0 @@ -// Button.stories.ts|tsx - -import type { Meta, StoryObj } from '@storybook/react' - -import BottomSheet from './BottomSheet' - -const meta: Meta = { - title: 'Components/BottomSheet', - component: BottomSheet, - argTypes: { - title: { - control: 'text' - } - } -} - -export default meta -type Story = StoryObj - -export const Default: Story = { - render: (args) => ( - - ) -} diff --git a/src/components/common/bottomsheet/BottomSheet.tsx b/src/components/common/bottomsheet/BottomSheet.tsx index 3112043e..dcfcefc5 100644 --- a/src/components/common/bottomsheet/BottomSheet.tsx +++ b/src/components/common/bottomsheet/BottomSheet.tsx @@ -1,8 +1,10 @@ import { useState } from 'react' +import { useQuery } from '@tanstack/react-query' import BottomSheetContent from '@/components/BottomSheet/BottomSheetContent' import BottomSheetHeader from '@/components/BottomSheet/BottomSheetHeader' +import Loading from '@/components/Loading/Loading.tsx' import Spacing from '@/components/common/spacing/Spacing.tsx' -import { DetailAcademyResponse } from '@/libs/api/mapapi/mapApiType.ts' +import { getAcademyDetail } from '@/libs/api/mapapi/mapApi.ts' /** * @param title BottomSheet에 들어갈 Title을 입력합니다. @@ -13,15 +15,24 @@ interface BottomSheetProps { title: string address: string number: string - detailInfo: DetailAcademyResponse + academyId: number } const BottomSheet = ({ title = '학원명 입력', address, number, - detailInfo + academyId }: BottomSheetProps) => { const [expanded, setExpanded] = useState(false) + const { data: detailAcademy, isLoading } = useQuery({ + queryKey: ['academy', academyId], + queryFn: () => + getAcademyDetail({ + academyId: academyId + }), + enabled: academyId > -1 + }) + return ( <>
    -
    - - -
    + {isLoading && } + {detailAcademy && ( +
    + + +
    + )}
    ) diff --git a/src/components/common/header/Header.tsx b/src/components/common/header/Header.tsx index b619079d..45dea6c0 100644 --- a/src/components/common/header/Header.tsx +++ b/src/components/common/header/Header.tsx @@ -52,9 +52,6 @@ const Header = ({
    -
    alert('알림보기!')}> - -
    diff --git a/src/components/common/progressBar/ProgressBar.tsx b/src/components/common/progressBar/ProgressBar.tsx index 0c74a880..34a89681 100644 --- a/src/components/common/progressBar/ProgressBar.tsx +++ b/src/components/common/progressBar/ProgressBar.tsx @@ -9,10 +9,6 @@ const ProgressBar = ({ fullStepNum: number step: number }) => { - if (step > fullStepNum) { - alert(`스텝은 전체 스텝 개수인 ${fullStepNum}보다 클 수 없습니다.`) - return <> - } const progressBarWidth = (step / fullStepNum) * 100 return (
    diff --git a/src/components/common/slider/Slider.stories.tsx b/src/components/common/slider/Slider.stories.tsx index 188055bd..3758d8af 100644 --- a/src/components/common/slider/Slider.stories.tsx +++ b/src/components/common/slider/Slider.stories.tsx @@ -12,7 +12,7 @@ const meta: Meta = { render: function Render() { return (
    - + console.log('')} />
    ) } diff --git a/src/components/common/slider/Slider.tsx b/src/components/common/slider/Slider.tsx index 701d0bd5..ed7ee524 100644 --- a/src/components/common/slider/Slider.tsx +++ b/src/components/common/slider/Slider.tsx @@ -1,49 +1,117 @@ import { useState } from 'react' -const Silder = ({ - minNum = 0, - maxNum = 100_000 -}: { - minNum: number - maxNum: number -}) => { - const [value, setValue] = useState(1) +const Silder = ({ onChange }: { onChange: () => void }) => { + const [value, setValue] = useState(0) + const parseAcademyFee = (value: number) => { + switch (value) { + case 0: { + return '0 ~ 10만원' + } + case 100_000: { + return '10만원 ~ 20만원' + } + case 200_000: { + return '20만원 ~ 30만원' + } + case 300_000: { + return '30만원 ~ 40만원' + } + case 400_000: { + return '40만원 ~ 50만원' + } + case 500_000: { + return '50만원 ~ 60만원' + } + case 600_000: { + return '60만원 ~ 70만원' + } + case 700_000: { + return '70만원 이상' + } + } + } return ( -
    -
    {value}
    -
    -
    -
    -
    -
    - { - const newValue = Number.parseInt(e.target.value, 10) - setValue(newValue) - }} - /> +
    +
    + {parseAcademyFee(value)} +
    +
    +
    +
    {'0'}
    +
    +
    + {'20'} +
    +
    + {'10'} +
    +
    +
    + {'30'} +
    +
    +
    + {'40'} +
    +
    +
    + {'50'} +
    +
    +
    + {'60'} +
    +
    + {'70~'} +
    + { + const newValue = Number.parseInt(e.target.value, 10) + onChange() + setValue(newValue * 1000) + }} + /> +
    ) } diff --git a/src/components/common/toast/index.tsx b/src/components/common/toast/index.tsx index 7a23a19f..5d15f848 100644 --- a/src/components/common/toast/index.tsx +++ b/src/components/common/toast/index.tsx @@ -2,7 +2,7 @@ import { ToastContainer } from 'react-toastify' const Toast = () => { return ( { toastStyle={{ borderRadius: '12px', backgroundColor: 'white', - margin: '6px' + margin: '10px' }} bodyStyle={{ color: 'ccc', lineHeight: 1.5 }} /> diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx index a612165d..c29b8cad 100644 --- a/src/components/layout/Layout.tsx +++ b/src/components/layout/Layout.tsx @@ -1,7 +1,9 @@ import { useEffect } from 'react' import { Outlet } from 'react-router-dom' +import useToastify from '@/libs/hooks/useToastify' const Layout = () => { + const { Toast } = useToastify() useEffect(() => { const vh = window.innerHeight * 0.01 document.documentElement.style.setProperty('--vh', `${vh}px`) @@ -17,6 +19,7 @@ const Layout = () => {
    +
    diff --git a/src/components/map/MapSearchBar.tsx b/src/components/map/MapSearchBar.tsx new file mode 100644 index 00000000..4e9e87fe --- /dev/null +++ b/src/components/map/MapSearchBar.tsx @@ -0,0 +1,132 @@ +import { useEffect, useState } from 'react' +import { useInView } from 'react-intersection-observer' +import { useNavigate } from 'react-router-dom' +import { useQuery } from '@tanstack/react-query' +import { useAtom } from 'jotai' +import Icon from '@/components/common/icon/Icon.tsx' +import Input from '@/components/common/inputbox/input/Input.tsx' +import { getAcademiesSearchResult } from '@/libs/api/academy/AcademyApi.ts' +import { SearchAcademiesResponse } from '@/libs/api/mapapi/mapApiType.ts' +import { useDebounce } from '@/libs/hooks/useDebounce.ts' +import { selectSearchAcademyAtom } from '@/libs/store/mapInfoAtom.ts' + +const MapSearchBar = () => { + const navigate = useNavigate() + const [searchValue, setSearchValue] = useState('') + const [page, setPage] = useState(0) + const debounceValue = useDebounce(searchValue, 300) + const [searchList, setSearchList] = useState([]) + const [_, setSelectValue] = useAtom(selectSearchAcademyAtom) + const [isHidden, setHidden] = useState(false) + + const { ref, inView } = useInView({ + threshold: 1 + }) + + const { data: searchData } = useQuery({ + queryKey: ['searchData', debounceValue, page], + queryFn: () => getAcademiesSearchResult(debounceValue, page), + enabled: debounceValue !== '' + }) + + useEffect(() => { + if (searchData && page === 0) { + // 검색어만 바뀌었을때 + setSearchList([...searchData.content]) + } else if (searchData && page > 0) { + // 페이지가 바뀌었을때 + setSearchList((prevList) => [...prevList, ...searchData.content]) + } else { + // debounceValue가 ''일때 + setSearchList([]) + } + }, [searchData, page, debounceValue]) + + const isLast = searchData?.last || false + const updatePage = () => { + setPage(page + 1) + } + + //검색어가 바뀔때마다 페이지도 0으로 맞춰 api를 호출 + const updateSearchValue = (value: string) => { + setSearchValue(value) + setHidden(true) + setPage(0) + } + + const observer = ( +
    + ) + + useEffect(() => { + if (isLast) { + return + } else if (inView) { + updatePage() + } + }, [inView]) + + return ( +
    +
    + { + updateSearchValue(e.target.value) + }} + /> +
    0 && isHidden + ? 'rounded-lg border-blue-350 border mb-4 mt-2' + : '' + }`}> + {isHidden && searchList.length > 0 && ( +
    + {searchList.map((data, index) => ( +
    { + setSelectValue(data) + setHidden(false) + }}> + +
    +
    + {data.academyName} +
    +
    + {data.address} +
    +
    +
    + ))} + {observer} +
    + )} +
    +
    +
    navigate('/map/filter')}> + + {'필터'} +
    +
    + ) +} + +export default MapSearchBar diff --git a/src/components/map/NaverMap.tsx b/src/components/map/NaverMap.tsx index 85fde460..294f3c14 100644 --- a/src/components/map/NaverMap.tsx +++ b/src/components/map/NaverMap.tsx @@ -1,17 +1,18 @@ import { useCallback, useEffect, useRef, useState } from 'react' -import { useQuery } from '@tanstack/react-query' +import { useNavigate } from 'react-router-dom' import { useAtom } from 'jotai/index' -import { SetLocationProps } from '../../types/mapPage.ts' -import BottomSheet from '@/components/common/bottomsheet/BottomSheet.tsx' import Icon from '@/components/common/icon/Icon.tsx' import { DefaultMapOption, initSelectAcademy, Marker } from '@/components/map/constants.ts' -import { getAcademyDetail } from '@/libs/api/mapapi/mapApi.ts' import { Academy } from '@/libs/api/mapapi/mapApiType.ts' -import { selectAcademyAtom } from '@/libs/store/mapInfoAtom.ts' +import { + mapInfoAtom, + selectAcademyAtom, + selectSearchAcademyAtom +} from '@/libs/store/mapInfoAtom.ts' import throttle from '@/libs/utils/throttle.ts' /** @@ -19,45 +20,17 @@ import throttle from '@/libs/utils/throttle.ts' * * **/ interface NaverMapProps { - latitude: number - longitude: number academyList: Academy[] - setLocation: ({ latitude, longitude }: SetLocationProps) => void - searchAcademy: number //나중에 제외해야합니다. } -const NaverMap = ({ - latitude, - longitude, - academyList, - setLocation, - searchAcademy -}: NaverMapProps) => { +const NaverMap = ({ academyList }: NaverMapProps) => { const mapRef = useRef(null) + const markerRef = useRef([]) + const [mapInfo, setMapInfo] = useAtom(mapInfoAtom) const [selectAcademy, setSelectAcademy] = useAtom(selectAcademyAtom) + const [selectValue, _] = useAtom(selectSearchAcademyAtom) const [isNewLocation, setIsNewLocation] = useState(false) - - useEffect(() => { - if (searchAcademy > -1) { - setSelectAcademy((prev) => ({ - ...prev, - isBottomSheet: true - })) - console.log(selectAcademy) - } - }, [searchAcademy]) - - const { data: detailAcademy } = useQuery({ - queryKey: ['academy', selectAcademy], - queryFn: () => - getAcademyDetail({ - academyId: - selectAcademy.academy.academyId > -1 - ? selectAcademy.academy.academyId - : searchAcademy - }), - enabled: selectAcademy.academy.academyId > -1 || searchAcademy > -1 - }) + const navigate = useNavigate() const currentLocation = useCallback(() => { if (!navigator.geolocation || !mapRef.current) { @@ -68,13 +41,12 @@ const NaverMap = ({ (position) => { const { latitude, longitude } = position.coords const center = new naver.maps.LatLng(latitude, longitude) - console.log(center) mapRef.current?.panTo(center, { duration: 500, easing: 'easeOutCubic' }) }, - () => console.log('test'), + () => navigate('/'), { enableHighAccuracy: false, timeout: 1000 @@ -84,17 +56,54 @@ const NaverMap = ({ const updateLocation = useCallback(() => { const location = mapRef.current?.getCenter() - setLocation({ + createMap({ latitude: location?.y as number, longitude: location?.x as number }) + + setMapInfo((prev) => ({ + ...prev, + latitude: location?.y as number, + longitude: location?.x as number + })) + setIsNewLocation(false) }, []) + const createMap = ({ + latitude, + longitude + }: { + latitude: number + longitude: number + }) => { + const center: naver.maps.LatLng = new naver.maps.LatLng(latitude, longitude) + const naverMapOption = { + center: center, + ...DefaultMapOption + } + + mapRef.current = new naver.maps.Map('map', naverMapOption) + + naver.maps.Event.addListener(mapRef.current, 'click', () => + setSelectAcademy({ + isBottomSheet: false, + academy: initSelectAcademy.academy + }) + ) + + naver.maps.Event.addListener( + mapRef.current, + 'dragend', + throttle(() => { + setIsNewLocation(true) + }, 100) + ) + } + useEffect(() => { if (mapRef.current) { academyList.map((data) => { - // console.log(Marker({ value: data.academyName, select: false })) const isSelected = data.academyId === selectAcademy.academy.academyId const marker = new naver.maps.Marker({ position: new naver.maps.LatLng(data.latitude, data.longitude), @@ -103,7 +112,7 @@ const NaverMap = ({ content: Marker({ value: data.academyName, select: isSelected }) } }) - + markerRef.current.push(marker) naver.maps.Event.addListener(marker, 'click', () => { setSelectAcademy((prev) => ({ isBottomSheet: !isSelected, @@ -118,31 +127,59 @@ const NaverMap = ({ }, [academyList, selectAcademy]) useEffect(() => { - const center: naver.maps.LatLng = new naver.maps.LatLng(latitude, longitude) - const naverMapOption = { - center: center, - ...DefaultMapOption - } + const { latitude, longitude } = mapInfo + createMap({ latitude: latitude, longitude: longitude }) + if (selectValue.academyId > -1) { + const marker = new naver.maps.Marker({ + position: new naver.maps.LatLng( + selectValue.latitude, + selectValue.longitude + ), + map: mapRef.current as naver.maps.Map, + icon: { + content: Marker({ value: selectValue.academyName, select: true }) + } + }) + markerRef.current = [marker] + naver.maps.Event.addListener(marker, 'click', () => { + setSelectAcademy(() => ({ + isBottomSheet: true, + academy: { + academyId: selectValue.academyId, + academyName: selectValue.academyName, + address: selectValue.address, + contact: '', + areaOfExpertise: '', + latitude: selectValue.latitude, + longitude: selectValue.longitude + } + })) + }) - if (!mapRef.current) { - mapRef.current = new naver.maps.Map('map', naverMapOption) + setSelectAcademy(() => ({ + isBottomSheet: true, + academy: { + academyId: selectValue.academyId, + academyName: selectValue.academyName, + address: selectValue.address, + contact: '', + areaOfExpertise: '', + latitude: selectValue.latitude, + longitude: selectValue.longitude + } + })) } + }, [mapInfo]) - naver.maps.Event.addListener(mapRef.current, 'click', () => - setSelectAcademy({ - isBottomSheet: false, - academy: initSelectAcademy.academy - }) - ) - - naver.maps.Event.addListener( - mapRef.current, - 'dragend', - throttle(() => { - setIsNewLocation(true) - }, 100) - ) - }, []) + useEffect(() => { + if (selectValue.academyId > -1) { + setMapInfo((prev) => ({ + ...prev, + longitude: selectValue.longitude, + latitude: selectValue.latitude + })) + } + }, [selectValue]) return (
    @@ -164,14 +201,6 @@ const NaverMap = ({ )} - {selectAcademy.isBottomSheet && detailAcademy && ( - - )}
    ) } diff --git a/src/components/review/ReviewBottomSheet.tsx b/src/components/review/ReviewBottomSheet.tsx index 55512347..61505700 100644 --- a/src/components/review/ReviewBottomSheet.tsx +++ b/src/components/review/ReviewBottomSheet.tsx @@ -6,6 +6,8 @@ import Spacing from '@/components/common/spacing/Spacing' import { postReview } from '@/libs/api/review/reviewApi' import { AcademyReview } from '@/libs/api/review/reviewType' import { ReviewRequestType } from '@/libs/api/review/reviewType' +import useToastify from '@/libs/hooks/useToastify' + const ReviewBottomSheet = ({ academyTitle, academyId, @@ -15,6 +17,7 @@ const ReviewBottomSheet = ({ academyId: number setBottomSheetClose: React.Dispatch> }) => { + const { setToast } = useToastify() const [reviewState, setReviewState] = useState({ academyId: academyId, KINDNESS: false, @@ -28,7 +31,7 @@ const ReviewBottomSheet = ({ mutationFn: (reviewState: ReviewRequestType) => postReview(reviewState), onSuccess: () => { setBottomSheetClose(false) - alert('리뷰 남기기 성공!') + setToast({ comment: '리뷰를 성공적으로 남겼어요.', type: 'success' }) } }) const handleMemo = (value: keyof ReviewRequestType) => { @@ -47,7 +50,8 @@ const ReviewBottomSheet = ({ useEffect(() => { const valueAry = Object.values(reviewState) const count = valueAry.filter(Boolean).length - if (count >= 4) alert('안돼') + if (count >= 4) + setToast({ comment: '리뷰는 4개 이상 남길 수 없어요.', type: 'warning' }) }, [reviewState]) return ( <> diff --git a/src/libs/api/academy/scheduleDetail/ScheduleDetailApi.ts b/src/libs/api/academy/scheduleDetail/ScheduleDetailApi.ts index ad45e77a..4a159fa7 100644 --- a/src/libs/api/academy/scheduleDetail/ScheduleDetailApi.ts +++ b/src/libs/api/academy/scheduleDetail/ScheduleDetailApi.ts @@ -7,27 +7,24 @@ import request from '@/libs/api' // /academy-schedules/detail?requestedDate=2023-11-06&lessonId=109347&childId=208&scheduleId=117 export const getAcademiesScheduleDetail = async ({ - requestedDate, lessonId, childId, scheduleId }: ScheduleDetailRequest): Promise => { - const scheduleDetail = await request.get(`/academy-schedules/detail`, { - params: { requestedDate, lessonId, childId, scheduleId } - }) + const scheduleDetail = await request.get( + `/academy-schedules/detail/${scheduleId}`, + { + params: { lessonId, childId } + } + ) return scheduleDetail.data } export const deleteAcademySchedule = async ({ academyScheduleId, - isAllDeleted, - requestDate + isAllDeleted }: DeleteScheduleRequest) => { - await request.delete('/academy-schedules', { - data: { - academyScheduleId, - isAllDeleted, - requestDate - } - }) + await request.delete( + `/academy-schedules/${academyScheduleId}?isAllDeleted=${isAllDeleted}` + ) } diff --git a/src/libs/api/academy/scheduleDetail/ScheduleDetailType.ts b/src/libs/api/academy/scheduleDetail/ScheduleDetailType.ts index d0efa3ab..365732e8 100644 --- a/src/libs/api/academy/scheduleDetail/ScheduleDetailType.ts +++ b/src/libs/api/academy/scheduleDetail/ScheduleDetailType.ts @@ -3,7 +3,7 @@ interface AcademyInfo { address: string } -interface LessonTimes { +interface LessonTime { startTime: string endTime: string } @@ -11,7 +11,7 @@ interface LessonInfo { lessonName: string capacity: number totalFee: number - lessonTimes: LessonTimes[] + lessonTimes: LessonTime periodicity: string } @@ -27,11 +27,11 @@ export interface ScheduleDetailResponse { date: string academyInfo: AcademyInfo lessonInfo: LessonInfo - childrenInfos: ChildrenInfo[] + childrenInfo: ChildrenInfo + categories: string[] } export interface ScheduleDetailRequest { - requestedDate: string lessonId: number childId: number scheduleId: number @@ -40,5 +40,4 @@ export interface ScheduleDetailRequest { export interface DeleteScheduleRequest { academyScheduleId: number isAllDeleted: boolean - requestDate: string } diff --git a/src/libs/api/index.ts b/src/libs/api/index.ts index 2769448c..9e6dcfea 100644 --- a/src/libs/api/index.ts +++ b/src/libs/api/index.ts @@ -1,6 +1,7 @@ import axios from 'axios' import { logoutApi } from './autorization/logout/LogoutApi' import { refreshApi } from './autorization/refresh/refreshApi' +import useToastify from '@/libs/hooks/useToastify' const request = axios.create({ baseURL: import.meta.env.VITE_API_ENDPOINT, @@ -31,13 +32,17 @@ request.interceptors.response.use( }, async (error) => { if (error.response.status === 403) { + const { setToast } = useToastify() try { const getRefreshToken = await refreshApi() const prevRequest = error.config prevRequest.headers.Authorization = `Bearer ${getRefreshToken.appToken}` return request(prevRequest) } catch { - alert('로그인이 풀리셨습니다... 로그인을 다시 진행해주세요😁') + setToast({ + comment: '로그인을 다시 진행해주세요.', + type: 'info' + }) await logoutApi() throw new Error('failed to request refresh token') } diff --git a/src/libs/api/mapapi/mapApi.ts b/src/libs/api/mapapi/mapApi.ts index 80b04c2f..b1bfbd4c 100644 --- a/src/libs/api/mapapi/mapApi.ts +++ b/src/libs/api/mapapi/mapApi.ts @@ -7,6 +7,7 @@ import { GetAcademysParams, GetLocationParam, GetTownParam, + LikeResponse, LocationResponse, ProvinceResponse, TownResponse @@ -64,3 +65,14 @@ export const getAcademyDetail = async ({ return res.data } + +export const postLike = async ({ + academyId +}: { + academyId: number +}): Promise => { + const res = await request.post(`/likes`, { + academyId: academyId + }) + return res.data +} diff --git a/src/libs/api/mapapi/mapApiType.ts b/src/libs/api/mapapi/mapApiType.ts index 65bcf8e4..d82e2096 100644 --- a/src/libs/api/mapapi/mapApiType.ts +++ b/src/libs/api/mapapi/mapApiType.ts @@ -49,6 +49,15 @@ export interface Academy { longitude: number } +export interface SearchAcademy { + academyId: number + academyName: string + address: string + areaOfExpertise: string + latitude: number + longitude: number +} + export interface AcademyResponse { academiesByLocationResponse: Academy[] } @@ -71,6 +80,7 @@ interface ReviewPercent { cheapFeePercent: number goodManagementPercent: number lovelyTeachingPercent: number + shuttleAvailabilityCount: number } export interface DetailAcademyResponse { @@ -80,7 +90,7 @@ export interface DetailAcademyResponse { shuttleAvailability: string expectedFee: number updatedDate: string - areaOfExpertise: string + categories: string[] lessonGetResponses: { lessons: Lesson[] } @@ -108,3 +118,9 @@ export interface InfiniteScrollPage { paged: boolean unpaged: boolean } + +export interface LikeResponse { + likeId: number + memberId: number + academyId: number +} diff --git a/src/libs/api/mypage/myPageApi.ts b/src/libs/api/mypage/myPageApi.ts index 854698a6..c2097527 100644 --- a/src/libs/api/mypage/myPageApi.ts +++ b/src/libs/api/mypage/myPageApi.ts @@ -3,7 +3,7 @@ import request from '@/libs/api' export const myPageApi = async (): Promise => { try { - const req = await request.get('http://3.114.43.57:8080/members') + const req = await request.get('/members') return req.data } catch { throw new Error('cannot get api from myPage') diff --git a/src/libs/store/likeacademyAtom.tsx b/src/libs/store/likeacademyAtom.tsx index 80da2dce..9f49eba2 100644 --- a/src/libs/store/likeacademyAtom.tsx +++ b/src/libs/store/likeacademyAtom.tsx @@ -2,9 +2,7 @@ import { atom } from 'jotai' import { GetLikeAcademyResponse } from '@/libs/api/likeacademy/LikeAcademyType' const initialLikeAcademyAtom: GetLikeAcademyResponse = { - likeAcademyInfos: [ - { likeId: 0, academyId: 0, academyName: '', expectedFee: 0 } - ], + likeAcademyInfos: [], totalFee: 0 } @@ -14,3 +12,7 @@ const checkGroup: boolean[] = [] export const totalAtom = atom(total) export const checkGroupAtom = atom(checkGroup) export const likeAcademyAtom = atom(initialLikeAcademyAtom) + +// onChange로직 다시 +// 아이 이름 입력 시 특수문자 => 에러메시지 거르자 +// 디폴트 이미지 => diff --git a/src/libs/store/mapFilterAtom.ts b/src/libs/store/mapFilterAtom.ts index 45d6e8f8..bf7f95b1 100644 --- a/src/libs/store/mapFilterAtom.ts +++ b/src/libs/store/mapFilterAtom.ts @@ -2,16 +2,15 @@ import { atom } from 'jotai' const initMapFilter: SubjectList = { subjectList: [ - { title: '예능', filter: '예능(대)', color: 'default' }, - { title: '국제화', filter: '국제화', color: 'default' }, - { title: '입시', filter: '입시, 검정 및 보습', color: 'default' }, - { title: '직업기술', filter: '직업기술', color: 'default' }, - { title: '종합', filter: '종합(대)', color: 'default' }, - { title: '독서실', filter: '독서실', color: 'default' }, - { title: '기예', filter: '기예(대)', color: 'default' }, - { title: '기타', filter: '기타(대)', color: 'default' }, - { title: '인문사회', filter: '인문사회(대)', color: 'default' }, - { title: '정보', filter: '정보', color: 'default' } + { title: '수학', filter: '수학', color: 'default' }, + { title: '과학', filter: '과학', color: 'default' }, + { title: '국어', filter: '국어', color: 'default' }, + { title: '영어', filter: '영어', color: 'default' }, + { title: '컴퓨터', filter: '컴퓨터', color: 'default' }, + { title: '예체능', filter: '예체능', color: 'default' }, + { title: '외국어', filter: '외국어', color: 'default' }, + { title: '보습', filter: '보습', color: 'default' }, + { title: '기타', filter: '기타', color: 'default' } ], maxMoney: 500_000, minMoney: 1 diff --git a/src/libs/store/mapInfoAtom.ts b/src/libs/store/mapInfoAtom.ts index 60bd0a30..478cfdd6 100644 --- a/src/libs/store/mapInfoAtom.ts +++ b/src/libs/store/mapInfoAtom.ts @@ -1,6 +1,7 @@ import { atom } from 'jotai' import { MapInfoAtomType } from '../../types/selectcity.ts' import { InitSelectAcademyType } from '@/components/map/naverMapType.ts' +import { SearchAcademiesResponse } from '@/libs/api/mapapi/mapApiType.ts' export const mapInfoAtom = atom({ selectProvince: '', @@ -22,3 +23,11 @@ export const selectAcademyAtom = atom({ longitude: -1 } }) + +export const selectSearchAcademyAtom = atom({ + academyId: -1, + academyName: '', + address: '', + latitude: -1, + longitude: -1 +}) diff --git a/src/pages/academy/AcademyDashboard.tsx b/src/pages/academy/AcademyDashboard.tsx index 0b348328..8014f570 100644 --- a/src/pages/academy/AcademyDashboard.tsx +++ b/src/pages/academy/AcademyDashboard.tsx @@ -16,6 +16,7 @@ import { patchToggleDashboardState } from '@/libs/api/dashboard/DashBoardApi' import { getAllDashboards } from '@/libs/api/dashboard/DashBoardApi' import { GetAllDashBoardResponse } from '@/libs/api/dashboard/DashBoardType' import useSidebar from '@/libs/hooks/useSidebar' +import useToastify from '@/libs/hooks/useToastify' import { childAtom } from '@/libs/store/childInfoAtom' import { getWeekday } from '@/libs/utils/weekParse' @@ -24,6 +25,7 @@ const AcademyDashboard = () => { const [dashboardData, setDashboardData] = useState( [] ) + const { setToast } = useToastify() const navigate = useNavigate() const { toggleOpen } = useSidebar() @@ -127,10 +129,17 @@ const AcademyDashboard = () => { } handleDelete={() => { if (data.isActive) { - alert('다니고 있는 학원은 삭제가 불가능합니다!') + setToast({ + comment: + '다니고 있는 학원은 삭제가 불가능해요. 먼저 미등록 상태로 변경해주세요.', + type: 'warning' + }) } else { deleteDashboardInfo(data.dashboardId) - alert('삭제 완료!') + setToast({ + comment: '삭제가 완료되었어요.', + type: 'success' + }) } }} onClick={(e) => { diff --git a/src/pages/academy/academyDetail/AcademySetting.tsx b/src/pages/academy/academyDetail/AcademySetting.tsx index 2a9b2286..c45818a2 100644 --- a/src/pages/academy/academyDetail/AcademySetting.tsx +++ b/src/pages/academy/academyDetail/AcademySetting.tsx @@ -6,8 +6,10 @@ import { deleteDashboard } from '@/libs/api/dashboard/DashBoardApi' import { patchToggleDashboardState } from '@/libs/api/dashboard/DashBoardApi' import { GetAllDashBoardResponse } from '@/libs/api/dashboard/DashBoardType' import { queryClient } from '@/libs/api/queryClient' +import useToastify from '@/libs/hooks/useToastify' const AcademySetting = ({ data }: { data: GetAllDashBoardResponse }) => { const navigate = useNavigate() + const { setToast } = useToastify() const [isbottomSheetOpen, setBottomSheetOpen] = useState(false) // eslint-disable-next-line unicorn/consistent-function-scoping const deleteDashboardInfo = async (dashboardId: number) => { @@ -45,7 +47,10 @@ const AcademySetting = ({ data }: { data: GetAllDashBoardResponse }) => { queryClient.invalidateQueries({ queryKey: ['dashboard', data.dashboardId] }) - alert('성공적으로 처리했음.') + setToast({ + comment: '학원 정보를 삭제했어요.', + type: 'success' + }) }} />
    @@ -60,7 +65,10 @@ const AcademySetting = ({ data }: { data: GetAllDashBoardResponse }) => { queryClient.invalidateQueries({ queryKey: ['dashboard', data.dashboardId] }) - alert('성공적으로 처리했음.') + setToast({ + comment: '학원을 등록했어요.', + type: 'success' + }) }} />
    diff --git a/src/pages/onboarding/OnbardingPage.tsx b/src/pages/onboarding/OnbardingPage.tsx index 40637a45..a4845523 100644 --- a/src/pages/onboarding/OnbardingPage.tsx +++ b/src/pages/onboarding/OnbardingPage.tsx @@ -16,11 +16,13 @@ import { onboardingApi } from '@/libs/api/onboarding/onboardingApi' import { PostOnboardingRequest } from '@/libs/api/onboarding/onboardingType' +import useToastify from '@/libs/hooks/useToastify' import { onboardingPageDataAtom } from '@/libs/store/onboardingAtom' import { getItem, setItem } from '@/libs/utils/storage' const Onboarding = () => { const navigate = useNavigate() + const { setToast } = useToastify() const [currentPage, setCurrentPage] = useState(0) const [isError, setIsError] = useState(false) const inputRef = useRef(null) @@ -70,7 +72,10 @@ const Onboarding = () => { const cntOfChild = async () => { const children = await getChildrenInfo() if (children.length === 5) { - alert('5명이 다 차있습니다!') + setToast({ + comment: '아이는 최대 5명까지만 등록이 가능해요.', + type: 'warning' + }) navigate('/') } setCurrentPage(children.length + 2) @@ -178,7 +183,10 @@ const Onboarding = () => { inputRef.current?.value === '' || selectRef.current?.value === '' ) { - alert('값을 입력해주세요😁👍') + setToast({ + comment: '값을 빠짐없이 입력해주세요.', + type: 'warning' + }) return } // ❗️ 값을 입력하고, child버튼일 때(자식입력 필드에서 존재하는 버튼 2개 diff --git a/src/pages/schedule/new/index.tsx b/src/pages/schedule/new/index.tsx index 3d783932..60df8782 100644 --- a/src/pages/schedule/new/index.tsx +++ b/src/pages/schedule/new/index.tsx @@ -1,3 +1,4 @@ +import { useNavigate } from 'react-router-dom' import { useMutation } from '@tanstack/react-query' import { useAtom } from 'jotai' import Button from '@/components/common/button/Button' @@ -5,17 +6,20 @@ import ListRowSelect from '@/components/common/listRowSelect/ListRowSelect' import Spacing from '@/components/common/spacing/Spacing' import { postScheduleApi } from '@/libs/api/schedule/scheduleApi' import { PostScheduleType } from '@/libs/api/schedule/scheduleType' +import useToastify from '@/libs/hooks/useToastify' import { scheduleAtom } from '@/libs/store/scheduleInfo' import AddScheduleAcademy from '@/pages/schedule/new/AddScheduleAcademy' import AddScheduleMemo from '@/pages/schedule/new/AddScheduleMemo' import AddScheduleTime from '@/pages/schedule/new/AddScheduleTime' - const NewSchedule = () => { + const navigate = useNavigate() const [scheduleInfo, setScheduleInfo] = useAtom(scheduleAtom) + const { setToast } = useToastify() + const postNewScheduleMutation = useMutation({ - onSuccess: () => { - alert('일정 생성 완료!') - // navigate(`/schedule/${data.academyTimeTemplateIds}`) + onSuccess: (data) => { + setToast({ comment: '일정이 생성되었어요.', type: 'success' }) + navigate(`/schedule/${data.academyTimeTemplateIds[0]}`) }, mutationFn: (scheduleInfo: PostScheduleType) => postScheduleApi(scheduleInfo) diff --git a/src/pages/setting/SettingPage.tsx b/src/pages/setting/SettingPage.tsx index 3f17606e..62bf3a06 100644 --- a/src/pages/setting/SettingPage.tsx +++ b/src/pages/setting/SettingPage.tsx @@ -16,7 +16,7 @@ const SettingPage = ({ isOpen }: SettingPage) => { {() => (
    Date: Wed, 29 Nov 2023 16:53:38 +0900 Subject: [PATCH 18/21] =?UTF-8?q?fix:=20homepage=20bug=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/home/HomePage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/home/HomePage.tsx b/src/pages/home/HomePage.tsx index b21294d6..a40127b7 100644 --- a/src/pages/home/HomePage.tsx +++ b/src/pages/home/HomePage.tsx @@ -17,7 +17,7 @@ const HomePage = () => { const { data, isLoading } = useQuery({ queryKey: ['children'], queryFn: () => getChildrenInfo() - + }) if (isLoading) { return } From c92df0db8c5ae355f5c6bf15be3813f76f1d9a98 Mon Sep 17 00:00:00 2001 From: sincerity developer <85999976+jisung24@users.noreply.github.com> Date: Thu, 30 Nov 2023 15:37:40 +0900 Subject: [PATCH 19/21] =?UTF-8?q?fix:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=A6=AC=EC=95=A1=ED=8A=B8=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=20=EC=A0=81=EC=9A=A9=20(#243)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 마이페이지 리액트 쿼리 적용 * fix: onSuccess시 navigate코드 --- src/libs/api/mypage/myPageApi.ts | 2 +- src/pages/mypage/MyPage.tsx | 62 +++++++++++++++----------------- 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/src/libs/api/mypage/myPageApi.ts b/src/libs/api/mypage/myPageApi.ts index c2097527..6b3eb752 100644 --- a/src/libs/api/mypage/myPageApi.ts +++ b/src/libs/api/mypage/myPageApi.ts @@ -1,7 +1,7 @@ import type { GetMyPageResponse } from './myPageType' import request from '@/libs/api' -export const myPageApi = async (): Promise => { +export const getAllUserInfo = async (): Promise => { try { const req = await request.get('/members') return req.data diff --git a/src/pages/mypage/MyPage.tsx b/src/pages/mypage/MyPage.tsx index 4ab7a009..c3983d3e 100644 --- a/src/pages/mypage/MyPage.tsx +++ b/src/pages/mypage/MyPage.tsx @@ -1,44 +1,42 @@ import { useEffect } from 'react' import { useNavigate } from 'react-router-dom' -import { useAtom } from 'jotai' +import { useQuery } from '@tanstack/react-query' import SettingPage from '../setting/SettingPage' +import Loading from '@/components/Loading/Loading' import Button from '@/components/common/button/Button' import Icon from '@/components/common/icon/Icon' import ListRow from '@/components/common/listRow/ListRow' import Profile from '@/components/common/profile/Profile' import Spacing from '@/components/common/spacing/Spacing' import { logoutApi } from '@/libs/api/autorization/logout/LogoutApi' -import { myPageApi } from '@/libs/api/mypage/myPageApi' +import { getAllUserInfo } from '@/libs/api/mypage/myPageApi' import useSidebar from '@/libs/hooks/useSidebar' import useToastify from '@/libs/hooks/useToastify' -import { myPageAtom } from '@/libs/store/myPageAtom' const MyPage = () => { const navigate = useNavigate() const { setToast } = useToastify() - const [myPageData, setMyPageData] = useAtom(myPageAtom) const { toggleOpen } = useSidebar() + const { data, isLoading, isSuccess } = useQuery({ + queryKey: ['members'], + queryFn: () => getAllUserInfo() + }) + useEffect(() => { - if (localStorage.getItem('token') === null) { - setToast({ comment: '로그인 페이지로 이동합니다.', type: 'info' }) - navigate('/login') - return - } - const response = async () => { - const res = await myPageApi() - setMyPageData(res) - } - response() - }, []) + navigate('/myPages') + }, [isSuccess]) + if (isLoading) { + return + } return (
    -

    {`${myPageData?.nickname}님 안녕하세요!`}

    -

    {myPageData?.email}

    +

    {`${data?.nickname}님 안녕하세요!`}

    +

    {data?.email}

    @@ -47,7 +45,7 @@ const MyPage = () => { icon={'Add'} classStyle={'w-[30px] h-[30px] cursor-pointer'} onClick={() => - myPageData.childInformationResponses.length === 5 + data?.childInformationResponses.length === 5 ? setToast({ comment: '아이는 최대 5명까지만 입력할 수 있어요.', type: 'warning' @@ -57,22 +55,18 @@ const MyPage = () => { />
    - {myPageData.childInformationResponses.map( - ({ childId, childName }) => ( -
  • - - navigate(`/edit/${childId}`, { state: childId }) - } - /> -
  • - ) - )} + {data?.childInformationResponses.map(({ childId, childName }) => ( +
  • + + navigate(`/edit/${childId}`, { state: childId }) + } + /> +
  • + ))}
    Date: Thu, 30 Nov 2023 15:40:57 +0900 Subject: [PATCH 20/21] =?UTF-8?q?[Feat]:=20=EC=9D=BC=EC=A0=95=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#246)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 패키지 관리 * feat: edit 페이지 기본 템플릿 생성 * feat: 수정 전 정보 불러오기 * feat: 일정 수정 기능 구현 * fix: 대시보드 스케쥴 스크롤 개선 --- package-lock.json | 480 ------------------ .../common/scheduleBox/ScheduleBox.tsx | 10 +- .../schedule/SelectAttendanceDate.tsx | 8 +- src/libs/api/dashboard/DashBoardType.ts | 2 +- src/libs/api/queryClient.ts | 2 +- src/libs/api/schedule/scheduleApi.ts | 22 +- src/libs/api/schedule/scheduleType.ts | 36 ++ src/libs/store/scheduleInfo.tsx | 4 +- src/pages/academy/AcademyDashboard.tsx | 21 +- .../{addSchedule => AddSchedule}/index.tsx | 0 src/pages/academy/editAcademy/index.tsx | 2 + .../schedule/edit/EditScheuleAcademy.tsx | 0 src/pages/schedule/edit/index.tsx | 90 ++++ src/pages/schedule/new/AddScheduleAcademy.tsx | 34 +- src/pages/schedule/new/AddScheduleMemo.tsx | 1 + src/pages/schedule/new/AddScheduleTime.tsx | 16 +- src/pages/schedule/new/index.tsx | 17 +- src/routes/index.tsx | 14 + tailwind.config.js | 1 + 19 files changed, 237 insertions(+), 523 deletions(-) rename src/pages/academy/{addSchedule => AddSchedule}/index.tsx (100%) create mode 100644 src/pages/schedule/edit/EditScheuleAcademy.tsx create mode 100644 src/pages/schedule/edit/index.tsx diff --git a/package-lock.json b/package-lock.json index ddfe0b83..76247f84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2436,54 +2436,6 @@ "react": ">=16.8.0" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/darwin-arm64": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", @@ -2500,294 +2452,6 @@ "node": ">=12" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -5692,150 +5356,6 @@ "node": ">=10" } }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.3.94", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.94.tgz", - "integrity": "sha512-HypemhyehQrLqXwfJv5ronD4BMAXdgMCP4Ei7rt3B6Ftmt9axwGvdwGiXxsYR9h1ncyxoVxN+coGxbNIhKhahw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.94", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.94.tgz", - "integrity": "sha512-KzKN54c7Y6X1db+bBVSXG4+bXmAPvXtDWk+TgwNJH4yYliOrnP/RKkHA5QZ9VFSnqJF06/sAO4kYBiL/aVQDBQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.94", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.94.tgz", - "integrity": "sha512-iAcR8Ho0Uck/SLSrgYfXkpcGOXuN5waMZO7GlL/52QODr7GJtOfZ0H1MCZLbIFkPJp/iXoJpYgym4d/qSd477Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.94", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.94.tgz", - "integrity": "sha512-VCHL1Mb9ENHx+sAeubSSg481MUeP9/PYzPPy9tfswunj/w35M+vEWflwK2dzQL9kUTFD3zcFTpAgsKnj6aX24w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.94", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.94.tgz", - "integrity": "sha512-gjq7U6clhJi0Oel2a4gwR4MbSu+THQ2hmBNVCOSA3JjPZWZTkJXaJDpnh/r7PJxKBwUDlo0VPlwiwjepAQR2Rw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.94", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.94.tgz", - "integrity": "sha512-rSylruWyeol2ujZDHmwiovupMR5ukMXivlA7DDxmQ1dFUV9HuiPknQrU5rEbI3V2V3V5RkpbEKjnADen7AeMPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.94", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.94.tgz", - "integrity": "sha512-OenDUr5MQkz506ebVQq6ezoZ3GZ26nchgf5mPnwab4gx2TEiyR9zn7MdX5LWskTmOK3+FszPbGK0B5oLK6Y5yw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.94", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.94.tgz", - "integrity": "sha512-mi6NcmtJKnaiHAxLtVz+WzunscsEwPdA0j15DuiYVx06Xo+MdRLJj4eVBgVLwGD1AI3IqKs4MVVx2cD7n0h5mg==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.94", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.94.tgz", - "integrity": "sha512-Ba0ZLcGMnqPWWF9Xa+rWhhnkpvE7XoQegMP/VCF2JIHb2ieGBC8jChO6nKRFKZjib/3wghGzxakyDQx3LDhDug==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, "node_modules/@swc/counter": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", diff --git a/src/components/common/scheduleBox/ScheduleBox.tsx b/src/components/common/scheduleBox/ScheduleBox.tsx index 5683f8e9..555ebcc2 100644 --- a/src/components/common/scheduleBox/ScheduleBox.tsx +++ b/src/components/common/scheduleBox/ScheduleBox.tsx @@ -19,13 +19,17 @@ const ScheduleBox = ({ ? 'w-[360px] h-[88px]' : scheduleType === 'toggle' ? isRegister - ? 'w-[345px] h-[142px] bg-white-0' - : 'w-[345px] h-[142px] bg-gray-200' + ? 'w-[345px] h-[142px] min-h-[142px] bg-white-0' + : 'w-[345px] h-[142px] min-h-[142px] bg-gray-200' : '' } shadow-md pt-[22px] pb-[20px] px-[24px] rounded-[20px] font-nsk`}>
    -
    {mainTitle}
    + {mainTitle.length > 15 ? ( +
    {mainTitle}
    + ) : ( +
    {mainTitle}
    + )}
    diff --git a/src/components/schedule/SelectAttendanceDate.tsx b/src/components/schedule/SelectAttendanceDate.tsx index b0624b55..5cb20b22 100644 --- a/src/components/schedule/SelectAttendanceDate.tsx +++ b/src/components/schedule/SelectAttendanceDate.tsx @@ -6,7 +6,7 @@ import CustomTimePicker from '@/components/academy/CustomTimePicker' import { scheduleAtom } from '@/libs/store/scheduleInfo' import { getFormattingDate } from '@/libs/utils/dateParse' -const SelectAttendanceDate = () => { +const SelectAttendanceDate = ({ isEdit = false }: { isEdit?: boolean }) => { const [startTime, setStartTime] = useState(new Date()) const [endTime, setEndTime] = useState() const [isSelected, setIsSelected] = useState(false) @@ -42,6 +42,12 @@ const SelectAttendanceDate = () => { setIsSelected(true) } + useEffect(() => { + if (isEdit) { + setIsSelected(true) + } + }, []) + return (
    { alert('에러 발생! 올바른 경로로 서비스를 이용해주세요.') - window.location.href = '/' + // window.location.href = '/' } }) }) diff --git a/src/libs/api/schedule/scheduleApi.ts b/src/libs/api/schedule/scheduleApi.ts index 9ad23ba8..b78a3455 100644 --- a/src/libs/api/schedule/scheduleApi.ts +++ b/src/libs/api/schedule/scheduleApi.ts @@ -4,7 +4,9 @@ import { CalendarPropsType, CalendarResponse, DayScheduleResponse, - PostScheduleType + PostScheduleType, + BeforeEditInfoResponseType, + UpdateScheduleType } from '@/libs/api/schedule/scheduleType.ts' export const getMonthScheduleAll = async ({ @@ -41,3 +43,21 @@ export const postScheduleApi = async ( const res = await request.post('/academy-schedules', schedule) return res.data } + +export const beforeEditInfoScheduleApi = async ({ + scheduleId +}: { + scheduleId: number +}): Promise => { + const res = await request.get(`/academy-schedules/${scheduleId}`) + return res.data +} + +export const editScheduleApi = async ({ + payload +}: { + payload: UpdateScheduleType | { isAllUpdated: true } +}) => { + const res = await request.put(`/academy-schedules`, payload) + return res.data +} diff --git a/src/libs/api/schedule/scheduleType.ts b/src/libs/api/schedule/scheduleType.ts index ab215ff3..31b86840 100644 --- a/src/libs/api/schedule/scheduleType.ts +++ b/src/libs/api/schedule/scheduleType.ts @@ -1,3 +1,4 @@ +import exp from 'node:constants' import { ClientWeekType } from '@/libs/api/academy/AcademyType' export interface CalendarPropsType { @@ -54,6 +55,31 @@ export interface PostScheduleType { memo: string } +export interface UpdateScheduleType { + lessonScheduleUpdateRequests: LessonScheduleType[] + attendanceDate: { + startDateOfAttendance: string + endDateOfAttendance: string + } + isAlarmed: boolean + periodicity: PeriodicityType + childId: number + dashboardId: number + memo: string +} + +export interface BeforeEditInfoResponseType { + dashboardId: number + childId: number + academyId: number + lessonId: number + lessonSchedule: LessonScheduleType[] + startDateOfAttendance: string + endDateOfAttendance: string + isAlarmed: true + memo: string +} + export interface LessonScheduleType { dayOfWeek: WeekStringType lessonTime: { @@ -94,3 +120,13 @@ export const ScheduleServerWeekData: ScheduleServerWeekDataType = { 6: 'SATURDAY', 7: 'SUNDAY' } + +export const ScheduleServerWeekDataString = { + MONDAY: 1, + TUESDAY: 2, + WEDNESDAY: 3, + THURSDAY: 4, + FRIDAY: 5, + SATURDAY: 6, + SUNDAY: 7 +} diff --git a/src/libs/store/scheduleInfo.tsx b/src/libs/store/scheduleInfo.tsx index bdb6852f..98fd8f52 100644 --- a/src/libs/store/scheduleInfo.tsx +++ b/src/libs/store/scheduleInfo.tsx @@ -2,7 +2,7 @@ import { atom } from 'jotai' import { PostScheduleType } from '@/libs/api/schedule/scheduleType' import { getFormattingDate } from '@/libs/utils/dateParse' -const initialChildAtom: PostScheduleType = { +export const initialScheduleAtom: PostScheduleType = { lessonScheduleCreateRequests: [], attendanceDate: { startDateOfAttendance: getFormattingDate(new Date()), @@ -15,4 +15,4 @@ const initialChildAtom: PostScheduleType = { memo: '' } -export const scheduleAtom = atom(initialChildAtom) +export const scheduleAtom = atom(initialScheduleAtom) diff --git a/src/pages/academy/AcademyDashboard.tsx b/src/pages/academy/AcademyDashboard.tsx index 8014f570..0ac3484f 100644 --- a/src/pages/academy/AcademyDashboard.tsx +++ b/src/pages/academy/AcademyDashboard.tsx @@ -76,14 +76,11 @@ const AcademyDashboard = () => { } return ( -
    +
    -
    +
    {data && data?.length > 0 ? (
    @@ -97,8 +94,9 @@ const AcademyDashboard = () => {
    ) : (
    {dashboardData.map((data, index) => { return ( @@ -107,7 +105,7 @@ const AcademyDashboard = () => { scheduleType={'toggle'} mainTitle={data.academyInfo.academyName} subElement={`매주 ${getWeekday(data.schedules)}요일 / ${ - data.lessonInfo.subject + data.lessonInfo.curriculum }`} isRegister={data.isActive} rightBottomElement={ @@ -151,16 +149,9 @@ const AcademyDashboard = () => { /> ) })} -
    )} -
    -
    +
    ) : (
    diff --git a/src/pages/academy/addSchedule/index.tsx b/src/pages/academy/AddSchedule/index.tsx similarity index 100% rename from src/pages/academy/addSchedule/index.tsx rename to src/pages/academy/AddSchedule/index.tsx diff --git a/src/pages/academy/editAcademy/index.tsx b/src/pages/academy/editAcademy/index.tsx index b594edb6..97de386e 100644 --- a/src/pages/academy/editAcademy/index.tsx +++ b/src/pages/academy/editAcademy/index.tsx @@ -29,6 +29,7 @@ const EditAcademy = () => { const navigate = useNavigate() const { setToast } = useToastify() const dashboardId = useLocation().state as number + const fetchDashboardData = async (dashboardId: number) => { if (dashboardId) { const res = await getDetailDashboard(dashboardId) @@ -50,6 +51,7 @@ const EditAcademy = () => { setToast({ comment: '학원 정보를 수정했어요.', type: 'success' }) } }) + useEffect(() => { if (data) { setAcademyInfo({ diff --git a/src/pages/schedule/edit/EditScheuleAcademy.tsx b/src/pages/schedule/edit/EditScheuleAcademy.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/pages/schedule/edit/index.tsx b/src/pages/schedule/edit/index.tsx new file mode 100644 index 00000000..c75e90c5 --- /dev/null +++ b/src/pages/schedule/edit/index.tsx @@ -0,0 +1,90 @@ +import { useEffect } from 'react' +import { useNavigate } from 'react-router-dom' +import { useLocation } from 'react-router-dom' +import { useMutation } from '@tanstack/react-query' +import { useQuery } from '@tanstack/react-query' +import { useAtom } from 'jotai' +import Button from '@/components/common/button/Button' +import Spacing from '@/components/common/spacing/Spacing' +import { beforeEditInfoScheduleApi } from '@/libs/api/schedule/scheduleApi' +import { editScheduleApi } from '@/libs/api/schedule/scheduleApi' +import { UpdateScheduleType } from '@/libs/api/schedule/scheduleType' +import useToastify from '@/libs/hooks/useToastify' +import { initialScheduleAtom, scheduleAtom } from '@/libs/store/scheduleInfo' +import AddScheduleAcademy from '@/pages/schedule/new/AddScheduleAcademy' +import AddScheduleMemo from '@/pages/schedule/new/AddScheduleMemo' +import AddScheduleTime from '@/pages/schedule/new/AddScheduleTime' + +const EditSchedule = () => { + const navigate = useNavigate() + const location = useLocation() + const scheduleId = Number.parseInt(location.pathname.split('/')[2], 10) + const [scheduleInfo, setScheduleInfo] = useAtom(scheduleAtom) + const { setToast } = useToastify() + const { data } = useQuery({ + queryKey: ['beforeEditData', scheduleId], + queryFn: () => beforeEditInfoScheduleApi({ scheduleId }) + }) + + const editScheduleMutation = useMutation({ + onSuccess: (data) => { + setToast({ comment: '일정을 수정했어요.', type: 'success' }) + navigate(`/schedule/${data.academyTimeTemplateIds[0]}`) + setScheduleInfo(initialScheduleAtom) + }, + mutationFn: ({ + payload + }: { + payload: UpdateScheduleType | { isAllUpdated: true } + }) => + editScheduleApi({ + payload: payload + }) + }) + useEffect(() => { + if (data) { + setScheduleInfo({ + dashboardId: data.dashboardId, + lessonScheduleCreateRequests: [...data.lessonSchedule], + childId: data.childId, + attendanceDate: { + startDateOfAttendance: data.startDateOfAttendance, + endDateOfAttendance: data.endDateOfAttendance + }, + isAlarmed: data.isAlarmed, + periodicity: 'WEEKLY', + memo: data.memo + }) + } + }, [data]) + + return ( +
    +
    + +

    {'일정 설정하기'}

    + + + +
    +
    + ) +} + +export default EditSchedule diff --git a/src/pages/schedule/new/AddScheduleAcademy.tsx b/src/pages/schedule/new/AddScheduleAcademy.tsx index b657e91a..5107f4c6 100644 --- a/src/pages/schedule/new/AddScheduleAcademy.tsx +++ b/src/pages/schedule/new/AddScheduleAcademy.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react' +import { useRef } from 'react' import { useQuery } from '@tanstack/react-query' import { useAtom } from 'jotai' import Select from '@/components/common/inputbox/select/Select' @@ -13,7 +14,14 @@ type AcademyListType = { academyName: string } -const AddScheduleAcademy = () => { +const AddScheduleAcademy = ({ + isEdit +}: { + isEdit?: boolean + lessonId?: number +}) => { + const childrenSelectRef = useRef(null) + const lessonSelectRef = useRef(null) const [scheduleInfo, setScheduleInfo] = useAtom(scheduleAtom) const [academyList, setAcademyList] = useState([]) const { data } = useQuery({ @@ -25,19 +33,38 @@ const AddScheduleAcademy = () => { const academyList = res.map((data) => { return { dashBoardId: data.dashboardId, - academyName: data.academyInfo.academyName + academyName: `${data.academyInfo.academyName} - ${data.lessonInfo.curriculum}` } }) setAcademyList([...academyList]) } + + useEffect(() => { + if (isEdit && lessonSelectRef.current) { + const idx = academyList?.findIndex( + (data) => data.dashBoardId === scheduleInfo.dashboardId + ) + lessonSelectRef.current.selectedIndex = idx + 1 + } + }, [academyList]) + useEffect(() => { - if (scheduleInfo.childId) fetchChildrenDashboard(scheduleInfo.childId) + if (scheduleInfo.childId) { + fetchChildrenDashboard(scheduleInfo.childId) + if (isEdit && childrenSelectRef.current) { + const idx = data?.findIndex( + (data) => data.childId === scheduleInfo.childId + ) as number + childrenSelectRef.current.selectedIndex = idx + 1 + } + } }, [scheduleInfo.childId]) return (
    { }} /> ( className={`${ fullWidth ? 'w-full h-[52px]' : 'w-[323px] h-[52px]' } rounded-[10px] ${ - field === 'email' && !validate('email', inputValue) + field === 'email' && + !validate('email', inputValue) && + inputValue !== '' ? 'border border-red-600' - : field === 'nickname' && !validate('nickname', inputValue) + : field === 'nickname' && + !validate('nickname', inputValue) && + inputValue !== '' ? 'border border-red-600' : 'border border-blue-350' } px-[20px] font-nsk text-black-800 bg-white-200 body-18 placeholder:text-gray-600 outline-none`} @@ -48,11 +53,18 @@ const Input = forwardRef( {...props} />

    - {field === 'email' && !validate('email', inputValue) + {field === 'email' && + !validate('email', inputValue) && + inputValue !== '' ? (errorMessage = '잘못된 이메일입니다') - : field === 'nickname' && !validate('nickname', inputValue) - ? (errorMessage = '한글과 영어만 사용가능합니다') - : field === 'childname' && !validate('childname', inputValue) + : field === 'nickname' && + !validate('nickname', inputValue) && + inputValue !== '' + ? (errorMessage = + '한글과 영어만 사용가능합니다(1 - 10글자 미만입니다)') + : field === 'childname' && + !validate('childname', inputValue) && + inputValue !== '' ? (errorMessage = '아이의 이름은 1글자에서 10글자여야 합니다!') : ''}

    diff --git a/src/libs/api/index.ts b/src/libs/api/index.ts index 9e6dcfea..0a885d79 100644 --- a/src/libs/api/index.ts +++ b/src/libs/api/index.ts @@ -1,4 +1,5 @@ import axios from 'axios' +import { setItem } from '../utils/storage' import { logoutApi } from './autorization/logout/LogoutApi' import { refreshApi } from './autorization/refresh/refreshApi' import useToastify from '@/libs/hooks/useToastify' @@ -37,6 +38,7 @@ request.interceptors.response.use( const getRefreshToken = await refreshApi() const prevRequest = error.config prevRequest.headers.Authorization = `Bearer ${getRefreshToken.appToken}` + setItem('token', getRefreshToken.appToken) return request(prevRequest) } catch { setToast({ diff --git a/src/libs/api/mypage/myPageApi.ts b/src/libs/api/mypage/myPageApi.ts index 6b3eb752..b7e1b096 100644 --- a/src/libs/api/mypage/myPageApi.ts +++ b/src/libs/api/mypage/myPageApi.ts @@ -3,8 +3,8 @@ import request from '@/libs/api' export const getAllUserInfo = async (): Promise => { try { - const req = await request.get('/members') - return req.data + const myPageData = await request.get('/members') + return myPageData.data } catch { throw new Error('cannot get api from myPage') } diff --git a/src/pages/home/HomePage.tsx b/src/pages/home/HomePage.tsx index a40127b7..8c901c3e 100644 --- a/src/pages/home/HomePage.tsx +++ b/src/pages/home/HomePage.tsx @@ -23,9 +23,11 @@ const HomePage = () => { } return ( -
    +
    -
    +
    { @@ -55,7 +58,7 @@ const HomePage = () => {
    )}
    -
    +
    { const navigate = useNavigate() @@ -32,6 +32,8 @@ const Onboarding = () => { const [storeStorage, setStoreStorage] = useState([]) const [pageData, setPageData] = useAtom(onboardingPageDataAtom) const [isDone, setIsDone] = useState(false) + const [myPageData, setMyPageData] = useState() + const handleInputChange = () => { setInputValue(inputRef.current?.value) validate( @@ -40,7 +42,7 @@ const Onboarding = () => { : currentPage === 1 ? 'email' : 'childname', - inputValue as string + inputRef.current?.value || '' ) ? setIsError(false) : setIsError(true) @@ -48,10 +50,33 @@ const Onboarding = () => { const handleSelectChange = () => { setSelectValue(selectRef.current?.value) } + useEffect(() => { + if (myPageData) { + const { nickname, email, childInformationResponses } = myPageData + if (!nickname || !email) { + setCurrentPage(0) + return + } + if (childInformationResponses.length === 5) { + setToast({ + comment: '아이는 최대 5명까지만 등록이 가능해요.', + type: 'warning' + }) + navigate('/') + } else setCurrentPage(childInformationResponses.length + 2) + } + }, [myPageData]) + + useEffect(() => { + const user = async () => { + const myPage = await getAllUserInfo() + setMyPageData(myPage) + } + user() + }, []) useEffect(() => { const req = async (pageData: PostOnboardingRequest) => { - setItem('onboarding', JSON.stringify(storeStorage)) const onboardingData = await onboardingApi(pageData) onboardingData && navigate('/') } @@ -63,35 +88,12 @@ const Onboarding = () => { data && navigate('/') } if (isDone) { - if (getItem('onboarding').length === 0) req(pageData) + const { nickname, email } = myPageData as GetMyPageResponse + if (!nickname || !email) req(pageData) else makeChild() } }, [isDone]) - useEffect(() => { - const cntOfChild = async () => { - const children = await getChildrenInfo() - if (children.length === 5) { - setToast({ - comment: '아이는 최대 5명까지만 등록이 가능해요.', - type: 'warning' - }) - navigate('/') - } - setCurrentPage(children.length + 2) - } - if (getItem('onboarding').length > 0) { - cntOfChild() - } - if (!Array.isArray(getItem('onboarding'))) { - const getMyChildren = async () => { - const numbers = await getChildrenInfo() - setCurrentPage(numbers.length + 2) - } - getMyChildren() - } - }, []) - useEffect(() => { if (inputRef.current) { inputRef.current.value = '' @@ -108,19 +110,13 @@ const Onboarding = () => {
    { - if (getItem('onboarding').length === 0) { - setCurrentPage(0) - setPageData({ - children: [{ nickname: '', grade: '' }], - email: '', - nickname: '' - }) - } else navigate(-1) - }} + onClick={() => navigate(-1)} /> - +
    {PAGE_CONTENT[currentPage].mainTitle.map((mainTitle) => ( @@ -165,7 +161,9 @@ const Onboarding = () => { (buttonLabel, i) => buttonLabel && (
  • - {i === 0 && getItem('onboarding').length > 0 ? ( + {i === 0 && + myPageData?.email !== '' && + myPageData?.nickname !== '' ? ( '' ) : (