@wger-project/react-components 25.10.24 → 25.11.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/build/chunks/{browser-ponyfill-DL_vVusK.js → browser-ponyfill-CyEkMYuR.js} +3 -3
  2. package/build/chunks/{browser-ponyfill-DL_vVusK.js.map → browser-ponyfill-CyEkMYuR.js.map} +1 -1
  3. package/build/locales/fil/translation.json +3 -0
  4. package/build/locales/sk/translation.json +26 -1
  5. package/build/main.js +137 -235
  6. package/build/main.js.map +1 -1
  7. package/package.json +14 -14
  8. package/src/components/BodyWeight/Form/WeightForm.tsx +3 -4
  9. package/src/components/BodyWeight/WeightChart/index.tsx +26 -29
  10. package/src/components/Exercises/Add/Step1Basics.test.tsx +4 -1
  11. package/src/components/Exercises/Detail/ExerciseDetailEdit.tsx +43 -27
  12. package/src/components/Exercises/Detail/ExerciseDetailView.tsx +57 -17
  13. package/src/components/Exercises/Detail/Head/ExerciseDeleteDialog.tsx +3 -4
  14. package/src/components/Exercises/Detail/OverviewCard.test.tsx +7 -11
  15. package/src/components/Exercises/ExerciseOverview.tsx +8 -8
  16. package/src/components/Exercises/Filter/NameAutcompleter.tsx +41 -39
  17. package/src/components/Exercises/Filter/NameAutocompleter.test.tsx +2 -1
  18. package/src/components/Exercises/Overview/ExerciseGrid.tsx +1 -2
  19. package/src/components/Exercises/forms/ExerciseAliases.tsx +78 -21
  20. package/src/components/Exercises/forms/yupValidators.ts +7 -4
  21. package/src/components/Exercises/models/exercise.ts +55 -43
  22. package/src/components/Measurements/Screens/MeasurementCategoryDetail.test.tsx +4 -2
  23. package/src/components/Measurements/widgets/MeasurementChart.tsx +27 -30
  24. package/src/components/Nutrition/components/BmiCalculator.tsx +40 -42
  25. package/src/components/Nutrition/widgets/charts/MacrosPieChart.tsx +17 -19
  26. package/src/components/Nutrition/widgets/charts/NutritionDiaryChart.tsx +39 -38
  27. package/src/components/Nutrition/widgets/charts/NutritionalValuesDashboardChart.tsx +27 -29
  28. package/src/components/Nutrition/widgets/charts/NutritionalValuesPlannedLoggedChart.tsx +20 -19
  29. package/src/components/WorkoutRoutines/Detail/WorkoutStats.tsx +17 -19
  30. package/src/components/WorkoutRoutines/widgets/DayDetails.tsx +3 -3
  31. package/src/components/WorkoutRoutines/widgets/LogWidgets.tsx +35 -38
  32. package/src/components/WorkoutRoutines/widgets/SlotDetails.tsx +4 -4
  33. package/src/components/WorkoutRoutines/widgets/forms/SessionLogsForm.tsx +4 -4
  34. package/src/services/exerciseTranslation.ts +24 -9
  35. package/src/services/measurements.ts +15 -2
  36. package/src/services/responseType.ts +3 -29
  37. package/src/tests/exerciseTestdata.ts +61 -55
  38. package/src/tests/exercises/searchResponse.ts +49 -22
  39. package/src/tests/responseApi.ts +25 -14
@@ -12,25 +12,25 @@ import {
12
12
  Switch,
13
13
  TextField,
14
14
  } from "@mui/material";
15
+ import { Exercise } from "components/Exercises/models/exercise";
15
16
  import { SERVER_URL } from "config";
16
17
  import debounce from "lodash/debounce";
17
18
  import * as React from "react";
18
19
  import { useState } from "react";
19
20
  import { useTranslation } from "react-i18next";
20
- import { getExercise, searchExerciseTranslations } from "services";
21
- import { ExerciseSearchResponse } from "services/responseType";
21
+ import { searchExerciseTranslations } from "services";
22
22
  import { LANGUAGE_SHORT_ENGLISH } from "utils/consts";
23
23
 
24
24
  type NameAutocompleterProps = {
25
- callback: (exerciseResponse: ExerciseSearchResponse | null) => void;
25
+ callback: (exercise: Exercise | null) => void;
26
26
  loadExercise?: boolean;
27
27
  };
