From 569f2e0de147f1ace624e1431ad3ade45fec9709 Mon Sep 17 00:00:00 2001 From: JeonYumin <40783675+JeonYumin94@users.noreply.github.com> Date: Sun, 28 Jul 2024 20:46:52 +0900 Subject: [PATCH] =?UTF-8?q?FE-37=20:sparkles:=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B0=90=EC=A0=95=EC=B0=A8?= =?UTF-8?q?=ED=8A=B8=20(#89)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FE-37 :recycle: 월별 감정로그 조회 코드 리팩토링 * FE-37 :sparkles: 감정 차트 * FE-37 :hammer: 상수 컨벤션 수정 --- src/hooks/useCalendar.ts | 9 +- src/pageLayout/MypageLayout/MyPageLayout.tsx | 5 +- src/types/emotion.ts | 7 ++ src/user/ui-profile/Calendar.tsx | 34 ++----- src/user/ui-profile/CalendarHeader.tsx | 6 +- src/user/ui-profile/Chart.tsx | 96 ++++++++++++++++++++ src/user/ui-profile/EmotionMonthlyLogs.tsx | 40 ++++++++ src/user/utill/constants.ts | 10 +- 8 files changed, 162 insertions(+), 45 deletions(-) create mode 100644 src/user/ui-profile/Chart.tsx create mode 100644 src/user/ui-profile/EmotionMonthlyLogs.tsx diff --git a/src/hooks/useCalendar.ts b/src/hooks/useCalendar.ts index 1687f326..44afc6a6 100644 --- a/src/hooks/useCalendar.ts +++ b/src/hooks/useCalendar.ts @@ -1,11 +1,8 @@ import { getDaysInMonth } from 'date-fns'; -import React, { useState } from 'react'; import { CALENDAR_LENGTH, DAY_OF_WEEK } from '../user/utill/constants'; interface CalendarData { weekCalendarList: number[][]; // 주별 날짜 리스트 - currentDate: Date; // 현재 날짜 - setCurrentDate: React.Dispatch>; // 현재 날짜를 설정하는 함수 } // 이전 달의 날짜를 계산하는 함수 @@ -21,9 +18,7 @@ const getNextDays = (currentDayList: number[], prevDayList: number[]): number[] return Array.from({ length: Math.max(nextDayCount, 0) }, (_, index) => index + 1); }; -const useCalendar = (): CalendarData => { - const [currentDate, setCurrentDate] = useState(new Date()); // 현재 날짜를 상태로 관리 - +const useCalendar = (currentDate: Date): CalendarData => { // 현재 월의 총 날짜 수를 가져옴 const totalMonthDays = getDaysInMonth(currentDate); @@ -66,8 +61,6 @@ const useCalendar = (): CalendarData => { // 캘린더 정보를 반환 return { weekCalendarList, // 주별 날짜 리스트 - currentDate, - setCurrentDate, }; }; diff --git a/src/pageLayout/MypageLayout/MyPageLayout.tsx b/src/pageLayout/MypageLayout/MyPageLayout.tsx index cac3bea6..12627ade 100644 --- a/src/pageLayout/MypageLayout/MyPageLayout.tsx +++ b/src/pageLayout/MypageLayout/MyPageLayout.tsx @@ -1,7 +1,7 @@ import Header from '@/components/Header/Header'; import { useMeQuery } from '@/hooks/userQueryHooks'; import UserInfo from '@/types/user'; -import Calendar from '@/user/ui-profile/Calendar'; +import EmotionMonthlyLogs from '@/user/ui-profile/EmotionMonthlyLogs'; import Profile from '@/user/ui-profile/Profile'; import { useRouter } from 'next/navigation'; @@ -31,8 +31,7 @@ export default function MyPageLayout() {
오늘의 감정
- -
감정차트
+
diff --git a/src/types/emotion.ts b/src/types/emotion.ts index 4bdcbd07..a5dfb576 100644 --- a/src/types/emotion.ts +++ b/src/types/emotion.ts @@ -6,3 +6,10 @@ export interface Emotion { // 감정 로그 타입 지정 export type EmotionType = 'MOVED' | 'HAPPY' | 'WORRIED' | 'SAD' | 'ANGRY'; + +export interface EmotionLog { + id: number; + userId: number; + emotion: EmotionType; + createdAt: Date; +} diff --git a/src/user/ui-profile/Calendar.tsx b/src/user/ui-profile/Calendar.tsx index e9f972b5..ff4ec107 100644 --- a/src/user/ui-profile/Calendar.tsx +++ b/src/user/ui-profile/Calendar.tsx @@ -1,32 +1,23 @@ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import Image from 'next/image'; import { subMonths } from 'date-fns'; -import useMonthlyEmotionLogs from '@/hooks/useGetEmotion'; -import { Emotion, EmotionType } from '@/types/emotion'; +import { EmotionLog, EmotionType } from '@/types/emotion'; import useCalendar from '../../hooks/useCalendar'; import { DAY_LIST, DATE_MONTH_FIXER, iconPaths } from '../utill/constants'; import CalendarHeader from './CalendarHeader'; interface CalendarProps { - userId: number; + currentDate: Date; // 현재 날짜 + setCurrentDate: React.Dispatch>; // 현재 날짜를 설정하는 함수 + monthlyEmotionLogs: EmotionLog[]; } -export default function Calendar({ userId }: CalendarProps) { +export default function Calendar({ currentDate, setCurrentDate, monthlyEmotionLogs }: CalendarProps) { // 캘린더 함수 호출 - const { weekCalendarList, currentDate, setCurrentDate } = useCalendar(); + const { weekCalendarList } = useCalendar(currentDate); // 감정 필터 const [selectedEmotion, setSelectedEmotion] = useState(null); - // 감정 달력 객체 상태 추가 - const [emotionRequest, setEmotionRequest] = useState({ - userId, - year: currentDate.getFullYear(), - month: currentDate.getMonth() + 1, - }); - - // 월별 감정 로그 조회 - const { data: monthlyEmotionLogs = [] } = useMonthlyEmotionLogs(emotionRequest); - // 달력에 출력할 수 있게 매핑 const emotionMap: Record = Array.isArray(monthlyEmotionLogs) ? monthlyEmotionLogs.reduce>((acc, log) => { @@ -37,15 +28,6 @@ export default function Calendar({ userId }: CalendarProps) { }, {}) : {}; - // '월'이 변경될 때마다 request 업데이트 - useEffect(() => { - setEmotionRequest({ - userId, - year: currentDate.getFullYear(), - month: currentDate.getMonth() + 1, - }); - }, [currentDate]); - // 이전 달 클릭 const handlePrevMonth = () => setCurrentDate((prevDate) => subMonths(prevDate, DATE_MONTH_FIXER)); // 다음 달 클릭 @@ -86,7 +68,7 @@ export default function Calendar({ userId }: CalendarProps) { const isToday = day === currentDate.getDate() && currentDate.getMonth() === new Date().getMonth() && currentDate.getFullYear() === new Date().getFullYear(); const dateString = `${currentDate.getFullYear()}-${String(currentDate.getMonth() + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; const emotion: EmotionType = filteredEmotionMap[dateString]; // 날짜에 해당하는 감정 가져오기 - const iconPath = iconPaths[emotion]; // 해당 감정 아이콘 출력 + const iconPath = emotion && iconPaths[emotion] ? iconPaths[emotion].path : '/icon/BW/SmileFaceBWIcon.svg'; return (
- {Object.entries(iconPaths).map(([emotionKey, iconPath]) => ( + {Object.entries(iconPaths).map(([emotionKey, { path, name }]) => ( ))} diff --git a/src/user/ui-profile/Chart.tsx b/src/user/ui-profile/Chart.tsx new file mode 100644 index 00000000..c84e3276 --- /dev/null +++ b/src/user/ui-profile/Chart.tsx @@ -0,0 +1,96 @@ +import { EmotionLog, EmotionType } from '@/types/emotion'; +import Image from 'next/image'; +import { iconPaths } from '../utill/constants'; + +interface ChartProps { + monthlyEmotionLogs: EmotionLog[]; +} + +export default function Chart({ monthlyEmotionLogs }: ChartProps) { + // 감정별 빈도수 계산 + const emotionCounts = monthlyEmotionLogs.reduce( + (count, log) => { + const { emotion } = log; + return { + ...count, // 기존의 count를 복사 + [emotion]: (count[emotion] || 0) + 1, // 현재 감정의 개수 증가 + }; + }, + {} as Record, + ); + + // 감정 종류 및 총 감정 수 계산 + const TOTAL_COUNT = monthlyEmotionLogs.length; + const EMOTIONS: EmotionType[] = ['MOVED', 'HAPPY', 'WORRIED', 'SAD', 'ANGRY']; + const RADIUS = 90; // 원의 반지름 + const CIRCUMFERENCE = 2 * Math.PI * RADIUS; + + // 가장 많이 나타나는 감정 찾기 + const maxEmotion = EMOTIONS.reduce((max, emotion) => (emotionCounts[emotion] > emotionCounts[max] ? emotion : max), EMOTIONS[0]); + + // 원형 차트의 각 감정에 대한 strokeDasharray와 strokeDashoffset 계산 + let offset = 0; + + return ( +
+

감정 차트

+
+
+ + + {EMOTIONS.map((emotion) => { + const count = emotionCounts[emotion] || 0; + const percentage = TOTAL_COUNT > 0 ? count / TOTAL_COUNT : 0; // 0으로 나누기 방지 + const strokeDasharray = `${CIRCUMFERENCE * percentage} ${CIRCUMFERENCE * (1 - percentage)}`; + + // 색상 설정 + let strokeColor; + switch (emotion) { + case 'HAPPY': + strokeColor = '#FBC85B'; + break; + case 'SAD': + strokeColor = '#E3E9F1'; + break; + case 'WORRIED': + strokeColor = '#C7D1E0'; + break; + case 'ANGRY': + strokeColor = '#EFF3F8'; + break; + default: + strokeColor = '#48BB98'; + } + + const circle = ; + + offset += CIRCUMFERENCE * percentage; // 다음 원을 위한 offset 업데이트 + return circle; + })} + + {/* 중앙에 가장 많이 나타나는 감정 출력 */} +
+ 감정 +

{iconPaths[maxEmotion].name}

+
+
+
+
+ {EMOTIONS.map((emotion) => { + const count = emotionCounts[emotion] || 0; + const percentage = TOTAL_COUNT > 0 ? Math.floor((count / TOTAL_COUNT) * 100) : 0; // 퍼센트 계산 및 소수점 버리기 + + return ( +
+

+ 감정 +

{percentage}%

+
+ ); + })} +
+
+
+
+ ); +} diff --git a/src/user/ui-profile/EmotionMonthlyLogs.tsx b/src/user/ui-profile/EmotionMonthlyLogs.tsx new file mode 100644 index 00000000..ee1f9a2c --- /dev/null +++ b/src/user/ui-profile/EmotionMonthlyLogs.tsx @@ -0,0 +1,40 @@ +import useMonthlyEmotionLogs from '@/hooks/useGetEmotion'; +import { Emotion } from '@/types/emotion'; +import { useEffect, useState } from 'react'; +import Calendar from './Calendar'; +import Chart from './Chart'; + +interface EmotionMonthlyLogsProps { + userId: number; +} + +export default function EmotionMonthlyLogs({ userId }: EmotionMonthlyLogsProps) { + // 현재 날짜를 상태로 관리 + const [currentDate, setCurrentDate] = useState(new Date()); + + // 감정 달력 객체 상태 추가 + const [emotionRequest, setEmotionRequest] = useState({ + userId, + year: currentDate.getFullYear(), + month: currentDate.getMonth() + 1, + }); + + // '월'이 변경될 때마다 request 업데이트 + useEffect(() => { + setEmotionRequest({ + userId, + year: currentDate.getFullYear(), + month: currentDate.getMonth() + 1, + }); + }, [currentDate]); + + // 월별 감정 로그 조회 + const { data: monthlyEmotionLogs = [] } = useMonthlyEmotionLogs(emotionRequest); + + return ( + <> + + + + ); +} diff --git a/src/user/utill/constants.ts b/src/user/utill/constants.ts index 30981ea0..9ff423c8 100644 --- a/src/user/utill/constants.ts +++ b/src/user/utill/constants.ts @@ -12,9 +12,9 @@ export const DEFAULT_TRASH_VALUE = -1; // 기본값 설정 (필요에 따라 사 // 아이콘 파일 경로 매핑 export const iconPaths = { - MOVED: '/icon/Color/HeartFaceColorIcon.svg', - HAPPY: '/icon/Color/SmileFaceColorIcon.svg', - WORRIED: '/icon/Color/ThinkFaceColorIcon.svg', - SAD: '/icon/Color/SadFaceColorIcon.svg', - ANGRY: '/icon/Color/AngryFaceColorIcon.svg', + MOVED: { path: '/icon/Color/HeartFaceColorIcon.svg', name: '기쁨', color: 'bg-illust-green' }, + HAPPY: { path: '/icon/Color/SmileFaceColorIcon.svg', name: '감동', color: 'bg-illust-yellow' }, + WORRIED: { path: '/icon/Color/ThinkFaceColorIcon.svg', name: '고민', color: 'bg-sub-gray_1' }, + SAD: { path: '/icon/Color/SadFaceColorIcon.svg', name: '슬픔', color: 'bg-sub-gray_2' }, + ANGRY: { path: '/icon/Color/AngryFaceColorIcon.svg', name: '분노', color: 'bg-sub-gray_3' }, };