Skip to content

Commit 27dad0b

Browse files
feat: add creation workout (#15)
1 parent 6626734 commit 27dad0b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+841
-53
lines changed

frontend/src/components/Calendar/components/CalendarDayCell.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,25 @@ export const CalendarDayCell = ({
1515
hasWorkout,
1616
colStart,
1717
isFirstDay,
18+
openModalByClickOnDayCell,
1819
}: ICalendarDayCellProps) => {
1920
const baseClasses =
2021
'h-10 w-10 flex relative items-center justify-center font-medium text-xs rounded-xl transition-colors';
2122
const activeClasses = isCurrent ? 'bg-primary text-main font-bold' : '';
2223
const pointClasses = isCurrent ? 'bg-main' : 'bg-primary';
2324

25+
const handleClick = () => (hasWorkout ? null : openModalByClickOnDayCell());
26+
2427
return (
25-
<div
28+
<button
2629
key={day}
2730
className={`${baseClasses} ${activeClasses}`}
2831
style={isFirstDay ? { gridColumnStart: `${colStart}` } : {}}
32+
onClick={handleClick}
2933
>
3034
{day}
3135

3236
{hasWorkout && <CalendarDayCellPoint pointClasses={pointClasses} />}
33-
</div>
37+
</button>
3438
);
3539
};

frontend/src/components/Calendar/components/CalendarGrid.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export const CalendarGrid = ({
77
indexOfFirstDayInMonth,
88
currentDay,
99
checkWorkoutInThisDay,
10+
openModalByClickOnDayCell,
11+
getWorkoutByDay,
1012
}: ICalendarGridProps) => {
1113
return (
1214
<div className="flex flex-col gap-4 border-b-2 border-white pb-7">
@@ -26,9 +28,11 @@ export const CalendarGrid = ({
2628
key={day}
2729
day={day}
2830
isCurrent={currentDay === day}
31+
initialWorkoutId={getWorkoutByDay(day)?.id || ''}
2932
hasWorkout={checkWorkoutInThisDay(day)}
3033
colStart={indexOfFirstDayInMonth}
3134
isFirstDay={day === 1}
35+
openModalByClickOnDayCell={openModalByClickOnDayCell}
3236
/>
3337
))}
3438
</div>

frontend/src/components/Calendar/hooks.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ export const useCalendar = (workouts: WorkoutPreview[]) => {
5555
});
5656
};
5757

58+
const getWorkoutByDay = (day: number): WorkoutPreview | undefined => {
59+
return workouts.find((workout) => {
60+
const wDate = new Date(workout.date);
61+
return (
62+
wDate.getFullYear() === year &&
63+
wDate.getMonth() === currentMonthIndex &&
64+
wDate.getDate() === day
65+
);
66+
});
67+
};
68+
5869
return {
5970
month,
6071
year,
@@ -65,5 +76,6 @@ export const useCalendar = (workouts: WorkoutPreview[]) => {
6576
currentDay,
6677
backToday,
6778
checkWorkoutInThisDay,
79+
getWorkoutByDay,
6880
};
6981
};

frontend/src/components/Calendar/index.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import { CalendarActions, CalendarGrid, MonthSwitcher } from './components';
22
import { useCalendar } from './hooks';
33
import type { ICalendarProps } from './types';
44

