Skip to content

Commit

Permalink
FE-36 ✨ 마이페이지 캘린더 출력 함수 (#58)
Browse files Browse the repository at this point in the history
* FE-36 💄 감정달력 UI

* FE-36 ✨ 캘린더 함수 생성
  • Loading branch information
JeonYumin94 authored Jul 23, 2024
1 parent d24ecd3 commit 6d5e64b
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 4 deletions.
10 changes: 10 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"axios": "^1.7.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"date-fns": "^3.6.0",
"formik": "^2.4.6",
"lucide-react": "^0.402.0",
"next": "14.2.4",
Expand Down
5 changes: 5 additions & 0 deletions public/icon/arrow-bottom-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions public/icon/arrow-right-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
74 changes: 74 additions & 0 deletions src/hooks/useCalendar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
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<React.SetStateAction<Date>>; // 현재 날짜를 설정하는 함수
}

// 이전 달의 날짜를 계산하는 함수
const getPreviousDays = (firstDayOfCurrentMonth: Date, totalPrevMonthDays: number): number[] =>
// 현재 월의 첫 번째 날의 요일을 기준으로 이전 달의 날짜를 배열로 반환
Array.from({ length: firstDayOfCurrentMonth.getDay() }, (_, index) => totalPrevMonthDays - firstDayOfCurrentMonth.getDay() + index + 1);
// 현재 월의 날짜를 배열로 반환하는 함수
const getCurrentDays = (totalMonthDays: number): number[] => Array.from({ length: totalMonthDays }, (_, i) => i + 1); // 1부터 totalMonthDays까지의 배열 생성
// 다음 달의 날짜를 계산하는 함수
const getNextDays = (currentDayList: number[], prevDayList: number[]): number[] => {
// 다음 달의 날짜 수를 계산하여 배열로 반환
const nextDayCount = CALENDAR_LENGTH - currentDayList.length - prevDayList.length;
return Array.from({ length: Math.max(nextDayCount, 0) }, (_, index) => index + 1);
};

const useCalendar = (): CalendarData => {
const [currentDate, setCurrentDate] = useState<Date>(new Date()); // 현재 날짜를 상태로 관리

// 현재 월의 총 날짜 수를 가져옴
const totalMonthDays = getDaysInMonth(currentDate);

// 이전 달의 마지막 날짜를 계산
const prevMonthLastDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), 0);
// 이전 달의 총 날짜 수를 가져옴
const totalPrevMonthDays = getDaysInMonth(prevMonthLastDate);

// 현재 월의 첫 번째 날짜를 계산
const firstDayOfCurrentMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
// 이전 달의 날짜 리스트
const prevDayList = getPreviousDays(firstDayOfCurrentMonth, totalPrevMonthDays);
// 현재 월의 날짜 리스트
const currentDayList = getCurrentDays(totalMonthDays);
// 다음 달의 날짜 리스트
const nextDayList = getNextDays(currentDayList, prevDayList);

// 전체 날짜 리스트 (이전 / 현재 / 다음 달 날짜 포함)
const currentCalendarList = [...prevDayList, ...currentDayList, ...nextDayList];

// 주별로 날짜 리스트를 분할
const weekCalendarList: number[][] = [];
currentCalendarList.forEach((currDate, index) => {
const chunkIndex = Math.floor(index / DAY_OF_WEEK);
if (!weekCalendarList[chunkIndex]) {
weekCalendarList[chunkIndex] = []; // 주 배열이 없으면 초기화
}
weekCalendarList[chunkIndex].push(currDate); // 누적값 반환
});

// NOTE: 한 달이 5주 일 수도, 6주 일 수도 있을 때 5주인 경우 해당 달에 필요없는 다음 달의 날짜가 출력되기 때문에 (CALENDAR_LENGTH를 최대치인 42로 잡아서) 마지막 주의 첫 번째 숫자가 10이하의 날짜로 시작한다면 해당 배열을 삭제하도록 추가.
// TODO: 추후 다른 방법이 있다면 변경 할 예정
if (weekCalendarList.length > 0) {
const lastWeek = weekCalendarList[weekCalendarList.length - 1];
if (lastWeek[0] <= 10) {
weekCalendarList.pop();
}
}

// 캘린더 정보를 반환
return {
weekCalendarList, // 주별 날짜 리스트
currentDate,
setCurrentDate,
};
};

export default useCalendar;
3 changes: 2 additions & 1 deletion src/pageLayout/MypageLayout/MyPageLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +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 Profile from '@/user/ui-profile/Profile';
import { useRouter } from 'next/navigation';

