Skip to content

Commit

Permalink
Feat: 체크박스 클릭시 선택이슈 열기 닫기, pipe함수 사용하여 필터링 테스트 #115
Browse files Browse the repository at this point in the history
  • Loading branch information
jeonyeonkyu committed Jun 18, 2021
1 parent 64ff358 commit 280fcad
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 80 deletions.
29 changes: 3 additions & 26 deletions fe/client/src/Pages/IssueListPage.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,12 @@
import React, { useCallback, useEffect } from 'react'
import React, { useCallback } from 'react'
import HeadContent from '@components/issueList/HeadContent';
import { ListWrapper } from '@components/common/baseStyle/baseStyle';
import ListHeader from '@components/issueList/ListHeader';
import ListItem from '@components/issueList/ListItem';
import { issueListItemAtom } from '@components/common/atoms/issueListAtom';
import { filterAtom, FilterBooleanType } from '@/components/common/atoms/filterAtom';
import { IssueListItemType } from '@components/common/types/APIType';
import API from '@/utils/API';
import useFetch, { AsyncState } from '@/utils/hook/useFetch';
import { useRecoilState } from '@/utils/myRecoil/useRecoilState';
import IssueList from './../components/issueList/IssueList';

const IssueListPage = () => {
const [issueItems] = useFetch(API.get.issues);
const { data, loading, error }: AsyncState<any, any> = issueItems;
const [, setFilterModalState] = useRecoilState(filterAtom);
const [, setFilteredList] = useRecoilState(issueListItemAtom)

useEffect(() => {
if (!data) return;
setFilteredList(data);
}, [data]);

const handleClickShowFilterModal = useCallback((title: string) => () => {
setFilterModalState((filterModalState: FilterBooleanType) => ({ ...filterModalState, [title]: true }));
}, []);
Expand All @@ -29,16 +15,7 @@ const IssueListPage = () => {
<>
<HeadContent {...{ handleClickShowFilterModal }} />
<ListWrapper wrapWidth="100%">
{
data && <>
<ListHeader issueItems={data} {...{ handleClickShowFilterModal }} />
{data.map((issueItem: IssueListItemType) => {
return <ListItem key={issueItem.id} {...{ issueItem }} />
})}
</>
}
{loading && <>loading...</>}
{error && <>error...</>}
<IssueList {...{ handleClickShowFilterModal }} />
</ListWrapper>
</>
)
Expand Down
16 changes: 10 additions & 6 deletions fe/client/src/components/common/FilterTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {

type FilterTabType = {
header: string;
filterList: Array<string>;
filterList: Array<{ name: string, info?: any }>;
inputType: string;
}

Expand All @@ -18,7 +18,8 @@ const filterHeaderNames: { [key: string]: string } = {
manager: '담당자',
label: '레이블',
milestone: '마일스톤',
writer: '작성자'
writer: '작성자',
stateChange: '상태 수정'
}

const FilterTab = ({ header, filterList, inputType }: FilterTabType) => {
Expand All @@ -36,6 +37,7 @@ const FilterTab = ({ header, filterList, inputType }: FilterTabType) => {
label: false,
milestone: false,
writer: false,
stateChange: false,
})
}, []);

Expand Down Expand Up @@ -73,6 +75,7 @@ const FilterTab = ({ header, filterList, inputType }: FilterTabType) => {
label: false,
milestone: false,
writer: false,
stateChange: false,
});
}, [defaultCheckerState])

