From 4b05cc95e8f4d5a04e9c4efec4bd156071e73d88 Mon Sep 17 00:00:00 2001 From: llddang <77055208+llddang@users.noreply.github.com> Date: Sun, 8 Sep 2024 21:13:29 +0900 Subject: [PATCH] =?UTF-8?q?Feature/#294=20qa=20=EB=B0=98=EC=98=81=20(#303)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 팀페이지에서 스터디 목록 라우팅 등록 #294 * feat: 스터디 생성 실패 시, 에러 문구 띄워주기 #294 * feat: 자신이 속한 팀이 아닐 때는 초대 코드 및 스터디/파일 생성 버튼이 안 보이도록 수정 - 전역변수로 myTeam이라는 자신이 속한 teamId를 담는 배열로 자신이 팀인지 검증했습니다. #294 * feat: 학습자료 혹은 스터디가 없을 때 생성해달라는 문구 추가 #294 * feat: 스터디 전체 보기 구현 #294 * feat: team 수정후 자동으로 다시 팀 정보 가져오도록 로직 수정 #294 * feat: 스터디 갤러리의 PageNavigator 의 pages 추가 #294 * feat: 수정된 팀 내 스터디 호출 api 반환 값에 따른 변화 #294 * feat: 팀 수정 시 바로 반영되도록 수정 #294 * refeactor: 사용하지 않는 mock data(studyCard) 삭제 #294 * feat: teamInfo 관련 querykey 수정 #294 --- src/app/api/fetcher.ts | 9 +- src/app/api/team.ts | 12 ++ src/app/team/[teamId]/page.tsx | 74 +++++++----- src/app/team/[teamId]/study-gallery/page.tsx | 28 +++++ src/atom.ts | 4 + src/components/StudyCard/index.tsx | 54 +++++---- .../study-gallery/StudyGallery/index.tsx | 68 ++++++++++++ .../team/NavigationButton/index.tsx | 20 ++-- src/containers/team/NavigationButton/types.ts | 1 + src/containers/team/StudyGridView/index.tsx | 3 +- src/containers/team/StudyGridView/types.ts | 1 + .../team/SuggestionCreate/index.tsx | 28 +++++ src/containers/team/TeamModal/index.tsx | 3 + src/hooks/useRefetchTeamInfo.ts | 13 +++ src/mocks/studyCard.ts | 105 ------------------ src/types.ts | 1 + 16 files changed, 255 insertions(+), 169 deletions(-) create mode 100644 src/app/team/[teamId]/study-gallery/page.tsx create mode 100644 src/containers/study-gallery/StudyGallery/index.tsx create mode 100644 src/containers/team/SuggestionCreate/index.tsx create mode 100644 src/hooks/useRefetchTeamInfo.ts delete mode 100644 src/mocks/studyCard.ts diff --git a/src/app/api/fetcher.ts b/src/app/api/fetcher.ts index af7563d9..d626b2c3 100644 --- a/src/app/api/fetcher.ts +++ b/src/app/api/fetcher.ts @@ -21,12 +21,7 @@ const defaultOptions: FetcherOptions = { }, interceptors: { request: async (config) => config, - response: async (response) => { - if (response.ok) { - return response; - } - throw new Error(response.statusText); - }, + response: async (response) => response, }, }; @@ -70,7 +65,7 @@ export const fetcher = (options?: FetcherOptions) => { const bodyText = await response.text(); const body = bodyText ? JSON.parse(bodyText) : null; - return { ok: true, body }; + return { ok: response.ok, body }; } catch (error) { let message = ''; if (error instanceof Error) message = error.message; diff --git a/src/app/api/team.ts b/src/app/api/team.ts index f0501ade..68a62061 100644 --- a/src/app/api/team.ts +++ b/src/app/api/team.ts @@ -1,3 +1,6 @@ +import { useQuery } from '@tanstack/react-query'; + +import useGetUser from '@/hooks/useGetUser'; import { Team } from '@/types'; import { fetcher } from './fetcher'; @@ -21,6 +24,14 @@ const getTeamInfo = (token: string, teamId: number) => }, }); +const useGetTeamInfoQuery = (teamId: number) => { + const user = useGetUser(); + return useQuery({ + queryFn: () => getTeamInfo(user?.token || '', teamId), + queryKey: ['teamInfo', teamId.toString()], + }); +}; + const putEditTeam = (token: string, teamId: number, teamInfo: Pick) => { return teamFetcher(`/teams/${teamId}`, { method: 'PUT', @@ -95,6 +106,7 @@ const getMyTeams = (memberId: number) => teamFetcher(`/teams/members/${memberId} export { postCreateTeam, getTeamInfo, + useGetTeamInfoQuery, putEditTeam, patchEditTeamImage, deleteTeam, diff --git a/src/app/team/[teamId]/page.tsx b/src/app/team/[teamId]/page.tsx index d4343c34..4c123ae4 100644 --- a/src/app/team/[teamId]/page.tsx +++ b/src/app/team/[teamId]/page.tsx @@ -3,13 +3,15 @@ 'use client'; import { Box, Button, Flex, useBreakpointValue } from '@chakra-ui/react'; +import { useAtomValue } from 'jotai'; import { useEffect, useState } from 'react'; import { BsLink45Deg } from 'react-icons/bs'; import { getDocumentList } from '@/app/api/document'; import { getGarden } from '@/app/api/garden'; import { getStudies } from '@/app/api/study'; -import { getTeamInfo, postInviteTeam } from '@/app/api/team'; +import { postInviteTeam, useGetTeamInfoQuery } from '@/app/api/team'; +import { myTeamAtom } from '@/atom'; import Garden3D from '@/components/Garden3D'; import TabButton from '@/components/TabButton'; import Title from '@/components/Title'; @@ -21,13 +23,14 @@ import AttendanceRate from '@/containers/team/AttendanceRate'; import DocumentGridView from '@/containers/team/DocumentGridView'; import NavigationButton from '@/containers/team/NavigationButton'; import StudyGridView from '@/containers/team/StudyGridView'; +import SuggestionCreate from '@/containers/team/SuggestionCreate'; import TeamControlPanel from '@/containers/team/TeamControlPanel'; import TeamMember from '@/containers/team/teamMember'; -import { useGetFetchWithToken, useMutateWithToken } from '@/hooks/useFetchWithToken'; +import { useMutateWithToken } from '@/hooks/useFetchWithToken'; import { DocumentList, Garden, StudyRank } from '@/types'; const Page = ({ params }: { params: { teamId: number } }) => { - const teamInfo = useGetFetchWithToken(getTeamInfo, [params.teamId]); + const { data: teamInfo } = useGetTeamInfoQuery(params.teamId); const [garden, setGarden] = useState([]); const [category, setCategory] = useState(TEAM_CATEGORY_INFOS[0].name); const [cardIdx, setCardIdx] = useState(0); @@ -47,7 +50,7 @@ const Page = ({ params }: { params: { teamId: number } }) => { getStudies(params.teamId, page, size).then((res) => { if (res.ok) { - setStudyArray(res.body); + setStudyArray(res.body.content); } }); } else if (category === '학습자료') { @@ -68,6 +71,7 @@ const Page = ({ params }: { params: { teamId: number } }) => { getGarden(params.teamId).then((res) => { setGarden(res.body); }); + TEAM_CATEGORY_INFOS[0].page = `/team/${params.teamId}/study-gallery`; TEAM_CATEGORY_INFOS[1].page = `/team/${params.teamId}/document`; }, []); @@ -92,7 +96,8 @@ const Page = ({ params }: { params: { teamId: number } }) => { getStudies(params.teamId, nextPage, size).then((res) => { if (res.ok) { - if (res.body.length > 0) { + if (res.body.content.length > 0) { + setStudyArray(res.body.content); setCardIdx((idx) => idx + CARD_PER_PAGE); } } @@ -137,27 +142,43 @@ const Page = ({ params }: { params: { teamId: number } }) => { }); }; + const myTeam = useAtomValue(myTeamAtom); + const [isMyTeam, setIsMyTeam] = useState(false); + useEffect(() => { + if (myTeam.teams !== undefined) { + const res = myTeam.teams.filter((teamId) => teamId === params.teamId); + setIsMyTeam(res.length === 1); + } + }, [myTeam, params.teamId]); + return ( <> - - {/* TODO 팀원 목록, 초대링크 버튼 */} - <Flex align="center" gap={{ base: '2', lg: '8' }}> - <TeamMember teamId={params.teamId} teamName={teamInfo?.name} /> - <Button - color="white" - bg="orange_dark" - onClick={handleInviteClick} - rightIcon={<BsLink45Deg size="24px" />} - rounded="full" - size="sm" - > - 초대 - </Button> - </Flex> + <Title + isTeam + imageUrl={teamInfo?.body.imageUrl} + name={teamInfo?.body.name} + description={teamInfo?.body.description} + /> + {/* TODO 자신의 팀일때만 보이도록 수정 */} + {isMyTeam && ( + <Flex align="center" gap={{ base: '2', lg: '8' }}> + <TeamMember teamId={params.teamId} teamName={teamInfo?.body.name} /> + <Button + color="white" + bg="orange_dark" + onClick={handleInviteClick} + rightIcon={<BsLink45Deg size="24px" />} + rounded="full" + size="sm" + > + 초대 + </Button> + </Flex> + )} </Flex> - <TeamControlPanel teamInfo={teamInfo} /> + <TeamControlPanel teamInfo={teamInfo?.body} /> <Flex pos="relative" align="center" flex="1" gap="8"> <Box pos="relative" overflow="hidden" w="100%" h={{ base: '250px', md: '300px', xl: '320px' }}> @@ -173,29 +194,30 @@ const Page = ({ params }: { params: { teamId: number } }) => { </Box> {/* TODO 진행도 */} - <AttendanceRate attendanceRate={teamInfo?.attendanceRatio} /> + <AttendanceRate attendanceRate={teamInfo?.body.attendanceRatio} /> </Flex> <Flex direction="column" flex="1" gap="4"> - {/* TODO 스터디, 학습자료, 작물창고 버튼 */} <TabButton currentTab={category} changeTab={handleCategoryChange} categoryInfos={TEAM_CATEGORY_INFOS} /> {category !== '작물창고' && ( <NavigationButton handlePrevClick={handlePrevClick} handleNextClick={handleNextClick} handlePlusClick={handlePlusClick} + isMyTeam={isMyTeam} /> )} - {/* TODO 전체보기, 네비게이션 이동 버튼 */} - {/* TODO 스터디 카드 */} - {category === '스터디' && ( + {category === '스터디' && studyArray.length === 0 && <SuggestionCreate category="스터디" />} + {category === '스터디' && studyArray.length !== 0 && ( <StudyGridView studyArray={studyArray.map((study, index) => ({ ...study.studyReferenceResponse, rank: cardIdx + index + 1, }))} + teamId={params.teamId} /> )} + {category === '학습자료' && documentArray.length === 0 && <SuggestionCreate category="학습자료" />} {category === '학습자료' && <DocumentGridView documentArray={documentArray} />} </Flex> </Flex> diff --git a/src/app/team/[teamId]/study-gallery/page.tsx b/src/app/team/[teamId]/study-gallery/page.tsx new file mode 100644 index 00000000..26ee01a3 --- /dev/null +++ b/src/app/team/[teamId]/study-gallery/page.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { Button, Flex, Text } from '@chakra-ui/react'; +import { useState } from 'react'; + +import StudyModal from '@/containers/study/Modal/StudyModal'; +import StudyGallery from '@/containers/study-gallery/StudyGallery'; + +const Page = ({ params }: { params: { teamId: number } }) => { + const [isOpenModal, setIsOpenModal] = useState(false); + + return ( + <Flex align="center" direction="column" gap="9" w="100%" p="8"> + <Flex justify="space-between" w="100%"> + <Flex direction="row" gap="2"> + <Text textStyle="bold_2xl">스터디 갤러리</Text> + </Flex> + <Button color="white" bg="orange_dark" onClick={() => setIsOpenModal(true)} rounded="full"> + 스터디 추가 + </Button> + </Flex> + <StudyGallery teamId={params.teamId} /> + <StudyModal teamId={params.teamId} isOpen={isOpenModal} setIsModalOpen={setIsOpenModal} studyInfo={null} /> + </Flex> + ); +}; + +export default Page; diff --git a/src/atom.ts b/src/atom.ts index 82a5da93..189d7245 100644 --- a/src/atom.ts +++ b/src/atom.ts @@ -15,4 +15,8 @@ export const defaultUserAtom = { export const userAtom = atomWithStorage('user', defaultUserAtom as UserAtomType); +export const myTeamAtom = atomWithStorage<{ teams: number[] }>('myTeam', { + teams: [], +}); + export const loginBackPathAtom = atomWithStorage('loginBackPath', '/'); diff --git a/src/components/StudyCard/index.tsx b/src/components/StudyCard/index.tsx index e7bd7262..c177cf7e 100644 --- a/src/components/StudyCard/index.tsx +++ b/src/components/StudyCard/index.tsx @@ -1,10 +1,20 @@ -import { Card, CardHeader, CardBody, CardFooter, Text, Image, Progress } from '@chakra-ui/react'; +import { Card, CardHeader, CardBody, CardFooter, Text, Image, Progress, Link } from '@chakra-ui/react'; import CROP from '@/constants/crop'; import { StudyCardProps } from './types'; -const StudyCard = ({ name, description, startDate, endDate, cropId, studyProgressRatio, rank }: StudyCardProps) => { +const StudyCard = ({ + name, + id, + teamId, + description, + startDate, + endDate, + cropId, + studyProgressRatio, + rank, +}: StudyCardProps) => { return ( <Card alignItems="center" @@ -17,25 +27,27 @@ const StudyCard = ({ name, description, startDate, endDate, cropId, studyProgres _hover={{ bg: 'gray.100', transition: '0.5s ease-in-out' }} rounded="2xl" > - <CardHeader py="2"> - <Text textStyle="bold_md">{name}</Text> - </CardHeader> - <CardBody py="0" textAlign="center" id={cropId.toString()}> - {CROP.filter((crop) => crop.id === cropId).map((crop) => ( - <Image key={crop.id} w="16" mx="auto" py="4" alt="crops" src={crop.imageUrl} /> - ))} - <Text textStyle="sm">{description}</Text> - <Text textStyle="sm"> - {startDate} ~ {endDate} - </Text> - </CardBody> - <CardFooter alignItems="center" justifyContent="center" gap="4" display="flex" w="100%" pt="0"> - <Card textStyle="bold_md" alignItems="center" w="8" h="8" textAlign="center" shadow="md"> - {rank} - </Card> - <Progress flex="1" h="1.5" colorScheme="blackAlpha" rounded="md" value={studyProgressRatio} /> - <Text textStyle="sm">{studyProgressRatio}%</Text> - </CardFooter> + <Link href={`/team/${teamId}/study/${id}`}> + <CardHeader py="2"> + <Text textStyle="bold_md">{name}</Text> + </CardHeader> + <CardBody py="0" textAlign="center" id={cropId.toString()}> + {CROP.filter((crop) => crop.id === cropId).map((crop) => ( + <Image key={crop.id} w="16" mx="auto" py="4" alt="crops" src={crop.imageUrl} /> + ))} + <Text textStyle="sm">{description}</Text> + <Text textStyle="sm"> + {startDate} ~ {endDate} + </Text> + </CardBody> + <CardFooter alignItems="center" justifyContent="center" gap="4" display="flex" w="100%" pt="0"> + <Card textStyle="bold_md" alignItems="center" w="8" h="8" textAlign="center" shadow="md"> + {rank} + </Card> + <Progress flex="1" h="1.5" colorScheme="blackAlpha" rounded="md" value={studyProgressRatio} /> + <Text textStyle="sm">{studyProgressRatio}%</Text> + </CardFooter> + </Link> </Card> ); }; diff --git a/src/containers/study-gallery/StudyGallery/index.tsx b/src/containers/study-gallery/StudyGallery/index.tsx new file mode 100644 index 00000000..5a492a8e --- /dev/null +++ b/src/containers/study-gallery/StudyGallery/index.tsx @@ -0,0 +1,68 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { Flex, Grid, useBreakpointValue } from '@chakra-ui/react'; +import { useEffect, useState } from 'react'; + +import { getStudies } from '@/app/api/study'; +import PageNavigator from '@/components/PageNavigator'; +import StudyCard from '@/components/StudyCard'; +import SuggestionCreate from '@/containers/team/SuggestionCreate'; +import { StudyRank } from '@/types'; + +const StudyGallery = ({ teamId }: { teamId: number }) => { + const [currentPage, setCurrentPage] = useState<number>(1); + const [studyArray, setStudyArray] = useState<StudyRank[]>([]); + const [cardIdx, setCardIdx] = useState<number>(0); + + const [studyLength, setStudyLength] = useState<number>(0); + + const itemsPerPage = useBreakpointValue({ base: 4, md: 8, xl: 10 })!; + + useEffect(() => { + getStudies(teamId, currentPage - 1, itemsPerPage).then((res) => { + if (res.ok) { + setStudyArray(res.body.content); + setStudyLength(res.body.totalElements); + } + }); + setCardIdx((currentPage - 1) * itemsPerPage); + }, [currentPage, itemsPerPage]); + + if (studyArray.length === 0) { + return <SuggestionCreate category="스터디" />; + } + + return ( + <Flex direction="column"> + <Grid gap={{ sm: '2', md: '4', xl: '8' }} templateColumns={`repeat(${itemsPerPage / 2}, 1fr)`} w="100%"> + {studyArray + .map((study, index) => ({ + ...study.studyReferenceResponse, + rank: cardIdx + index + 1, + })) + .map((study) => ( + <StudyCard + key={study.id} + teamId={teamId} + id={study.id} + name={study.name} + description={study.description} + startDate={study.startDate} + endDate={study.endDate} + status={study.status} + cropId={study.cropId} + studyProgressRatio={study.studyProgressRatio} + rank={study.rank} + /> + ))} + </Grid> + <PageNavigator + currentPage={currentPage} + setCurrentPage={setCurrentPage} + componentLength={studyLength} + itemsPerPage={itemsPerPage} + /> + </Flex> + ); +}; + +export default StudyGallery; diff --git a/src/containers/team/NavigationButton/index.tsx b/src/containers/team/NavigationButton/index.tsx index 7ec98dfe..85d7ffcb 100644 --- a/src/containers/team/NavigationButton/index.tsx +++ b/src/containers/team/NavigationButton/index.tsx @@ -4,7 +4,7 @@ import { BsPlus } from 'react-icons/bs'; import { NavigationButtonProps } from './types'; -const NavigationButton = ({ handlePrevClick, handleNextClick, handlePlusClick }: NavigationButtonProps) => { +const NavigationButton = ({ handlePrevClick, handleNextClick, handlePlusClick, isMyTeam }: NavigationButtonProps) => { return ( <Flex align="center" justify="flex-end" gap="4" w="100%"> <IconButton @@ -23,14 +23,16 @@ const NavigationButton = ({ handlePrevClick, handleNextClick, handlePlusClick }: size="icon_sm" variant="icon_white" /> - <IconButton - shadow="base" - aria-label="" - icon={<BsPlus />} - onClick={handlePlusClick} - size="icon_md" - variant="icon_orange_dark" - /> + {isMyTeam && ( + <IconButton + shadow="base" + aria-label="" + icon={<BsPlus />} + onClick={handlePlusClick} + size="icon_md" + variant="icon_orange_dark" + /> + )} </Flex> ); }; diff --git a/src/containers/team/NavigationButton/types.ts b/src/containers/team/NavigationButton/types.ts index 688ffe99..b2e584a6 100644 --- a/src/containers/team/NavigationButton/types.ts +++ b/src/containers/team/NavigationButton/types.ts @@ -2,4 +2,5 @@ export interface NavigationButtonProps { handlePrevClick: () => void; handleNextClick: () => void; handlePlusClick: () => void; + isMyTeam: boolean; } diff --git a/src/containers/team/StudyGridView/index.tsx b/src/containers/team/StudyGridView/index.tsx index 8b595f01..32681651 100644 --- a/src/containers/team/StudyGridView/index.tsx +++ b/src/containers/team/StudyGridView/index.tsx @@ -4,13 +4,14 @@ import StudyCard from '@/components/StudyCard'; import { StudyGridViewProps } from './types'; -const StudyGridView = ({ studyArray }: StudyGridViewProps) => { +const StudyGridView = ({ studyArray, teamId }: StudyGridViewProps) => { return ( <Grid gap="4" templateColumns={{ base: 'repeat(2, 1fr)', lg: 'repeat(4, 1fr)' }}> {studyArray.map((study) => { return ( <StudyCard key={study.id} + teamId={teamId} id={study.id} name={study.name} description={study.description} diff --git a/src/containers/team/StudyGridView/types.ts b/src/containers/team/StudyGridView/types.ts index e3fa833c..1aafe558 100644 --- a/src/containers/team/StudyGridView/types.ts +++ b/src/containers/team/StudyGridView/types.ts @@ -2,4 +2,5 @@ import { StudyCardProps } from '@/components/StudyCard/types'; export interface StudyGridViewProps { studyArray: StudyCardProps[]; + teamId: number; } diff --git a/src/containers/team/SuggestionCreate/index.tsx b/src/containers/team/SuggestionCreate/index.tsx new file mode 100644 index 00000000..08f860fc --- /dev/null +++ b/src/containers/team/SuggestionCreate/index.tsx @@ -0,0 +1,28 @@ +import { Flex, Text } from '@chakra-ui/react'; + +import color from '@/constants/color'; + +const SuggestionCreate = ({ category }: { category: string }) => { + return ( + <Flex + align="center" + justify="center" + direction="column" + w="full" + mt="2" + p="10" + bg="white" + shadow="lg" + rounded="2xl" + > + <Text textStyle="bold_xl"> + 아직 생성된 <span style={{ color: color.orange_dark }}>{category}</span>가 없습니다. + </Text> + <Text textStyle="bold_xl"> + <span style={{ color: color.orange_dark }}>{category}</span>를 생성해주세요. + </Text> + </Flex> + ); +}; + +export default SuggestionCreate; diff --git a/src/containers/team/TeamModal/index.tsx b/src/containers/team/TeamModal/index.tsx index 4e40aeb2..be9002ed 100644 --- a/src/containers/team/TeamModal/index.tsx +++ b/src/containers/team/TeamModal/index.tsx @@ -10,6 +10,7 @@ import ActionModal from '@/components/Modal/ActionModal'; import S3_URL from '@/constants/s3Url'; import { useMutateWithToken } from '@/hooks/useFetchWithToken'; import useRefetchSideBar from '@/hooks/useRefetchSideBar'; +import useRefetchTeamInfo from '@/hooks/useRefetchTeamInfo'; import { TeamModalProps } from './type'; @@ -35,6 +36,7 @@ const TeamModal = ({ teamInfo, isOpen, onClose }: TeamModalProps) => { const editTeamImage = useMutateWithToken(patchEditTeamImage); const refetchSideBar = useRefetchSideBar(); + const refetchTeamInfo = useRefetchTeamInfo(); const resetState = () => { setName(''); @@ -79,6 +81,7 @@ const TeamModal = ({ teamInfo, isOpen, onClose }: TeamModalProps) => { }); } refetchSideBar(); + refetchTeamInfo(teamInfo.id); resetAndCloseModal(); } }); diff --git a/src/hooks/useRefetchTeamInfo.ts b/src/hooks/useRefetchTeamInfo.ts new file mode 100644 index 00000000..dd2a3b2d --- /dev/null +++ b/src/hooks/useRefetchTeamInfo.ts @@ -0,0 +1,13 @@ +import { useQueryClient } from '@tanstack/react-query'; + +const useRefetchTeamInfo = () => { + const queryClient = useQueryClient(); + const refetchTeamInfo = (teamId: number) => { + queryClient.invalidateQueries({ + queryKey: ['teamInfo', teamId.toString()], + }); + }; + return refetchTeamInfo; +}; + +export default useRefetchTeamInfo; diff --git a/src/mocks/studyCard.ts b/src/mocks/studyCard.ts deleted file mode 100644 index 8a440a8e..00000000 --- a/src/mocks/studyCard.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { StudyCardProps } from '@/components/StudyCard/types'; - -const studyCardData: StudyCardProps[] = [ - { - id: 1, - name: '홍당무 스터디', - description: '함께 홍당무를 심고 수확까지 합니다.', - startDate: '2024/01/01', - endDate: '2024/01/31', - status: 'IN_PROGRESS', - cropId: 1, - studyProgressRatio: 80, - rank: 1, - }, - { - id: 2, - name: '고구마 스터디', - description: '함께 고구마를 심고 수확까지 합니다.', - startDate: '2024/01/01', - endDate: '2024/01/31', - status: 'IN_PROGRESS', - cropId: 2, - studyProgressRatio: 40, - rank: 2, - }, - { - id: 3, - name: '완두콩 스터디', - description: '함께 완두콩을 심고 수확까지 합니다.', - startDate: '2024/01/01', - endDate: '2024/01/31', - status: 'IN_PROGRESS', - cropId: 3, - studyProgressRatio: 50, - rank: 3, - }, - { - id: 4, - name: '당근 스터디', - description: '함께 당근 심고 수확까지 합니다.', - startDate: '2024/01/01', - endDate: '2024/01/31', - status: 'IN_PROGRESS', - cropId: 4, - studyProgressRatio: 50, - rank: 4, - }, - { - id: 5, - name: '당근 스터디1', - description: '함께 당근 심고 수확까지 합니다.', - startDate: '2024/01/01', - endDate: '2024/01/31', - status: 'IN_PROGRESS', - cropId: 4, - studyProgressRatio: 50, - rank: 4, - }, - { - id: 6, - name: '당근 스터디2', - description: '함께 당근 심고 수확까지 합니다.', - startDate: '2024/01/01', - endDate: '2024/01/31', - status: 'IN_PROGRESS', - cropId: 4, - studyProgressRatio: 50, - rank: 4, - }, - { - id: 7, - name: '당근 스터디3', - description: '함께 당근 심고 수확까지 합니다.', - startDate: '2024/01/01', - endDate: '2024/01/31', - status: 'IN_PROGRESS', - cropId: 4, - studyProgressRatio: 50, - rank: 4, - }, - { - id: 8, - name: '당근 스터디3', - description: '함께 당근 심고 수확까지 합니다.', - startDate: '2024/01/01', - endDate: '2024/01/31', - status: 'IN_PROGRESS', - cropId: 4, - studyProgressRatio: 50, - rank: 4, - }, - { - id: 9, - name: '당근 스터디3', - description: '함께 당근 심고 수확까지 합니다.', - startDate: '2024/01/01', - endDate: '2024/01/31', - status: 'IN_PROGRESS', - cropId: 4, - studyProgressRatio: 50, - rank: 4, - }, -]; - -export default studyCardData; diff --git a/src/types.ts b/src/types.ts index 241a97c0..68793917 100644 --- a/src/types.ts +++ b/src/types.ts @@ -29,6 +29,7 @@ interface TeamReference { export interface Study { readonly id: number; + teamId: number; name: string; description: string; startDate: string;