Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[민우] - 발자취 페이지 : 내 발자취 탭 구현 #495

Open
wants to merge 19 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
adf2d67
♻️ : #492 - 발자취 페이지 파일 생성
MinwooP Mar 11, 2024
7281ae2
✨ : #492 - 발자취 페이지 하위 컴포넌트 파일 및 state 생성
MinwooP Mar 26, 2024
7b29439
✨ : #492 - 발자취 페이지 상단 탭 FootPrintTab 컴포넌트 기능 및 디자인 구현
MinwooP Mar 27, 2024
454925d
✨ : #492 - FootPrintFilter 컴포넌트 기본 로직 구현
MinwooP Mar 28, 2024
ef5eeb5
✨ : #492 - 발자취 페이지 필터에 사용할 계획 List를 불러오는 useGetMyPlansForFootprintQue…
MinwooP Mar 28, 2024
6efe877
✨ : #492 - FootPrintFilter 컴포넌트 내 useGetMyPlansForFootprintQuery 훅 사용…
MinwooP Mar 28, 2024
5210f92
♻️ : #492 - Icon 컴포넌트 내 검색 아이콘 search 종류 추가
MinwooP Mar 28, 2024
659b773
💄 : #492 - FootPrintFilter 컴포넌트 검색 버튼 CSS 구현
MinwooP Mar 28, 2024
e5527f6
💄 : #492 - Dropdown 컴포넌트 내 text 고정 width 및 말줄임표 설정
MinwooP Mar 29, 2024
f878dd7
♻️ : #492 - FOOTPRINT_PLAN 상수 정의 => 모든 계획, 계획 없음 data 상수화
MinwooP Mar 29, 2024
3befbb9
✨ : #492 - FootPrintItem 컴포넌트 기본 로직 정의
MinwooP Apr 20, 2024
e71a5be
✨ : #492 - FootPrintItem dummy data 추가 및 css 작업
MinwooP Apr 22, 2024
37638f0
💄 : #492 - FootPrintList 스크롤 위해 height 설정
MinwooP Apr 23, 2024
f7f4ea9
✨ : #492 - 발자취 조회 무한 스크롤 적용 위해 임시로 getAllPlans API 사용
MinwooP Apr 24, 2024
2a67198
🐛 : #492 - npm react-intersection-observer 라이브러리 설치
MinwooP Apr 24, 2024
44e6590
✨ : #492 - useInView hook를 사용해 발자취조회 무한 스크롤 구현
MinwooP Jun 3, 2024
d071d13
✨ : #492 - 계획 작성 날짜를 YYYY-MM-DD 형식으로 바꿔주는 changeCreateAtToDate hook 구현
MinwooP Jun 3, 2024
75ebb5c
🐛 : #492 - merge origin dev
MinwooP Jun 3, 2024
1053af7
♻️ : #492 - footprints 폴더명 url에 맞게 footprint로 변경
MinwooP Jun 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"react-form-stepper": "^2.0.3",
"react-hot-toast": "^2.4.1",
"react-infinite-scroller": "^1.2.6",
"react-intersection-observer": "^9.8.2",
"react-spinners": "^0.13.8",
"recoil": "^0.7.7"
},
Expand Down
19 changes: 19 additions & 0 deletions src/apis/client/getMyFootPrints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { DOMAIN } from '@/constants/api';
import {
GetAllPlansRequestQuery,
GetAllPlansResponse,
} from '@/types/apis/plan/GetAllPlans';
import { axiosInstanceClient } from '../axiosInstanceClient';

export const getMyFootPrints = async (query: GetAllPlansRequestQuery) => {
const { data } = await axiosInstanceClient.get<GetAllPlansResponse>(
DOMAIN.GET_PLANS_ALL,
{
authorization: false,
params: {
...query,
},
},
);
return data;
};
5 changes: 5 additions & 0 deletions src/app/footprint/_Components/AllFootPrints/AllFootPrints.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';

export default function AllFootPrints() {
return <div></div>;
}
Empty file.
87 changes: 87 additions & 0 deletions src/app/footprint/_Components/FootPrintFilter/FootPrintFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Dropdown, Icon } from '@/components';
import { FOOTPRINT_PLAN } from '@/constants';
import { useGetMyPlansForFootprintQuery } from '@/hooks/apis';
import classNames from 'classnames';
import React, { useEffect, useMemo, useState } from 'react';
import { planType } from '../MyFootPrints/MyFootPrints';
import './index.scss';