5-
export const Calendar = ({ workouts }: ICalendarProps) => {
5+
export const Calendar = ({
6+
workouts,
7+
openModalByClickOnDayCell,
8+
}: ICalendarProps) => {
69
const {
710
month,
811
year,
@@ -13,6 +16,7 @@ export const Calendar = ({ workouts }: ICalendarProps) => {
1316
currentDay,
1417
backToday,
1518
checkWorkoutInThisDay,
19+
getWorkoutByDay,
1620
} = useCalendar(workouts);
1721

1822
return (
@@ -28,6 +32,8 @@ export const Calendar = ({ workouts }: ICalendarProps) => {
2832
indexOfFirstDayInMonth={indexOfFirstDayInMonth}
2933
currentDay={currentDay}
3034
checkWorkoutInThisDay={checkWorkoutInThisDay}
35+
getWorkoutByDay={getWorkoutByDay}
36+
openModalByClickOnDayCell={openModalByClickOnDayCell}
3137
/>
3238
<CalendarActions backToday={backToday} />
3339
</div>

frontend/src/components/Calendar/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { WorkoutPreview } from '@types';
22

33
export interface ICalendarProps {
44
workouts: WorkoutPreview[];
5+
openModalByClickOnDayCell: () => void;
56
}
67

78
export interface IMounthSwitcherProps {
@@ -16,6 +17,8 @@ export interface ICalendarGridProps {
1617
indexOfFirstDayInMonth: number;
1718
currentDay: number;
1819
checkWorkoutInThisDay: (day: number) => boolean;
20+
openModalByClickOnDayCell: () => void;
21+
getWorkoutByDay: (day: number) => WorkoutPreview | undefined;
1922
}
2023

2124
export interface ICalendarActionsProps {
@@ -28,6 +31,8 @@ export interface ICalendarDayCellProps {
2831
hasWorkout: boolean;
2932
colStart: number;
3033
isFirstDay: boolean;
34+
openModalByClickOnDayCell: () => void;
35+
initialWorkoutId?: string;
3136
}
3237

3338
export interface ICalendarDayCellPointProps {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { Box, Typography, Chip, TextField } from '@mui/material';
2+
3+
import { BaseAutocomplete } from '@ui';
4+
import type { IExerciseSelectProps } from './types';
5+
import { useGetAllExercises } from './queries';
6+
7+
export const ExerciseSelect = ({
8+
onSelect,
9+
disabled,
10+
}: IExerciseSelectProps) => {
11+
const { data: exercises = [], isLoading } = useGetAllExercises();
12+
return (
13+
<BaseAutocomplete
14+
disabled={disabled || isLoading}
15+
options={exercises}
16+
getOptionLabel={(option) => option.name}
17+
filterOptions={(options, state) => {
18+
const inputValue = state.inputValue.toLowerCase();
19+
return options.filter(
20+
(option) =>
21+
option.name.toLowerCase().includes(inputValue) ||
22+
option.muscleGroup.toLowerCase().includes(inputValue),
23+
);
24+
}}
25+
renderOption={(props, option) => {
26+
const { key, ...otherProps } = props;
27+
return (
28+
<li
29+
key={key}
30+
{...otherProps}
31+
style={{ borderBottom: '1px solid #ffffff10' }}
32+
>
33+
<Box
34+
display="flex"
35+
justifyContent="space-between"
36+
width="100%"
37+
alignItems="center"
38+
>
39+
<Typography variant="body1" color="textPrimary">
40+
{option.name}
41+
</Typography>
42+
<Chip
43+
label={option.muscleGroup}
44+
size="small"
45+
sx={{
46+
backgroundColor: '#8CEF0D20',
47+
color: '#8CEF0D',
48+
fontSize: '10px',
49+
height: '20px',
50+
}}
51+
/>
52+
</Box>
53+
</li>
54+
);
55+
}}
56+
onChange={(_, value) => {
57+
if (value) {
58+
onSelect(value);
59+
}
60+
}}
61+
loading={isLoading}
62+
itemType="Find Exercise"
63+
renderInput={(params) => <TextField {...params} />}
64+
/>
65+
);
66+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { useQuery } from '@tanstack/react-query';
2+
3+
import { QUERY_KEYS } from '@constants';
4+
import { exercisesService } from '@services';
5+
import type { ExercisesResponse } from '@types';
6+
7+
export const useGetAllExercises = () => {
8+
return useQuery<ExercisesResponse[], Error>({
9+
queryKey: [QUERY_KEYS.EXERCISES],
10+
queryFn: () => exercisesService.getAllExercises(),
11+
retry: false,
12+
staleTime: 1000 * 60 * 60,
13+
});
14+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { ExercisesResponse } from '@types';
2+
3+
export interface IExerciseSelectProps {
4+
onSelect: (exercise: ExercisesResponse) => void;
5+
disabled?: boolean;
6+
}

frontend/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './AuthPageHeader';
22
export * from './Calendar';
3+
export * from './ExerciseSelect';
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Autocomplete, TextField, Paper } from '@mui/material';
2+
import type { AutocompleteProps } from '@mui/material';
3+
4+
export const BaseAutocomplete = <T,>(
5+
props: AutocompleteProps<T, false, false, false>,
6+
) => {
7+
return (
8+
<Autocomplete
9+
{...props}
10+
PaperComponent={(paperProps) => (
11+
<Paper
12+
{...paperProps}
13+
sx={{
14+
backgroundColor: '#181D1B',
15+
color: 'white',
16+
borderRadius: '12px',
17+
marginTop: '8px',
18+
boxShadow: '0 10px 30px rgba(0,0,0,0.5)',
19+
}}
20+
/>
21+
)}
22+
renderInput={(params) => (
23+
<TextField
24+
{...params}
25+
label={props.itemType || 'Select item'}
26+
placeholder="Type to search..."
27+
/>
28+
)}
29+
/>
30+
);
31+
};

0 commit comments

Comments
 (0)