28
28
 
29
29
  export function NameAutocompleter({ callback, loadExercise }: NameAutocompleterProps) {
30
- const [value, setValue] = React.useState<ExerciseSearchResponse | null>(null);
30
+ const [value, setValue] = React.useState<Exercise | null>(null);
31
31
  const [inputValue, setInputValue] = React.useState("");
32
32
  const [searchEnglish, setSearchEnglish] = useState<boolean>(true);
33
- const [options, setOptions] = React.useState<readonly ExerciseSearchResponse[]>([]);
33
+ const [options, setOptions] = React.useState<readonly Exercise[]>([]);
34
34
  const [t, i18n] = useTranslation();
35
35
 
36
36
  loadExercise = loadExercise === undefined ? false : loadExercise;
@@ -61,7 +61,7 @@ export function NameAutocompleter({ callback, loadExercise }: NameAutocompleterP
61
61
  <>
62
62
  <Autocomplete
63
63
  id="exercise-name-autocomplete"
64
- getOptionLabel={(option) => option.value}
64
+ getOptionLabel={(option) => option.getTranslation().name}
65
65
  data-testid="autocomplete"
66
66
  filterOptions={(x) => x}
67
67
  options={options}
@@ -70,13 +70,10 @@ export function NameAutocompleter({ callback, loadExercise }: NameAutocompleterP
70
70
  filterSelectedOptions
71
71
  value={value}
72
72
  noOptionsText={t("noResults")}
73
- isOptionEqualToValue={(option, value) => option.value === value.value}
74
- onChange={async (event: React.SyntheticEvent, newValue: ExerciseSearchResponse | null) => {
73
+ isOptionEqualToValue={(option, value) => option.id === value.id}
74
+ onChange={async (event: React.SyntheticEvent, newValue: Exercise | null) => {
75
75
  setOptions(newValue ? [newValue, ...options] : options);
76
76
  setValue(newValue);
77
- if (loadExercise && newValue !== null) {
78
- newValue.exercise = await getExercise(newValue.data.base_id);
79
- }
80
77
  callback(newValue);
81
78
  }}
82
79
  onInputChange={(event, newInputValue) => {
@@ -102,34 +99,39 @@ export function NameAutocompleter({ callback, loadExercise }: NameAutocompleterP
102
99
  }}
103
100
  />
104
101
  )}
105
- renderOption={(props, option, state) => (
106
- <li
107
- {...props}
108
- key={`exercise-${state.index}-${option.data.id}`}
109
- data-testid={`autocompleter-result-${option.data.base_id}`}
110
- >
111
- <ListItem disablePadding component="div">
112
- <ListItemIcon>
113
- {option.data.image ? (
114
- <Avatar alt="" src={`${SERVER_URL}${option.data.image}`} variant="rounded" />
115
- ) : (
116
- <PhotoIcon fontSize="large" />
117
- )}
118
- </ListItemIcon>
119
- <ListItemText
120
- primary={option.value}
121
- slotProps={{
122
- primary: {
123
- whiteSpace: "nowrap",
124
- overflow: "hidden",
125
- textOverflow: "ellipsis",
126
- },
127
- }}
128
- secondary={option.data.category}
129
- />
130
- </ListItem>
131
- </li>
132
- )}
102
+ renderOption={(props, option, state) => {
103
+ const translation = option.getTranslation();
104
+ const mainImage = option.mainImage;
105
+
106
+ return (
107
+ <li
108
+ {...props}
109
+ key={`exercise-${state.index}-${option.id}`}
110
+ data-testid={`autocompleter-result-${option.id}`}
111
+ >
112
+ <ListItem disablePadding component="div">
113
+ <ListItemIcon>
114
+ {mainImage ? (
115
+ <Avatar alt="" src={`${SERVER_URL}${mainImage.url}`} variant="rounded" />
116
+ ) : (
117
+ <PhotoIcon fontSize="large" />
118
+ )}
119
+ </ListItemIcon>
120
+ <ListItemText
121
+ primary={translation.name}
122
+ slotProps={{
123
+ primary: {
124
+ whiteSpace: "nowrap",
125
+ overflow: "hidden",
126
+ textOverflow: "ellipsis",
127
+ },
128
+ }}
129
+ secondary={option.category.name}
130
+ />
131
+ </ListItem>
132
+ </li>
133
+ );
134
+ }}
133
135
  />
134
136
  {i18n.language !== LANGUAGE_SHORT_ENGLISH && (
135
137
  <FormGroup>
@@ -3,6 +3,7 @@ import { NameAutocompleter } from "components/Exercises/Filter/NameAutcompleter"
3
3
  import React from 'react';
4
4
  import { searchExerciseTranslations } from "services";
5
5
  import { searchResponse } from "tests/exercises/searchResponse";
6
+ import { Exercise } from "components/Exercises/models/exercise";
6
7
 
7
8
  jest.mock("services");
8
9
  const mockCallback = jest.fn();
@@ -71,6 +72,6 @@ describe("Test the NameAutocompleter component", () => {
71
72
  });
72
73
 
73
74
  // Assert
74
- expect(mockCallback).toHaveBeenLastCalledWith(searchResponse[0]);
75
+ expect(mockCallback).toHaveBeenCalledWith(expect.any(Exercise));
75
76
  });
76
77
  });
@@ -15,8 +15,7 @@ export const ExerciseGrid = ({ exercises }: ExerciseGridProps) => {
15
15
 
16
16
  const languageQuery = useLanguageQuery();
17
17
 
18
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
19
- const [t, i18n] = useTranslation();
18
+ const { i18n } = useTranslation();
20
19
 
21
20
  let currentUserLanguage: Language | undefined;
22
21
  if (languageQuery.isSuccess) {
@@ -1,37 +1,94 @@
1
- import { Autocomplete, Chip, TextField } from "@mui/material";
1
+ import { Autocomplete, Chip, InputAdornment, TextField } from "@mui/material";
2
2
  import { useField } from "formik";
3
3
  import React from "react";
4
4
  import { useTranslation } from "react-i18next";
5
5
 
6
+ type AliasItem = { id?: number; alias: string };
7
+
6
8
  export function ExerciseAliases(props: { fieldName: string }) {
7
9
  const [t] = useTranslation();
8
- const [field, meta, helpers] = useField(props.fieldName);
10
+ const [field, meta, helpers] = useField<AliasItem[]>(props.fieldName);
11
+
12
+ const normalize = (items: (AliasItem | string)[] | null | undefined): AliasItem[] =>
13
+ (items || []).map(item =>
14
+ typeof item === "string" ? { alias: item } : ("alias" in item ? (item as AliasItem) : { alias: String(item) })
15
+ );
16
+
17
+ /**
18
+ * Extract a human-readable error string from the Yup alias validator, which
19
+ * returns a list of errors.
20
+ */
21
+ const formatError = (err: unknown): string | undefined => {
22
+ if (!err) return undefined;
23
+ if (typeof err === "string") return err;
24
+
25
+ if (typeof err === "object") {
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
+ const o = err as any;
28
+ if (typeof o.alias === "string") return o.alias;
29
+ if (typeof o.message === "string") return o.message;
30
+
31
+ for (const k of Object.keys(o)) {
32
+ const v = o[k];
33
+ if (typeof v === "string") return v;
34
+ if (v && typeof v === "object") {
35
+ if (typeof v.alias === "string") return v.alias;
36
+ if (typeof v.message === "string") return v.message;
37
+ }
38
+ }
39
+ }
40
+
41
+ return String(err);
42
+ };
9
43
 
10
44
  return <Autocomplete
11
45
  multiple
12
46
  freeSolo
13
47
  id={props.fieldName}
14
- value={field.value}
15
- options={field.value}
16
- onChange={(event, newValue) => {
17
- helpers.setValue(newValue);
48
+ value={field.value || []}
49
+ options={[]}
50
+ getOptionLabel={(opt) => (typeof opt === "string" ? opt : opt.alias)}
51
+ isOptionEqualToValue={(option, value) => option.alias === value.alias && (option.id === value.id || option.id === undefined || value.id === undefined)}
52
+ onChange={(_, newValue) => {
53
+ helpers.setValue(normalize(newValue));
18
54
  }}
19
- renderValue={(value: readonly string[], getTagProps) => {
20
- return value.map((option: string, index: number) => (
21
- <Chip label={option} {...getTagProps({ index })} key={index} />
55
+ onBlur={field.onBlur}
56
+ renderInput={(params) => {
57
+ const chips = (field.value || []).map((option, index) => (
58
+ <Chip
59
+ label={option.alias}
60
+ onDelete={() => {
61
+ const newVal = [...(field.value || [])];
62
+ newVal.splice(index, 1);
63
+ helpers.setValue(newVal);
64
+ }}
65
+ key={option.id ?? `${option.alias}-${index}`}
66
+ />
22
67
  ));
68
+
69
+ return (
70
+ <TextField
71
+ {...params}
72
+ id="exerciseAliases"
73
+ variant="standard"
74
+ label={t("exercises.alternativeNames")}
75
+ error={meta.touched && Boolean(meta.error)}
76
+ helperText={meta.touched ? formatError(meta.error) : undefined}
77
+ slotProps={{
78
+ input: {
79
+ startAdornment: (
80
+ <InputAdornment
81
+ position="start"
82
+ sx={{ display: "flex", gap: 0.5, alignItems: "center" }}
83
+ >
84
+ {chips}
85
+ {/*{params.InputProps?.startAdornment}*/}
86
+ </InputAdornment>
87
+ ),
88
+ },
89
+ }}
90
+ />
91
+ );
23
92
  }}
24
- onBlur={field.onBlur}
25
- renderInput={params => (
26
- <TextField
27
- {...params}
28
- id="exerciseAliases"
29
- variant="standard"
30
- label={t("exercises.alternativeNames")}
31
- error={meta.touched && Boolean(meta.error)}
32
- helperText={meta.touched && meta.error}
33
- value={field.value}
34
- />
35
- )}
36
93
  />;
37
94
  }
@@ -20,10 +20,13 @@ export const alternativeNameValidator = () => yup
20
20
  .ensure()
21
21
  .compact()
22
22
  .of(
23
- yup
24
- .string()
25
- .min(MIN_CHAR_NAME, i18n.t("forms.minLength", { 'chars': MIN_CHAR_NAME }))
26
- .max(MAX_CHAR_NAME, i18n.t("forms.maxLength", { 'chars': MAX_CHAR_NAME }))
23
+ yup.object({
24
+ id: yup.number().nullable(),
25
+ alias: yup.string()
26
+ .min(MIN_CHAR_NAME, i18n.t("forms.minLength", { 'chars': MIN_CHAR_NAME }))
27
+ .max(MAX_CHAR_NAME, i18n.t("forms.maxLength", { 'chars': MAX_CHAR_NAME }))
28
+ .required()
29
+ })
27
30
  );
28
31
 
29
32
  export const descriptionValidator = () => yup
@@ -8,42 +8,53 @@ import { ExerciseVideo, ExerciseVideoAdapter } from "components/Exercises/models
8
8
  import { Adapter } from "utils/Adapter";
9
9
  import { ENGLISH_LANGUAGE_ID } from "utils/consts";
10
10
 
11
+ export type ExerciseConstructorParams = {
12
+ id: number | null;
13
+ uuid: string | null;
14
+ category: Category;
15
+ equipment?: Equipment[];
16
+ muscles?: Muscle[];
17
+ musclesSecondary?: Muscle[];
18
+ images?: ExerciseImage[];
19
+ variationId?: number | null;
20
+ lastUpdateGlobal?: Date;
21
+ translations?: Translation[];
22
+ videos?: ExerciseVideo[];
23
+ authors?: string[];
24
+ };
25
+
11
26
  export class Exercise {
12
- translations: Translation[] = [];
27
+ id: number | null;
28
+ uuid: string | null;
29
+ variationId: number | null;
30
+ category: Category;
31
+ lastUpdateGlobal: Date;
32
+
33
+ muscles: Muscle[] = [];
34
+ musclesSecondary: Muscle[] = [];
35
+ images: ExerciseImage[] = [];
13
36
  videos: ExerciseVideo[] = [];
37
+ equipment: Equipment[] = [];
14
38
  authors: string[] = [];
39
+ translations: Translation[] = [];
15
40
 
16
- constructor(
17
- public id: number | null,
18
- public uuid: string | null,
19
- public category: Category,
20
- public equipment: Equipment[],
21
- public muscles: Muscle[],
22
- public musclesSecondary: Muscle[],
23
- public images: ExerciseImage[],
24
- public variationId: number | null,
25
- translations?: Translation[],
26
- videos?: ExerciseVideo[],
27
- authors?: string[]
28
- /*
29
- license: number,
30
- licenseAuthorS: string[],
31
- */
32
- ) {
33
- if (translations) {
34
- this.translations = translations;
35
- }
36
-
37
- if (videos) {
38
- this.videos = videos;
39
- }
40
-
41
- if (authors) {
42
- this.authors = authors;
43
- }
41
+ constructor(init: ExerciseConstructorParams) {
42
+ this.id = init.id;
43
+ this.uuid = init.uuid;
44
+ this.category = init.category;
45
+ this.variationId = init.variationId ?? null;
46
+ this.lastUpdateGlobal = init.lastUpdateGlobal ?? new Date();
47
+
48
+ this.muscles = init.muscles ?? [];
49
+ this.musclesSecondary = init.musclesSecondary ?? [];
50
+ this.images = init.images ?? [];
51
+ this.videos = init.videos ?? [];
52
+ this.equipment = init.equipment ?? [];
53
+ this.authors = init.authors ?? [];
54
+ this.translations = init.translations ?? [];
44
55
  }
45
56
 
46
- // Returns the users translation or english as a fallback
57
+ // Returns the users' translation or English as a fallback
47
58
  //
48
59
  // Note that we still check for the case that no english translation can be
49
60
  // found. While this can't happen for the "regular" wger server, other local
@@ -97,25 +108,26 @@ export class ExerciseAdapter implements Adapter<Exercise> {
97
108
  const translationAdapter = new TranslationAdapter();
98
109
  const videoAdapter = new ExerciseVideoAdapter();
99
110
 
100
- const exercise = new Exercise(
101
- item.id,
102
- item.uuid,
103
- categoryAdapter.fromJson(item.category),
111
+ const exercise = new Exercise({
112
+ id: item.id,
113
+ uuid: item.uuid,
114
+ category: categoryAdapter.fromJson(item.category),
104
115
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
105
- item.equipment.map((e: any) => (equipmentAdapter.fromJson(e))),
116
+ equipment: item.equipment.map((e: any) => equipmentAdapter.fromJson(e)),
106
117
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
- item.muscles.map((m: any) => (muscleAdapter.fromJson(m))),
118
+ muscles: item.muscles.map((m: any) => muscleAdapter.fromJson(m)),
108
119
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
- item.muscles_secondary.map((m: any) => (muscleAdapter.fromJson(m))),
120
+ musclesSecondary: item.muscles_secondary.map((m: any) => muscleAdapter.fromJson(m)),
110
121
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
- item.images.map((i: any) => (imageAdapter.fromJson(i))),
112
- item.variations,
122
+ images: item.images.map((i: any) => imageAdapter.fromJson(i)),
123
+ variationId: item.variations,
113
124
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
114
- item.translations.map((t: any) => translationAdapter.fromJson(t)),
125
+ translations: item.translations.map((t: any) => translationAdapter.fromJson(t)),
115
126
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
116
- item.videos.map((t: any) => videoAdapter.fromJson(t)),
117
- item.author_history
118
- );
127
+ videos: item.videos.map((t: any) => videoAdapter.fromJson(t)),
128
+ authors: item.total_authors_history,
129
+ lastUpdateGlobal: new Date(item.last_update_global),
130
+ });
119
131
 
120
132
  if (!exercise.translations.some(t => t.language === ENGLISH_LANGUAGE_ID)) {
121
133
  console.info(`No english translation found for exercise base ${exercise.uuid}!`);
@@ -52,15 +52,17 @@ describe("Test the MeasurementCategoryDetail component", () => {
52
52
  </QueryClientProvider>
53
53
  );
54
54
 
55
+ screen.logTestingPlaygroundURL();
56
+
55
57
  // Assert
56
58
  expect(useMeasurementsQuery).toHaveBeenCalled();
57
59
  expect(screen.getByText('Biceps')).toBeInTheDocument();
58
60
 
59
- expect(screen.getByText('10cm')).toBeInTheDocument();
61
+ expect(screen.getByRole('gridcell', { name: /10cm/i })).toBeInTheDocument();
60
62
  expect(screen.getByText(/Feb 1, 2023/i)).toBeInTheDocument();
61
63
  expect(screen.getByText('test note')).toBeInTheDocument();
62
64
 
63
- expect(screen.getByText('20cm')).toBeInTheDocument();
65
+ expect(screen.getByRole('gridcell', { name: /20cm/i })).toBeInTheDocument();
64
66
  expect(screen.getByText(/Feb 2, 2023/i)).toBeInTheDocument();
65
67
  expect(screen.getByText('important note')).toBeInTheDocument();
66
68
  });
@@ -1,7 +1,7 @@
1
1
  import { Box } from "@mui/material";
2
2
  import { MeasurementCategory } from "components/Measurements/models/Category";
3
3
  import React from "react";
4
- import { CartesianGrid, Line, LineChart, ResponsiveContainer, XAxis, YAxis } from "recharts";
4
+ import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
5
5
  import { theme } from "theme";
6
6
  import { dateToLocale } from "utils/date";
7
7
 
@@ -19,34 +19,31 @@ export const MeasurementChart = (props: { category: MeasurementCategory }) => {
19
19
 
20
20
 
21
21
  return <Box alignItems={'center'} display={'flex'} flexDirection={'column'}>
22
- <ResponsiveContainer width="90%" height={200}>
23
-
24
- <LineChart data={entryData}>
25
- <Line
26
- type="monotone"
27
- dataKey="value"
28
- stroke={theme.palette.secondary.main}
29
- strokeWidth={2}
30
- dot={entryData.length > NR_OF_ENTRIES_CHART_DOT ? false : { strokeWidth: 1, r: 4 }}
31
- activeDot={{
32
- stroke: 'black',
33
- strokeWidth: 1,
34
- r: 6,
35
- //onClick: handleClick
36
- }} />
37
- <CartesianGrid
38
- stroke="#ccc"
39
- strokeDasharray="5 5" />
40
- <XAxis
41
- dataKey="date"
42
- type={'number'}
43
- domain={['dataMin', 'dataMax']}
44
- tickFormatter={timeStr => dateToLocale(new Date(timeStr))!}
45
- tickCount={10}
46
- />
47
- <YAxis domain={['auto', 'auto']} unit={props.category.unit} />
48
- {/*<Tooltip content={<CustomTooltip />} />*/}
49
- </LineChart>
50
- </ResponsiveContainer>
22
+ <LineChart data={entryData} responsive width="90%" height={200}>
23
+ <Line
24
+ type="monotone"
25
+ dataKey="value"
26
+ stroke={theme.palette.secondary.main}
27
+ strokeWidth={2}
28
+ dot={entryData.length > NR_OF_ENTRIES_CHART_DOT ? false : { strokeWidth: 1, r: 4 }}
29
+ activeDot={{
30
+ stroke: 'black',
31
+ strokeWidth: 1,
32
+ r: 6,
33
+ //onClick: handleClick
34
+ }} />
35
+ <CartesianGrid
36
+ stroke="#ccc"
37
+ strokeDasharray="5 5" />
38
+ <XAxis
39
+ dataKey="date"
40
+ type={'number'}
41
+ domain={['dataMin', 'dataMax']}
42
+ tickFormatter={timeStr => dateToLocale(new Date(timeStr))!}
43
+ tickCount={10}
44
+ />
45
+ <YAxis domain={['auto', 'auto']} unit={props.category.unit} />
46
+ {/*<Tooltip content={<CustomTooltip />} />*/}
47
+ </LineChart>
51
48
  </Box>;
52
49
  };
@@ -6,7 +6,7 @@ import { WgerContainerRightSidebar } from "components/Core/Widgets/Container";
6
6
  import { useProfileQuery } from "components/User/queries/profile";
7
7
  import React, { useEffect, useState } from "react";
8
8
  import { useTranslation } from "react-i18next";
9
- import { Area, AreaChart, CartesianGrid, ReferenceDot, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts";
9
+ import { Area, AreaChart, CartesianGrid, ReferenceDot, Tooltip, XAxis, YAxis, } from "recharts";
10
10
 
11
11
  const bmiRanges = [
12
12
  { range: "obese", color: "#FF5733", min: 30, max: 100 },
@@ -128,48 +128,46 @@ export const BmiCalculator = () => {
128
128
  {t('bmi.result', { value: bmi.toFixed(1) })}
129
129
  </Typography>
130
130
  }
131
- <ResponsiveContainer width="100%" height={400}>
132
- <AreaChart data={chartData}>
133
- <XAxis
134
- dataKey="height"
135
- type="number"
136
- domain={[140, 220]}
137
- unit="cm"
138
- />
139
- <YAxis
140
- domain={[40, 150]}
141
- tickFormatter={(value) => Math.round(value).toString()} // Format as integers
142
- unit="kg"
131
+ <AreaChart data={chartData} responsive width="100%" height={400}>
132
+ <XAxis
133
+ dataKey="height"
134
+ type="number"
135
+ domain={[140, 220]}
136
+ unit="cm"
137
+ />
138
+ <YAxis
139
+ domain={[40, 150]}
140
+ tickFormatter={(value) => Math.round(value).toString()} // Format as integers
141
+ unit="kg"
142
+ />
143
+ <CartesianGrid strokeDasharray="3 3" />
144
+ <Tooltip
145
+ // @ts-ignore
146
+ formatter={(value, name) => [Math.round(value as number), t('bmi.' + (name as string))]}
147
+ />
148
+
149
+ {bmiRanges.map((range) => (
150
+ <Area
151
+ key={range.range}
152
+ type="monotone"
153
+ dataKey={range.range}
154
+ stroke={"black"}
155
+ // stroke={range.color}
156
+ fill={range.color}
157
+ fillOpacity={0.8}
143
158
  />
144
- <CartesianGrid strokeDasharray="3 3" />
145
- <Tooltip
146
- // @ts-ignore
147
- formatter={(value, name) => [Math.round(value as number), t('bmi.' + (name as string))]}
148
- />
149
-
150
- {bmiRanges.map((range) => (
151
- <Area
152
- key={range.range}
153
- type="monotone"
154
- dataKey={range.range}
155
- stroke={"black"}
156
- // stroke={range.color}
157
- fill={range.color}
158
- fillOpacity={0.8}
159
- />
160
- ))}
161
-
162
- {bmi !== null &&
163
- <ReferenceDot
164
- x={height!}
165
- y={weight!}
166
- r={8}
167
- fill="black"
168
- stroke="none"
169
- />}
170
-
171
- </AreaChart>
172
- </ResponsiveContainer>
159
+ ))}
160
+
161
+ {bmi !== null &&
162
+ <ReferenceDot
163
+ x={height!}
164
+ y={weight!}
165
+ r={8}
166
+ fill="black"
167
+ stroke="none"
168
+ />}
169
+
170
+ </AreaChart>
173
171
  <Stack direction={"row"} justifyContent="center">
174
172
  <Box
175
173
  height={20}
@@ -1,7 +1,7 @@
1
1
  import { NutritionalValues } from "components/Nutrition/helpers/nutritionalValues";
2
2
  import React from "react";
3
3
  import { useTranslation } from "react-i18next";
4
- import { Cell, Legend, Pie, PieChart, ResponsiveContainer } from 'recharts';
4
+ import { Cell, Legend, Pie, PieChart } from 'recharts';
5
5
  import { generateChartColors } from "utils/colors";
6
6
  import { numberGramLocale } from "utils/numbers";
7
7
 
@@ -45,22 +45,20 @@ export const MacrosPieChart = (props: { data: NutritionalValues }) => {
45
45
  };
46
46
 
47
47
 
48
- return <ResponsiveContainer width={"100%"} height={300}>
49
- <PieChart>
50
- <Pie
51
- data={data}
52
- labelLine={false}
53
- // outerRadius={80}
54
- label={renderCustomizedLabel}
55
- fill="#8884d8"
56
- dataKey="value"
57
- >
58
- {data.map((entry, index) => (
59
- <Cell key={`cell-${index}`} fill={colorGenerator.next().value!} />
60
- ))}
61
- </Pie>
62
- {/*<Tooltip />*/}
63
- <Legend />
64
- </PieChart>
65
- </ResponsiveContainer>;
48
+ return <PieChart responsive width={"100%"} height={300}>
49
+ <Pie
50
+ data={data}
51
+ labelLine={false}
52
+ // outerRadius={80}
53
+ label={renderCustomizedLabel}
54
+ fill="#8884d8"
55
+ dataKey="value"
56
+ >
57
+ {data.map((entry, index) => (
58
+ <Cell key={`cell-${index}`} fill={colorGenerator.next().value!} />
59
+ ))}
60
+ </Pie>
61
+ {/*<Tooltip />*/}
62
+ <Legend />
63
+ </PieChart>;
66
64
  };