Skip to content

Commit 37a9c0a

Browse files
feat: some small fixed & add calendar to dashboard
1 parent 3b9c878 commit 37a9c0a

File tree

21 files changed

+606
-57
lines changed

21 files changed

+606
-57
lines changed

frontend/package-lock.json

Lines changed: 321 additions & 48 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,17 @@
1818
"@emotion/styled": "^11.14.1",
1919
"@mui/icons-material": "^7.3.6",
2020
"@mui/material": "^7.3.6",
21+
"@tailwindcss/vite": "^4.1.18",
2122
"@tanstack/react-query": "^5.90.16",
2223
"@tanstack/react-query-devtools": "^5.91.2",
2324
"axios": "^1.13.2",
2425
"formik": "^2.4.9",
2526
"react": "^19.2.0",
2627
"react-dom": "^19.2.0",
2728
"react-hot-toast": "^2.6.0",
29+
"react-icons": "^5.5.0",
2830
"react-router-dom": "^7.11.0",
31+
"tailwindcss": "^4.1.18",
2932
"yup": "^1.7.1"
3033
},
3134
"devDependencies": {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { DICTIONARY } from '@locales';
2+
import type { ICalendarActionsProps } from '../types';
3+
4+
export const CalendarActions = ({ backToday }: ICalendarActionsProps) => {
5+
return (
6+
<div className="flex items-center gap-2.5">
7+
<button
8+
onClick={backToday}
9+
className="py-1 px-4 bg-slate-700 w-full rounded-lg"
10+
>
11+
{DICTIONARY.calendar.buttons.today}
12+
</button>
13+
</div>
14+
);
15+
};
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { WEEK_DAYS } from '../constants';
2+
import type { ICalendarGridProps } from '../types';
3+
4+
export const CalendarGrid = ({
5+
arrayByDaysInMonth,
6+
indexOfFirstDayInMonth,
7+
currentDay,
8+
checkWorkoutInThisDay,
9+
}: ICalendarGridProps) => {
10+
return (
11+
<div className="flex flex-col gap-4 border-b-2 border-white pb-7">
12+
<div className="grid grid-cols-7 gap-y-8 space-b gap-2.5">
13+
{WEEK_DAYS.map((day) => (
14+
<div
15+
key={day}
16+
className="flex justify-center font-medium text-xs text-[#9C9D9F]"
17+
>
18+
{day.slice(0, 3)}
19+
</div>
20+
))}
21+
</div>
22+
<div className="grid grid-cols-7 gap-y-8 gap-x-2.5">
23+
{arrayByDaysInMonth?.map((day) => {
24+
const currentDayStyle =
25+
currentDay === day ? 'bg-[#8CEF0D] text-[#0C110F] font-bold' : '';
26+
const hasWorkout = checkWorkoutInThisDay(day);
27+
return (
28+
<div
29+
key={day}
30+
className={`h-10 w-10 flex relative items-center justify-center font-medium text-xs rounded-xl ${currentDayStyle}`}
31+
style={
32+
day === 1
33+
? { gridColumnStart: `${indexOfFirstDayInMonth}` }
34+
: {}
35+
}
36+
>
37+
{day}
38+
{hasWorkout ? (
39+
<span
40+
className={`w-2 h-2 ${currentDayStyle ? 'bg-[#0C110F]' : 'bg-[#8CEF0D]'} rounded-full absolute left-1/2 -translate-x-1/2 bottom-0.5`}
41+
></span>
42+
) : (
43+
''
44+
)}
45+
</div>
46+
);
47+
})}
48+
</div>
49+
</div>
50+
);
51+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { IoIosArrowBack, IoIosArrowForward } from 'react-icons/io';
2+
3+
import type { IMounthSwitcherProps } from '../types';
4+
5+
export const MounthSwitcher = ({
6+
prevMonth,
7+
nextMonth,
8+
month,
9+
year,
10+
}: IMounthSwitcherProps) => {
11+
return (
12+
<div className="flex items-center gap-2 justify-between">
13+
<button
14+
onClick={prevMonth}
15+
className="flex items-center justify-center w-8 h-8 rounded-lg border"
16+
>
17+
<IoIosArrowBack />
18+
</button>
19+
<div className="font-bold text-white px-4 py-1 rounded-3xl bg-slate-700 text-lg flex items-baseline gap-1">
20+
{month} <span className="text-[10px]">{year}</span>
21+
</div>
22+
<button
23+
onClick={nextMonth}
24+
className="flex items-center justify-center w-8 h-8 rounded-lg border"
25+
>
26+
<IoIosArrowForward />
27+
</button>
28+
</div>
29+
);
30+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './CalendarActions';
2+
export * from './CalendarGrid';
3+
export * from './MounthSwitcher';
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export const MONTH_NAMES: string[] = [
2+
'January',
3+
'February',
4+
'March',
5+
'April',
6+
'May',
7+
'June',
8+
'July',
9+
'August',
10+
'September',
11+
'October',
12+
'November',
13+
'December',
14+
] as const;
15+
16+
export const WEEK_DAYS: string[] = [
17+
'Sunday',
18+
'Monday',
19+
'Tuesday',
20+
'Wednesday',
21+
'Thursday',
22+
'Friday',
23+
'Saturday',
24+
] as const;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { useState } from 'react';
2+
3+
import type { WorkoutPreview } from '@types';
4+
import { MONTH_NAMES } from './constants';
5+
6+
export const useCalendar = (workouts: WorkoutPreview[]) => {
7+
const now = new Date();
8+
const [viewDate, setViewDate] = useState(now);
9+
10+
const year = viewDate.getFullYear();
11+
const currentMonthIndex = viewDate.getMonth();
12+
const month = MONTH_NAMES[currentMonthIndex];
13+
14+
const prevMonth = () => {
15+
setViewDate(
16+
(prevMonth) =>
17+
new Date(prevMonth.getFullYear(), prevMonth.getMonth() - 1),
18+
);
19+
};
20+
21+
const nextMonth = () => {
22+
setViewDate(
23+
(prevMonth) =>
24+
new Date(prevMonth.getFullYear(), prevMonth.getMonth() + 1),
25+
);
26+
};
27+
28+
const daysInMonth = new Date(year, currentMonthIndex + 1, 0).getDate();
29+
30+
const arrayByDaysInMonth = Array.from(
31+
{ length: daysInMonth },
32+
(_, index) => index + 1,
33+
);
34+
35+
const indexOfFirstDayInMonth =
36+
new Date(year, currentMonthIndex, 1).getDay() + 1;
37+
38+
const backToday = () => {
39+
setViewDate(now);
40+
};
41+
42+
const currentDay =
43+
now.getFullYear() === year && now.getMonth() === currentMonthIndex
44+
? now.getDate()
45+
: NaN;
46+
47+
const checkWorkoutInThisDay = (day: number) => {
48+
return workouts.some((workout) => {
49+
const workoutDate = new Date(workout.date);
50+
return (
51+
workoutDate.getFullYear() === year &&
52+
workoutDate.getMonth() === currentMonthIndex &&
53+
workoutDate.getDate() === day
54+
);
55+
});
56+
};
57+
58+
return {
59+
month,
60+
year,
61+
prevMonth,
62+
nextMonth,
63+
arrayByDaysInMonth,
64+
indexOfFirstDayInMonth,
65+
currentDay,
66+
backToday,
67+
checkWorkoutInThisDay,
68+
};
69+
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { CalendarActions, CalendarGrid, MounthSwitcher } from './components';
2+
import { useCalendar } from './hooks';
3+
import type { ICalendarProps } from './types';
4+
5+
export const Calendar = ({ workouts }: ICalendarProps) => {
6+
const {
7+
month,
8+
year,
9+
prevMonth,
10+
nextMonth,
11+
arrayByDaysInMonth,
12+
indexOfFirstDayInMonth,
13+
currentDay,
14+
backToday,
15+
checkWorkoutInThisDay,
16+
} = useCalendar(workouts);
17+
18+
return (
19+
<div className="flex flex-col gap-20 w-[384px] p-6">
20+
<MounthSwitcher
21+
prevMonth={prevMonth}
22+
nextMonth={nextMonth}
23+
month={month}
24+
year={year}
25+
/>
26+
<CalendarGrid
27+
arrayByDaysInMonth={arrayByDaysInMonth}
28+
indexOfFirstDayInMonth={indexOfFirstDayInMonth}
29+
currentDay={currentDay}
30+
checkWorkoutInThisDay={checkWorkoutInThisDay}
31+
/>
32+
<CalendarActions backToday={backToday} />
33+
</div>
34+
);
35+
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { WorkoutPreview } from '@types';
2+
3+
export interface ICalendarProps {
4+
workouts: WorkoutPreview[];
5+
}
6+
7+
export interface IMounthSwitcherProps {
8+
prevMonth: () => void;
9+
nextMonth: () => void;
10+
month: string;
11+
year: number;
12+
}
13+
14+
export interface ICalendarGridProps {
15+
arrayByDaysInMonth: number[];
16+
indexOfFirstDayInMonth: number;
17+
currentDay: number;
18+
checkWorkoutInThisDay: (day: number) => boolean;
19+
}
20+
21+
export interface ICalendarActionsProps {
22+
backToday: () => void;
23+
}

0 commit comments

Comments
 (0)