From af327eb413b67c2bc7d8b648a4254b398131e4e0 Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Mon, 4 Dec 2023 17:46:50 +0900 Subject: [PATCH 01/18] =?UTF-8?q?feat:=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=EB=8B=AC=EB=A0=A5=20UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/assets/leftIcon.svg | 3 + FE/src/assets/rightIcon.svg | 3 + FE/src/assets/toggleIcon.svg | 3 + FE/src/components/DiaryModal/Calendar.js | 300 ++++++++++++++++++ .../components/DiaryModal/DiaryCreateModal.js | 11 +- 5 files changed, 311 insertions(+), 9 deletions(-) create mode 100644 FE/src/assets/leftIcon.svg create mode 100644 FE/src/assets/rightIcon.svg create mode 100644 FE/src/assets/toggleIcon.svg create mode 100644 FE/src/components/DiaryModal/Calendar.js diff --git a/FE/src/assets/leftIcon.svg b/FE/src/assets/leftIcon.svg new file mode 100644 index 0000000..29d75ea --- /dev/null +++ b/FE/src/assets/leftIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/FE/src/assets/rightIcon.svg b/FE/src/assets/rightIcon.svg new file mode 100644 index 0000000..4c4d281 --- /dev/null +++ b/FE/src/assets/rightIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/FE/src/assets/toggleIcon.svg b/FE/src/assets/toggleIcon.svg new file mode 100644 index 0000000..5097b11 --- /dev/null +++ b/FE/src/assets/toggleIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/FE/src/components/DiaryModal/Calendar.js b/FE/src/components/DiaryModal/Calendar.js new file mode 100644 index 0000000..6e62e3d --- /dev/null +++ b/FE/src/components/DiaryModal/Calendar.js @@ -0,0 +1,300 @@ +import React from "react"; +import styled from "styled-components"; +import toggleIcon from "../../assets/toggleIcon.svg"; +import leftIcon from "../../assets/leftIcon.svg"; +import rightIcon from "../../assets/rightIcon.svg"; + +// function getFormattedDate(date) { +// const year = date.getFullYear(); +// const month = String(date.getMonth() + 1).padStart(2, "0"); +// const day = String(date.getDate()).padStart(2, "0"); + +// const formattedDate = `${year}-${month}-${day}`; +// return formattedDate; +// } + +function getCalendarDate(date) { + const year = date.getFullYear(); + const month = date.getMonth() + 1; + + const firstDay = new Date(year, month - 1, 1); + const lastDay = new Date(year, month, 0); + + const firstDayOfWeek = firstDay.getDay(); + const lastDayOfWeek = lastDay.getDay(); + + const firstDate = firstDay.getDate(); + const lastDate = lastDay.getDate(); + + const calendarDate = []; + + for (let i = 0; i < firstDayOfWeek; i += 1) { + calendarDate.push(""); + } + + for (let i = firstDate; i <= lastDate; i += 1) { + calendarDate.push(i); + } + + for (let i = lastDayOfWeek; i < 6; i += 1) { + calendarDate.push(""); + } + + return calendarDate; +} + +const getColor = (index) => { + if (index % 7 === 0) { + return "red"; + } + if (index % 7 === 6) { + return "blue"; + } + return "black"; +}; + +function Calendar() { + const [isCalendarOpen, setIsCalendarOpen] = React.useState(false); + const [calendarDate, setCalendarDate] = React.useState(new Date()); + const [selectedDate, setSelectedDate] = React.useState(new Date()); + + return ( + + setIsCalendarOpen(!isCalendarOpen)}> + + {selectedDate.toLocaleDateString("ko-KR", { + year: "numeric", + month: "long", + day: "numeric", + })} + + + + {isCalendarOpen && ( + + + + setCalendarDate( + new Date( + calendarDate.getFullYear(), + calendarDate.getMonth() - 1, + calendarDate.getDate(), + ), + ) + } + /> + + {calendarDate.toLocaleDateString("ko-KR", { + year: "numeric", + month: "long", + })} + + + setCalendarDate( + new Date( + calendarDate.getFullYear(), + calendarDate.getMonth() + 1, + calendarDate.getDate(), + ), + ) + } + /> + + + + {["일", "월", "화", "수", "목", "금", "토"].map((item) => ( + {item} + ))} + + + {getCalendarDate(calendarDate).map((item, index) => ( + {} + : () => + setSelectedDate( + new Date( + calendarDate.getFullYear(), + calendarDate.getMonth(), + item, + ), + ) + } + > + + {item} + + + ))} + + + + )} + + ); +} + +const CalendarWrapper = styled.div` + height: 1.5rem; + display: flex; + justify-content: space-between; + gap: 1rem; + cursor: pointer; +`; + +const CalendarHeader = styled.div` + height: 100%; + z-index: 3000; + display: flex; + justify-content: flex-start; + align-items: center; + gap: 0.8rem; +`; + +const CalendarHeaderTitle = styled.div` + height: 100%; + display: flex; + justify-content: space-between; + gap: 1rem; + font-size: 1.2rem; +`; + +const CalendarButton = styled.img` + width: 0.9rem; + height: 0.9rem; + + position: relative; + top: -0.1rem; + + &.rotate { + transform: rotate(-180deg); + transition: transform 0.25s; + } + + &.unrotate { + transform: rotate(0deg); + transition: transform 0.25s; + } +`; + +const CalendarBodyWrapper = styled.div` + width: 15rem; + padding: 1rem; + z-index: 2000; + + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 1rem; + + position: absolute; + top: 5.5rem; + left: 3.5rem; + + background-color: #bbc2d4; + border-radius: 0.5rem; + + cursor: default; +`; + +const CalendarBody = styled.div` + width: 100%; + color: black; + + display: flex; + flex-direction: column; + justify-content: space-between; +`; + +const CalendarBodyHeaderWrapper = styled.div` + width: 100%; + height: 2rem; + display: flex; + justify-content: center; + align-items: center; + gap: 1rem; + color: black; +`; + +const CalendarBodyHeader = styled.div` + width: 45%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +`; + +const ArrowButton = styled.img` + width: 0.7rem; + height: 0.7rem; + cursor: pointer; +`; + +const CalendarBodyDayWrapper = styled.div` + width: 100%; + height: 1.8rem; + background-color: #000000dd; + border-top-left-radius: 0.5rem; + border-top-right-radius: 0.5rem; + display: flex; + justify-content: space-between; + align-items: center; + color: white; + font-size: 0.8rem; +`; + +const CalendarBodyDay = styled.div` + width: 14%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +`; + +const CalendarBodyDateWrapper = styled.div` + width: 100%; + height: 100%; + background-color: #ffffff; + border-bottom-left-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; + overflow: hidden; + display: flex; + justify-content: space-between; + flex-wrap: wrap; +`; + +const CalendarBodyDate = styled.div` + width: 14%; + height: 2rem; + display: flex; + justify-content: center; + align-items: center; + + background-color: #ffffff; + cursor: pointer; + + &:hover { + background-color: #e0e0e0; + } +`; + +const CalendarBodyDateNumber = styled.div` + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + color: ${(props) => props.color}; + font-size: 0.8rem; +`; + +export default Calendar; diff --git a/FE/src/components/DiaryModal/DiaryCreateModal.js b/FE/src/components/DiaryModal/DiaryCreateModal.js index 7656e0e..ca0b30c 100644 --- a/FE/src/components/DiaryModal/DiaryCreateModal.js +++ b/FE/src/components/DiaryModal/DiaryCreateModal.js @@ -6,7 +6,7 @@ import userAtom from "../../atoms/userAtom"; import diaryAtom from "../../atoms/diaryAtom"; import shapeAtom from "../../atoms/shapeAtom"; import ModalWrapper from "../../styles/Modal/ModalWrapper"; -import DiaryModalHeader from "../../styles/Modal/DiaryModalHeader"; +import Calendar from "./Calendar"; import deleteIcon from "../../assets/deleteIcon.svg"; import preventBeforeUnload from "../../utils/utils"; @@ -94,10 +94,7 @@ function DiaryCreateModal(props) { return ( - - 새로운 별의 이야기를 적어주세요. - {diaryData.date} - + Date: Mon, 4 Dec 2023 22:04:22 +0900 Subject: [PATCH 02/18] =?UTF-8?q?feat:=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=EB=8B=AC=EB=A0=A5=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 일기 생성 / 수정할 때 커스텀 달력과 Date 객체 활용 - utils에 날짜 포매팅 함수 추가 --- FE/src/components/DiaryModal/Calendar.js | 32 +++-- .../components/DiaryModal/DiaryCreateModal.js | 17 ++- .../components/DiaryModal/DiaryUpdateModal.js | 126 +++++++++--------- FE/src/pages/MainPage.js | 2 +- FE/src/styles/Modal/DiaryModalHeader.js | 10 -- FE/src/utils/utils.js | 11 +- 6 files changed, 104 insertions(+), 94 deletions(-) delete mode 100644 FE/src/styles/Modal/DiaryModalHeader.js diff --git a/FE/src/components/DiaryModal/Calendar.js b/FE/src/components/DiaryModal/Calendar.js index 6e62e3d..1be12b7 100644 --- a/FE/src/components/DiaryModal/Calendar.js +++ b/FE/src/components/DiaryModal/Calendar.js @@ -1,18 +1,9 @@ -import React from "react"; +import React, { useLayoutEffect } from "react"; import styled from "styled-components"; import toggleIcon from "../../assets/toggleIcon.svg"; import leftIcon from "../../assets/leftIcon.svg"; import rightIcon from "../../assets/rightIcon.svg"; -// function getFormattedDate(date) { -// const year = date.getFullYear(); -// const month = String(date.getMonth() + 1).padStart(2, "0"); -// const day = String(date.getDate()).padStart(2, "0"); - -// const formattedDate = `${year}-${month}-${day}`; -// return formattedDate; -// } - function getCalendarDate(date) { const year = date.getFullYear(); const month = date.getMonth() + 1; @@ -53,11 +44,17 @@ const getColor = (index) => { return "black"; }; -function Calendar() { +function Calendar(props) { + const { date, setDiaryData } = props; const [isCalendarOpen, setIsCalendarOpen] = React.useState(false); const [calendarDate, setCalendarDate] = React.useState(new Date()); const [selectedDate, setSelectedDate] = React.useState(new Date()); + useLayoutEffect(() => { + setCalendarDate(date); + setSelectedDate(date); + }, [date]); + return ( setIsCalendarOpen(!isCalendarOpen)}> @@ -120,14 +117,23 @@ function Calendar() { onClick={ item === "" ? () => {} - : () => + : () => { setSelectedDate( new Date( calendarDate.getFullYear(), calendarDate.getMonth(), item, ), - ) + ); + setDiaryData((prev) => ({ + ...prev, + date: new Date( + calendarDate.getFullYear(), + calendarDate.getMonth(), + item, + ), + })); + } } > diff --git a/FE/src/components/DiaryModal/DiaryCreateModal.js b/FE/src/components/DiaryModal/DiaryCreateModal.js index ca0b30c..e2b3087 100644 --- a/FE/src/components/DiaryModal/DiaryCreateModal.js +++ b/FE/src/components/DiaryModal/DiaryCreateModal.js @@ -8,7 +8,7 @@ import shapeAtom from "../../atoms/shapeAtom"; import ModalWrapper from "../../styles/Modal/ModalWrapper"; import Calendar from "./Calendar"; import deleteIcon from "../../assets/deleteIcon.svg"; -import preventBeforeUnload from "../../utils/utils"; +import { preventBeforeUnload, getFormattedDate } from "../../utils/utils"; function DiaryCreateModal(props) { const { refetch } = props; @@ -21,7 +21,7 @@ function DiaryCreateModal(props) { const [diaryData, setDiaryData] = useState({ title: "", content: "", - date: "2023-11-19", + date: new Date(), point: diaryState.diaryPoint, tags: [], shapeUuid: "", @@ -57,13 +57,22 @@ function DiaryCreateModal(props) { }; async function createDiaryFn(data) { + const diaryData = { + title: data.diaryData.title, + content: data.diaryData.content, + date: getFormattedDate(data.diaryData.date), + point: data.diaryData.point, + tags: data.diaryData.tags, + shapeUuid: data.diaryData.shapeUuid, + }; + return fetch("http://223.130.129.145:3005/diaries", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${data.accessToken}`, }, - body: JSON.stringify(data.diaryData), + body: JSON.stringify(diaryData), }) .then((res) => { if (res.status === 201) { @@ -94,7 +103,7 @@ function DiaryCreateModal(props) { return ( - + res.json()); -} +import { preventBeforeUnload, getFormattedDate } from "../../utils/utils"; // TODO: 일기 데이터 수정 API 연결 function DiaryUpdateModal(props) { @@ -34,7 +24,7 @@ function DiaryUpdateModal(props) { uuid: diaryState.diaryUuid, title: "", content: "", - date: "2023-11-19", + date: "", point: diaryState.diaryPoint, tags: [], shapeUuid: diaryState.diaryList.find( @@ -42,30 +32,19 @@ function DiaryUpdateModal(props) { ).shapeUuid, }); - async function updateDiaryFn(data) { - return fetch("http://223.130.129.145:3005/diaries", { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${data.accessToken}`, - }, - body: JSON.stringify(data.diaryData), - }).then(() => { - refetch(); - setDiaryState((prev) => ({ - ...prev, - isLoading: true, - })); - }); - } - - useEffect(() => { - window.addEventListener("beforeunload", preventBeforeUnload); + const { + mutate: updateDiary, + // isLoading: diaryIsLoading, + // isError: diaryIsError, + } = useMutation(updateDiaryFn); - return () => { - window.removeEventListener("beforeunload", preventBeforeUnload); - }; - }, []); + const { + data: originData, + isLoading, + isError, + } = useQuery("diary", () => + getDiary(userState.accessToken, diaryState.diaryUuid), + ); const closeModal = () => { window.history.back(); @@ -92,27 +71,49 @@ function DiaryUpdateModal(props) { setDiaryData({ ...diaryData, tags: diaryData.tags.slice(0, -1) }); }; - const { - mutate: updateDiary, - // isLoading: diaryIsLoading, - // isError: diaryIsError, - } = useMutation(updateDiaryFn); + async function updateDiaryFn(data) { + const diaryData = { + uuid: data.diaryData.uuid, + title: data.diaryData.title, + content: data.diaryData.content, + date: getFormattedDate(data.diaryData.date), + point: data.diaryData.point, + tags: data.diaryData.tags, + shapeUuid: data.diaryData.shapeUuid, + }; - const { - data: originData, - isLoading, - isError, - } = useQuery("diary", () => - getDiary(userState.accessToken, diaryState.diaryUuid), - ); + return fetch("http://223.130.129.145:3005/diaries", { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${data.accessToken}`, + }, + body: JSON.stringify(diaryData), + }).then(() => { + refetch(); + setDiaryState((prev) => ({ + ...prev, + isLoading: true, + })); + }); + } + async function getDiary(accessToken, diaryUuid) { + return fetch(`http://223.130.129.145:3005/diaries/${diaryUuid}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${accessToken}`, + }, + }).then((res) => res.json()); + } - useEffect(() => { + useLayoutEffect(() => { if (originData) { setDiaryData({ ...diaryData, title: originData.title, content: originData.content, - date: originData.date, + date: new Date(originData.date), tags: originData.tags, }); titleRef.current && (titleRef.current.value = originData.title); @@ -120,6 +121,14 @@ function DiaryUpdateModal(props) { } }, [originData]); + useEffect(() => { + window.addEventListener("beforeunload", preventBeforeUnload); + + return () => { + window.removeEventListener("beforeunload", preventBeforeUnload); + }; + }, []); + if (isLoading) return ( @@ -136,16 +145,7 @@ function DiaryUpdateModal(props) { return ( - - 바뀐 별의 이야기를 적어주세요. - - {new Date().toLocaleDateString("ko-KR", { - year: "numeric", - month: "long", - day: "numeric", - })} - - + { e.returnValue = ""; }; -export default preventBeforeUnload; +const getFormattedDate = (date) => { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + + const formattedDate = `${year}-${month}-${day}`; + return formattedDate; +}; + +export { preventBeforeUnload, getFormattedDate }; From 9bc7585e8119f0bcb00132d25fbb232f7c58003d Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Mon, 4 Dec 2023 23:16:15 +0900 Subject: [PATCH 03/18] =?UTF-8?q?chore:=20=EB=AA=A8=EB=93=9C=20=EC=8A=A4?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EB=B2=84=ED=8A=BC=20=EC=95=A0=EB=8B=88?= =?UTF-8?q?=EB=A9=94=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 모달 등장 애니메이션 추가 --- FE/src/components/Button/SwitchButton.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/FE/src/components/Button/SwitchButton.js b/FE/src/components/Button/SwitchButton.js index 7e83b40..2c9151b 100644 --- a/FE/src/components/Button/SwitchButton.js +++ b/FE/src/components/Button/SwitchButton.js @@ -48,6 +48,16 @@ const SwitchButtonWrapper = styled.div` overflow: hidden; cursor: pointer; + + animation: modalFadeIn 0.5s; + @keyframes modalFadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } + } `; const SwitchButtonContent = styled.div` From 051e5ba46a37465c8c0a53f02de842efd8afb22a Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Mon, 4 Dec 2023 23:21:01 +0900 Subject: [PATCH 04/18] =?UTF-8?q?chore:=20=EB=AA=A8=EB=8B=AC=20=EB=B0=B0?= =?UTF-8?q?=EA=B2=BD=EC=83=89=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 모달 배경색 투명도를 0.3에서 0.2로 변경 --- FE/src/components/DiaryModal/DiaryCreateModal.js | 9 ++++----- FE/src/components/DiaryModal/DiaryListModal.js | 4 ++-- FE/src/components/DiaryModal/DiaryLoadingModal.js | 1 + FE/src/components/DiaryModal/DiaryReadModal.js | 10 +++++----- FE/src/components/DiaryModal/DiaryUpdateModal.js | 13 ++++++------- FE/src/pages/HomePage.js | 1 - FE/src/styles/Modal/ModalWrapper.js | 2 +- 7 files changed, 19 insertions(+), 21 deletions(-) diff --git a/FE/src/components/DiaryModal/DiaryCreateModal.js b/FE/src/components/DiaryModal/DiaryCreateModal.js index e2b3087..66f4227 100644 --- a/FE/src/components/DiaryModal/DiaryCreateModal.js +++ b/FE/src/components/DiaryModal/DiaryCreateModal.js @@ -102,7 +102,7 @@ function DiaryCreateModal(props) { } = useMutation(createDiaryFn); return ( - + props.width || "2.5rem"}; height: 2.5rem; - background-color: rgba(255, 255, 255, 0.3); + background-color: rgba(255, 255, 255, 0.2); border-radius: 2rem; z-index: 1001; @@ -275,8 +275,7 @@ const ModalSideButton = styled.div` cursor: pointer; &:hover { - background-color: rgba(255, 255, 255, 0.5); - transition: 0.25s; + background-color: rgba(255, 255, 255, 0.3); } `; @@ -367,7 +366,7 @@ const DiaryModalTagBox = styled.div` padding: 0.5rem 1rem; border-radius: 1.5rem; border: 1px solid #ffffff; - background-color: rgba(255, 255, 255, 0.3); + background-color: rgba(255, 255, 255, 0.2); flex-shrink: 0; diff --git a/FE/src/components/DiaryModal/DiaryListModal.js b/FE/src/components/DiaryModal/DiaryListModal.js index 397d90e..c7cdc6e 100644 --- a/FE/src/components/DiaryModal/DiaryListModal.js +++ b/FE/src/components/DiaryModal/DiaryListModal.js @@ -114,7 +114,7 @@ const DiaryListModalWrapper = styled.div` const DiaryListModalItem = styled.div` width: ${(props) => props.width || "25%"}; height: 85%; - background-color: rgba(255, 255, 255, 0.3); + background-color: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); border-radius: 1rem; @@ -200,7 +200,7 @@ const DiaryTitleListItem = styled.div` text-overflow: ellipsis; &:hover { - background-color: rgba(255, 255, 255, 0.3); + background-color: rgba(255, 255, 255, 0.2); } `; diff --git a/FE/src/components/DiaryModal/DiaryLoadingModal.js b/FE/src/components/DiaryModal/DiaryLoadingModal.js index d0229a3..3d83239 100644 --- a/FE/src/components/DiaryModal/DiaryLoadingModal.js +++ b/FE/src/components/DiaryModal/DiaryLoadingModal.js @@ -48,6 +48,7 @@ function DiaryLoadingModal() { const DiaryLoadingModalWrapper = styled(ModalWrapper)` width: 10rem; height: 6rem; + background-color: rgba(255, 255, 255, 0.2); padding: 2rem 4rem; top: 45%; left: 50%; diff --git a/FE/src/components/DiaryModal/DiaryReadModal.js b/FE/src/components/DiaryModal/DiaryReadModal.js index 2df56d3..6f2d20a 100644 --- a/FE/src/components/DiaryModal/DiaryReadModal.js +++ b/FE/src/components/DiaryModal/DiaryReadModal.js @@ -108,20 +108,20 @@ function DiaryReadModal(props) { if (isLoading) return ( - + Loading... ); if (isError) return ( - + 에러 발생 ); return ( - + {data.title} + Loading... ); if (isError) return ( - + 에러 발생 ); return ( - + props.width || "2.5rem"}; height: 2.5rem; - background-color: rgba(255, 255, 255, 0.3); + background-color: rgba(255, 255, 255, 0.2); border-radius: 2rem; z-index: 1001; @@ -315,8 +315,7 @@ const ModalSideButton = styled.div` cursor: pointer; &:hover { - background-color: rgba(255, 255, 255, 0.5); - transition: 0.25s; + background-color: rgba(255, 255, 255, 0.3); } `; @@ -407,7 +406,7 @@ const DiaryModalTagBox = styled.div` padding: 0.5rem 1rem; border-radius: 1.5rem; border: 1px solid #ffffff; - background-color: rgba(255, 255, 255, 0.3); + background-color: rgba(255, 255, 255, 0.2); flex-shrink: 0; diff --git a/FE/src/pages/HomePage.js b/FE/src/pages/HomePage.js index 1419921..a2de8df 100644 --- a/FE/src/pages/HomePage.js +++ b/FE/src/pages/HomePage.js @@ -2,7 +2,6 @@ import React from "react"; import styled from "styled-components"; import { useRecoilValue } from "recoil"; import headerAtom from "../atoms/headerAtom"; - import homeBackground from "../assets/homeBackground.png"; import LoginModal from "../components/LoginModal/LoginModal"; import SignUpModal from "../components/SignUpModal/SignUpModal"; diff --git a/FE/src/styles/Modal/ModalWrapper.js b/FE/src/styles/Modal/ModalWrapper.js index 19ce016..cf3bc5d 100644 --- a/FE/src/styles/Modal/ModalWrapper.js +++ b/FE/src/styles/Modal/ModalWrapper.js @@ -12,7 +12,7 @@ const ModalWrapper = styled.div` z-index: 1001; width: ${(props) => props.width}; height: ${(props) => props.height}; - background-color: rgba(255, 255, 255, ${(props) => props.opacity || 0.3}); + background-color: rgba(255, 255, 255, 0.2); backdrop-filter: blur(10px); transform: translate(-50%, -50%); border-radius: ${(props) => props.borderRadius || "1rem"}; From bf9e7a0dee05034c22a88bf11f7774be8aeba7c8 Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Mon, 4 Dec 2023 23:23:16 +0900 Subject: [PATCH 05/18] =?UTF-8?q?chore:=20=EB=AA=A8=EB=8B=AC=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=8A=A4=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EB=B2=84=ED=8A=BC=20=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 일기 나열 / 분석 페이지에서 스위치 버튼 비활성화 --- FE/src/pages/StarPage.js | 64 +++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/FE/src/pages/StarPage.js b/FE/src/pages/StarPage.js index f28aeb1..4f57a07 100644 --- a/FE/src/pages/StarPage.js +++ b/FE/src/pages/StarPage.js @@ -19,7 +19,7 @@ import arrow from "../assets/arrow.svg"; import paint from "../assets/paint.svg"; function StarPage() { - const setDiaryState = useSetRecoilState(diaryAtom); + const [diaryState, setDiaryState] = useRecoilState(diaryAtom); const [starState, setStarState] = useRecoilState(starAtom); return ( @@ -42,35 +42,37 @@ function StarPage() { - { - setStarState((prev) => ({ - ...prev, - mode: "create", - drag: true, - selected: null, - })); - }} - rightEvent={() => { - setDiaryState((prev) => ({ - ...prev, - isCreate: false, - isRead: false, - isUpdate: false, - isDelete: false, - })); - setStarState((prev) => ({ - ...prev, - mode: "stella", - drag: false, - selected: null, - })); - }} - /> + {!(diaryState.isList || diaryState.isAnalysis) ? ( + { + setStarState((prev) => ({ + ...prev, + mode: "create", + drag: true, + selected: null, + })); + }} + rightEvent={() => { + setDiaryState((prev) => ({ + ...prev, + isCreate: false, + isRead: false, + isUpdate: false, + isDelete: false, + })); + setStarState((prev) => ({ + ...prev, + mode: "stella", + drag: false, + selected: null, + })); + }} + /> + ) : null} {starState.mode !== "create" ? ( - props.selected ? "#ffffff80" : "transparent"}; + props.selected ? "rgba(255, 255, 255, 0.2)" : "transparent"}; border-radius: 1.5rem; display: flex; From 83ad0e03ca960bdfbf6200600a324c594a6cf64d Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Tue, 5 Dec 2023 10:58:16 +0900 Subject: [PATCH 06/18] =?UTF-8?q?chore:=20=EC=9E=AC=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=ED=95=A8=EC=88=98=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - setDiaryData에서 setData로 변경 --- FE/src/components/DiaryModal/Calendar.js | 4 ++-- FE/src/components/DiaryModal/DiaryCreateModal.js | 2 +- FE/src/components/DiaryModal/DiaryUpdateModal.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/FE/src/components/DiaryModal/Calendar.js b/FE/src/components/DiaryModal/Calendar.js index 1be12b7..02bde35 100644 --- a/FE/src/components/DiaryModal/Calendar.js +++ b/FE/src/components/DiaryModal/Calendar.js @@ -45,7 +45,7 @@ const getColor = (index) => { }; function Calendar(props) { - const { date, setDiaryData } = props; + const { date, setData } = props; const [isCalendarOpen, setIsCalendarOpen] = React.useState(false); const [calendarDate, setCalendarDate] = React.useState(new Date()); const [selectedDate, setSelectedDate] = React.useState(new Date()); @@ -125,7 +125,7 @@ function Calendar(props) { item, ), ); - setDiaryData((prev) => ({ + setData((prev) => ({ ...prev, date: new Date( calendarDate.getFullYear(), diff --git a/FE/src/components/DiaryModal/DiaryCreateModal.js b/FE/src/components/DiaryModal/DiaryCreateModal.js index 66f4227..102bf8b 100644 --- a/FE/src/components/DiaryModal/DiaryCreateModal.js +++ b/FE/src/components/DiaryModal/DiaryCreateModal.js @@ -103,7 +103,7 @@ function DiaryCreateModal(props) { return ( - + - + Date: Tue, 5 Dec 2023 10:59:13 +0900 Subject: [PATCH 07/18] =?UTF-8?q?feat:=20=EC=9D=BC=EA=B8=B0=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=91=9C=EC=8B=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 모달 상태 관리와 삼항 연산자 활용 --- FE/src/atoms/diaryAtom.js | 1 + .../DiaryModal/DiaryAnalysisModal.js | 112 ++++++++++++++++++ FE/src/components/SideBar/SideBar.js | 36 +++++- FE/src/pages/MainPage.js | 4 +- 4 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 FE/src/components/DiaryModal/DiaryAnalysisModal.js diff --git a/FE/src/atoms/diaryAtom.js b/FE/src/atoms/diaryAtom.js index 96641a4..6237fe3 100644 --- a/FE/src/atoms/diaryAtom.js +++ b/FE/src/atoms/diaryAtom.js @@ -8,6 +8,7 @@ const diaryAtom = atom({ isUpdate: false, isDelete: false, isList: false, + isAnalysis: false, diaryUuid: "", diaryPoint: "", diaryList: [], diff --git a/FE/src/components/DiaryModal/DiaryAnalysisModal.js b/FE/src/components/DiaryModal/DiaryAnalysisModal.js new file mode 100644 index 0000000..3db74ee --- /dev/null +++ b/FE/src/components/DiaryModal/DiaryAnalysisModal.js @@ -0,0 +1,112 @@ +import React from "react"; +import styled from "styled-components"; + +function DiaryAnalysisModal() { + return ( + + + + 2023년의 감정 +
년도
+
+ 행복 +
+ + + + + 월별 통계 + + + + + + + 가장 많이 쓴 태그 순위 + + + + + + + 가장 많이 쓴 모양 순위 + + + + +
+ ); +} + +// 일기 나열 페이지와 중복되는 부분이 많아서 일단은 일기 나열 페이지를 재활용했습니다. +const DiaryAnalysisModalWrapper = styled.div` + width: 95%; + height: 97.5%; + padding: 0 2.5%; + position: absolute; + top: 2.5%; + z-index: 1001; + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 2%; +`; + +const DiaryAnalysisModalItem = styled.div` + width: ${(props) => props.width || "33%"}; + height: ${(props) => props.height || "85%"}; + background-color: rgba(255, 255, 255, 0.2); + backdrop-filter: blur(10px); + border-radius: 1rem; + + display: flex; + flex-direction: column; + align-items: center; + + font-size: 1.3rem; + color: #ffffff; + + animation: modalFadeIn 0.5s; + @keyframes modalFadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } + } +`; + +const DiaryStreak = styled.div` + width: 90%; + height: 55%; + border: 1px solid #ffffff; + display: flex; + justify-content: center; + align-items: center; +`; + +const DiaryAnalysisModalSubItemWrapper = styled.div` + width: 80%; + height: 30%; + display: flex; + justify-content: space-between; + align-items: center; + gap: 1.3%; +`; + +const DiaryAnalysisModalTitleWrapper = styled.div` + width: ${(props) => props.width || "80%"}; + height: 5rem; + display: flex; + justify-content: space-between; + align-items: center; +`; + +const DiaryAnalysisModalTitle = styled.div` + font-size: ${(props) => props.size || "1.3rem"}; +`; + +export default DiaryAnalysisModal; diff --git a/FE/src/components/SideBar/SideBar.js b/FE/src/components/SideBar/SideBar.js index 3ccb2fb..be461a5 100644 --- a/FE/src/components/SideBar/SideBar.js +++ b/FE/src/components/SideBar/SideBar.js @@ -31,6 +31,7 @@ function SideBar() { ...prev, isRead: false, isList: false, + isAnalysis: false, }; }); }} @@ -51,6 +52,7 @@ function SideBar() { isRead: false, isUpdate: false, isList: true, + isAnalysis: false, }, "", "", @@ -61,13 +63,45 @@ function SideBar() { isRead: false, isUpdate: false, isList: true, + isAnalysis: false, }; }); }} > 일기 목록 - 일기 분석 + { + setHeaderState((prev) => ({ + ...prev, + isSideBar: false, + })); + setDiaryState((prev) => { + window.history.pushState( + { + ...prev, + isCreate: false, + isRead: false, + isUpdate: false, + isList: false, + isAnalysis: true, + }, + "", + "", + ); + return { + ...prev, + isCreate: false, + isRead: false, + isUpdate: false, + isList: false, + isAnalysis: true, + }; + }); + }} + > + 일기 분석 + 환경 설정 별숲 상점 diff --git a/FE/src/pages/MainPage.js b/FE/src/pages/MainPage.js index f40a126..9436847 100644 --- a/FE/src/pages/MainPage.js +++ b/FE/src/pages/MainPage.js @@ -10,6 +10,7 @@ import userAtom from "../atoms/userAtom"; import DiaryCreateModal from "../components/DiaryModal/DiaryCreateModal"; import DiaryReadModal from "../components/DiaryModal/DiaryReadModal"; import DiaryListModal from "../components/DiaryModal/DiaryListModal"; +import DiaryAnalysisModal from "../components/DiaryModal/DiaryAnalysisModal"; import DiaryUpdateModal from "../components/DiaryModal/DiaryUpdateModal"; import DiaryLoadingModal from "../components/DiaryModal/DiaryLoadingModal"; import StarPage from "./StarPage"; @@ -23,7 +24,7 @@ function MainPage() { const { refetch } = useQuery( ["diaryList", userState.accessToken], - () => { + async () => { return fetch("http://223.130.129.145:3005/diaries", { method: "GET", headers: { @@ -140,6 +141,7 @@ function MainPage() { {diaryState.isRead ? : null} {diaryState.isUpdate ? : null} {diaryState.isList ? : null} + {diaryState.isAnalysis ? : null} {diaryState.isLoading ? : null} ) : null} From 5029cd7b2748182a273f08d472fd4270f295cf77 Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Tue, 5 Dec 2023 17:11:41 +0900 Subject: [PATCH 08/18] =?UTF-8?q?feat:=20=EA=B0=80=EC=9E=A5=20=EB=A7=8E?= =?UTF-8?q?=EC=9D=B4=20=EC=93=B4=20=ED=83=9C=EA=B7=B8=20=EB=B0=8F=20?= =?UTF-8?q?=EB=AA=A8=EC=96=91=20=EC=88=9C=EC=9C=84=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - UI 구현 및 API 연동 --- .../DiaryModal/DiaryAnalysisModal.js | 215 +++++++++++++++++- 1 file changed, 206 insertions(+), 9 deletions(-) diff --git a/FE/src/components/DiaryModal/DiaryAnalysisModal.js b/FE/src/components/DiaryModal/DiaryAnalysisModal.js index 3db74ee..ea816c4 100644 --- a/FE/src/components/DiaryModal/DiaryAnalysisModal.js +++ b/FE/src/components/DiaryModal/DiaryAnalysisModal.js @@ -1,43 +1,182 @@ import React from "react"; +import { useQuery } from "react-query"; +import { useRecoilState, useRecoilValue } from "recoil"; import styled from "styled-components"; +import userAtom from "../../atoms/userAtom"; +import shapeAtom from "../../atoms/shapeAtom"; +import { preventBeforeUnload } from "../../utils/utils"; function DiaryAnalysisModal() { + const [userState, setUserState] = useRecoilState(userAtom); + + async function getDataFn(data) { + const currentYear = new Date().getFullYear(); + return fetch(`http://223.130.129.145:3005/stat/${data}/${currentYear}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${userState.accessToken}`, + }, + }).then((res) => { + if (res.status === 200) { + return res.json(); + } + if (res.status === 403) { + alert("로그인이 만료되었습니다. 다시 로그인해주세요."); + localStorage.removeItem("accessToken"); + sessionStorage.removeItem("accessToken"); + window.removeEventListener("beforeunload", preventBeforeUnload); + window.location.href = "/"; + } + if (res.status === 401) { + return fetch("http://223.130.129.145:3005/auth/reissue", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${userState.accessToken}`, + }, + }) + .then((res) => res.json()) + .then((data) => { + if (localStorage.getItem("accessToken")) { + localStorage.setItem("accessToken", data.accessToken); + } + if (sessionStorage.getItem("accessToken")) { + sessionStorage.setItem("accessToken", data.accessToken); + } + setUserState((prev) => ({ + ...prev, + accessToken: data.accessToken, + })); + }); + } + return {}; + }); + } + + const { data: diaryAnalysisData } = useQuery(["diaryAnalysis"], async () => { + const result = await getDataFn("diaries"); + return result; + }); + + const { data: shapesRankData } = useQuery(["shapesRank"], async () => { + const result = await getDataFn("shapes-rank"); + return result; + }); + + const { data: tagsRankData } = useQuery(["tagsRank"], async () => { + const result = await getDataFn("tags-rank"); + return result; + }); + return ( - 2023년의 감정 + 2023년의 감정
년도
- 행복 + {diaryAnalysisData?.diaries}
- + 월별 통계 - + - + 가장 많이 쓴 태그 순위 - + + + + + + - + 가장 많이 쓴 모양 순위 - + + + + + +
); } +function TagRanking(props) { + const { rank, tag, count } = props; + + return ( + + + {rank}위 + + {count}회 + + + {tag} + + ); +} + +function ShapeRanking(props) { + const { rank, uuid, count } = props; + const shapeState = useRecoilValue(shapeAtom); + + return ( + + + {rank}위 + + {count}회 + + +
shape.uuid === uuid)?.data, + }} + style={{ width: "100%", height: "100%" }} + /> + + ); +} + // 일기 나열 페이지와 중복되는 부분이 많아서 일단은 일기 나열 페이지를 재활용했습니다. const DiaryAnalysisModalWrapper = styled.div` width: 95%; @@ -95,6 +234,8 @@ const DiaryAnalysisModalSubItemWrapper = styled.div` justify-content: space-between; align-items: center; gap: 1.3%; + + overflow: hidden; `; const DiaryAnalysisModalTitleWrapper = styled.div` @@ -105,8 +246,64 @@ const DiaryAnalysisModalTitleWrapper = styled.div` align-items: center; `; -const DiaryAnalysisModalTitle = styled.div` +const DiaryAnalysisModalText = styled.div` font-size: ${(props) => props.size || "1.3rem"}; + color: ${(props) => props.color || "#ffffff"}; +`; + +const DiaryAnalysisModalContentWrapper = styled.div` + width: 100%; + height: 80%; + display: flex; + flex-direction: ${(props) => props.direction || "column"}; + justify-content: center; + align-items: center; +`; + +const TagRankingWrapper = styled.div` + width: 80%; + height: 15%; + padding-bottom: 7%; + display: flex; + align-items: center; + gap: 5%; +`; + +const TagRankingTextWrapper = styled.div` + width: 5rem; + display: flex; + align-items: flex-end; + gap: 1rem; +`; + +const Tag = styled.div` + display: flex; + align-items: center; + justify-content: center; + padding: 0.5rem 1rem; + border-radius: 1rem; + background-color: rgba(255, 255, 255, 0.2); + box-sizing: border-box; + color: #ffffff; + outline: none; + white-space: nowrap; + font-size: 1rem; +`; + +const ShapeRankingWrapper = styled.div` + width: 30%; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; +`; + +const ShapeRankingTextWrapper = styled.div` + width: 100%; + display: flex; + justify-content: center; + align-items: flex-end; + gap: 10%; `; export default DiaryAnalysisModal; From 6daba44d8d160340c596891474ab82f4a9fb1bf3 Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Tue, 5 Dec 2023 17:15:16 +0900 Subject: [PATCH 09/18] =?UTF-8?q?refactor:=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=9E=AC=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - styles 폴더 내 Tag 스타일 추가 - 일기 분석 및 조회 모달에서 재사용 --- .../components/DiaryModal/DiaryAnalysisModal.js | 15 +-------------- FE/src/components/DiaryModal/DiaryReadModal.js | 16 ++-------------- FE/src/styles/Modal/Tag.js | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 28 deletions(-) create mode 100644 FE/src/styles/Modal/Tag.js diff --git a/FE/src/components/DiaryModal/DiaryAnalysisModal.js b/FE/src/components/DiaryModal/DiaryAnalysisModal.js index ea816c4..f6191ca 100644 --- a/FE/src/components/DiaryModal/DiaryAnalysisModal.js +++ b/FE/src/components/DiaryModal/DiaryAnalysisModal.js @@ -5,6 +5,7 @@ import styled from "styled-components"; import userAtom from "../../atoms/userAtom"; import shapeAtom from "../../atoms/shapeAtom"; import { preventBeforeUnload } from "../../utils/utils"; +import Tag from "../../styles/Modal/Tag"; function DiaryAnalysisModal() { const [userState, setUserState] = useRecoilState(userAtom); @@ -276,20 +277,6 @@ const TagRankingTextWrapper = styled.div` gap: 1rem; `; -const Tag = styled.div` - display: flex; - align-items: center; - justify-content: center; - padding: 0.5rem 1rem; - border-radius: 1rem; - background-color: rgba(255, 255, 255, 0.2); - box-sizing: border-box; - color: #ffffff; - outline: none; - white-space: nowrap; - font-size: 1rem; -`; - const ShapeRankingWrapper = styled.div` width: 30%; display: flex; diff --git a/FE/src/components/DiaryModal/DiaryReadModal.js b/FE/src/components/DiaryModal/DiaryReadModal.js index 6f2d20a..6b0d908 100644 --- a/FE/src/components/DiaryModal/DiaryReadModal.js +++ b/FE/src/components/DiaryModal/DiaryReadModal.js @@ -6,6 +6,7 @@ import diaryAtom from "../../atoms/diaryAtom"; import userAtom from "../../atoms/userAtom"; import shapeAtom from "../../atoms/shapeAtom"; import ModalWrapper from "../../styles/Modal/ModalWrapper"; +import Tag from "../../styles/Modal/Tag"; import DiaryDeleteModal from "./DiaryDeleteModal"; import editIcon from "../../assets/edit.svg"; import deleteIcon from "../../assets/delete.svg"; @@ -176,7 +177,7 @@ function DiaryReadModal(props) { 태그 {data.tags?.map((tag) => ( - {tag} + {tag} ))} @@ -261,19 +262,6 @@ const DiaryModalTagBar = styled.div` gap: 1.5rem; `; -const DiaryModalTag = styled.div` - display: flex; - align-items: center; - justify-content: center; - padding: 0.5rem 1rem; - border-radius: 1rem; - background-color: rgba(255, 255, 255, 0.2); - box-sizing: border-box; - color: #ffffff; - outline: none; - white-space: nowrap; -`; - const DiaryModalTagList = styled.div` width: 100%; display: flex; diff --git a/FE/src/styles/Modal/Tag.js b/FE/src/styles/Modal/Tag.js new file mode 100644 index 0000000..9837c7a --- /dev/null +++ b/FE/src/styles/Modal/Tag.js @@ -0,0 +1,17 @@ +import styled from "styled-components"; + +const Tag = styled.div` + display: flex; + align-items: center; + justify-content: center; + padding: 0.5rem 1rem; + border-radius: 1rem; + background-color: rgba(255, 255, 255, 0.2); + box-sizing: border-box; + color: #ffffff; + outline: none; + white-space: nowrap; + font-size: 1rem; +`; + +export default Tag; From 494c9e1dfd188273719f5e54140b1cf2d9469d98 Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Tue, 5 Dec 2023 17:22:27 +0900 Subject: [PATCH 10/18] =?UTF-8?q?feat:=20day.js=20=EC=84=A4=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 일기 작성 스트릭을 위한 날짜 관리 라이브러리 설 --- FE/package-lock.json | 6 ++++++ FE/package.json | 1 + 2 files changed, 7 insertions(+) diff --git a/FE/package-lock.json b/FE/package-lock.json index a19a33c..3afd0f1 100644 --- a/FE/package-lock.json +++ b/FE/package-lock.json @@ -13,6 +13,7 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "dayjs": "^1.11.10", "react": "^18.2.0", "react-dom": "^18.2.0", "react-query": "^3.39.3", @@ -7178,6 +7179,11 @@ "node": ">=10" } }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "node_modules/debounce": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", diff --git a/FE/package.json b/FE/package.json index 9c2f622..4d75c85 100644 --- a/FE/package.json +++ b/FE/package.json @@ -8,6 +8,7 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "dayjs": "^1.11.10", "react": "^18.2.0", "react-dom": "^18.2.0", "react-query": "^3.39.3", From ddf50a527b88a36f17ff41ada00d6416fd88e899 Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Tue, 5 Dec 2023 22:55:44 +0900 Subject: [PATCH 11/18] =?UTF-8?q?feat:=20=EC=9D=BC=EA=B8=B0=20=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A6=AD=20=EB=B0=8F=20=EA=B0=90=EC=A0=95=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - day.js 활용 스트릭 구현 - 서버에서 받아온 데이터를 통해 감정 분석 구현 - 연도 변경 화살표는 사용할 때마다 0.5초 이후 활성화 - 데이터가 없는 경우 표시하지 않도록 변경 --- .../DiaryModal/DiaryAnalysisModal.js | 377 +++++++++++++++--- .../components/DiaryModal/DiaryReadModal.js | 74 +--- .../EmotionIndicator/DiaryEmotionIndicator.js | 76 ++++ 3 files changed, 401 insertions(+), 126 deletions(-) create mode 100644 FE/src/components/DiaryModal/EmotionIndicator/DiaryEmotionIndicator.js diff --git a/FE/src/components/DiaryModal/DiaryAnalysisModal.js b/FE/src/components/DiaryModal/DiaryAnalysisModal.js index f6191ca..09195e7 100644 --- a/FE/src/components/DiaryModal/DiaryAnalysisModal.js +++ b/FE/src/components/DiaryModal/DiaryAnalysisModal.js @@ -1,24 +1,39 @@ -import React from "react"; +/* eslint-disable no-unused-vars */ +import React, { useEffect, useState } from "react"; import { useQuery } from "react-query"; import { useRecoilState, useRecoilValue } from "recoil"; import styled from "styled-components"; +import dayjs from "dayjs"; import userAtom from "../../atoms/userAtom"; import shapeAtom from "../../atoms/shapeAtom"; import { preventBeforeUnload } from "../../utils/utils"; +import DiaryEmotionIndicator from "./EmotionIndicator/DiaryEmotionIndicator"; import Tag from "../../styles/Modal/Tag"; +import leftIcon from "../../assets/leftIcon.svg"; +import rightIcon from "../../assets/rightIcon.svg"; function DiaryAnalysisModal() { + const [buttonDisabled, setButtonDisabled] = useState(false); + const [currentYear, setCurrentYear] = useState(dayjs("2023")); + const [emotion, setEmotion] = useState({ + positive: 0, + negative: 0, + neutral: 0, + }); + const [monthAnalysis, setMonthAnalysis] = useState(Array(12).fill(0)); const [userState, setUserState] = useRecoilState(userAtom); async function getDataFn(data) { - const currentYear = new Date().getFullYear(); - return fetch(`http://223.130.129.145:3005/stat/${data}/${currentYear}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${userState.accessToken}`, + return fetch( + `http://223.130.129.145:3005/stat/${data}/${currentYear.year()}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${userState.accessToken}`, + }, }, - }).then((res) => { + ).then((res) => { if (res.status === 200) { return res.json(); } @@ -55,29 +70,196 @@ function DiaryAnalysisModal() { }); } - const { data: diaryAnalysisData } = useQuery(["diaryAnalysis"], async () => { - const result = await getDataFn("diaries"); + const { + data: tagsRankData, + refetch: tagsRankRefetch, + isLoading: tagsRankIsLoading, + } = useQuery(["tagsRank"], async () => { + const result = await getDataFn("tags-rank"); return result; }); - const { data: shapesRankData } = useQuery(["shapesRank"], async () => { - const result = await getDataFn("shapes-rank"); - return result; - }); + const { + data: shapesRankData, + refetch: shapesRankRefetch, + isLoading: shapesRankIsLoading, + } = useQuery( + ["shapesRank"], + async () => { + const result = await getDataFn("shapes-rank"); + return result; + }, + { + onSuccess: () => { + tagsRankRefetch(); + }, + }, + ); - const { data: tagsRankData } = useQuery(["tagsRank"], async () => { - const result = await getDataFn("tags-rank"); - return result; - }); + const { + data: diaryAnalysisData, + refetch: diaryAnalysisRefetch, + isLoading: diaryAnalysisIsLoading, + } = useQuery( + ["diaryAnalysis"], + async () => { + const result = await getDataFn("diaries"); + return result; + }, + { + onSuccess: (data) => { + const newEmotion = { + positive: 0, + negative: 0, + neutral: 0, + }; + const newMonthAnalysis = Array(12).fill(0); + Object.keys(data).forEach((date) => { + const { sentiment } = data[date]; + newEmotion[sentiment] += 1; + newMonthAnalysis[dayjs(date).month()] += 1; + }); + setEmotion({ + positive: + (newEmotion.positive * 100) / + Object.values(newEmotion).reduce((acc, cur) => acc + cur, 0), + negative: + (newEmotion.negative * 100) / + Object.values(newEmotion).reduce((acc, cur) => acc + cur, 0), + neutral: + (newEmotion.neutral * 100) / + Object.values(newEmotion).reduce((acc, cur) => acc + cur, 0), + }); + setMonthAnalysis(newMonthAnalysis); + shapesRankRefetch(); + }, + }, + ); + + useEffect(() => { + diaryAnalysisRefetch(); + }, [currentYear]); return ( - 2023년의 감정 -
년도
+ + {currentYear.year()}년의 감정 + + + { + if (!buttonDisabled) { + setButtonDisabled(true); + setCurrentYear(currentYear.subtract(1, "y")); + + setTimeout(() => { + setButtonDisabled(false); + }, 500); + } + }} + /> + { + if (!buttonDisabled) { + setButtonDisabled(true); + setCurrentYear(currentYear.add(1, "y")); + + setTimeout(() => { + setButtonDisabled(false); + }, 500); + } + }} + /> +
- {diaryAnalysisData?.diaries} + {diaryAnalysisData && ( + + {["일", "월", "화", "수", "목", "금", "토"].map((day) => ( + + {day} + + ))} + { + // dayjs로 1월 1일 이 무슨 요일인지 알아내서 그거에 맞게 빈칸 넣어주기 + Array.from({ length: currentYear.day() }, (v, i) => i + 1).map( + (day) => ( + + ), + ) + } + {Array.from( + { + length: -currentYear.diff( + dayjs(currentYear).endOf("year"), + "day", + ), + }, + (v, i) => i + 1, + ).map((day) => { + let color = "#bbbbbb"; + const date = currentYear.add(day, "d").format("YYYY-MM-DD"); + if (date in diaryAnalysisData) { + const { sentiment } = diaryAnalysisData[date]; + if (sentiment === "positive") { + color = "#618cf7"; + } else if (sentiment === "negative") { + color = "#e5575b"; + } else if (sentiment === "neutral") { + color = "#a848f6"; + } + } + + return ; + })} + + )} + {diaryAnalysisData && Object.keys(diaryAnalysisData).length !== 0 ? ( + + + + 올해의 감정 상태 + + + 마우스를 올려 수치를 확인해보세요. + + + + + + + + + 긍정 + + + + + + 부정 + + + + + + 중립 + + + + + + ) : null}
@@ -94,21 +276,27 @@ function DiaryAnalysisModal() { - - - + {tagsRankData && tagsRankData?.first ? ( + + ) : null} + {tagsRankData && tagsRankData?.second ? ( + + ) : null} + {tagsRankData && tagsRankData?.third ? ( + + ) : null} @@ -118,21 +306,27 @@ function DiaryAnalysisModal() { - - - + {shapesRankData && shapesRankData?.first ? ( + + ) : null} + {shapesRankData && shapesRankData?.second ? ( + + ) : null} + {shapesRankData && shapesRankData?.third ? ( + + ) : null} @@ -208,6 +402,8 @@ const DiaryAnalysisModalItem = styled.div` font-size: 1.3rem; color: #ffffff; + overflow: auto; + animation: modalFadeIn 0.5s; @keyframes modalFadeIn { from { @@ -219,13 +415,84 @@ const DiaryAnalysisModalItem = styled.div` } `; -const DiaryStreak = styled.div` - width: 90%; - height: 55%; - border: 1px solid #ffffff; +const ArrowButtonWrapper = styled.div` + width: 5%; + height: 2rem; + display: flex; + justify-content: space-between; + align-items: center; +`; + +const ArrowButton = styled.img` + width: 1rem; + height: 1rem; + filter: ${(props) => props.filter || "invert(1)"}; + cursor: pointer; +`; + +const StreakBar = styled.div` + width: 65rem; + padding: 2% 0; + margin: 0 auto; + display: grid; + grid-auto-flow: column; + grid-template-columns: repeat(54, 1fr); + grid-template-rows: repeat(7, 1fr); + gap: 0.2rem; +`; + +const DailyStreak = styled.div` + width: 1rem; + height: 1rem; + flex-shrink: 0; + border-radius: 20%; + background-color: ${(props) => props.bg || "#bbbbbb"}; + font-size: 0.8rem; + display: flex; + justify-content: center; + align-items: center; +`; + +const EmotionBar = styled.div` + width: 85%; + height: 15%; + margin: 3rem 0; + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 2.5rem; +`; + +const EmotionBarTextWrapper = styled.div` + width: 100%; + height: 100%; + display: flex; + justify-content: flex-start; + align-items: flex-end; + gap: 1.5rem; +`; + +const EmotionBarContentWrapper = styled.div` + width: 100%; + display: flex; + justify-content: space-between; + align-items: flex-end; + gap: 1rem; +`; + +const EmotionStreakBar = styled.div` + width: 14rem; + height: 100%; + display: flex; + justify-content: space-between; + gap: 0.5rem; +`; + +const EmotionStreak = styled.div` display: flex; justify-content: center; align-items: center; + gap: 0.5rem; `; const DiaryAnalysisModalSubItemWrapper = styled.div` diff --git a/FE/src/components/DiaryModal/DiaryReadModal.js b/FE/src/components/DiaryModal/DiaryReadModal.js index 6b0d908..42150b3 100644 --- a/FE/src/components/DiaryModal/DiaryReadModal.js +++ b/FE/src/components/DiaryModal/DiaryReadModal.js @@ -8,40 +8,9 @@ import shapeAtom from "../../atoms/shapeAtom"; import ModalWrapper from "../../styles/Modal/ModalWrapper"; import Tag from "../../styles/Modal/Tag"; import DiaryDeleteModal from "./DiaryDeleteModal"; +import DiaryEmotionIndicator from "./EmotionIndicator/DiaryEmotionIndicator"; import editIcon from "../../assets/edit.svg"; import deleteIcon from "../../assets/delete.svg"; -import indicatorArrowIcon from "../../assets/indicator-arrow.svg"; - -function DiaryModalEmotionIndicator({ emotion }) { - return ( - - - - - arrow - - - - arrow - - - - - 긍정 {emotion.positive}% - 중립 {emotion.neutral}% - 부정 {emotion.negative}% - - - ); -} async function getDiary(accessToken, diaryUuid, setUserState) { return fetch(`http://223.130.129.145:3005/diaries/${diaryUuid}`, { @@ -182,12 +151,13 @@ function DiaryReadModal(props) { -
props.ratio}; - height: 100%; - background-color: ${(props) => props.color}; -`; - -const EmotionIndicatorArrow = styled.div` - display: flex; - justify-content: center; - width: 0; - height: 4rem; -`; - -const EmotionTextWrapper = styled.div` - display: flex; - flex-direction: column; - gap: 0.5rem; -`; - -const EmotionText = styled.div` - font-size: 0.9rem; -`; - export default DiaryReadModal; diff --git a/FE/src/components/DiaryModal/EmotionIndicator/DiaryEmotionIndicator.js b/FE/src/components/DiaryModal/EmotionIndicator/DiaryEmotionIndicator.js new file mode 100644 index 0000000..796b0fe --- /dev/null +++ b/FE/src/components/DiaryModal/EmotionIndicator/DiaryEmotionIndicator.js @@ -0,0 +1,76 @@ +import React from "react"; +import styled from "styled-components"; +import indicatorArrowIcon from "../../../assets/indicator-arrow.svg"; + +function DiaryEmotionIndicator({ emotion, width, text }) { + return ( + + + + + arrow + + + + arrow + + + + {text === true ? ( + + 긍정 {emotion.positive}% + 중립 {emotion.neutral}% + 부정 {emotion.negative}% + + ) : null} + + ); +} + +const EmotionIndicatorWrapper = styled.div` + width: 70%; + display: flex; + align-items: center; + gap: 1.5rem; +`; + +const EmotionIndicatorBar = styled.div` + width: ${(props) => props.width || "20rem"}; + height: 1rem; + display: flex; + align-items: center; + justify-content: space-between; +`; + +const EmotionIndicator = styled.div` + width: ${(props) => props.ratio}; + height: 100%; + background-color: ${(props) => props.color}; +`; + +const EmotionIndicatorArrow = styled.div` + display: flex; + justify-content: center; + width: 0; + height: 4rem; +`; + +const EmotionTextWrapper = styled.div` + display: flex; + flex-direction: column; + gap: 0.5rem; +`; + +const EmotionText = styled.div` + font-size: 0.9rem; +`; + +export default DiaryEmotionIndicator; From 12e7a13c1f6f4d11cbd350de1c87e573ba4b715a Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Tue, 5 Dec 2023 23:57:59 +0900 Subject: [PATCH 12/18] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=B3=80=EC=88=98=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DiaryModal/DiaryAnalysisModal.js | 71 +++++++++++++------ 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/FE/src/components/DiaryModal/DiaryAnalysisModal.js b/FE/src/components/DiaryModal/DiaryAnalysisModal.js index 09195e7..57b68c8 100644 --- a/FE/src/components/DiaryModal/DiaryAnalysisModal.js +++ b/FE/src/components/DiaryModal/DiaryAnalysisModal.js @@ -1,4 +1,3 @@ -/* eslint-disable no-unused-vars */ import React, { useEffect, useState } from "react"; import { useQuery } from "react-query"; import { useRecoilState, useRecoilValue } from "recoil"; @@ -70,20 +69,15 @@ function DiaryAnalysisModal() { }); } - const { - data: tagsRankData, - refetch: tagsRankRefetch, - isLoading: tagsRankIsLoading, - } = useQuery(["tagsRank"], async () => { - const result = await getDataFn("tags-rank"); - return result; - }); + const { data: tagsRankData, refetch: tagsRankRefetch } = useQuery( + ["tagsRank"], + async () => { + const result = await getDataFn("tags-rank"); + return result; + }, + ); - const { - data: shapesRankData, - refetch: shapesRankRefetch, - isLoading: shapesRankIsLoading, - } = useQuery( + const { data: shapesRankData, refetch: shapesRankRefetch } = useQuery( ["shapesRank"], async () => { const result = await getDataFn("shapes-rank"); @@ -96,11 +90,7 @@ function DiaryAnalysisModal() { }, ); - const { - data: diaryAnalysisData, - refetch: diaryAnalysisRefetch, - isLoading: diaryAnalysisIsLoading, - } = useQuery( + const { data: diaryAnalysisData, refetch: diaryAnalysisRefetch } = useQuery( ["diaryAnalysis"], async () => { const result = await getDataFn("diaries"); @@ -152,7 +142,7 @@ function DiaryAnalysisModal() { src={leftIcon} alt='left' filter={buttonDisabled ? "invert(0.5) grayscale(1)" : "invert(1)"} - onClick={(e) => { + onClick={() => { if (!buttonDisabled) { setButtonDisabled(true); setCurrentYear(currentYear.subtract(1, "y")); @@ -167,7 +157,7 @@ function DiaryAnalysisModal() { src={rightIcon} alt='right' filter={buttonDisabled ? "invert(0.5) grayscale(1)" : "invert(1)"} - onClick={(e) => { + onClick={() => { if (!buttonDisabled) { setButtonDisabled(true); setCurrentYear(currentYear.add(1, "y")); @@ -268,6 +258,19 @@ function DiaryAnalysisModal() { 월별 통계 + + {monthAnalysis.map((month, index) => ( + + + + {index + 1} + + + ))} + @@ -528,6 +531,32 @@ const DiaryAnalysisModalContentWrapper = styled.div` align-items: center; `; +const MonthGraphBar = styled.div` + width: 85%; + flex-grow: 0.8; + display: flex; + justify-content: space-between; + align-items: flex-end; + gap: 5%; +`; + +const MonthGraphWrapper = styled.div` + width: 0.7rem; + height: 100%; + display: flex; + flex-direction: column; + justify-content: flex-end; + align-items: center; + gap: 10%; +`; + +const MonthGraph = styled.div` + width: 100%; + height: ${(props) => props.height || "100%"}; + background-color: #bbbbbb; + border-radius: 0.2rem; +`; + const TagRankingWrapper = styled.div` width: 80%; height: 15%; From c6e0566f674231b8fb0c88ca886b8cbd6430d828 Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Wed, 6 Dec 2023 10:46:19 +0900 Subject: [PATCH 13/18] =?UTF-8?q?chore:=20CSS=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 태그 순위 보기가 3등까지 표시되면 높이가 변하는 현상 해결 --- FE/src/components/DiaryModal/DiaryAnalysisModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FE/src/components/DiaryModal/DiaryAnalysisModal.js b/FE/src/components/DiaryModal/DiaryAnalysisModal.js index 57b68c8..dd521a5 100644 --- a/FE/src/components/DiaryModal/DiaryAnalysisModal.js +++ b/FE/src/components/DiaryModal/DiaryAnalysisModal.js @@ -524,7 +524,7 @@ const DiaryAnalysisModalText = styled.div` const DiaryAnalysisModalContentWrapper = styled.div` width: 100%; - height: 80%; + height: 65%; display: flex; flex-direction: ${(props) => props.direction || "column"}; justify-content: center; From 2f10fd4fc74b3ab88ba7d8e17514a0ee6aeb4485 Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Wed, 6 Dec 2023 10:52:03 +0900 Subject: [PATCH 14/18] =?UTF-8?q?chore:=20CSS=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 그래프 굵기를 조금 더 굵게 수정 --- FE/src/components/DiaryModal/DiaryAnalysisModal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FE/src/components/DiaryModal/DiaryAnalysisModal.js b/FE/src/components/DiaryModal/DiaryAnalysisModal.js index dd521a5..f41038c 100644 --- a/FE/src/components/DiaryModal/DiaryAnalysisModal.js +++ b/FE/src/components/DiaryModal/DiaryAnalysisModal.js @@ -551,7 +551,7 @@ const MonthGraphWrapper = styled.div` `; const MonthGraph = styled.div` - width: 100%; + width: 120%; height: ${(props) => props.height || "100%"}; background-color: #bbbbbb; border-radius: 0.2rem; From c1814243e5a1e10ec75b358322b6ac793e107b98 Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Wed, 6 Dec 2023 11:02:18 +0900 Subject: [PATCH 15/18] =?UTF-8?q?feat:=20=EC=B4=9D=20=EC=9D=BC=EA=B8=B0=20?= =?UTF-8?q?=EC=88=98=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 월별 통계 우상단 총 일기 수 출력하도록 작성 --- FE/src/components/DiaryModal/DiaryAnalysisModal.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/FE/src/components/DiaryModal/DiaryAnalysisModal.js b/FE/src/components/DiaryModal/DiaryAnalysisModal.js index f41038c..ec0cfee 100644 --- a/FE/src/components/DiaryModal/DiaryAnalysisModal.js +++ b/FE/src/components/DiaryModal/DiaryAnalysisModal.js @@ -257,6 +257,14 @@ function DiaryAnalysisModal() { 월별 통계 + + 총 일기 수{" "} + {Object.values(diaryAnalysisData).reduce( + (acc, cur) => acc + cur.count, + 0, + )} + 개 + {monthAnalysis.map((month, index) => ( From e3117ea97b1b5445ac8f71b1a506cae59cff8909 Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Wed, 6 Dec 2023 15:24:18 +0900 Subject: [PATCH 16/18] =?UTF-8?q?feat:=20=EA=B0=90=EC=A0=95=20=EB=B0=94=20?= =?UTF-8?q?=ED=98=B8=EB=B2=84=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 호버 시 감정 퍼센트 표시 --- FE/src/assets/picket.svg | 3 ++ .../DiaryModal/DiaryAnalysisModal.js | 10 +++-- .../EmotionIndicator/DiaryEmotionIndicator.js | 45 ++++++++++++++++--- .../EmotionIndicator/EmotionPicket.js | 36 +++++++++++++++ 4 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 FE/src/assets/picket.svg create mode 100644 FE/src/components/DiaryModal/EmotionIndicator/EmotionPicket.js diff --git a/FE/src/assets/picket.svg b/FE/src/assets/picket.svg new file mode 100644 index 0000000..ecc07e6 --- /dev/null +++ b/FE/src/assets/picket.svg @@ -0,0 +1,3 @@ + + + diff --git a/FE/src/components/DiaryModal/DiaryAnalysisModal.js b/FE/src/components/DiaryModal/DiaryAnalysisModal.js index ec0cfee..acc9278 100644 --- a/FE/src/components/DiaryModal/DiaryAnalysisModal.js +++ b/FE/src/components/DiaryModal/DiaryAnalysisModal.js @@ -259,10 +259,12 @@ function DiaryAnalysisModal() { 총 일기 수{" "} - {Object.values(diaryAnalysisData).reduce( - (acc, cur) => acc + cur.count, - 0, - )} + {diaryAnalysisData + ? Object.values(diaryAnalysisData).reduce( + (acc, cur) => acc + cur.count, + 0, + ) + : 0} 개 diff --git a/FE/src/components/DiaryModal/EmotionIndicator/DiaryEmotionIndicator.js b/FE/src/components/DiaryModal/EmotionIndicator/DiaryEmotionIndicator.js index 796b0fe..cea886d 100644 --- a/FE/src/components/DiaryModal/EmotionIndicator/DiaryEmotionIndicator.js +++ b/FE/src/components/DiaryModal/EmotionIndicator/DiaryEmotionIndicator.js @@ -1,12 +1,23 @@ -import React from "react"; +import React, { useState } from "react"; import styled from "styled-components"; +import EmotionPicket from "./EmotionPicket"; import indicatorArrowIcon from "../../../assets/indicator-arrow.svg"; function DiaryEmotionIndicator({ emotion, width, text }) { + const [isHover, setIsHover] = useState(""); return ( - + setIsHover("positive")} + onMouseLeave={() => setIsHover("")} + > + {isHover === "positive" ? ( + + ) : null} + - + setIsHover("neutral")} + onMouseLeave={() => setIsHover("")} + > + {isHover === "neutral" ? ( + + ) : null} + - + setIsHover("negative")} + onMouseLeave={() => setIsHover("")} + > + {isHover === "negative" ? ( + + ) : null} + {text === true ? ( - 긍정 {emotion.positive}% - 중립 {emotion.neutral}% - 부정 {emotion.negative}% + 긍정 {emotion.positive.toFixed(1)}% + 중립 {emotion.neutral.toFixed(1)}% + 부정 {emotion.negative.toFixed(1)}% ) : null} @@ -54,6 +83,8 @@ const EmotionIndicator = styled.div` width: ${(props) => props.ratio}; height: 100%; background-color: ${(props) => props.color}; + display: flex; + justify-content: center; `; const EmotionIndicatorArrow = styled.div` diff --git a/FE/src/components/DiaryModal/EmotionIndicator/EmotionPicket.js b/FE/src/components/DiaryModal/EmotionIndicator/EmotionPicket.js new file mode 100644 index 0000000..5e9fc1f --- /dev/null +++ b/FE/src/components/DiaryModal/EmotionIndicator/EmotionPicket.js @@ -0,0 +1,36 @@ +import React from "react"; +import styled from "styled-components"; +import picket from "../../../assets/picket.svg"; + +function EmotionPicket({ percent }) { + return ( + + {percent.toFixed(1)}% + + ); +} + +const EmotionPicketWrapper = styled.div` + width: 5rem; + height: 3.3rem; + position: float; + display: flex; + justify-content: center; + align-items: flex-end; + font-size: 1rem; + color: #ffffff; +`; + +const Picket = styled.div` + width: 100%; + height: 3rem; + background-image: url(${picket}); + background-position: center; + background-repeat: no-repeat; + display: flex; + justify-content: center; + align-items: flex-end; + line-height: 2.3rem; +`; + +export default EmotionPicket; From 24e60d17e7e5e4bf4593bbf974463f3570070880 Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Wed, 6 Dec 2023 15:54:51 +0900 Subject: [PATCH 17/18] =?UTF-8?q?chore:=20styled-components=20=EA=B2=BD?= =?UTF-8?q?=EA=B3=A0=20=EB=A9=94=EC=84=B8=EC=A7=80=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 각 요소에 고유 키값 부여 - 중복 CSS 제거 --- .../DiaryModal/DiaryAnalysisModal.js | 30 ++++++++++++------- .../components/DiaryModal/DiaryReadModal.js | 6 ++-- .../components/DiaryModal/DiaryUpdateModal.js | 6 ++-- .../EmotionIndicator/DiaryEmotionIndicator.js | 8 ++--- .../EmotionIndicator/EmotionPicket.js | 2 +- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/FE/src/components/DiaryModal/DiaryAnalysisModal.js b/FE/src/components/DiaryModal/DiaryAnalysisModal.js index acc9278..763828e 100644 --- a/FE/src/components/DiaryModal/DiaryAnalysisModal.js +++ b/FE/src/components/DiaryModal/DiaryAnalysisModal.js @@ -173,7 +173,7 @@ function DiaryAnalysisModal() { {diaryAnalysisData && ( {["일", "월", "화", "수", "목", "금", "토"].map((day) => ( - + {day} ))} @@ -181,7 +181,7 @@ function DiaryAnalysisModal() { // dayjs로 1월 1일 이 무슨 요일인지 알아내서 그거에 맞게 빈칸 넣어주기 Array.from({ length: currentYear.day() }, (v, i) => i + 1).map( (day) => ( - + ), ) } @@ -207,7 +207,7 @@ function DiaryAnalysisModal() { } } - return ; + return ; })} )} @@ -229,19 +229,19 @@ function DiaryAnalysisModal() { /> - + 긍정 - + 부정 - + 중립 @@ -270,12 +270,16 @@ function DiaryAnalysisModal() { {monthAnalysis.map((month, index) => ( - + - + {index + 1} @@ -291,6 +295,7 @@ function DiaryAnalysisModal() { {tagsRankData && tagsRankData?.first ? ( {shapesRankData && shapesRankData?.first ? ( props.bg || "#bbbbbb"}; + background-color: ${(props) => props.$bg || "#bbbbbb"}; font-size: 0.8rem; display: flex; justify-content: center; diff --git a/FE/src/components/DiaryModal/DiaryReadModal.js b/FE/src/components/DiaryModal/DiaryReadModal.js index f03d49c..2b80c45 100644 --- a/FE/src/components/DiaryModal/DiaryReadModal.js +++ b/FE/src/components/DiaryModal/DiaryReadModal.js @@ -89,20 +89,20 @@ function DiaryReadModal(props) { if (isLoading) return ( - + Loading... ); if (isError) return ( - + 에러 발생 ); return ( - + {data.title} + Loading... ); if (isError) return ( - + 에러 발생 ); return ( - + setIsHover("positive")} onMouseLeave={() => setIsHover("")} @@ -26,7 +26,7 @@ function DiaryEmotionIndicator({ emotion, width, text }) { /> setIsHover("neutral")} onMouseLeave={() => setIsHover("")} @@ -43,7 +43,7 @@ function DiaryEmotionIndicator({ emotion, width, text }) { /> setIsHover("negative")} onMouseLeave={() => setIsHover("")} @@ -80,7 +80,7 @@ const EmotionIndicatorBar = styled.div` `; const EmotionIndicator = styled.div` - width: ${(props) => props.ratio}; + width: ${(props) => props.$ratio}; height: 100%; background-color: ${(props) => props.color}; display: flex; diff --git a/FE/src/components/DiaryModal/EmotionIndicator/EmotionPicket.js b/FE/src/components/DiaryModal/EmotionIndicator/EmotionPicket.js index 5e9fc1f..4595d62 100644 --- a/FE/src/components/DiaryModal/EmotionIndicator/EmotionPicket.js +++ b/FE/src/components/DiaryModal/EmotionIndicator/EmotionPicket.js @@ -5,7 +5,7 @@ import picket from "../../../assets/picket.svg"; function EmotionPicket({ percent }) { return ( - {percent.toFixed(1)}% + {percent.toFixed(1)}% ); } From fa52b2faba62df4faf877d1ef74e1ca7370b6d3d Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Wed, 6 Dec 2023 16:08:59 +0900 Subject: [PATCH 18/18] =?UTF-8?q?chore:=20useState=20=EC=82=AC=EC=9A=A9=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 직접 import 해서 사용 --- FE/src/components/DiaryModal/Calendar.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FE/src/components/DiaryModal/Calendar.js b/FE/src/components/DiaryModal/Calendar.js index 02bde35..813ae3a 100644 --- a/FE/src/components/DiaryModal/Calendar.js +++ b/FE/src/components/DiaryModal/Calendar.js @@ -1,4 +1,4 @@ -import React, { useLayoutEffect } from "react"; +import React, { useState, useLayoutEffect } from "react"; import styled from "styled-components"; import toggleIcon from "../../assets/toggleIcon.svg"; import leftIcon from "../../assets/leftIcon.svg"; @@ -46,9 +46,9 @@ const getColor = (index) => { function Calendar(props) { const { date, setData } = props; - const [isCalendarOpen, setIsCalendarOpen] = React.useState(false); - const [calendarDate, setCalendarDate] = React.useState(new Date()); - const [selectedDate, setSelectedDate] = React.useState(new Date()); + const [isCalendarOpen, setIsCalendarOpen] = useState(false); + const [calendarDate, setCalendarDate] = useState(new Date()); + const [selectedDate, setSelectedDate] = useState(new Date()); useLayoutEffect(() => { setCalendarDate(date);