@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
@@ -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 { Bar, BarChart, CartesianGrid, Legend, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
4
+ import { Bar, BarChart, CartesianGrid, Legend, Tooltip, XAxis, YAxis } from 'recharts';
5
5
  import { generateChartColors } from "utils/colors";
6
6
  import { numberLocale } from "utils/numbers";
7
7
 
@@ -50,46 +50,47 @@ export const NutritionDiaryChart = ({ showPlanned, planned, today, avg7Days }: N
50
50
  ];
51
51
 
52
52
  return (
53
- <ResponsiveContainer width={"100%"} height={300}>
54
- <BarChart
55
- data={data}
56
- margin={{
57
- top: 20,
58
- right: 30,
59
- left: 20,
60
- bottom: 5,
61
- }}
62
- >
63
- <CartesianGrid strokeDasharray="3 4" />
64
- <XAxis dataKey="name" />
65
- <YAxis
66
- type="number"
67
- orientation="left"
68
- unit={t('nutrition.gramShort')}
69
- />
70
- <Tooltip formatter={(value: number) => numberLocale(value, i18n.language)} />
71
- <Legend />
72
- {showPlanned &&
73
- <Bar
74
- dataKey="planned"
75
- unit={t('nutrition.gramShort')}
76
- name={t('nutrition.planned')}
77
- fill={colorGenerator.next().value!}
78
- />
79
- }
80
- <Bar
81
- dataKey="today"
82
- unit={t('nutrition.gramShort')}
83
- name={t('nutrition.today')}
84
- fill={colorGenerator.next().value!}
85
- />
53
+ <BarChart
54
+ data={data}
55
+ margin={{
56
+ top: 20,
57
+ right: 30,
58
+ left: 20,
59
+ bottom: 5,
60
+ }}
61
+ responsive
62
+ width={"100%"}
63
+ height={300}
64
+ >
65
+ <CartesianGrid strokeDasharray="3 4" />
66
+ <XAxis dataKey="name" />
67
+ <YAxis
68
+ type="number"
69
+ orientation="left"
70
+ unit={t('nutrition.gramShort')}
71
+ />
72
+ <Tooltip formatter={(value: number) => numberLocale(value, i18n.language)} />
73
+ <Legend />
74
+ {showPlanned &&
86
75
  <Bar
87
- dataKey="avg7Days"
76
+ dataKey="planned"
88
77
  unit={t('nutrition.gramShort')}
89
- name={t('nutrition.7dayAvg')}
78
+ name={t('nutrition.planned')}
90
79
  fill={colorGenerator.next().value!}
91
80
  />
92
- </BarChart>
93
- </ResponsiveContainer>
81
+ }
82
+ <Bar
83
+ dataKey="today"
84
+ unit={t('nutrition.gramShort')}
85
+ name={t('nutrition.today')}
86
+ fill={colorGenerator.next().value!}
87
+ />
88
+ <Bar
89
+ dataKey="avg7Days"
90
+ unit={t('nutrition.gramShort')}
91
+ name={t('nutrition.7dayAvg')}
92
+ fill={colorGenerator.next().value!}
93
+ />
94
+ </BarChart>
94
95
  );
95
96
  };
@@ -3,7 +3,7 @@ import { NutritionalValues } from "components/Nutrition/helpers/nutritionalValue
3
3
  import { LinearPlannedLoggedChart } from "components/Nutrition/widgets/charts/LinearPlannedLoggedChart";
4
4
  import React from 'react';
5
5
  import { useTranslation } from "react-i18next";
6
- import { Cell, Pie, PieChart, ResponsiveContainer } from 'recharts';
6
+ import { Cell, Pie, PieChart } from 'recharts';
7
7
  import { numberLocale } from "utils/numbers";
8
8
 
9
9
 
