Skip to content

Commit

Permalink
Add a drawer with filters for mobile use
Browse files Browse the repository at this point in the history
On the exercise overview page, the filters are now placed
in a drawer.
  • Loading branch information
StijnKing committed Apr 10, 2024
1 parent 96f5c0c commit 12c7a97
Show file tree
Hide file tree
Showing 7 changed files with 403 additions and 217 deletions.
1 change: 1 addition & 0 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"unit": "Unit",
"alsoSearchEnglish": "Also search for names in English",
"copyToClipboard": "Copy to clipboard",
"filter": "Filter",
"exercises": {
"replacements": "Replacements",
"replacementsInfoText": "Optionally, you can also select an exercise that should replace this one (e.g. because it was submitted twice, or similar). This will replace the exercise in routines as well as training logs, instead of just deleting it. These changes will also propagate to any instance that syncs the exercises from this one.",
Expand Down
202 changes: 113 additions & 89 deletions src/components/Exercises/ExerciseOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import AddIcon from '@mui/icons-material/Add';
import { Box, Button, Container, Grid, Pagination, Stack, Typography, } from "@mui/material";
import { LoadingPlaceholder } from "components/Core/LoadingWidget/LoadingWidget";
import { CategoryFilter } from "components/Exercises/Filter/CategoryFilter";
import { EquipmentFilter } from "components/Exercises/Filter/EquipmentFilter";
import { MuscleFilter } from "components/Exercises/Filter/MuscleFilter";
import { Box, Button, Container, Grid, Pagination, Stack, Typography, useMediaQuery } from "@mui/material";
import { CategoryFilter, CategoryFilterDropdown } from "components/Exercises/Filter/CategoryFilter";
import { EquipmentFilter, EquipmentFilterDropdown } from "components/Exercises/Filter/EquipmentFilter";
import { MuscleFilter, MuscleFilterDropdown } from "components/Exercises/Filter/MuscleFilter";
import { NameAutocompleter } from "components/Exercises/Filter/NameAutcompleter";
import { Category } from "components/Exercises/models/category";
import { Equipment } from "components/Exercises/models/equipment";
import { Muscle } from "components/Exercises/models/muscle";
import { ExerciseGrid } from "components/Exercises/Overview/ExerciseGrid";
import { ExerciseGridSkeleton } from "components/Exercises/Overview/ExerciseGridLoadingSkeleton";
import {
useCategoriesQuery,
useEquipmentQuery,
useExercisesQuery,
useMusclesQuery
} from "components/Exercises/queries";
import React from "react";
import { useExercisesQuery } from "components/Exercises/queries";
import React, { useContext, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { Link, useNavigate } from "react-router-dom";
import { ExerciseSearchResponse } from "services/responseType";
import { makeLink, WgerLink } from "utils/url";
import { FilterDrawer } from './Filter/FilterDrawer';
import { ExerciseFiltersContext } from './Filter/ExerciseFiltersContext';

const ContributeExerciseBanner = () => {
const [t, i18n] = useTranslation();
Expand Down Expand Up @@ -74,17 +70,12 @@ const NoResultsBanner = () => {
);
};

export const ExerciseOverview = () => {
export const ExerciseOverviewList = () => {
const basesQuery = useExercisesQuery();
const categoryQuery = useCategoriesQuery();
const musclesQuery = useMusclesQuery();
const equipmentQuery = useEquipmentQuery();
const [t, i18n] = useTranslation();
const navigate = useNavigate();

const [selectedEquipment, setSelectedEquipment] = React.useState<Equipment[]>([]);
const [selectedMuscles, setSelectedMuscles] = React.useState<Muscle[]>([]);
const [selectedCategories, setSelectedCategories] = React.useState<Category[]>([]);
const { selectedCategories, selectedEquipment, selectedMuscles} = useContext(ExerciseFiltersContext);
const isMobile = useMediaQuery('(max-width:600px)');

const [page, setPage] = React.useState(1);
const handlePageChange = (event: any, value: number) => {
Expand All @@ -96,40 +87,44 @@ export const ExerciseOverview = () => {
});
};

let filteredExercises = useMemo(() => {
let filteredExercises = basesQuery.data || [];

// Filter exercise bases by categories
if (selectedCategories.length > 0) {
filteredExercises = filteredExercises!.filter(exercise => {
return selectedCategories.some(
category => exercise.category.id === category.id
);
});
}

// Filter exercises that have one of the selected equipment
if (selectedEquipment.length > 0) {
filteredExercises = filteredExercises!.filter(exercise => {
return exercise.equipment.some(equipment =>
selectedEquipment.some(
selectedEquipment => selectedEquipment.id === equipment.id
)
);
});
}

// Filter exercises that have one of the selected muscles
if (selectedMuscles.length > 0) {
filteredExercises = filteredExercises!.filter(exercise => {
return exercise.muscles.some(muscle =>
selectedMuscles.some(selectedMuscle => selectedMuscle.id === muscle.id)
);
});
}

return filteredExercises;
}, [basesQuery.data, selectedCategories, selectedEquipment, selectedMuscles]);

// Should be a multiple of three, since there are three columns in the grid
const ITEMS_PER_PAGE = 21;

let filteredExercises = basesQuery.data || [];

// Filter exercise bases by categories
if (selectedCategories.length > 0) {
filteredExercises = filteredExercises!.filter(exercise => {
return selectedCategories.some(
category => exercise.category.id === category.id
);
});
}

// Filter exercises that have one of the selected equipment
if (selectedEquipment.length > 0) {
filteredExercises = filteredExercises!.filter(exercise => {
return exercise.equipment.some(equipment =>
selectedEquipment.some(
selectedEquipment => selectedEquipment.id === equipment.id
)
);
});
}

// Filter exercises that have one of the selected muscles
if (selectedMuscles.length > 0) {
filteredExercises = filteredExercises!.filter(exercise => {
return exercise.muscles.some(muscle =>
selectedMuscles.some(selectedMuscle => selectedMuscle.id === muscle.id)
);
});
}

// Pagination calculations
const pageCount = Math.ceil(filteredExercises!.length / ITEMS_PER_PAGE);
const paginatedExercises = filteredExercises!.slice(
Expand All @@ -144,57 +139,67 @@ export const ExerciseOverview = () => {
return (
<Container maxWidth="lg">
<Grid container spacing={2} mt={2}>
<Grid item xs={12} sm={6}>
<Grid item xs={10} sm={6}>
<Typography gutterBottom variant="h3" component="div">
{t("exercises.exercises")}
</Typography>
</Grid>
<Grid item xs={12} sm={3}>
<NameAutocompleter callback={exerciseAdded} />
</Grid>
<Grid item xs={12} sm={3}>
<Button
variant="contained"
startIcon={<AddIcon />}
onClick={() => navigate(makeLink(WgerLink.EXERCISE_CONTRIBUTE, i18n.language))}
>
{t('exercises.contributeExercise')}
</Button>
</Grid>

<Grid item xs={12} sm={3}>
<Grid container spacing={1}>
{categoryQuery.isLoading ? <LoadingPlaceholder /> : (
{isMobile ? (
<>
<Grid item xs={2} sm={6}>
<Button
variant="contained"
onClick={() => navigate(makeLink(WgerLink.EXERCISE_CONTRIBUTE, i18n.language))}
>
<AddIcon />
</Button>
</Grid>
<Grid item sm={6} flexGrow={1}>
<NameAutocompleter callback={exerciseAdded} />
</Grid>
<Grid item xs={2} sm={6} display="flex" justifyContent="center" alignItems="center">
<FilterDrawer>
<CategoryFilterDropdown />
<EquipmentFilterDropdown />
<MuscleFilterDropdown />
</FilterDrawer>
</Grid>
</>
) : (
<>
<Grid item xs={12} sm={3}>
<NameAutocompleter callback={exerciseAdded} />
</Grid>
<Grid item xs={12} sm={3}>
<Button
variant="contained"
startIcon={<AddIcon />}
onClick={() => navigate(makeLink(WgerLink.EXERCISE_CONTRIBUTE, i18n.language))}
>
{t('exercises.contributeExercise')}
</Button>
</Grid>
</>
)}

{!isMobile && (
<Grid item xs={12} sm={3}>
<Grid container spacing={1}>
<Grid item xs={6} sm={12}>
<CategoryFilter
categories={categoryQuery.data!}
selectedCategories={selectedCategories}
setSelectedCategories={setSelectedCategories}
/>
<CategoryFilter />
</Grid>
)}

{equipmentQuery.isLoading ? <LoadingPlaceholder /> : (
<Grid item xs={6} sm={12}>
<EquipmentFilter
equipment={equipmentQuery.data!}
selectedEquipment={selectedEquipment}
setSelectedEquipment={setSelectedEquipment}
/>
<EquipmentFilter />
</Grid>
)}

{musclesQuery.isLoading ? <LoadingPlaceholder /> : (
<Grid item xs={12}>
<MuscleFilter
muscles={musclesQuery.data!}
selectedMuscles={selectedMuscles}
setSelectedMuscles={setSelectedMuscles}
/>
<MuscleFilter />
</Grid>
)}
</Grid>
</Grid>
</Grid>
)}

<Grid item xs={12} sm={9}>
{basesQuery.isLoading
? <ExerciseGridSkeleton />
Expand All @@ -217,3 +222,22 @@ export const ExerciseOverview = () => {
</Container>
);
};

export const ExerciseOverview = () => {
const [selectedEquipment, setSelectedEquipment] = useState<Equipment[]>([]);
const [selectedMuscles, setSelectedMuscles] = useState<Muscle[]>([]);
const [selectedCategories, setSelectedCategories] = React.useState<Category[]>([]);

return (
<ExerciseFiltersContext.Provider value={{
selectedEquipment,
setSelectedEquipment,
selectedMuscles,
setSelectedMuscles,
selectedCategories,
setSelectedCategories
}}>
<ExerciseOverviewList />
</ExerciseFiltersContext.Provider>
);
};
Loading

0 comments on commit 12c7a97

Please sign in to comment.