diff --git a/package-lock.json b/package-lock.json index dd802009..40a3c787 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@chakra-ui/react": "^2.8.2", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", + "@tanstack/react-query": "^5.51.23", "dayjs": "^1.11.11", "framer-motion": "^10.18.0", "jotai": "^2.6.1", @@ -1930,6 +1931,30 @@ "tslib": "^2.4.0" } }, + "node_modules/@tanstack/query-core": { + "version": "5.51.21", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.51.21.tgz", + "integrity": "sha512-POQxm42IUp6n89kKWF4IZi18v3fxQWFRolvBA6phNVmA8psdfB1MvDnGacCJdS+EOX12w/CyHM62z//rHmYmvw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.51.23", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.51.23.tgz", + "integrity": "sha512-CfJCfX45nnVIZjQBRYYtvVMIsGgWLKLYC4xcUiYEey671n1alvTZoCBaU9B85O8mF/tx9LPyrI04A6Bs2THv4A==", + "dependencies": { + "@tanstack/query-core": "5.51.21" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", diff --git a/package.json b/package.json index 5a9e920a..b388253b 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@chakra-ui/react": "^2.8.2", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", + "@tanstack/react-query": "^5.51.23", "dayjs": "^1.11.11", "framer-motion": "^10.18.0", "jotai": "^2.6.1", diff --git a/public/images/crops/growth/carrot/1.png b/public/images/crops/growth/carrot/1.png new file mode 100644 index 00000000..cc092f92 Binary files /dev/null and b/public/images/crops/growth/carrot/1.png differ diff --git a/public/images/crops/growth/carrot/2.png b/public/images/crops/growth/carrot/2.png new file mode 100644 index 00000000..cbf846d8 Binary files /dev/null and b/public/images/crops/growth/carrot/2.png differ diff --git a/public/images/crops/growth/carrot/3.png b/public/images/crops/growth/carrot/3.png new file mode 100644 index 00000000..06920ccd Binary files /dev/null and b/public/images/crops/growth/carrot/3.png differ diff --git a/public/images/crops/growth/carrot/4.png b/public/images/crops/growth/carrot/4.png new file mode 100644 index 00000000..2d61a07b Binary files /dev/null and b/public/images/crops/growth/carrot/4.png differ diff --git a/public/images/crops/growth/carrot/5.png b/public/images/crops/growth/carrot/5.png new file mode 100644 index 00000000..534719d6 Binary files /dev/null and b/public/images/crops/growth/carrot/5.png differ diff --git a/public/images/crops/growth/pea/1.png b/public/images/crops/growth/pea/1.png new file mode 100644 index 00000000..bc3e9656 Binary files /dev/null and b/public/images/crops/growth/pea/1.png differ diff --git a/public/images/crops/growth/pea/2.png b/public/images/crops/growth/pea/2.png new file mode 100644 index 00000000..462d8787 Binary files /dev/null and b/public/images/crops/growth/pea/2.png differ diff --git a/public/images/crops/growth/pea/3.png b/public/images/crops/growth/pea/3.png new file mode 100644 index 00000000..814b0402 Binary files /dev/null and b/public/images/crops/growth/pea/3.png differ diff --git a/public/images/crops/growth/pea/4.png b/public/images/crops/growth/pea/4.png new file mode 100644 index 00000000..252d32ce Binary files /dev/null and b/public/images/crops/growth/pea/4.png differ diff --git a/public/images/crops/growth/pea/5.png b/public/images/crops/growth/pea/5.png new file mode 100644 index 00000000..509283b9 Binary files /dev/null and b/public/images/crops/growth/pea/5.png differ diff --git a/public/images/crops/growth/rice/1.png b/public/images/crops/growth/rice/1.png new file mode 100644 index 00000000..1954b213 Binary files /dev/null and b/public/images/crops/growth/rice/1.png differ diff --git a/public/images/crops/growth/rice/2.png b/public/images/crops/growth/rice/2.png new file mode 100644 index 00000000..6f6523f6 Binary files /dev/null and b/public/images/crops/growth/rice/2.png differ diff --git a/public/images/crops/growth/rice/3.png b/public/images/crops/growth/rice/3.png new file mode 100644 index 00000000..3c67ce4f Binary files /dev/null and b/public/images/crops/growth/rice/3.png differ diff --git a/public/images/crops/growth/rice/4.png b/public/images/crops/growth/rice/4.png new file mode 100644 index 00000000..9db6ea9c Binary files /dev/null and b/public/images/crops/growth/rice/4.png differ diff --git a/public/images/crops/growth/rice/5.png b/public/images/crops/growth/rice/5.png new file mode 100644 index 00000000..363d3704 Binary files /dev/null and b/public/images/crops/growth/rice/5.png differ diff --git a/public/images/crops/growth/sweetPotato/1.png b/public/images/crops/growth/sweetPotato/1.png new file mode 100644 index 00000000..06b2b357 Binary files /dev/null and b/public/images/crops/growth/sweetPotato/1.png differ diff --git a/public/images/crops/growth/sweetPotato/2.png b/public/images/crops/growth/sweetPotato/2.png new file mode 100644 index 00000000..b383dd25 Binary files /dev/null and b/public/images/crops/growth/sweetPotato/2.png differ diff --git a/public/images/crops/growth/sweetPotato/3.png b/public/images/crops/growth/sweetPotato/3.png new file mode 100644 index 00000000..dea1ac4e Binary files /dev/null and b/public/images/crops/growth/sweetPotato/3.png differ diff --git a/public/images/crops/growth/sweetPotato/4.png b/public/images/crops/growth/sweetPotato/4.png new file mode 100644 index 00000000..7677cc7b Binary files /dev/null and b/public/images/crops/growth/sweetPotato/4.png differ diff --git a/public/images/crops/growth/sweetPotato/5.png b/public/images/crops/growth/sweetPotato/5.png new file mode 100644 index 00000000..d9fcb36c Binary files /dev/null and b/public/images/crops/growth/sweetPotato/5.png differ diff --git a/public/images/crops/growth/tomato/1.png b/public/images/crops/growth/tomato/1.png new file mode 100644 index 00000000..e1394607 Binary files /dev/null and b/public/images/crops/growth/tomato/1.png differ diff --git a/public/images/crops/growth/tomato/2.png b/public/images/crops/growth/tomato/2.png new file mode 100644 index 00000000..740f94c2 Binary files /dev/null and b/public/images/crops/growth/tomato/2.png differ diff --git a/public/images/crops/growth/tomato/3.png b/public/images/crops/growth/tomato/3.png new file mode 100644 index 00000000..d9cd2e04 Binary files /dev/null and b/public/images/crops/growth/tomato/3.png differ diff --git a/public/images/crops/growth/tomato/4.png b/public/images/crops/growth/tomato/4.png new file mode 100644 index 00000000..6482cbb7 Binary files /dev/null and b/public/images/crops/growth/tomato/4.png differ diff --git a/public/images/crops/growth/tomato/5.png b/public/images/crops/growth/tomato/5.png new file mode 100644 index 00000000..169520f9 Binary files /dev/null and b/public/images/crops/growth/tomato/5.png differ diff --git a/public/images/curriculumCrops/carrot_5.png b/public/images/curriculumCrops/carrot_5.png deleted file mode 100644 index 59fbc365..00000000 Binary files a/public/images/curriculumCrops/carrot_5.png and /dev/null differ diff --git a/src/app/api/login.ts b/src/app/api/login.ts index ccf0f545..f3edc2ea 100644 --- a/src/app/api/login.ts +++ b/src/app/api/login.ts @@ -3,10 +3,10 @@ import { fetcher } from '@/app/api/fetcher'; const loginFetcher = fetcher(); -const postGoogleLogin = (code: string | null) => +const postGoogleLogin = (code: string | null, redirectUri: string | undefined) => loginFetcher('/login/google', { method: 'POST', - body: { code }, + body: { code, redirectUri }, }); export { postGoogleLogin }; diff --git a/src/app/api/member.ts b/src/app/api/member.ts index e20e4e67..fb2ee8c3 100644 --- a/src/app/api/member.ts +++ b/src/app/api/member.ts @@ -1,4 +1,7 @@ +/* eslint-disable import/prefer-default-export */ +import { useQuery } from '@tanstack/react-query'; import { fetcher } from '@/app/api/fetcher'; +import useGetUser from '@/hooks/useGetUser'; const memberFetcher = fetcher(); @@ -17,4 +20,12 @@ const patchStudyMandate = (token: string, studyId: number, newStudyLeaderId: num }, }); -export { getSidebarInfo, patchStudyMandate }; +const useGetSideBarInfoQuery = () => { + const user = useGetUser(); + return useQuery({ + queryFn: () => getSidebarInfo(user?.token || '', user?.memberId || 0), + queryKey: ['sidebar', user?.memberId], + }); +}; + +export { getSidebarInfo, useGetSideBarInfoQuery, patchStudyMandate }; diff --git a/src/app/oauth2/code/google/page.tsx b/src/app/oauth2/code/google/page.tsx index 4343bdf8..d93753ee 100644 --- a/src/app/oauth2/code/google/page.tsx +++ b/src/app/oauth2/code/google/page.tsx @@ -16,7 +16,7 @@ const Page = ({ searchParams }: { searchParams: { code: string } }) => { useEffect(() => { if (code) { - postGoogleLogin(code).then((res) => { + postGoogleLogin(code, process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URL).then((res) => { if (res?.ok) { setUser({ memberId: res.body?.memberId, diff --git a/src/app/providers.tsx b/src/app/providers.tsx index 523e91d9..f4b89796 100644 --- a/src/app/providers.tsx +++ b/src/app/providers.tsx @@ -1,17 +1,21 @@ 'use client'; import { ChakraProvider } from '@chakra-ui/react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { Provider, createStore } from 'jotai'; import theme from '@/theme'; const store = createStore(); +const queryClient = new QueryClient(); const Providers = ({ children }: { children: React.ReactNode }) => { return ( - - {children} - + + + {children} + + ); }; diff --git a/src/app/team/[teamId]/join/page.tsx b/src/app/team/[teamId]/join/page.tsx index 5579980b..493d5d13 100644 --- a/src/app/team/[teamId]/join/page.tsx +++ b/src/app/team/[teamId]/join/page.tsx @@ -9,6 +9,7 @@ import { loginBackPathAtom } from '@/atom'; import GOOGLE_LOGIN_URL from '@/constants/googleLoginUrl'; import { useMutateWithToken } from '@/hooks/useFetchWithToken'; import useGetUser from '@/hooks/useGetUser'; +import useRefetchSideBar from '@/hooks/useRefetchSideBar'; const Page = ({ searchParams }: { searchParams: { code: string } }) => { const params = useParams<{ teamId: string }>(); @@ -18,12 +19,14 @@ const Page = ({ searchParams }: { searchParams: { code: string } }) => { const user = useGetUser(); const setLoginBackPath = useSetAtom(loginBackPathAtom); const joinTeam = useMutateWithToken(postJoinTeam); + const refetchSidebar = useRefetchSideBar(); useEffect(() => { if (user) { if (user.isLogin) { joinTeam(teamId, code).then((res) => { if (res?.ok) { + refetchSidebar(); router.replace(`/team/${teamId}`); } else { alert('유효하지 않은 초대링크입니다.'); @@ -35,7 +38,7 @@ const Page = ({ searchParams }: { searchParams: { code: string } }) => { window.location.href = GOOGLE_LOGIN_URL; } } - }, [user, teamId, code, router, setLoginBackPath, joinTeam]); + }, [user, teamId, code, router, setLoginBackPath, joinTeam, refetchSidebar]); return
; }; diff --git a/src/app/team/[teamId]/study/[studyId]/page.tsx b/src/app/team/[teamId]/study/[studyId]/page.tsx index f20d99cb..dcfddcaf 100644 --- a/src/app/team/[teamId]/study/[studyId]/page.tsx +++ b/src/app/team/[teamId]/study/[studyId]/page.tsx @@ -10,7 +10,7 @@ import { getStudy } from '@/app/api/study'; import DocumentCard from '@/components/DocumentCard'; import Title from '@/components/Title'; import CurriculumCard from '@/containers/study/CurriculumCard'; -import Feed from '@/containers/study/Feed'; +// import Feed from '@/containers/study/Feed'; import DeleteStudyModal from '@/containers/study/Modal/DeleteStudyModal'; import StudyModal from '@/containers/study/Modal/StudyModal'; import TerminateStudyModal from '@/containers/study/Modal/TerminateStudyModal'; @@ -61,7 +61,10 @@ const Page = ({ params }: { params: { teamId: number; studyId: number } }) => { /> - + {studyData && ( + + )} + { - + {/* */} {studyData && ( { const [isTeamModalOpen, setIsTeamModalOpen] = useState(false); const user = useGetUser(); - const sidebarInfo = useGetFetchWithToken(getSidebarInfo, [user?.memberId], user); + const setUser = useSetAtom(userAtom); + const { data: sidebarInfo } = useGetSideBarInfoQuery(); return ( <> @@ -48,16 +50,16 @@ const SidebarContent = ({ isOpen, setIsOpen }: SidebarContentProps) => { /> - + {isOpen && ( - {user?.isLogin ? sidebarInfo?.name : '비회원'} + {user?.isLogin ? sidebarInfo?.body?.name : '비회원'} )} } onClick={() => {}} /> } onClick={() => {}} /> - } onClick={() => {}} /> + } onClick={() => setUser(defaultUserAtom)} /> {isOpen && user?.isLogin && ( @@ -87,7 +89,7 @@ const SidebarContent = ({ isOpen, setIsOpen }: SidebarContentProps) => { bg="green_dark" roundedBottom="2xl" > - {sidebarInfo?.myTeamsAndStudies?.map((team: SidebarTeam) => ( + {sidebarInfo?.body?.myTeamsAndStudies?.map((team: SidebarTeam) => ( { w={!isDesktop && isOpen ? '100%' : '0'} h="100%" bg={!isDesktop && isOpen ? 'rgba(0,0,0,0.5)' : ''} + onClick={() => { + setIsOpen(false); + }} > - + e.stopPropagation()} + > diff --git a/src/constants/crop.ts b/src/constants/crop.ts index de7e62db..7fd0dd18 100644 --- a/src/constants/crop.ts +++ b/src/constants/crop.ts @@ -1,9 +1,9 @@ const CROP = [ - { id: 1, name: '토마토', imageUrl: '/images/crops/tomato.png' }, - { id: 2, name: '고구마', imageUrl: '/images/crops/sweet_potato.png' }, - { id: 3, name: '당근', imageUrl: '/images/crops/carrot.png' }, - { id: 4, name: '완두콩', imageUrl: '/images/crops/pea.png' }, - { id: 5, name: '벼', imageUrl: '/images/crops/rice.png' }, + { id: 1, name: '토마토', engName: 'tomato', imageUrl: '/images/crops/tomato.png' }, + { id: 2, name: '고구마', engName: 'sweetPotato', imageUrl: '/images/crops/sweet_potato.png' }, + { id: 3, name: '당근', engName: 'carrot', imageUrl: '/images/crops/carrot.png' }, + { id: 4, name: '완두콩', engName: 'pea', imageUrl: '/images/crops/pea.png' }, + { id: 5, name: '벼', engName: 'rice', imageUrl: '/images/crops/rice.png' }, ]; export default CROP; diff --git a/src/containers/study/CurriculumCard/index.tsx b/src/containers/study/CurriculumCard/index.tsx index 28145c0d..28474d63 100644 --- a/src/containers/study/CurriculumCard/index.tsx +++ b/src/containers/study/CurriculumCard/index.tsx @@ -6,13 +6,15 @@ import { useState } from 'react'; import { MdOutlineArrowForwardIos } from 'react-icons/md'; import { getCurriculum } from '@/app/api/study'; +import CROP from '@/constants/crop'; import { useGetFetchWithToken } from '@/hooks/useFetchWithToken'; import { Curriculum } from '@/types'; import CurriculumItem from './CurriculumItem'; +import { CurriculumCardProps } from './types'; import CurriculumModal from '../CurriculumModal'; -const CurriculumCard = () => { +const CurriculumCard = ({ cropId, studyProgressRatio }: CurriculumCardProps) => { const [isStudyLeader] = useState(true); // NOTE 추후 스터디장 여부 props로 받아올 예정 const { studyId } = useParams<{ studyId: string }>(); @@ -20,6 +22,14 @@ const CurriculumCard = () => { const { isOpen: isCurriculumModalOpen, onOpen: onActionModalOpen, onClose: onCurriculumModalClose } = useDisclosure(); + const getCropImage = () => { + const engName = CROP.find((crop) => crop.id === cropId)?.engName; + const growthLevel = studyProgressRatio === 0 ? 1 : Math.ceil(studyProgressRatio / 20); + const cropImageURL = `/images/crops/growth/${engName}/${growthLevel}.png`; + + return cropImageURL; + }; + return ( {isStudyLeader && ( @@ -38,7 +48,7 @@ const CurriculumCard = () => { borderBottomLeftRadius="2xl" borderBottomRightRadius="0" alt="curriculum card" - src="/images/curriculumCrops/carrot_5.png" + src={getCropImage()} /> { const deletedStudy = useMutateWithToken(deleteStudy); + const refetchSidebar = useRefetchSideBar(); const handleClickDelete = () => { deletedStudy(id).then((res) => { if (res.ok) { + refetchSidebar(); setIsOpen(false); } }); diff --git a/src/containers/study/Modal/StudyModal/index.tsx b/src/containers/study/Modal/StudyModal/index.tsx index bf2ca865..aba2719c 100644 --- a/src/containers/study/Modal/StudyModal/index.tsx +++ b/src/containers/study/Modal/StudyModal/index.tsx @@ -13,6 +13,7 @@ import Selector from '@/components/Selector'; import StyledDatePicker from '@/components/StyledDatePicker'; import CROP from '@/constants/crop'; import { useMutateWithToken } from '@/hooks/useFetchWithToken'; +import useRefetchSideBar from '@/hooks/useRefetchSideBar'; import { StudyModalProps } from './types'; @@ -40,6 +41,8 @@ const StudyModal = ({ teamId, studyId, studyInfo, isOpen, setIsModalOpen }: Stud const createStudy = useMutateWithToken(postStudy); const editStudy = useMutateWithToken(putEditStudy); + const refetchSidebar = useRefetchSideBar(); + const onClose = () => { setStep(1); setName(''); @@ -74,7 +77,10 @@ const StudyModal = ({ teamId, studyId, studyInfo, isOpen, setIsModalOpen }: Stud endDate: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', cropId, }).then((res) => { - if (res.ok) onClose(); + if (res.ok) { + refetchSidebar(); + onClose(); + } }); } else if (studyId && studyInfo) { editStudy(studyId, { @@ -84,6 +90,7 @@ const StudyModal = ({ teamId, studyId, studyInfo, isOpen, setIsModalOpen }: Stud endDate: endDate ? dayjs(endDate).format('YYYY-MM-DD') : '', status: startDate <= new Date() ? 'IN_PROGRESS' : 'UPCOMING', }).then((res) => { + refetchSidebar(); if (res.ok) onClose(); }); } diff --git a/src/containers/study/Participant/index.tsx b/src/containers/study/Participant/index.tsx index 3e642dcb..9d2f9279 100644 --- a/src/containers/study/Participant/index.tsx +++ b/src/containers/study/Participant/index.tsx @@ -13,8 +13,9 @@ const Participant = ({ participantInfos }: ParticipantProps) => { return 0; }); + // Feed 없을때 임시로 maxH="45vh"로 설정 원래는 maxH="30vh" return ( - + {[ { data: leader, color: colors.orange_dark }, diff --git a/src/containers/team/DeleteTeamModal/index.tsx b/src/containers/team/DeleteTeamModal/index.tsx index d7ef9578..046f3116 100644 --- a/src/containers/team/DeleteTeamModal/index.tsx +++ b/src/containers/team/DeleteTeamModal/index.tsx @@ -4,15 +4,18 @@ import { useRouter } from 'next/navigation'; import { deleteTeam as deleteTeamApi } from '@/app/api/team'; import ConfirmModal from '@/components/Modal/ConfirmModal'; import { useMutateWithToken } from '@/hooks/useFetchWithToken'; +import useRefetchSideBar from '@/hooks/useRefetchSideBar'; import { DeleteTeamModalProps } from './type'; const DeleteTeamModal = ({ id, name, isOpen, onClose }: DeleteTeamModalProps) => { const deleteTeam = useMutateWithToken(deleteTeamApi); + const refetchSidebar = useRefetchSideBar(); const router = useRouter(); const handleDeleteTeamButtonClick = () => { deleteTeam(id).then(() => { + refetchSidebar(); onClose(); router.replace('/'); }); diff --git a/src/containers/team/TeamModal/index.tsx b/src/containers/team/TeamModal/index.tsx index 530a2727..4e40aeb2 100644 --- a/src/containers/team/TeamModal/index.tsx +++ b/src/containers/team/TeamModal/index.tsx @@ -9,6 +9,7 @@ import IconBox from '@/components/IconBox'; import ActionModal from '@/components/Modal/ActionModal'; import S3_URL from '@/constants/s3Url'; import { useMutateWithToken } from '@/hooks/useFetchWithToken'; +import useRefetchSideBar from '@/hooks/useRefetchSideBar'; import { TeamModalProps } from './type'; @@ -33,6 +34,8 @@ const TeamModal = ({ teamInfo, isOpen, onClose }: TeamModalProps) => { const editTeam = useMutateWithToken(putEditTeam); const editTeamImage = useMutateWithToken(patchEditTeamImage); + const refetchSideBar = useRefetchSideBar(); + const resetState = () => { setName(''); setDescription(''); @@ -75,6 +78,7 @@ const TeamModal = ({ teamInfo, isOpen, onClose }: TeamModalProps) => { } }); } + refetchSideBar(); resetAndCloseModal(); } }); @@ -96,6 +100,7 @@ const TeamModal = ({ teamInfo, isOpen, onClose }: TeamModalProps) => { createTeam(teamForm).then((res) => { if (res.ok) { + refetchSideBar(); resetAndCloseModal(); } }); diff --git a/src/hooks/useRefetchSideBar.ts b/src/hooks/useRefetchSideBar.ts new file mode 100644 index 00000000..11bd199c --- /dev/null +++ b/src/hooks/useRefetchSideBar.ts @@ -0,0 +1,16 @@ +import { useQueryClient } from '@tanstack/react-query'; + +import useGetUser from '@/hooks/useGetUser'; + +const useRefetchSideBar = () => { + const queryClient = useQueryClient(); + const user = useGetUser(); + const refetchSideBar = () => { + queryClient.invalidateQueries({ + queryKey: ['sidebar', user?.memberId], + }); + }; + return refetchSideBar; +}; + +export default useRefetchSideBar;