@@ -32,34 +32,32 @@ export const NutritionalValuesDashboardChart = (props: {
32
32
 
33
33
 
34
34
  return <Stack direction={'row'}>
35
- <ResponsiveContainer width={'50%'} height={140}>
36
- <PieChart>
37
- <Pie
38
- // cx={0}
39
- // cy={'10'}
40
- height={100}
41
- data={data}
42
- startAngle={200}
43
- endAngle={-20}
44
- innerRadius={60}
45
- outerRadius={70}
46
- paddingAngle={2}
47
- dataKey="value"
48
- >
49
- {data.map((entry, index) => (
50
- <Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
51
- ))}
52
- </Pie>
53
- <g>
54
- <text x={'50%'} y={'45%'} fontSize="1.25em" textAnchor="middle">{/*fill="#333"*/}
55
- {t('nutrition.valueEnergyKcal', { value: numberLocale(energyDiff, i18n.language) })}
56
- </text>
57
- <text x={'50%'} y={'60%'} fontSize="1em" textAnchor="middle">
58
- {props.planned.energy > 0 && t(energyPercentage < 100 ? 'nutrition.valueRemaining' : 'nutrition.valueTooMany')}
59
- </text>
60
- </g>
61
- </PieChart>
62
- </ResponsiveContainer>
35
+ <PieChart responsive width={'50%'} height={140}>
36
+ <Pie
37
+ // cx={0}
38
+ // cy={'10'}
39
+ height={100}
40
+ data={data}
41
+ startAngle={200}
42
+ endAngle={-20}
43
+ innerRadius={60}
44
+ outerRadius={70}
45
+ paddingAngle={2}
46
+ dataKey="value"
47
+ >
48
+ {data.map((entry, index) => (
49
+ <Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
50
+ ))}
51
+ </Pie>
52
+ <g>
53
+ <text x={'50%'} y={'45%'} fontSize="1.25em" textAnchor="middle">{/*fill="#333"*/}
54
+ {t('nutrition.valueEnergyKcal', { value: numberLocale(energyDiff, i18n.language) })}
55
+ </text>
56
+ <text x={'50%'} y={'60%'} fontSize="1em" textAnchor="middle">
57
+ {props.planned.energy > 0 && t(energyPercentage < 100 ? 'nutrition.valueRemaining' : 'nutrition.valueTooMany')}
58
+ </text>
59
+ </g>
60
+ </PieChart>
63
61
  <Stack width={'50%'} spacing={1}>
64
62
  <LinearPlannedLoggedChart
65
63
  title={t('nutrition.protein')}
@@ -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 { Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis } from 'recharts';
4
+ import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from 'recharts';
5
5
  import { generateChartColors } from "utils/colors";
6
6
 
7
7
 
@@ -32,23 +32,24 @@ export const NutritionalValuesPlannedLoggedChart = (props: {
32
32
 
33
33
 
34
34
  return (
35
- <ResponsiveContainer width={"100%"} height={150}>
36
- <BarChart
37
- data={data}
38
- layout="vertical"
39
- margin={{
40
- left: 60,
41
- }}
42
- >
43
- <CartesianGrid strokeDasharray="3 4" />
44
- <XAxis type={'number'} unit={'%'} />
45
- <YAxis type={'category'} dataKey={'name'} />
46
- <Bar
47
- dataKey="value"
48
- unit={'%'}
49
- fill={colorGenerator.next().value!}
50
- />
51
- </BarChart>
52
- </ResponsiveContainer>
35
+ <BarChart
36
+ data={data}
37
+ layout="vertical"
38
+ margin={{
39
+ left: 60,
40
+ }}
41
+ responsive
42
+ width={"100%"}
43
+ height={150}
44
+ >
45
+ <CartesianGrid strokeDasharray="3 4" />
46
+ <XAxis type={'number'} unit={'%'} />
47
+ <YAxis type={'category'} dataKey={'name'} />
48
+ <Bar
49
+ dataKey="value"
50
+ unit={'%'}
51
+ fill={colorGenerator.next().value!}
52
+ />
53
+ </BarChart>
53
54
  );
54
55
  };
@@ -209,25 +209,23 @@ export const WorkoutStats = () => {
209
209
 
210
210
  <Grid size={12}>
211
211
  <Box width="100%" height={400}>
212
- <ResponsiveContainer width="100%" height="100%">
213
- <LineChart width={500} height={300}>
214
- <CartesianGrid strokeDasharray="3 3" />
215
- <XAxis dataKey="category" type="category" allowDuplicatedCategory={false} />
216
- <YAxis dataKey="value" />
217
- <Tooltip
218
- formatter={(value: number) => Number.isInteger(value) ? value.toFixed(0) : value.toFixed(2)} />
219
- <Legend
220
- layout={"vertical"}
221
- verticalAlign="middle"
222
- align="right"
223
- wrapperStyle={{ paddingLeft: "20px" }}
224
- />
225
- {chartData.map((s) => (
226
- <Line dataKey="value" data={s.data} name={s.name} key={s.name}
227
- stroke={colorGenerator.next()!.value!} />
228
- ))}
229
- </LineChart>
230
- </ResponsiveContainer>
212
+ <LineChart width="100%" height="100%" responsive>
213
+ <CartesianGrid strokeDasharray="3 3" />
214
+ <XAxis dataKey="category" type="category" allowDuplicatedCategory={false} />
215
+ <YAxis dataKey="value" />
216
+ <Tooltip
217
+ formatter={(value: number) => Number.isInteger(value) ? value.toFixed(0) : value.toFixed(2)} />
218
+ <Legend
219
+ layout={"vertical"}
220
+ verticalAlign="middle"
221
+ align="right"
222
+ wrapperStyle={{ paddingLeft: "20px" }}
223
+ />
224
+ {chartData.map((s) => (
225
+ <Line dataKey="value" data={s.data} name={s.name} key={s.name}
226
+ stroke={colorGenerator.next()!.value!} />
227
+ ))}
228
+ </LineChart>
231
229
  </Box>
232
230
  </Grid>
233
231
 
@@ -24,6 +24,7 @@ import {
24
24
  import Grid from '@mui/material/Grid';
25
25
  import { LoadingPlaceholder, LoadingProgressIcon } from "components/Core/LoadingWidget/LoadingWidget";
26
26
  import { NameAutocompleter } from "components/Exercises/Filter/NameAutcompleter";
27
+ import { Exercise } from "components/Exercises/models/exercise";
27
28
  import { useProfileQuery } from "components/User/queries/profile";
28
29
  import { Day } from "components/WorkoutRoutines/models/Day";
29
30
  import { Slot } from "components/WorkoutRoutines/models/Slot";
@@ -43,7 +44,6 @@ import { SlotDetails } from "components/WorkoutRoutines/widgets/SlotDetails";
43
44
  import React, { useState } from "react";
44
45
  import { useTranslation } from "react-i18next";
45
46
  import { Link } from "react-router-dom";
46
- import { ExerciseSearchResponse } from "services/responseType";
47
47
  import { SNACKBAR_AUTO_HIDE_DURATION, WEIGHT_UNIT_KG, WEIGHT_UNIT_LB } from "utils/consts";
48
48
  import { makeLink, WgerLink } from "utils/url";
49
49
 
@@ -381,13 +381,13 @@ export const DayDetails = (props: {
381
381
  && <Grid size={12}>
382
382
  <Box height={20} />
383
383
  <NameAutocompleter
384
- callback={(exercise: ExerciseSearchResponse | null) => {
384
+ callback={(exercise: Exercise | null) => {
385
385
  if (exercise === null) {
386
386
  return;
387
387
  }
388
388
  addSlotEntryQuery.mutate(new SlotEntry({
389
389
  slotId: slot.id!,
390
- exerciseId: exercise.data.base_id,
390
+ exerciseId: exercise.id!,
391
391
  type: 'normal',
392
392
  order: slot.entries.length + 1,
393
393
  weightUnitId: userProfileQuery.data!.useMetric ? WEIGHT_UNIT_KG : WEIGHT_UNIT_LB,
@@ -27,7 +27,6 @@ import { useTranslation } from "react-i18next";
27
27
  import {
28
28
  CartesianGrid,
29
29
  Legend,
30
- ResponsiveContainer,
31
30
  Scatter,
32
31
  ScatterChart,
33
32
  Tooltip,
@@ -292,43 +291,41 @@ export const TimeSeriesChart = (props: { data: WorkoutLog[] }) => {
292
291
 
293
292
  return (
294
293
  <Box>
295
- <ResponsiveContainer width={"100%"} height={250}>
296
- <ScatterChart>
297
- <XAxis
298
- dataKey="time"
299
- domain={["auto", "auto"]}
300
- name="Time"
301
- tickFormatter={unixTime => luxonDateTimeToLocale(DateTime.fromMillis(unixTime))}
302
- type="number"
303
- />
304
- <YAxis
305
- domain={["auto", "auto"]}
306
- dataKey="value"
307
- name="Value"
308
- unit="kg"
309
- />
310
-
311
- {Array.from(result).map(([key, value]) => {
312
- const color = colorGenerator.next().value!;
313
- const formattedData = formatData(value);
314
-
315
- return <Scatter
316
- key={key}
317
- data={formattedData}
318
- fill={color}
319
- line={{ stroke: color }}
320
- lineType="joint"
321
- lineJointType="monotoneX"
322
- name={key?.toString()}
323
- />;
324
- }
325
- )}
326
-
327
- <Tooltip content={<ExerciseLogTooltip />} />
328
- <CartesianGrid strokeDasharray="3 3" />
329
- <Legend />
330
- </ScatterChart>
331
- </ResponsiveContainer>
294
+ <ScatterChart responsive width={"100%"} height={250}>
295
+ <XAxis
296
+ dataKey="time"
297
+ domain={["auto", "auto"]}
298
+ name="Time"
299
+ tickFormatter={unixTime => luxonDateTimeToLocale(DateTime.fromMillis(unixTime))}
300
+ type="number"
301
+ />
302
+ <YAxis
303
+ domain={["auto", "auto"]}
304
+ dataKey="value"
305
+ name="Value"
306
+ unit="kg"
307
+ />
308
+
309
+ {Array.from(result).map(([key, value]) => {
310
+ const color = colorGenerator.next().value!;
311
+ const formattedData = formatData(value);
312
+
313
+ return <Scatter
314
+ key={key}
315
+ data={formattedData}
316
+ fill={color}
317
+ line={{ stroke: color }}
318
+ lineType="joint"
319
+ lineJointType="monotoneX"
320
+ name={key?.toString()}
321
+ />;
322
+ }
323
+ )}
324
+
325
+ <Tooltip content={<ExerciseLogTooltip />} />
326
+ <CartesianGrid strokeDasharray="3 3" />
327
+ <Legend />
328
+ </ScatterChart>
332
329
  </Box>
333
330
  );
334
331
  };
@@ -4,6 +4,7 @@ import EditOffIcon from '@mui/icons-material/EditOff';
4
4
  import { Alert, AlertTitle, IconButton, Typography } from "@mui/material";
5
5
  import Grid from '@mui/material/Grid';
6
6
  import { NameAutocompleter } from "components/Exercises/Filter/NameAutcompleter";
7
+ import { Exercise } from "components/Exercises/models/exercise";
7
8
  import { useLanguageQuery } from "components/Exercises/queries";
8
9
  import { BaseConfig } from "components/WorkoutRoutines/models/BaseConfig";
9
10
  import { Slot } from "components/WorkoutRoutines/models/Slot";
@@ -21,7 +22,6 @@ import {
21
22
  import React, { useState } from "react";
22
23
  import { useTranslation } from "react-i18next";
23
24
  import { getLanguageByShortName } from "services";
24
- import { ExerciseSearchResponse } from "services/responseType";
25
25
 
26
26
  /*
27
27
  * Converts a number to an alphabetic string, useful for counting
@@ -95,12 +95,12 @@ export const SlotEntryDetails = (props: {
95
95
 
96
96
  const isPending = editSlotEntryQuery.isPending || deleteSlotEntryQuery.isPending;
97
97
 
98
- const handleExerciseChange = (searchResponse: ExerciseSearchResponse | null) => {
99
- if (searchResponse === null) {
98
+ const handleExerciseChange = (exercise: Exercise | null) => {
99
+ if (exercise === null) {
100
100
  return;
101
101
  }
102
102
 
103
- editSlotEntryQuery.mutate(SlotEntry.clone(props.slotEntry, { exerciseId: searchResponse.data.base_id }));
103
+ editSlotEntryQuery.mutate(SlotEntry.clone(props.slotEntry, { exerciseId: exercise.id! }));
104
104
  setEditExercise(false);
105
105
  };
106
106
 
@@ -6,6 +6,7 @@ import Grid from '@mui/material/Grid';
6
6
  import { WgerTextField } from "components/Common/forms/WgerTextField";
7
7
  import { LoadingPlaceholder } from "components/Core/LoadingWidget/LoadingWidget";
8
8
  import { NameAutocompleter } from "components/Exercises/Filter/NameAutcompleter";
9
+ import { Exercise } from "components/Exercises/models/exercise";
9
10
  import { useLanguageQuery } from "components/Exercises/queries";
10
11
  import { RIR_VALUES_SELECT } from "components/WorkoutRoutines/models/BaseConfig";
11
12
  import { LogEntryForm } from "components/WorkoutRoutines/models/WorkoutLog";
@@ -15,7 +16,6 @@ import { DateTime } from "luxon";
15
16
  import React, { useState } from 'react';
16
17
  import { useTranslation } from "react-i18next";
17
18
  import { getLanguageByShortName } from "services";
18
- import { ExerciseSearchResponse } from "services/responseType";
19
19
  import { REP_UNIT_REPETITIONS, SNACKBAR_AUTO_HIDE_DURATION } from "utils/consts";
20
20
  import * as yup from "yup";
21
21
 
@@ -96,11 +96,11 @@ export const SessionLogsForm = ({ dayId, routineId, selectedDate }: SessionLogsF
96
96
  setSnackbarOpen(true);
97
97
  };
98
98
 
99
- const handleCallback = async (exerciseResponse: ExerciseSearchResponse | null, formik: FormikProps<{
99
+ const handleCallback = async (exercise: Exercise | null, formik: FormikProps<{
100
100
  logs: LogEntryForm[]
101
101
  }>) => {
102
102
 
103
- if (exerciseResponse === null) {
103
+ if (exercise === null) {
104
104
  return;
105
105
  }
106
106
 
@@ -115,7 +115,7 @@ export const SessionLogsForm = ({ dayId, routineId, selectedDate }: SessionLogsF
115
115
  repetitionsTarget: '',
116
116
  rir: '',
117
117
  rirTarget: '',
118
- exercise: exerciseResponse.exercise!,
118
+ exercise: exercise,
119
119
  };
120
120
  }
121
121
  return log;
@@ -1,12 +1,12 @@
1
1
  import axios from 'axios';
2
+ import { Exercise, ExerciseAdapter } from "components/Exercises/models/exercise";
2
3
  import { Translation, TranslationAdapter } from "components/Exercises/models/translation";
3
4
  import { ENGLISH_LANGUAGE_CODE, LANGUAGE_SHORT_ENGLISH } from "utils/consts";
4
5
  import { makeHeader, makeUrl } from "utils/url";
5
- import { ExerciseSearchResponse, ExerciseSearchType, ResponseType } from "./responseType";
6
+ import { ResponseType } from "./responseType";
6
7
 
7
8
  export const EXERCISE_PATH = 'exercise';
8
9
  export const EXERCISE_TRANSLATION_PATH = 'exercise-translation';
9
- export const EXERCISE_SEARCH_PATH = 'exercise/search';
10
10
 
11
11
 
12
12
  /*
@@ -15,7 +15,7 @@ export const EXERCISE_SEARCH_PATH = 'exercise/search';
15
15
  export const getExerciseTranslations = async (id: number): Promise<Translation[]> => {
16
16
  const url = makeUrl(EXERCISE_PATH, { query: { exercise: id } });
17
17
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
- const { data } = await axios.get<ResponseType<any>>(url, {
18
+ const { data } = await axios.get<ResponseType<Translation>>(url, {
19
19
  headers: makeHeader(),
20
20
  });
21
21
  const adapter = new TranslationAdapter();
@@ -24,19 +24,34 @@ export const getExerciseTranslations = async (id: number): Promise<Translation[]
24
24
 
25
25
 
26
26
  /*
27
- * Fetch all exercise translations for a given exercise base
27
+ * Search for exercises by name using the exerciseinfo endpoint
28
28
  */
29
- export const searchExerciseTranslations = async (name: string, languageCode: string = ENGLISH_LANGUAGE_CODE, searchEnglish: boolean = true,): Promise<ExerciseSearchResponse[]> => {
29
+ export const searchExerciseTranslations = async (name: string,languageCode: string = ENGLISH_LANGUAGE_CODE,searchEnglish: boolean = true): Promise<Exercise[]> => {
30
30
  const languages = [languageCode];
31
31
  if (languageCode !== LANGUAGE_SHORT_ENGLISH && searchEnglish) {
32
32
  languages.push(LANGUAGE_SHORT_ENGLISH);
33
33
  }
34
34
 
35
+ const url = makeUrl('exerciseinfo', {
36
+ query: {
37
+ name__search: name,
38
+ language__code: languages.join(','),
39
+ limit: 50,
40
+ }
41
+ });
35
42
 
36
- const url = makeUrl(EXERCISE_SEARCH_PATH, { query: { term: name, language: languages.join(',') } });
37
-
38
- const { data } = await axios.get<ExerciseSearchType>(url);
39
- return data.suggestions;
43
+ try {
44
+ const { data } = await axios.get<ResponseType<Exercise>>(url);
45
+
46
+ if (!data || !data.results || !Array.isArray(data.results)) {
47
+ return [];
48
+ }
49
+
50
+ const adapter = new ExerciseAdapter();
51
+ return data.results.map((item: unknown) => adapter.fromJson(item));
52
+ } catch {
53
+ return [];
54
+ }
40
55
  };
41
56
 
42
57
 
@@ -2,6 +2,7 @@ import axios from 'axios';
2
2
  import { MeasurementCategory, MeasurementCategoryAdapter } from "components/Measurements/models/Category";
3
3
  import { MeasurementEntry, MeasurementEntryAdapter } from "components/Measurements/models/Entry";
4
4
  import { ApiMeasurementCategoryType } from 'types';
5
+ import { API_MAX_PAGE_SIZE } from "utils/consts";
5
6
  import { dateToYYYYMMDD } from "utils/date";
6
7
  import { fetchPaginated } from 'utils/requests';
7
8
  import { makeHeader, makeUrl } from "utils/url";
@@ -20,8 +21,14 @@ export const getMeasurementCategories = async (options?: MeasurementQueryOptions
20
21
  const adapter = new MeasurementCategoryAdapter();
21
22
  const entryAdapter = new MeasurementEntryAdapter();
22
23
  const categories: MeasurementCategory[] = [];
24
+ const categoryUrl = makeUrl(API_MEASUREMENTS_CATEGORY_PATH, {
25
+ query: {
26
+ limit: API_MAX_PAGE_SIZE,
27
+ ...filtersetQueryCategories
28
+ }
29
+ });
23
30
 
24
- for await (const page of fetchPaginated(makeUrl(API_MEASUREMENTS_CATEGORY_PATH, { query: { ...filtersetQueryCategories } }), makeHeader())) {
31
+ for await (const page of fetchPaginated(categoryUrl, makeHeader())) {
25
32
  for (const catData of page) {
26
33
  categories.push(adapter.fromJson(catData));
27
34
  }
@@ -30,7 +37,13 @@ export const getMeasurementCategories = async (options?: MeasurementQueryOptions
30
37
  // Load entries for each category
31
38
  const entryResponses = categories.map(async (category) => {
32
39
  const out: MeasurementEntry[] = [];
33
- const url = makeUrl(API_MEASUREMENTS_ENTRY_PATH, { query: { category: category.id, ...filtersetQueryEntries } });
40
+ const url = makeUrl(API_MEASUREMENTS_ENTRY_PATH, {
41
+ query: {
42
+ category: category.id,
43
+ limit: API_MAX_PAGE_SIZE,
44
+ ...filtersetQueryEntries,
45
+ }
46
+ });
34
47
 
35
48
  // Collect all pages of entries
36
49
  for await (const page of fetchPaginated(url, makeHeader())) {
@@ -1,35 +1,9 @@
1
- import { Exercise } from "components/Exercises/models/exercise";
2
-
1
+ /*
2
+ * Generic paginated response from the wger API
3
+ */
3
4
  export interface ResponseType<T> {
4
5
  count: number,
5
6
  next: number | null,
6
7
  previous: number | null,
7
8
  results: T[]
8
9
  }
9
-
10
- export interface ExerciseSearchResponse {
11
- value: string,
12
- data: {
13
- id: number,
14
- base_id: number,
15
- name: string,
16
- category: string,
17
- image: string | null,
18
- image_thumbnail: string | null,
19
- },
20
- exercise?: Exercise
21
- }
22
-
23
- export interface ExerciseSearchType {
24
- suggestions: ExerciseSearchResponse[];
25
- }
26
-
27
- export interface IngredientSearchResponse {
28
- value: string,
29
- data: {
30
- id: number,
31
- name: string,
32
- image: string | null,
33
- image_thumbnail: string | null,
34
- }
35
- }