Expand Down Expand Up @@ -30,7 +31,7 @@ export default function MyPageLayout() {
<div className='w-full flex flex-col items-center bg-blue-100 rounded-3xl relative shadow-3xl'>
<Profile image={data.image} nickname={data.nickname} />
<div className='flex flex-col w-full lg:max-w-[640px] md:max-w-[640px] mt-[160px] space-y-0 md:mb-10 mb-5 border border-black'>오늘의 감정</div>
<div className='flex flex-col w-full lg:max-w-[640px] md:max-w-[640px] mt-[160px] space-y-0 md:mb-10 mb-5 border border-black'>캘린더</div>
<Calendar />
<div className='flex flex-col w-full lg:max-w-[640px] md:max-w-[640px] mt-[160px] space-y-0 md:mb-10 mb-5 border border-black'>감정차트</div>
</div>
<div className='bg-background-100 flex flex-col items-center w-full py-[100px]'>
Expand Down
76 changes: 76 additions & 0 deletions src/user/ui-profile/Calendar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, { useState } from 'react';
import Image from 'next/image';
import { subMonths } from 'date-fns';
import { Button } from '@/components/ui/button';
import { DropdownMenu, DropdownMenuContent, DropdownMenuRadioGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
import ARROW_BOTTOM_ICON from '../../../public/icon/arrow-bottom-icon.svg';
import ARROW_RIGHT_ICON from '../../../public/icon/arrow-right-icon.svg';
import ARROW_LEFT_ICON from '../../../public/icon/arrow-left-icon.svg';
import useCalendar from '../../hooks/useCalendar';
import { DAY_LIST, DATE_MONTH_FIXER, DEFAULT_TRASH_VALUE } from '../utill/constants';

export default function Calendar() {
const [position, setPosition] = useState<string>('bottom');
const { weekCalendarList, currentDate, setCurrentDate } = useCalendar();

// 이전 달 클릭
const handlePrevMonth = () => setCurrentDate((prevDate) => subMonths(prevDate, DATE_MONTH_FIXER));
// 다음 달 클릭
const handleNextMonth = () => setCurrentDate((prevDate) => subMonths(prevDate, -DATE_MONTH_FIXER));

return (
<div className='flex flex-col w-full lg:max-w-[640px] md:max-w-[640px] mt-[160px] space-y-0 md:mb-10 mb-5 gap-[48px]'>
{/* 캘린더 헤더 */}
<div className='w-full flex justify-between items-center'>
<div className='flex w-full h-[52px] justify-between items-center'>
<div className='text-neutral-700 text-2xl font-semibold leading-loose'>{`${currentDate.getFullYear()}${currentDate.getMonth() + 1}월`}</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant='outline' className='flex items-center gap-1 bg-slate-100 rounded-[14px] text-center text-stone-300 text-xl'>
필터: 감동
<div className='w-9 h-9 relative'>
<Image src={ARROW_BOTTOM_ICON} alt='필터 선택' width={36} height={36} />
</div>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className='bg-white'>
<DropdownMenuSeparator />
<DropdownMenuRadioGroup value={position} onValueChange={setPosition} className='flex flex-row'>
<DropdownMenuItem>EmotionSelector 추가 예정</DropdownMenuItem>
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className='flex gap-6'>
<Button variant='default' className='p-0' onClick={handlePrevMonth}>
<Image src={ARROW_LEFT_ICON} alt='이전' width={36} height={36} />
</Button>
<Button variant='default' className='p-0' onClick={handleNextMonth}>
<Image src={ARROW_RIGHT_ICON} alt='다음' width={36} height={36} />
</Button>
</div>
</div>
{/* 캘린더 */}
<div>
<div className='flex'>
{DAY_LIST.map((day) => (
<div key={day} className='w-[91px] h-[91px] px-[15px] py-[9px] border-b border-gray-100 text-stone-300 font-semibold flex items-center justify-center'>
{day}
</div>
))}
</div>
{weekCalendarList.map((week, weekIndex) => (
// eslint-disable-next-line react/no-array-index-key
<div key={weekIndex} className='flex'>
{week.map((day, dayIndex) => (
// eslint-disable-next-line react/no-array-index-key
<div key={dayIndex} className='w-[91px] h-[91px] px-[15px] py-[9px] border-b border-gray-100 text-stone-300 font-semibold flex items-center justify-center'>
{day === DEFAULT_TRASH_VALUE ? '' : day}
</div>
))}
</div>
))}
</div>
</div>
);
}
14 changes: 11 additions & 3 deletions src/user/utill/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
export const MAX_FILE_SIZE = 1024 * 1024 * 5; // 5MB
export const ACCEPTED_IMAGE_TYPES = ['image/jpeg', 'image/jpg', 'image/png'];
export const sampleImage = '/ProfileTestImage.jpg';
// 파일 업로드 관련
export const MAX_FILE_SIZE = 1024 * 1024 * 5; // 파일 업로드 최대 용량 5MB
export const ACCEPTED_IMAGE_TYPES = ['image/jpeg', 'image/jpg', 'image/png']; // 허용 가능 확장자
export const sampleImage = '/ProfileTestImage.jpg'; // 초기프로필 이미지

// 캘린더 관련 상수
export const DAY_LIST = ['일', '월', '화', '수', '목', '금', '토']; // 요일
export const DATE_MONTH_FIXER = 1; // 날짜 조정 상수 (현재 사용되지 않음, 필요에 따라 활용 가능)
export const CALENDAR_LENGTH = 42; // 6주에 맞추어 캘린더의 총 길이를 42로 설정
export const DAY_OF_WEEK = 7; // 한 주의 날 수 (일~토)
export const DEFAULT_TRASH_VALUE = -1; // 기본값 설정 (필요에 따라 사용 가능)

0 comments on commit 6d5e64b

Please sign in to comment.