diff --git a/src/components/Routines/screens/Detail/WorkoutLogs.test.tsx b/src/components/Routines/screens/Detail/WorkoutLogs.test.tsx index ac1458c4a..3b35b3f86 100644 --- a/src/components/Routines/screens/Detail/WorkoutLogs.test.tsx +++ b/src/components/Routines/screens/Detail/WorkoutLogs.test.tsx @@ -21,6 +21,9 @@ import { import type { Mock } from 'vitest'; vi.mock("@/components/Routines/queries"); +vi.mock('@/components/User/queries/profile', () => ({ + useProfileQuery: () => ({ isLoading: false, data: { useMetric: true } }), +})); const queryClient = new QueryClient(); diff --git a/src/components/Routines/widgets/LogWidgets.test.tsx b/src/components/Routines/widgets/LogWidgets.test.tsx index f1f928952..305db406f 100644 --- a/src/components/Routines/widgets/LogWidgets.test.tsx +++ b/src/components/Routines/widgets/LogWidgets.test.tsx @@ -1,7 +1,9 @@ import { render, screen } from '@testing-library/react'; import userEvent from "@testing-library/user-event"; import { useDeleteRoutineLogQuery, useEditRoutineLogQuery } from '@/components/Routines/queries'; -import { ExerciseLog } from "@/components/Routines/widgets/LogWidgets"; +import { WorkoutLog } from "@/components/Routines/models/WorkoutLog"; +import { ExerciseLog, formatData } from "@/components/Routines/widgets/LogWidgets"; +import { WEIGHT_UNIT_KG, WEIGHT_UNIT_LB } from "@/core/lib/consts"; import { testExerciseBenchPress } from "@/tests/exerciseTestdata"; import { testWorkoutLogs } from "@/tests/workoutLogsRoutinesTestData"; import type { Mock } from 'vitest'; @@ -10,6 +12,9 @@ vi.mock('@/components/Routines/queries', () => ({ useDeleteRoutineLogQuery: vi.fn(), useEditRoutineLogQuery: vi.fn() })); +vi.mock('@/components/User/queries/profile', () => ({ + useProfileQuery: () => ({ isLoading: false, data: { useMetric: true } }), +})); describe('ExerciseLog', () => { let mockDeleteMutate: Mock; @@ -75,4 +80,22 @@ describe('ExerciseLog', () => { expect(mockDeleteMutate).not.toHaveBeenCalled(); expect(mockEditMutate).toHaveBeenCalled(); }); -}); \ No newline at end of file +}); + +describe('formatData', () => { + const logIn = (weight: number | null, weightUnitId: number | null) => + new WorkoutLog({ ...testWorkoutLogs[0], weight: weight, weightUnitId: weightUnitId }); + + test('converts weights logged in kg or lb to the chart unit', () => { + const data = [logIn(100, WEIGHT_UNIT_LB), logIn(50, WEIGHT_UNIT_KG)]; + + expect(formatData(data, 'kg').map(entry => entry.value)).toEqual([45.36, 50]); + expect(formatData(data, 'lb').map(entry => entry.value)).toEqual([100, 110.23]); + }); + + test('passes other units and missing weights through unchanged', () => { + const data = [logIn(12, 3), logIn(null, WEIGHT_UNIT_LB)]; + + expect(formatData(data, 'lb').map(entry => entry.value)).toEqual([12, null]); + }); +}); diff --git a/src/components/Routines/widgets/LogWidgets.tsx b/src/components/Routines/widgets/LogWidgets.tsx index 7c33104d7..6ee703a75 100644 --- a/src/components/Routines/widgets/LogWidgets.tsx +++ b/src/components/Routines/widgets/LogWidgets.tsx @@ -18,6 +18,7 @@ import { } from "@mui/x-data-grid"; import { FormQueryErrors } from "@/core/ui/Widgets/FormError"; import { Exercise } from "@/components/Exercises"; +import { useDisplayWeightUnit } from "@/components/Measurements"; import { RIR_VALUES_SELECT_LIST } from "@/components/Routines/models/BaseConfig"; import { WorkoutLog } from "@/components/Routines/models/WorkoutLog"; import { useDeleteRoutineLogQuery, useEditRoutineLogQuery } from "@/components/Routines/queries"; @@ -36,8 +37,9 @@ import { } from "recharts"; import { NameType, ValueType } from "recharts/types/component/DefaultTooltipContent"; import { generateChartColors } from "@/core/lib/colors"; -import { PAGINATION_OPTIONS } from "@/core/lib/consts"; +import { PAGINATION_OPTIONS, WEIGHT_UNIT_KG, WEIGHT_UNIT_LB } from "@/core/lib/consts"; import { dateToLocale, luxonDateTimeToLocale } from "@/core/lib/date"; +import { convertWeight, WeightUnit } from "@/core/lib/weightUnit"; export const ExerciseLog = (props: { exercise: Exercise, routineId: number, logEntries: WorkoutLog[] | undefined }) => { @@ -243,13 +245,18 @@ export const ExerciseLog = (props: { exercise: Exercise, routineId: number, logE * Format the log entries so that they can be passed to the chart * * This is mostly due to the time, which needs to be a number to be shown - * in the scatter plot + * in the scatter plot. Weights logged in kg or lb are converted to the unit + * the chart is drawn in, other units are passed through unchanged. */ -const formatData = (data: WorkoutLog[]) => +export const formatData = (data: WorkoutLog[], unit: WeightUnit) => data.map((log) => { + const logUnit: WeightUnit | null = log.weightUnitId === WEIGHT_UNIT_KG + ? 'kg' + : log.weightUnitId === WEIGHT_UNIT_LB ? 'lb' : null; + return { id: log.id, - value: log.weight, + value: log.weight !== null && logUnit !== null ? convertWeight(log.weight, logUnit, unit) : log.weight, time: log.date.getTime(), entry: log, }; @@ -279,6 +286,9 @@ const ExerciseLogTooltip = ({ active, payload }: TooltipContentProps { + const { t } = useTranslation(); + const weightUnit = useDisplayWeightUnit(); + const weightUnitLabel = t(weightUnit === 'lb' ? 'server.lb' : 'server.kg'); // Group by rep count // @@ -306,12 +316,12 @@ export const TimeSeriesChart = (props: { data: WorkoutLog[] }) => { domain={["auto", "auto"]} dataKey="value" name="Value" - unit="kg" + unit={weightUnitLabel} /> {Array.from(result).map(([key, value]) => { const color = colorGenerator.next().value!; - const formattedData = formatData(value); + const formattedData = formatData(value, weightUnit); return