Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/components/Routines/screens/Detail/WorkoutLogs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
27 changes: 25 additions & 2 deletions src/components/Routines/widgets/LogWidgets.test.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -75,4 +80,22 @@ describe('ExerciseLog', () => {
expect(mockDeleteMutate).not.toHaveBeenCalled();
expect(mockEditMutate).toHaveBeenCalled();
});
});
});

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]);
});
});
22 changes: 16 additions & 6 deletions src/components/Routines/widgets/LogWidgets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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 }) => {
Expand Down Expand Up @@ -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'
Comment thread
rolandgeider marked this conversation as resolved.
: 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,
};
Expand Down Expand Up @@ -279,6 +286,9 @@ const ExerciseLogTooltip = ({ active, payload }: TooltipContentProps<ValueType,
};

export const TimeSeriesChart = (props: { data: WorkoutLog[] }) => {
const { t } = useTranslation();
const weightUnit = useDisplayWeightUnit();
const weightUnitLabel = t(weightUnit === 'lb' ? 'server.lb' : 'server.kg');

// Group by rep count
//
Expand Down Expand Up @@ -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 <Scatter
key={key}
Expand Down
Loading