Expand All @@ -84,13 +87,14 @@ const FilterTab = ({ header, filterList, inputType }: FilterTabType) => {
{filterList.map((listItem: any, idx) => {

return (<div key={`${header}-${idx}`}>
<FilterLabel>
<FilterLabel isReverse={header !== 'stateChange'}>
<input type={inputType} name={header}
onChange={handleChangeDefaultChecker(listItem)}
checked={inputType === 'radio' ?
defaultCheckerState[header].name === listItem.name :
defaultCheckerState[header]
.findIndex((item: any) => item.name === listItem.name) !== -1} />
.findIndex((item: any) => item.name === listItem.name) !== -1}
style={{ display: header === 'stateChange' ? 'none' : '' }} />
<span>{listItem.name}</span>
</FilterLabel>
</div>)
Expand Down Expand Up @@ -128,10 +132,10 @@ const FilterTabHeader = styled.div`
`;


const FilterLabel = styled.label`
const FilterLabel = styled.label<{ isReverse: boolean }>`
display:flex;
justify-content: space-between;
flex-direction: row-reverse;
flex-direction: ${({ isReverse }) => isReverse && 'row-reverse'};
& > span{
color: #A3A1B0;
font-size: 12px;
Expand Down
7 changes: 4 additions & 3 deletions fe/client/src/components/common/LabelMilestoneToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import MilestoneIcon from '@/Icons/Milestone.svg';

const LabelMilestoneToggle = () => {
const location = useLocation();
const defaultChecked = location.pathname === '/labelList';
const [labelState] = useFetch(API.get.labelsCount);
const [milestoneState] = useFetch(API.get.milestonesCount);
const { data: labelData }: AsyncState<any, any> = labelState;
Expand All @@ -29,15 +28,17 @@ const LabelMilestoneToggle = () => {
<ToggleWrapper>
<Link to="/labelList">
<ToggleItem>
<RadioButton type="radio" name="labelMilestone" defaultChecked={defaultChecked} />
<RadioButton type="radio" name="labelMilestone"
defaultChecked={location.pathname === '/labelList'} />
<LabelBelongSpanLeft> <img src={LabelIcon} alt="" /> &nbsp; 레이블
{labelData && ` (${labelData.count})`}
</LabelBelongSpanLeft>
</ToggleItem>
</Link>
<Link to="/milestoneList">
<ToggleItem>
<RadioButton type="radio" name="labelMilestone" defaultChecked={!defaultChecked} />
<RadioButton type="radio" name="labelMilestone"
defaultChecked={location.pathname === '/milestoneList'} />
<LabelBelongSpanRight> <img src={MilestoneIcon} alt="" /> &nbsp; 마일스톤
{milestoneData && ` (${milestoneData.count})`}
</LabelBelongSpanRight>
Expand Down
7 changes: 5 additions & 2 deletions fe/client/src/components/common/atoms/filterAtom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type FilterBooleanType = {
label: boolean;
milestone: boolean;
writer: boolean;
stateChange: boolean;
}

export const filterAtom = atom<FilterBooleanType>({
Expand All @@ -16,7 +17,8 @@ export const filterAtom = atom<FilterBooleanType>({
manager: false,
label: false,
milestone: false,
writer: false
writer: false,
stateChange: false,
}
})

Expand All @@ -31,7 +33,8 @@ export const filterRadioButtonListAtom = atom<FilterRadioButtonListType>({
manager: { name: '', info: {} },
label: { name: '', info: {} },
milestone: { name: '', info: {} },
writer: { name: '', info: {} }
writer: { name: '', info: {} },
stateChange: { name: '', info: {} }
}
})

Expand Down
8 changes: 0 additions & 8 deletions fe/client/src/components/common/atoms/issueListAtom.ts

This file was deleted.

13 changes: 5 additions & 8 deletions fe/client/src/components/createIssue/SendButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,17 @@ const SendButton = () => {
const [commentInputValue] = useRecoilState(issueCommentInputAtom);

const handleClickSendData = async () => {

const data = {
title: titleInputValue,
mainCommentContents: commentInputValue,
authorId: 3, //수정필요
assigneeIds: checkedItems.manager.map(getCheckedItemId),
labelIds: checkedItems.label.map(getCheckedItemId),
milestoneId: checkedItems.milestone.map(getCheckedItemId)[0]
milestoneId: checkedItems.milestone.length && checkedItems.milestone[0].id
};

const a = await API.post.issues(data);
console.log(a);
console.log(data)
const responseData = await API.post.issues(data);
console.log(responseData);
}

return (
Expand All @@ -43,8 +42,6 @@ const SendButton = () => {
)
}

function getCheckedItemId({ info: { id } }: FilteredIdType) {
return id;
}
const getCheckedItemId = ({ info: { id } }: FilteredIdType) => id;

export default SendButton;
10 changes: 5 additions & 5 deletions fe/client/src/components/issueList/HeadContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import ArrowBottomIcon from '@/Icons/ArrowBottom.svg';
import SearchIcon from '@/Icons/Search.svg';

const issueFilterList = [
'열린 이슈',
'내가 작성한 이슈',
'나에게 할당된 이슈',
'내가 댓글을 남긴 이슈',
'닫힌 이슈'
{ name: '열린 이슈' },
{ name: '내가 작성한 이슈' },
{ name: '나에게 할당된 이슈' },
{ name: '내가 댓글을 남긴 이슈' },
{ name: '닫힌 이슈' },
];

type HeadContentType = {
Expand Down
57 changes: 57 additions & 0 deletions fe/client/src/components/issueList/IssueList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useEffect } from 'react'
import ListHeader from '@components/issueList/ListHeader';
import ListItem from '@components/issueList/ListItem';
import { IssueListItemType } from '@components/common/types/APIType';
import { issueCheckedItemAtom } from '@components/common/atoms/checkBoxAtom';
import { filterRadioButtonListAtom } from '@components/common/atoms/filterAtom';
import API from '@/utils/API';
import { useRecoilState } from '@/utils/myRecoil/useRecoilState';
import useFetch, { AsyncState } from '@/utils/hook/useFetch';
import { pipe, _ } from '@/utils/functionalUtils';

type IssueListType = {
handleClickShowFilterModal: (title: string) => () => void;
}

const IssueList = ({ handleClickShowFilterModal }: IssueListType) => {
const [issueItems] = useFetch(API.get.issues);
const { data, loading, error }: AsyncState<any, any> = issueItems;
const [checkedFilteredState, setCheckedFilteredState] = useRecoilState(filterRadioButtonListAtom);

if (data) {
console.log(
pipe(
_.filter(getFilteredIssueItems('assignees', checkedFilteredState.manager.info.id)),
_.filter(getFilteredIssueItems('labels', checkedFilteredState.label.info.id)),
_.filter(getFilteredIssueItems('milestone', checkedFilteredState.milestone.info.id)),
_.filter(getFilteredIssueItems('author', checkedFilteredState.writer.info.id))
)(data))
}

return (
<>
{
data && <>
<ListHeader issueItems={data} {...{ handleClickShowFilterModal }} />
{data.map((issueItem: IssueListItemType) => {
return <ListItem key={issueItem.id} {...{ issueItem }} />
})}
</>
}
{loading && <>loading...</>}
{error && <>error...</>}
</>
)
}


const getFilteredIssueItems = (filterName: string, targetId: any) => (data: any) => {
if (!targetId) return data;
const property = data[filterName];
if (!Array.isArray(property)) {
return property.id === targetId;
} else {
return property.find(({ id }: { id: number }) => id === targetId);
}
}
export default IssueList;
49 changes: 49 additions & 0 deletions fe/client/src/components/issueList/IssueStateChangeFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react'
import FilterTab from '@components/common/FilterTab';
import styled from 'styled-components';
import ArrowBottomIcon from '@/Icons/ArrowBottom.svg';

type IssueStateChangeFilterType = {
title: string;
handleClickShowFilterModal: (title: string) => () => void
}
const filterItems = [
{ name: '선택한 이슈 열기' },
{ name: '선택한 이슈 닫기' }
];

const IssueStateChangeFilter = ({ title, handleClickShowFilterModal }: IssueStateChangeFilterType) => {
return (
<FilterWrapper>
<div onClick={handleClickShowFilterModal(title)}>
<FilterTitleSpan>상태 수정</FilterTitleSpan>
<ArrowImageTag src={ArrowBottomIcon} alt="" />
</div>
<FilterTab
inputType='radio'
header={title}
filterList={filterItems} />
</FilterWrapper>
)
}
const FilterWrapper = styled.div`
display: flex;
position: relative;
&:hover{
cursor:pointer;
}
`;

const ArrowImageTag = styled.img`
width: 16px;
height: 16px;
transform: translateY(3px);
`;

const FilterTitleSpan = styled.span`
max-width: 70px;
padding-right: 10px;
line-height: 24px;
`;

export default IssueStateChangeFilter;
Loading

0 comments on commit 280fcad

Please sign in to comment.