interface FootPrintFilterProps {
setYear: (year: number) => void;
setPlan: (plan: planType) => void;
}

const yearOptions = [{ value: 2024, name: '2024년' }];

export default function FootPrintFilter({
setYear,
setPlan,
}: FootPrintFilterProps) {
const [selectedYear, setSelectedYear] = useState(2024);
const [selectedPlan, setSelectedPlan] = useState<planType>(
FOOTPRINT_PLAN.ALL_PLAN,
);

const handleSelectedPlan = (newSelectedPlanId: number) => {
const newSelectedPlan = planOptions.find(
(plan) => plan.value === newSelectedPlanId,
);
setSelectedPlan({
planId: newSelectedPlan!.value,
planTitle: newSelectedPlan!.name,
});
};

const { yearPlans } = useGetMyPlansForFootprintQuery(selectedYear);

const planOptions = useMemo(() => {
// 서버로부터 받아온 yearPlans가 변경되지 않는 이상 변하지 않는 변수
return yearPlans.map((plan) => ({
value: plan.planId,
name: plan.planTitle,
}));
}, [yearPlans]);

useEffect(() => {
// selectedYear이 바뀔 때마다, yearPlans의 값이 바뀔테니
// selectedPlan의 값은 "모든 계획"으로 변경해주기
setSelectedPlan(FOOTPRINT_PLAN.ALL_PLAN);
}, [selectedYear]);

const handleClickSearchBtn = () => {
// 현재 dropdown에서 선택되어있는 year, plan 값으로 부모의 year, plan을 변경
if (selectedPlan.planId !== FOOTPRINT_PLAN.EMPTY.planId) {
// "계획 없음"이 선택되지 않았을 때만 변경 가능하도록
setYear(selectedYear);
setPlan(selectedPlan);
}
};

return (
<div className={classNames('footprint-filter')}>
<Dropdown
dropdownId="year-filter"
options={yearOptions}
selectedValue={selectedYear}
setSelectedValue={(newSelectedYear: number) => {
setSelectedYear(newSelectedYear);
}}
classNameList={['footprint-filter__dropdown--year']}
/>

<Dropdown
dropdownId="plan-filter"
options={planOptions}
selectedValue={selectedPlan.planId}
setSelectedValue={handleSelectedPlan}
classNameList={['footprint-filter__dropdown--filter']}
/>

<button
className={classNames('footprint-filter__search-btn')}
onClick={handleClickSearchBtn}>
<Icon name={'SEARCH'} color={'white-100'} size="lg" />
</button>
</div>
);
}
17 changes: 17 additions & 0 deletions src/app/footprint/_Components/FootPrintFilter/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.footprint-filter {
display: flex;
gap: 0.75rem;
align-items: center;

&__search-btn {
&:hover {
cursor: pointer;
}
display: flex;
justify-content: center;
padding: 0.25rem;
align-items: center;
border-radius: 10px;
background-color: var(--origin-primary);
}
}
67 changes: 67 additions & 0 deletions src/app/footprint/_Components/FootPrintList/FootPrintList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use client';

import { COLOR } from '@/constants';
import { useMyFootPrintsQuery } from '@/hooks/apis/useMyFootPrintsQuery';
import classNames from 'classnames';
import React, { useEffect } from 'react';
import { useInView } from 'react-intersection-observer';
import { FadeLoader } from 'react-spinners';
import FootprintItem from '../FootprintItem/FootprintItem';
import { planType } from '../MyFootPrints/MyFootPrints';
import './index.scss';

interface FootPrintListProps {
year: number;
plan: planType;
}

