Skip to content

Commit

Permalink
Feature/#200 스터디 네비게이션 구현 (#219)
Browse files Browse the repository at this point in the history
* feat: tabButton 밑에 전체보기 button 구현

#200

* feat: 학습자료와 스터디의 네비게이션 버튼 구현

* feat: 스터디의 grid view 구현

#200

* feat: 학습자료의 grid view 구현

* fix: 학습자료에 map key 없어서 생기는 에러 수정

#200

* chore: study mocks data 추가

#200

* fix: styledDataPicker의 eslint 에러 수정

#200

* feat: 스터디 및 학습자료 네비게이션 구현

#200

* refact: 코드 리뷰 반영

#200
  • Loading branch information
llddang authored May 30, 2024
1 parent d24a185 commit 38bd852
Show file tree
Hide file tree
Showing 17 changed files with 347 additions and 46 deletions.
101 changes: 75 additions & 26 deletions src/app/team/[teamId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,81 @@
/* eslint-disable react-hooks/exhaustive-deps */

'use client';

import { Box, Button, Flex, Grid, useBreakpointValue } from '@chakra-ui/react';
import { useState } from 'react';
import { Box, Button, Flex, useBreakpointValue } from '@chakra-ui/react';
import { useEffect, useState } from 'react';
import { BsLink45Deg } from 'react-icons/bs';

import Garden3D from '@/components/Garden3D';
import StudyCard from '@/components/StudyCard';
import { StudyAssetCardProps } from '@/components/StudyAssetCard/types';
import { StudyCardProps } from '@/components/StudyCard/types';
import TabButton from '@/components/TabButton';
import Title from '@/components/Title';
import { CARD_PER_PAGE, TEAM_CATEGORY_INFOS } from '@/constants/team';
import AssetGridView from '@/containers/team/AssetGridView';
import AttendanceRate from '@/containers/team/AttendanceRate';
import NavigationButton from '@/containers/team/NavigationButton';
import StudyGridView from '@/containers/team/StudyGridView';
import TeamMember from '@/containers/team/teamMember';
import { gardenInfos1 } from '@/mocks/Garden3D';
import studyAssetCardData from '@/mocks/studyAssetCard';
import studyCardData from '@/mocks/studyCard';
import teamPageCategoryInfos from '@/mocks/team';

const Page = () => {
const [category, setCategory] = useState<string>(teamPageCategoryInfos[0].name);
const [category, setCategory] = useState<string>(TEAM_CATEGORY_INFOS[0].name);
const [cardIdx, setCardIdx] = useState<number>(0);

const [studyArray, setStudyArray] = useState<StudyCardProps[]>([]);
const [studyLength, setStudyLength] = useState<number>(0);
const [assetArray, setAssetArray] = useState<StudyAssetCardProps[]>([]);
const [assetLength, setAssetLength] = useState<number>(0);

const getCardData = (start: number) => {
if (category === '스터디') {
// TODO: 스터디 목록 조회하기.
setStudyArray(studyCardData.slice(start, start + CARD_PER_PAGE));
} else if (category === '학습자료') {
// TODO: 학습자료 목록 조회하기.
setAssetArray(studyAssetCardData.slice(start, start + CARD_PER_PAGE));
}
};

useEffect(() => {
// TODO: 아래의 handleNextClick의 조건문을 기능시키기 위해,
// 팀 상세 정보 조회 api에서 팀의 스터디와 학습자료 갯수를 받아와야할 것 같습니다.
setStudyLength(studyCardData.length);
setAssetLength(studyAssetCardData.length);
}, []);

useEffect(() => {
getCardData(cardIdx);
}, [cardIdx]);

const handlePrevClick = () => {
if (cardIdx - CARD_PER_PAGE < 0) return;

setCardIdx((idx) => idx - CARD_PER_PAGE);
};

const handleNextClick = () => {
if (category === '스터디' && cardIdx + CARD_PER_PAGE >= studyLength) return;
if (category === '학습자료' && cardIdx + CARD_PER_PAGE >= assetLength) return;

setCardIdx((idx) => idx + CARD_PER_PAGE);
};

const handlePlusClick = () => {
if (category === '스터디') {
// TODO: create study modal 띄우기
} else if (category === '학습자료') {
// TODO: create study asset modal 띄우기
}
};

const handleCategoryChange = (tab: string) => {
setCategory(tab);
setCardIdx(0);
};

return (
<Flex direction="column" gap="8" w="100%" p="8">
Expand Down Expand Up @@ -50,29 +110,18 @@ const Page = () => {

<Flex direction="column" flex="1" gap="4">
{/* TODO 스터디, 학습자료, 작물창고 버튼 */}
<TabButton currentTab={category} changeTab={setCategory} categoryInfos={teamPageCategoryInfos} />
<TabButton currentTab={category} changeTab={handleCategoryChange} categoryInfos={TEAM_CATEGORY_INFOS} />
{category !== '작물창고' && (
<NavigationButton
handlePrevClick={handlePrevClick}
handleNextClick={handleNextClick}
handlePlusClick={handlePlusClick}
/>
)}
{/* TODO 전체보기, 네비게이션 이동 버튼 */}
{/* TODO 스터디 카드 */}
<Grid gap="4" templateColumns={{ base: 'repeat(2, 1fr)', lg: 'repeat(4, 1fr)' }}>
{studyCardData.map((study) => {
return (
<StudyCard
key={study.id}
id={study.id}
name={study.name}
description={study.description}
startDate={study.startDate}
endDate={study.endDate}
status={study.status}
isDeleted={study.isDeleted}
cropId={study.cropId}
teamId={study.teamId}
percent={study.percent}
rank={study.rank}
/>
);
})}
</Grid>
{category === '스터디' && <StudyGridView studyArray={studyArray} />}
{category === '학습자료' && <AssetGridView assetArray={assetArray} />}
</Flex>
</Flex>
);
Expand Down
1 change: 1 addition & 0 deletions src/components/StudyAssetCard/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface StudyAssetCardProps {
id: number;
title: string;
content: string;
date: string;
Expand Down
23 changes: 18 additions & 5 deletions src/components/TabButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,38 @@ import { Flex, Button, Text } from '@chakra-ui/react';
import { TabButtonProps } from './type';

const TabButton = ({ currentTab, changeTab, categoryInfos }: TabButtonProps) => {
const currentIdx = categoryInfos.findIndex((data) => data.name === currentTab);

return (
<Flex gap="30px">
<Flex pos="relative" gap="30px">
{categoryInfos.map((data) => {
return (
<Button
key={data.id}
w="28"
bg={data.name === currentTab ? 'orange_dark' : 'white'}
borderRadius="30px"
shadow="md"
_hover={{ bg: 'orange_light' }}
onClick={() => changeTab(data.name)}
variant={data.name === currentTab ? 'orange_dark' : 'white'}
>
<Text color={data.name === currentTab ? 'white' : 'black'} fontSize="md" fontWeight="bold">
<Text fontSize="md" fontWeight="bold">
{data.name}
</Text>
</Button>
);
})}
{categoryInfos[currentIdx].wholeView && (
<Button
as="a"
pos="absolute"
top="12"
left={`${currentIdx * (30 + 112) + 8}px`}
shadow="md"
href={categoryInfos[currentIdx].page}
variant="orange_light"
>
전체보기
</Button>
)}
</Flex>
);
};
Expand Down
9 changes: 9 additions & 0 deletions src/constants/team.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { TabButtonInfoType } from '@/types';

export const CARD_PER_PAGE = 4;

export const TEAM_CATEGORY_INFOS: TabButtonInfoType[] = [
{ id: 1, name: '스터디', wholeView: true, page: '/' },
{ id: 2, name: '학습자료', wholeView: true, page: '/' },
{ id: 3, name: '작물창고', wholeView: false },
];
6 changes: 3 additions & 3 deletions src/containers/study/StudyAssetModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ const StudyAssetModal = ({ isOpen, setIsModalOpen, title, content, type }: Study
<Flex direction="column" gap="2">
{type === 'image' &&
imgData.map((data) => (
<Link href={data.url} download>
<Link key={data.url} href={data.url} download>
<Image alt={data.name} id={data.id.toString()} rounded="2xl" src={data.url} />{' '}
</Link>
))}
{type === 'file' &&
fileData.map((data) => (
<Link href={data.url} download id={data.id.toString()}>
<Link key={data.url} href={data.url} download id={data.id.toString()}>
<IconBox
leftIcon={data.type === 'pdf' ? <BiFile size={30} /> : <BsFolder2Open size={30} />}
content={data.name}
Expand All @@ -63,7 +63,7 @@ const StudyAssetModal = ({ isOpen, setIsModalOpen, title, content, type }: Study
))}
{type === 'link' &&
linkData.map((data) => (
<Link href={data.url}>
<Link key={data.url} href={data.url}>
<IconBox leftIcon={<BiLink size="30" />} content={data.name} />
</Link>
))}
Expand Down
27 changes: 27 additions & 0 deletions src/containers/team/AssetGridView/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Grid } from '@chakra-ui/react';

import StudyAssetCard from '@/components/StudyAssetCard';

import { AssetGridViewProps } from './types';

const AssetGridView = ({ assetArray }: AssetGridViewProps) => {
return (
<Grid gap="4" templateColumns={{ base: 'repeat(2, 1fr)', lg: 'repeat(4, 1fr)' }}>
{assetArray.map((asset) => {
return (
<StudyAssetCard
key={`${asset.title}-${asset.id}`}
id={asset.id}
title={asset.title}
content={asset.content}
date={asset.date}
bookmark={asset.bookmark}
img={asset.img}
/>
);
})}
</Grid>
);
};

export default AssetGridView;
5 changes: 5 additions & 0 deletions src/containers/team/AssetGridView/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { StudyAssetCardProps } from '@/components/StudyAssetCard/types';

export interface AssetGridViewProps {
assetArray: StudyAssetCardProps[];
}
38 changes: 38 additions & 0 deletions src/containers/team/NavigationButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { IconButton, Flex } from '@chakra-ui/react';
import { BiChevronRight, BiChevronLeft } from 'react-icons/bi';
import { BsPlus } from 'react-icons/bs';

import { NavigationButtonProps } from './types';

const NavigationButton = ({ handlePrevClick, handleNextClick, handlePlusClick }: NavigationButtonProps) => {
return (
<Flex align="center" justify="flex-end" gap="4" w="100%">
<IconButton
shadow="base"
aria-label=""
icon={<BiChevronLeft />}
onClick={handlePrevClick}
size="icon_sm"
variant="icon_white"
/>
<IconButton
shadow="base"
aria-label=""
icon={<BiChevronRight />}
onClick={handleNextClick}
size="icon_sm"
variant="icon_white"
/>
<IconButton
shadow="base"
aria-label=""
icon={<BsPlus />}
onClick={handlePlusClick}
size="icon_md"
variant="icon_orange_dark"
/>
</Flex>
);
};

export default NavigationButton;
5 changes: 5 additions & 0 deletions src/containers/team/NavigationButton/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface NavigationButtonProps {
handlePrevClick: () => void;
handleNextClick: () => void;
handlePlusClick: () => void;
}
32 changes: 32 additions & 0 deletions src/containers/team/StudyGridView/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Grid } from '@chakra-ui/react';

import StudyCard from '@/components/StudyCard';

import { StudyGridViewProps } from './types';

const StudyGridView = ({ studyArray }: StudyGridViewProps) => {
return (
<Grid gap="4" templateColumns={{ base: 'repeat(2, 1fr)', lg: 'repeat(4, 1fr)' }}>
{studyArray.map((study) => {
return (
<StudyCard
key={study.id}
id={study.id}
name={study.name}
description={study.description}
startDate={study.startDate}
endDate={study.endDate}
status={study.status}
isDeleted={study.isDeleted}
cropId={study.cropId}
teamId={study.teamId}
percent={study.percent}
rank={study.rank}
/>
);
})}
</Grid>
);
};

export default StudyGridView;
5 changes: 5 additions & 0 deletions src/containers/team/StudyGridView/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { StudyCardProps } from '@/components/StudyCard/types';

export interface StudyGridViewProps {
studyArray: StudyCardProps[];
}
36 changes: 36 additions & 0 deletions src/mocks/studyAssetCard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,69 @@ import { StudyAssetCardProps } from '@/components/StudyAssetCard/types';

const studyAssetCardData: StudyAssetCardProps[] = [
{
id: 1,
title: '자료이름1',
content: '이번 포스팅은 [스터디 목적]에 관한것입니다. 먼저 [목차]는 이렇습니다.',
date: '2024/01/01',
bookmark: 1111,
img: 'https://images.unsplash.com/photo-1531403009284-440f080d1e12?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80',
},
{
id: 2,
title: '자료이름2',
content: '이번 포스팅은 [스터디 목적]에 관한것입니다. 먼저 [목차]는 이렇습니다.',
date: '2024/02/02',
bookmark: 2222,
img: 'https://images.unsplash.com/photo-1531403009284-440f080d1e12?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80',
},
{
id: 3,
title: '자료이름3',
content: '이번 포스팅은 [스터디 목적]에 관한것입니다. 먼저 [목차]는 이렇습니다.',
date: '2024/03/03',
bookmark: 3333,
img: 'https://images.unsplash.com/photo-1531403009284-440f080d1e12?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80',
},
{
id: 4,
title: '자료이름4',
content: '이번 포스팅은 [스터디 목적]에 관한것입니다. 먼저 [목차]는 이렇습니다.',
date: '2024/04/04',
bookmark: 4444,
img: 'https://images.unsplash.com/photo-1531403009284-440f080d1e12?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80',
},
{
id: 5,
title: '자료이름5',
content: '이번 포스팅은 [스터디 목적]에 관한것입니다. 먼저 [목차]는 이렇습니다.',
date: '2024/04/04',
bookmark: 4444,
img: 'https://images.unsplash.com/photo-1531403009284-440f080d1e12?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80',
},
{
id: 6,
title: '자료이름6',
content: '이번 포스팅은 [스터디 목적]에 관한것입니다. 먼저 [목차]는 이렇습니다.',
date: '2024/04/04',
bookmark: 4444,
img: 'https://images.unsplash.com/photo-1531403009284-440f080d1e12?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80',
},
{
id: 7,
title: '자료이름7',
content: '이번 포스팅은 [스터디 목적]에 관한것입니다. 먼저 [목차]는 이렇습니다.',
date: '2024/04/04',
bookmark: 4444,
img: 'https://images.unsplash.com/photo-1531403009284-440f080d1e12?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80',
},
{
id: 8,
title: '자료이름8',
content: '이번 포스팅은 [스터디 목적]에 관한것입니다. 먼저 [목차]는 이렇습니다.',
date: '2024/04/04',
bookmark: 4444,
img: 'https://images.unsplash.com/photo-1531403009284-440f080d1e12?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80',
},
];

export default studyAssetCardData;
Loading

0 comments on commit 38bd852

Please sign in to comment.