Skip to content

Commit

Permalink
Merge pull request #272 from boostcampwm2023/refactor/268-fe-refactor
Browse files Browse the repository at this point in the history
[Refactor] 컴포넌트 중복 요소 분리 및 정리
dbwhdtjr0457 authored Dec 14, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 850b46b + 9e8eb2a commit d1601e3
Showing 6 changed files with 358 additions and 396 deletions.
121 changes: 40 additions & 81 deletions FE/src/components/DiaryModal/DiaryAnalysisModal.js
Original file line number Diff line number Diff line change
@@ -30,6 +30,11 @@ function DiaryAnalysisModal() {
left: 0,
text: "",
});
const emotions = [
{ bg: "#618cf7", text: "긍정" },
{ bg: "#e5575b", text: "부정" },
{ bg: "#a848f6", text: "중립" },
];

async function getDataFn(data) {
return fetch(
@@ -250,39 +255,19 @@ function DiaryAnalysisModal() {
text={false}
/>
<EmotionStreakBar>
<EmotionStreak>
<DailyStreak
$bg='#618cf7'
$paddingBottom='0'
$width='1.2rem'
$height='1.2rem'
/>
<DiaryAnalysisModalText size='1rem'>
긍정
</DiaryAnalysisModalText>
</EmotionStreak>
<EmotionStreak>
<DailyStreak
$bg='#e5575b'
$paddingBottom='0'
$width='1.2rem'
$height='1.2rem'
/>
<DiaryAnalysisModalText size='1rem'>
부정
</DiaryAnalysisModalText>
</EmotionStreak>
<EmotionStreak>
<DailyStreak
$bg='#a848f6'
$paddingBottom='0'
$width='1.2rem'
$height='1.2rem'
/>
<DiaryAnalysisModalText size='1rem'>
중립
</DiaryAnalysisModalText>
</EmotionStreak>
{emotions.map((emotion, index) => (
<EmotionStreak key={`emotion-${index + 1}`}>
<DailyStreak
$bg={emotion.bg}
$paddingBottom='0'
$width='1.2rem'
$height='1.2rem'
/>
<DiaryAnalysisModalText size='1rem'>
{emotion.text}
</DiaryAnalysisModalText>
</EmotionStreak>
))}
</EmotionStreakBar>
</EmotionBarContentWrapper>
</EmotionBar>
@@ -340,30 +325,17 @@ function DiaryAnalysisModal() {
</DiaryAnalysisModalText>
</DiaryAnalysisModalTitleWrapper>
<DiaryAnalysisModalContentWrapper>
{tagsRankData && tagsRankData?.first ? (
<TagRanking
key='first-tag'
rank={tagsRankData.first.rank}
tag={tagsRankData.first.tag}
count={tagsRankData.first.count}
/>
) : null}
{tagsRankData && tagsRankData?.second ? (
<TagRanking
key='second-tag'
rank={tagsRankData.second.rank}
tag={tagsRankData.second.tag}
count={tagsRankData.second.count}
/>
) : null}
{tagsRankData && tagsRankData?.third ? (
<TagRanking
key='third-tag'
rank={tagsRankData.third.rank}
tag={tagsRankData.third.tag}
count={tagsRankData.third.count}
/>
) : null}
{tagsRankData &&
["first", "second", "third"].map((rank) =>
tagsRankData[rank] ? (
<TagRanking
key={`${rank}-tag`}
rank={tagsRankData[rank].rank}
tag={tagsRankData[rank].tag}
count={tagsRankData[rank].count}
/>
) : null,
)}
</DiaryAnalysisModalContentWrapper>
</>
) : (
@@ -379,30 +351,17 @@ function DiaryAnalysisModal() {
</DiaryAnalysisModalText>
</DiaryAnalysisModalTitleWrapper>
<DiaryAnalysisModalContentWrapper direction='row'>
{shapesRankData && shapesRankData?.first ? (
<ShapeRanking
key='first-shape'
rank={shapesRankData.first.rank}
uuid={shapesRankData.first.uuid}
count={shapesRankData.first.count}
/>
) : null}
{shapesRankData && shapesRankData?.second ? (
<ShapeRanking
key='second-shape'
rank={shapesRankData.second.rank}
uuid={shapesRankData.second.uuid}
count={shapesRankData.second.count}
/>
) : null}
{shapesRankData && shapesRankData?.third ? (
<ShapeRanking
key='third-shape'
rank={shapesRankData.third.rank}
uuid={shapesRankData.third.uuid}
count={shapesRankData.third.count}
/>
) : null}
{shapesRankData &&
["first", "second", "third"].map((rank) =>
shapesRankData[rank] ? (
<ShapeRanking
key={`${rank}-shape`}
rank={shapesRankData[rank].rank}
uuid={shapesRankData[rank].uuid}
count={shapesRankData[rank].count}
/>
) : null,
)}
</DiaryAnalysisModalContentWrapper>
</>
) : (
163 changes: 74 additions & 89 deletions FE/src/components/DiaryModal/DiaryListModal.js
Original file line number Diff line number Diff line change
@@ -52,57 +52,77 @@ function DiaryListModal() {
}, [selectedDiary]);

useEffect(() => {
if (diaryState.diaryList) {
const filteredList = diaryState.diaryList
.filter((diary) => {
return (
(!filterState.date.start && !filterState.date.end) ||
(filterState.date.start &&
filterState.date.end &&
new Date(diary.date) >= filterState.date.start &&
new Date(diary.date) <= filterState.date.end) ||
(filterState.date.start &&
!filterState.date.end &&
new Date(diary.date) >= filterState.date.start) ||
(filterState.date.end &&
!filterState.date.start &&
new Date(diary.date) <= filterState.date.end)
);
})
.filter((diary) => {
return (
(!filterState.emotion.positive &&
!filterState.emotion.neutral &&
!filterState.emotion.negative) ||
(filterState.emotion.positive &&
diary.emotion.sentiment === "positive") ||
(filterState.emotion.neutral &&
diary.emotion.sentiment === "neutral") ||
(filterState.emotion.negative &&
diary.emotion.sentiment === "negative")
);
})
.filter((diary) => {
return (
filterState.shape.length === 0 ||
filterState.shape.includes(diary.shapeUuid)
);
})
.filter((diary) => {
return (
filterState.tag.length === 0 ||
filterState.tag.every((tag) => diary.tags.includes(tag))
);
});
setFilteredDiaryList([...filteredList]);
}
if (!diaryState.diaryList) return;

const isDateInRange = (diaryDate) => {
const startDateCondition =
!filterState.date.start ||
new Date(diaryDate) >= filterState.date.start;

const endDateCondition =
!filterState.date.end || new Date(diaryDate) <= filterState.date.end;

return startDateCondition && endDateCondition;
};

const isEmotionMatch = (diaryEmotion) => {
const { positive, neutral, negative } = filterState.emotion;

return (
(!positive && !neutral && !negative) ||
(positive && diaryEmotion === "positive") ||
(neutral && diaryEmotion === "neutral") ||
(negative && diaryEmotion === "negative")
);
};

const isShapeMatch = (diaryShapeUuid) => {
return (
filterState.shape.length === 0 ||
filterState.shape.includes(diaryShapeUuid)
);
};

const areTagsMatch = (diaryTags) => {
return (
filterState.tag.length === 0 ||
filterState.tag.every((tag) => diaryTags.includes(tag))
);
};

const filteredList = diaryState.diaryList.filter((diary) => {
const isDateValid = isDateInRange(diary.date);
const isEmotionValid = isEmotionMatch(diary.emotion.sentiment);
const isShapeValid = isShapeMatch(diary.shapeUuid);
const areTagsValid = areTagsMatch(diary.tags);

return isDateValid && isEmotionValid && isShapeValid && areTagsValid;
});

setFilteredDiaryList([...filteredList]);
}, [
filterState.date,
filterState.emotion,
filterState.shape,
filterState.tag,
]);

const toggleEmotionFilter = (emotionType) => {
setFilterState((prev) => ({
...prev,
emotion: {
...prev.emotion,
[emotionType]: !prev.emotion[emotionType],
},
}));
};

const emotionButtons = [
{ type: "positive", borderColor: "#00ccff", text: "긍정" },
{ type: "neutral", borderColor: "#ba55d3", text: "중립" },
{ type: "negative", borderColor: "#d1180b", text: "부정" },
];

return (
<DiaryListModalWrapper>
<DiaryListModalItem $justifyContent='center'>
@@ -154,51 +174,16 @@ function DiaryListModal() {
<DiaryListModalFilterWrapper $height='17%'>
<DiaryTitleListHeader>감정</DiaryTitleListHeader>
<DiaryListModalFilterContent $height='50%'>
<FilterEmotionButton
selected={filterState.emotion.positive}
$borderColor='#00ccff'
onClick={() => {
setFilterState((prev) => ({
...prev,
emotion: {
...prev.emotion,
positive: !prev.emotion.positive,
},
}));
}}
>
긍정
</FilterEmotionButton>
<FilterEmotionButton
selected={filterState.emotion.neutral}
$borderColor='#ba55d3'
onClick={() => {
setFilterState((prev) => ({
...prev,
emotion: {
...prev.emotion,
neutral: !prev.emotion.neutral,
},
}));
}}
>
중립
</FilterEmotionButton>
<FilterEmotionButton
selected={filterState.emotion.negative}
$borderColor='#d1180b'
onClick={() => {
setFilterState((prev) => ({
...prev,
emotion: {
...prev.emotion,
negative: !prev.emotion.negative,
},
}));
}}
>
부정
</FilterEmotionButton>
{emotionButtons.map(({ type, borderColor, text }) => (
<FilterEmotionButton
key={type}
selected={filterState.emotion[type]}
$borderColor={borderColor}
onClick={() => toggleEmotionFilter(type)}
>
{text}
</FilterEmotionButton>
))}
</DiaryListModalFilterContent>
</DiaryListModalFilterWrapper>
<DiaryListModalFilterWrapper $height='28%'>
167 changes: 87 additions & 80 deletions FE/src/components/PurchaseModal/PurchaseModal.js
Original file line number Diff line number Diff line change
@@ -12,6 +12,50 @@ import fourStar from "../../assets/fourstar.svg";
import diaryAtom from "../../atoms/diaryAtom";
import handleResponse from "../../utils/handleResponse";

function PurchaseButtonComponent(props) {
const {
onClick = () => {
alert("준비 중인 서비스입니다.");
},
text,
price,
} = props;

return (
<PurchaseModalContent onClick={onClick}>
<PurchaseModalText>{text}</PurchaseModalText>
<PurchaseModalText $size='1rem'>{price} 별가루</PurchaseModalText>
</PurchaseModalContent>
);
}

function ExchangeButtonComponent(props) {
const {
onClick = () => {
alert("준비 중인 서비스입니다.");
},
icon,
width,
star,
bonus,
price,
} = props;

return (
<PurchaseModalContent onClick={onClick}>
<StarIcon src={icon} alt='star' width={width} />
<ContentTextWrapper>
<PurchaseModalText>별가루</PurchaseModalText>
<PurchaseModalText $size='1.2rem'>{star}</PurchaseModalText>
{bonus && (
<PurchaseModalText $size='0.8rem'>+ {bonus}</PurchaseModalText>
)}
</ContentTextWrapper>
<PurchaseModalText>{price}</PurchaseModalText>
</PurchaseModalContent>
);
}

function PurchaseModal(props) {
const { premiumRefetch } = props;
const [userState, setUserState] = useRecoilState(userAtom);
@@ -47,6 +91,31 @@ function PurchaseModal(props) {
),
);

const exchangeButtons = [
{ icon: oneStar, width: "5rem", star: "100", price: "1000" },
{
icon: twoStar,
width: "4.5rem",
star: "300",
bonus: "120",
price: "5000",
},
{
icon: threeStar,
width: "4rem",
star: "1000",
bonus: "500",
price: "10000",
},
{
icon: fourStar,
width: "10rem",
star: "2000",
bonus: "1000",
price: "20000",
},
];

const { mutate: purchase } = useMutation((data) => {
if (data.credit > creditData.credit) {
alert("별가루가 부족합니다.");
@@ -107,42 +176,20 @@ function PurchaseModal(props) {
</PurchaseModalText>
</PurchaseModalCreditWrapper>
<PurchaseModalContentWrapper>
<PurchaseModalContent
onClick={() => {
alert("준비 중인 서비스입니다.");
}}
>
<PurchaseModalText>땅 스킨 구매</PurchaseModalText>
<PurchaseModalText $size='1rem'>100 별가루</PurchaseModalText>
</PurchaseModalContent>
<PurchaseModalContent
onClick={() => {
alert("준비 중인 서비스입니다.");
}}
>
<PurchaseModalText>모양 슬롯 확장</PurchaseModalText>
<PurchaseModalText $size='1rem'>100 별가루</PurchaseModalText>
</PurchaseModalContent>
<PurchaseModalContent
<PurchaseButtonComponent text='땅 스킨 구매' price='100' />
<PurchaseButtonComponent text='모양 슬롯 확장' price='100' />
<PurchaseButtonComponent
text='광고 제거'
price='350'
onClick={() => {
purchase({
credit: 350,
item: "premium",
accessToken: userState.accessToken,
});
}}
>
<PurchaseModalText>광고 제거</PurchaseModalText>
<PurchaseModalText $size='1rem'>350 별가루</PurchaseModalText>
</PurchaseModalContent>
<PurchaseModalContent
onClick={() => {
alert("준비 중인 서비스입니다.");
}}
>
<PurchaseModalText>별숲 후원</PurchaseModalText>
<PurchaseModalText $size='1rem'>30000 별가루</PurchaseModalText>
</PurchaseModalContent>
/>
<PurchaseButtonComponent text='별숲 후원' price='30000' />
</PurchaseModalContentWrapper>
</PurchaseModalContainer>
<PurchaseModalContainer>
@@ -156,57 +203,17 @@ function PurchaseModal(props) {
</PurchaseModalText>
</PurchaseModalCreditWrapper>
<ExchangeModalContentWrapper>
<PurchaseModalContent
onClick={() => {
alert("준비 중인 서비스입니다.");
}}
>
<StarIcon src={oneStar} alt='oneStar' width='5rem' />
<ContentTextWrapper>
<PurchaseModalText>별가루</PurchaseModalText>
<PurchaseModalText $size='1.2rem'>100</PurchaseModalText>
</ContentTextWrapper>
<PurchaseModalText>₩ 1000</PurchaseModalText>
</PurchaseModalContent>
<PurchaseModalContent
onClick={() => {
alert("준비 중인 서비스입니다.");
}}
>
<StarIcon src={twoStar} alt='twoStar' width='4.5rem' />
<ContentTextWrapper>
<PurchaseModalText>별가루</PurchaseModalText>
<PurchaseModalText $size='1.2rem'>300</PurchaseModalText>
<PurchaseModalText $size='0.8rem'>+ 120</PurchaseModalText>
</ContentTextWrapper>
<PurchaseModalText>₩ 5000</PurchaseModalText>
</PurchaseModalContent>
<PurchaseModalContent
onClick={() => {
alert("준비 중인 서비스입니다.");
}}
>
<StarIcon src={threeStar} alt='threeStar' width='4rem' />
<ContentTextWrapper>
<PurchaseModalText>별가루</PurchaseModalText>
<PurchaseModalText $size='1.2rem'>1000</PurchaseModalText>
<PurchaseModalText $size='0.8rem'>+ 300</PurchaseModalText>
</ContentTextWrapper>
<PurchaseModalText>₩ 10000</PurchaseModalText>
</PurchaseModalContent>
<PurchaseModalContent
onClick={() => {
alert("준비 중인 서비스입니다.");
}}
>
<StarIcon src={fourStar} alt='fourStar' width='10rem' />
<ContentTextWrapper>
<PurchaseModalText>별가루</PurchaseModalText>
<PurchaseModalText $size='1.2rem'>2000</PurchaseModalText>
<PurchaseModalText $size='0.8rem'>+ 1000</PurchaseModalText>
</ContentTextWrapper>
<PurchaseModalText>₩ 20000</PurchaseModalText>
</PurchaseModalContent>
{exchangeButtons.map((button, index) => (
<ExchangeButtonComponent
key={`${index + 1}`}
icon={button.icon}
width={button.width}
star={button.star}
bonus={button.bonus}
price={button.price}
onclick={button.onClick}
/>
))}
</ExchangeModalContentWrapper>
</PurchaseModalContainer>
<ArrowIcon
199 changes: 103 additions & 96 deletions FE/src/components/SideBar/SideBar.js
Original file line number Diff line number Diff line change
@@ -9,112 +9,119 @@ import userAtom from "../../atoms/userAtom";
import starAtom from "../../atoms/starAtom";
import boostcampImg from "../../assets/boostcamp.png";

function SidebarButton(props) {
const setHeaderState = useSetRecoilState(headerAtom);
const { text, onClick } = props;

return (
<SideBarContent
onClick={() => {
setHeaderState((prev) => ({
...prev,
isSideBar: false,
}));
onClick();
}}
>
{text}
</SideBarContent>
);
}

function SideBar() {
const setDiaryState = useSetRecoilState(diaryAtom);
const setHeaderState = useSetRecoilState(headerAtom);
const [userState, setUserState] = useRecoilState(userAtom);
const setStarState = useSetRecoilState(starAtom);
const sideBarList = [
{
text: "나의 별숲",
onClick: () => {
setDiaryState((prev) => ({
...prev,
isRead: false,
isList: false,
isAnalysis: false,
isPurchase: false,
}));
},
},
{
text: "별숲 목록",
onClick: () => {
setDiaryState((prev) => ({
...prev,
isCreate: false,
isRead: false,
isUpdate: false,
isList: true,
isAnalysis: false,
isPurchase: false,
}));
setStarState((prev) => ({
...prev,
mode: "create",
}));
},
},
{
text: "별숲 현황",
onClick: () => {
setDiaryState((prev) => ({
...prev,
isCreate: false,
isRead: false,
isUpdate: false,
isList: false,
isAnalysis: true,
isPurchase: false,
}));
setStarState((prev) => ({
...prev,
mode: "create",
}));
},
},
{
text: "별숲 상점",
onClick: () => {
setDiaryState((prev) => ({
...prev,
isCreate: false,
isRead: false,
isUpdate: false,
isList: false,
isAnalysis: false,
isPurchase: true,
}));
setStarState((prev) => ({
...prev,
mode: "create",
}));
},
},
{
text: "도움말",
onClick: () => {
window.open(
"https://byeolsoop.notion.site/551e1070f73a405badb8aeb178dac192?pvs=4",
"_blank",
);
},
},
];

return (
<AnimationWrapper>
<SideBarWrapper>
<SideBarContentWrapper>
<SideBarContent
onClick={() => {
setHeaderState((prev) => ({
...prev,
isSideBar: false,
}));
setDiaryState((prev) => ({
...prev,
isRead: false,
isList: false,
isAnalysis: false,
isPurchase: false,
}));
}}
>
나의 별숲
</SideBarContent>
<SideBarContent
onClick={() => {
setHeaderState((prev) => ({
...prev,
isSideBar: false,
}));
setDiaryState((prev) => ({
...prev,
isCreate: false,
isRead: false,
isUpdate: false,
isList: true,
isAnalysis: false,
isPurchase: false,
}));
setStarState((prev) => ({
...prev,
mode: "create",
}));
}}
>
별숲 목록
</SideBarContent>
<SideBarContent
onClick={() => {
setHeaderState((prev) => ({
...prev,
isSideBar: false,
}));
setDiaryState((prev) => ({
...prev,
isCreate: false,
isRead: false,
isUpdate: false,
isList: false,
isAnalysis: true,
isPurchase: false,
}));
setStarState((prev) => ({
...prev,
mode: "create",
}));
}}
>
별숲 현황
</SideBarContent>
<SideBarContent
onClick={() => {
setHeaderState((prev) => ({
...prev,
isSideBar: false,
}));
setDiaryState((prev) => ({
...prev,
isCreate: false,
isRead: false,
isUpdate: false,
isList: false,
isAnalysis: false,
isPurchase: true,
}));
setStarState((prev) => ({
...prev,
mode: "create",
}));
}}
>
별숲 상점
</SideBarContent>
<SideBarContent
onClick={() =>
window.open(
"https://byeolsoop.notion.site/551e1070f73a405badb8aeb178dac192?pvs=4",
"_blank",
)
}
>
도움말
</SideBarContent>
{sideBarList.map((item) => (
<SidebarButton
key={item.text}
text={item.text}
onClick={item.onClick}
/>
))}
</SideBarContentWrapper>
<LogOutButton
onClick={() => {
102 changes: 53 additions & 49 deletions FE/src/components/SignUpModal/SignUpModal.js
Original file line number Diff line number Diff line change
@@ -10,6 +10,16 @@ import ModalInputBox from "../../styles/Modal/ModalInputBox";
import ModalBackground from "../ModalBackground/ModalBackground";
import SignUpRuleGuide from "./SignUpRuleGuide";

function SignUpModalInputComponent(props) {
const { title, type, onChange } = props;
return (
<SignUpModalInput>
<SignUpModalInputTitle>{title}</SignUpModalInputTitle>
<ModalInputBox type={type} onChange={onChange} />
</SignUpModalInput>
);
}

function SignUpModal() {
const setHeaderState = useSetRecoilState(headerAtom);
const [email, setEmail] = useState("");
@@ -18,6 +28,43 @@ function SignUpModal() {
const [password, setPassword] = useState("");
const [passwordCheck, setPasswordCheck] = useState("");
const errorRef = useRef();
const signUpInputList = [
{
title: "* 아이디",
type: "text",
onChange: (e) => {
setUserId(e.target.value);
},
},
{
title: "* 비밀번호",
type: "password",
onChange: (e) => {
setPassword(e.target.value);
},
},
{
title: "* 비밀번호 확인",
type: "password",
onChange: (e) => {
setPasswordCheck(e.target.value);
},
},
{
title: "* 이메일",
type: "email",
onChange: (e) => {
setEmail(e.target.value);
},
},
{
title: "* 닉네임",
type: "text",
onChange: (e) => {
setNickname(e.target.value);
},
},
];

const { mutate: signUp } = useMutation(() => {
fetch(`${process.env.REACT_APP_BACKEND_URL}/auth/signup`, {
@@ -106,56 +153,13 @@ function SignUpModal() {
<SignUpModalSubtitle>당신의 이야기를 펼쳐보세요!</SignUpModalSubtitle>
</SignUpModalHeaderWrapper>
<SignUpModalInputWrapper>
<SignUpModalInput>
<SignUpModalInputTitle>* 아이디</SignUpModalInputTitle>
<ModalInputBox
height='3.1rem'
type='text'
onChange={(e) => {
setUserId(e.target.value);
}}
/>
</SignUpModalInput>
<SignUpModalInput>
<SignUpModalInputTitle>* 비밀번호</SignUpModalInputTitle>
<ModalInputBox
height='3.1rem'
type='password'
onChange={(e) => {
setPassword(e.target.value);
}}
/>
</SignUpModalInput>
<SignUpModalInput>
<SignUpModalInputTitle>* 비밀번호 확인</SignUpModalInputTitle>
<ModalInputBox
height='3.1rem'
type='password'
onChange={(e) => {
setPasswordCheck(e.target.value);
}}
/>
</SignUpModalInput>
<SignUpModalInput>
<SignUpModalInputTitle>* 이메일</SignUpModalInputTitle>
<ModalInputBox
height='3.1rem'
type='email'
onChange={(e) => {
setEmail(e.target.value);
}}
/>
</SignUpModalInput>
<SignUpModalInput>
<SignUpModalInputTitle>* 닉네임</SignUpModalInputTitle>
<ModalInputBox
height='3.1rem'
type='text'
onChange={(e) => {
setNickname(e.target.value);
}}
{signUpInputList.map((input) => (
<SignUpModalInputComponent
title={input.title}
type={input.type}
onChange={input.onChange}
/>
</SignUpModalInput>
))}
</SignUpModalInputWrapper>
<ModalButtonContainer>
<div id='sign-up-error' style={{ color: "red" }} ref={errorRef} />
2 changes: 1 addition & 1 deletion FE/src/styles/Modal/ModalInputBox.js
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ import styled from "styled-components";

const ModalInputBox = styled.input`
width: 100%;
height: ${(props) => props.height || "3.5rem"};
height: 3.1rem;
border-radius: 0.2rem;
border: 1px solid #ffffff;
background-color: transparent;

0 comments on commit d1601e3

Please sign in to comment.