export default function FootPrintList({ year, plan }: FootPrintListProps) {
// TODO: prop으로 받은 year과 plan에 해당하는 발자취 list들을 서버로부터 받아온다.
// - plan.planId === -1 => 모든 계획일 것
// - plan.planId === -2 => 해당 year에 해당하는 계획이 없는 것
console.log(`${year}과 ${plan}에 해당하는 발자취 List로 변경 필요`);

const { tempFootPrintList, fetchNextPage, isFetchingNextPage } =
useMyFootPrintsQuery({
sort: 'latest',
current: true,
});

const { ref, inView } = useInView();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

둘러보기 페이지와 다르게 useInView 쓴 이유가 있을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아직 적용하지는 않았지만, useInView를 사용해 특정 element가 viewPort에 들어오는지 나가는지를 파악해, 스크롤이 많이 된다면 현재 viewPort에 보이지 않고 상단에 쌓여있는 element들은 dom에서 제거해주는 기능을 추가해주기 위해 사용했습니다!!

혜수님은 둘러보기 페이지에서 react-infinite-scroller 라이브러리를 사용하신 것 같은데 이 라이브러리로는 각 element의 viewPort 진입 여부를 각각 판단해주는 기능은 없는 것 같아서요!!


useEffect(() => {
// ref로 참조하고 있는 div 요소가 viewPort에 보여진다면 다음 페이지 fetch
if (inView) {
fetchNextPage();
}
}, [inView]);

return (
<ul className={classNames('footprint-list')}>
{tempFootPrintList.map((item) => {
return (
<FootprintItem
key={item.id}
id={item.id}
iconNumber={item.iconNumber}
title={item.title}
createdAt={item.createdAt}
ajajas={item.ajajas}
tags={item.tags}
/>
);
})}

<div className="footprint-list__loading-wrapper">
{isFetchingNextPage ? (
<FadeLoader color={COLOR.PRIMARY} speedMultiplier={1.3} />
) : (
<div className="footprint-list__end" ref={ref} />
)}
</div>
</ul>

// TODO: ${year}와 ${plan.planTitle}에 해당하는 FootPrintList 출력
// -> 지금은 임시로 "계획 전체조회 api(계획 둘러보기)"를 사용하고 있음
);
}
19 changes: 19 additions & 0 deletions src/app/footprint/_Components/FootPrintList/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.footprint-list {
display: flex;
flex-direction: column;
gap: 1rem;
overflow-y: scroll;

&__loading-wrapper {
display: flex;
justify-content: center;
}

&__end {
&:before {
content: '';
display: block;
height: 12px;
}
}
}
69 changes: 69 additions & 0 deletions src/app/footprint/_Components/FootPrintTab/FootPrintTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import classNames from 'classnames';
import React from 'react';
import './index.scss';

interface FootPrintTabProps {
isMyFootPrintsTab: boolean;
setMyFootPrintsTab: () => void;
setAllFootPrintsTab: () => void;
}

const TAB_MENU = {
MY: '내 발자취',
ALL: '둘러보기',
};

export default function FootPrintTab({
isMyFootPrintsTab,
setMyFootPrintsTab,
setAllFootPrintsTab,
}: FootPrintTabProps) {
const handleClickMyFootPrintsTab = () => {
if (!isMyFootPrintsTab) {
setMyFootPrintsTab();
}
};

const handleClickAllFootPrintsTab = () => {
if (isMyFootPrintsTab) {
setAllFootPrintsTab();
}
};

return (
<div className={classNames('footprint-tab')}>
<div
className={classNames(
'footprint-tab__menu',
'font-size-base',
isMyFootPrintsTab
? 'footprint-tab--focused'
: 'footprint-tab--normal',
)}
onClick={handleClickMyFootPrintsTab}>
{TAB_MENU.MY}
<div
className={classNames('footprint-tab__underline', {
'footprint-tab__underline--focused': isMyFootPrintsTab,
})}
/>
</div>
<div
className={classNames(
'footprint-tab__menu',
'font-size-base',
!isMyFootPrintsTab
? 'footprint-tab--focused'
: 'footprint-tab--normal',
)}
onClick={handleClickAllFootPrintsTab}>
{TAB_MENU.ALL}
<div
className={classNames('footprint-tab__underline', {
'footprint-tab__underline--focused': !isMyFootPrintsTab,
})}
/>
</div>
</div>
);
}
34 changes: 34 additions & 0 deletions src/app/footprint/_Components/FootPrintTab/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.footprint-tab {
margin-top: 2rem;
display: flex;
gap: 0rem;
&:hover {
cursor: pointer;
}

&__menu {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
width: 6rem;
}

&__underline {
background-color: var(--origin-secondary);
height: 0.125rem;
width: 100%;

&--focused {
background-color: var(--origin-primary);
}
}

&--normal {
color: var(--origin-secondary);
}

&--focused {
color: var(--origin-primary);
}
}
Loading
Loading