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
6 changes: 6 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,12 @@ ij_json_spaces_within_braces = false
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false

# Weblate writes translation files with 4-space indent. Match that to avoid
# whole-file reformat diffs when an editor saves them.
[public/locales/**/*.json]
indent_size = 4
tab_width = 4

[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}]
ij_continuation_indent_size = 4
ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3
Expand Down
8 changes: 6 additions & 2 deletions src/components/Calendar/Components/CalendarComponent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,14 @@ describe('CalendarComponent', () => {

(getMeasurementCategories as Mock).mockImplementation(() => Promise.resolve([
new MeasurementCategory(
1,
'cccccccc-cccc-cccc-cccc-000000000001',
"Body Fat",
"%",
[new MeasurementEntry(1, 1, new Date(currentYear, currentMonth, 1, 12, 0), 20, "Normal")]
[new MeasurementEntry(
'dddddddd-dddd-dddd-dddd-000000000001',
'cccccccc-cccc-cccc-cccc-000000000001',
new Date(currentYear, currentMonth, 1, 12, 0), 20, "Normal"
)]
),
]));

Expand Down
2 changes: 1 addition & 1 deletion src/components/Dashboard/NutritionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ function NutritionCardContent(props: { plan: NutritionalPlan }) {
);
}

const MealListItem = (props: { meal: Meal; planId: number }) => {
const MealListItem = (props: { meal: Meal; planId: string }) => {
const [t, i18n] = useTranslation();
const addDiaryEntryQuery = useAddDiaryEntryQuery(props.planId);

Expand Down
5 changes: 2 additions & 3 deletions src/components/Exercises/api/exerciseTranslation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ describe("Exercise translation service API tests", () => {
});



test('POST a new exercise translation', async () => {

// Arrange
Expand Down Expand Up @@ -122,10 +121,10 @@ describe("Exercise translation service API tests", () => {
name: "Squats",
description: "Do a squat",
notes: [],
creation_date: "2022-06-23",
creation_date: "2022-06-23",
language: 2,
license: 2,
author_history: ["tester"],
author_history: ["tester"],
},
],
};
Expand Down
64 changes: 35 additions & 29 deletions src/components/Measurements/api/measurements.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,21 @@ import type { Mock } from 'vitest';

vi.mock("axios");

// Recognisable test-marker UUIDs matching the Django fixtures convention
const CATEGORY_UUID = 'cccccccc-cccc-cccc-cccc-000000000001';
const CATEGORY_UUID_2 = 'cccccccc-cccc-cccc-cccc-000000000009';
const ENTRY_UUID = 'dddddddd-dddd-dddd-dddd-000000000001';
const ENTRY_UUID_2 = 'dddddddd-dddd-dddd-dddd-000000000005';

describe('measurement service tests', () => {
const measurementEntryResponse = {
count: 2,
next: null,
previous: null,
results: [
{
"id": 1,
"category": 1,
"id": ENTRY_UUID,
"category": CATEGORY_UUID,
"value": 80,
"date": "2021-01-01",
"notes": ""
Expand All @@ -37,15 +43,15 @@ describe('measurement service tests', () => {
previous: null,
results: [
{
"id": 1,
"id": CATEGORY_UUID,
"name": "Weight",
"unit": "kg"
}
]
};

const measurementDetailResponse = {
"id": 1,
"id": CATEGORY_UUID,
"name": "Weight",
"unit": "kg"
};
Expand All @@ -57,7 +63,7 @@ describe('measurement service tests', () => {
(axios.get as Mock).mockImplementation((url: string) => {
if (url.includes("measurement-category")) {
return Promise.resolve({ data: measurementOverviewResponse });
} else if (url.includes("measurement/?category=1")) {
} else if (url.includes(`measurement/?category=${CATEGORY_UUID}`)) {
return Promise.resolve({ data: measurementEntryResponse });
}
});
Expand Down Expand Up @@ -88,35 +94,35 @@ describe('measurement service tests', () => {
expect(axios.get).toHaveBeenCalledTimes(2);

expect(result).toStrictEqual([
new MeasurementCategory(1, "Weight", "kg", [
new MeasurementEntry(1, 1, new Date("2021-01-01"), 80, "")
new MeasurementCategory(CATEGORY_UUID, "Weight", "kg", [
new MeasurementEntry(ENTRY_UUID, CATEGORY_UUID, new Date("2021-01-01"), 80, "")
])
]);
});

test('GET measurement category', async () => {

(axios.get as Mock).mockImplementation((url: string) => {
if (url.includes("measurement-category/1")) {
if (url.includes(`measurement-category/${CATEGORY_UUID}`)) {
return Promise.resolve({ data: measurementDetailResponse });
} else if (url.includes("measurement/?category=1")) {
} else if (url.includes(`measurement/?category=${CATEGORY_UUID}`)) {
return Promise.resolve({ data: measurementEntryResponse });
}
});

const result = await getMeasurementCategory(1);
const result = await getMeasurementCategory(CATEGORY_UUID);
expect(axios.get).toHaveBeenCalledTimes(2);

expect(result).toStrictEqual(
new MeasurementCategory(1, "Weight", "kg", [
new MeasurementEntry(1, 1, new Date("2021-01-01"), 80, "")
new MeasurementCategory(CATEGORY_UUID, "Weight", "kg", [
new MeasurementEntry(ENTRY_UUID, CATEGORY_UUID, new Date("2021-01-01"), 80, "")
])
);
});

test('addMeasurementCategory POSTs name + unit and returns the parsed category', async () => {
(axios.post as Mock).mockResolvedValue({
data: { id: 9, name: "Body fat", unit: "%" },
data: { id: CATEGORY_UUID_2, name: "Body fat", unit: "%" },
});

const result = await addMeasurementCategory({ name: "Body fat", unit: "%" });
Expand All @@ -126,41 +132,41 @@ describe('measurement service tests', () => {
expect(url).toMatch(/\/api\/v2\/measurement-category\/$/);
expect(body).toEqual({ name: "Body fat", unit: "%" });
expect(result).toBeInstanceOf(MeasurementCategory);
expect(result.id).toBe(9);
expect(result.id).toBe(CATEGORY_UUID_2);
});

test('editMeasurementCategory PATCHes /measurement-category/<id>/', async () => {
(axios.patch as Mock).mockResolvedValue({
data: { id: 9, name: "Renamed", unit: "%" },
data: { id: CATEGORY_UUID_2, name: "Renamed", unit: "%" },
});

const result = await editMeasurementCategory({ id: 9, name: "Renamed", unit: "%" });
const result = await editMeasurementCategory({ id: CATEGORY_UUID_2, name: "Renamed", unit: "%" });

expect(axios.patch).toHaveBeenCalledTimes(1);
const [url, body] = (axios.patch as Mock).mock.calls[0];
expect(url).toMatch(/\/api\/v2\/measurement-category\/9\/$/);
expect(url).toMatch(new RegExp(`/api/v2/measurement-category/${CATEGORY_UUID_2}/$`));
expect(body).toEqual({ name: "Renamed", unit: "%" });
expect(result.name).toBe("Renamed");
});

test('deleteMeasurementCategory DELETEs /measurement-category/<id>/', async () => {
(axios.delete as Mock).mockResolvedValue({ status: 204 });

await deleteMeasurementCategory(9);
await deleteMeasurementCategory(CATEGORY_UUID_2);

expect(axios.delete).toHaveBeenCalledWith(
expect.stringMatching(/\/api\/v2\/measurement-category\/9\/$/),
expect.stringMatching(new RegExp(`/api/v2/measurement-category/${CATEGORY_UUID_2}/$`)),
expect.anything()
);
});

test('addMeasurementEntry POSTs the entry with serialized date', async () => {
(axios.post as Mock).mockResolvedValue({
data: { id: 5, category: 1, value: 80.5, date: "2024-08-01", notes: "" },
data: { id: ENTRY_UUID_2, category: CATEGORY_UUID, value: 80.5, date: "2024-08-01", notes: "" },
});

const result = await addMeasurementEntry({
categoryId: 1,
categoryId: CATEGORY_UUID,
date: new Date("2024-08-01T12:34:00Z"),
value: 80.5,
notes: "",
Expand All @@ -170,32 +176,32 @@ describe('measurement service tests', () => {
const [url, body] = (axios.post as Mock).mock.calls[0];
expect(url).toMatch(/\/api\/v2\/measurement\/$/);
expect(body).toMatchObject({
category: 1,
category: CATEGORY_UUID,
value: 80.5,
notes: "",
});
// Date is YYYY-MM-DD
expect(body.date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
expect(result).toBeInstanceOf(MeasurementEntry);
expect(result.id).toBe(5);
expect(result.id).toBe(ENTRY_UUID_2);
});

test('editMeasurementEntry PATCHes /measurement/<id>/ with date/value/notes only', async () => {
(axios.patch as Mock).mockResolvedValue({
data: { id: 5, category: 1, value: 81, date: "2024-08-02", notes: "edited" },
data: { id: ENTRY_UUID_2, category: CATEGORY_UUID, value: 81, date: "2024-08-02", notes: "edited" },
});

const result = await editMeasurementEntry({
id: 5,
categoryId: 1,
id: ENTRY_UUID_2,
categoryId: CATEGORY_UUID,
date: new Date("2024-08-02T00:00:00Z"),
value: 81,
notes: "edited",
});

expect(axios.patch).toHaveBeenCalledTimes(1);
const [url, body] = (axios.patch as Mock).mock.calls[0];
expect(url).toMatch(/\/api\/v2\/measurement\/5\/$/);
expect(url).toMatch(new RegExp(`/api/v2/measurement/${ENTRY_UUID_2}/$`));
// Note: 'category' is NOT sent on edit (categoryId is part of the params but ignored in body)
expect(body).not.toHaveProperty("category");
expect(body).toMatchObject({ value: 81, notes: "edited" });
Expand All @@ -206,10 +212,10 @@ describe('measurement service tests', () => {
test('deleteMeasurementEntry DELETEs /measurement/<id>/', async () => {
(axios.delete as Mock).mockResolvedValue({ status: 204 });

await deleteMeasurementEntry(5);
await deleteMeasurementEntry(ENTRY_UUID_2);

expect(axios.delete).toHaveBeenCalledWith(
expect.stringMatching(/\/api\/v2\/measurement\/5\/$/),
expect.stringMatching(new RegExp(`/api/v2/measurement/${ENTRY_UUID_2}/$`)),
expect.anything()
);
});
Expand Down
16 changes: 8 additions & 8 deletions src/components/Measurements/api/measurements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const getMeasurementCategories = async (options?: MeasurementQueryOptions
const settingsResponses = await Promise.all(entryResponses);

// Save entries to each category
let categoryId: number;
let categoryId: string;
settingsResponses.forEach((entries) => {
if (entries.length > 0) {
categoryId = entries[0].category;
Expand All @@ -65,7 +65,7 @@ export const getMeasurementCategories = async (options?: MeasurementQueryOptions
return categories;
};

export const getMeasurementCategory = async (id: number): Promise<MeasurementCategory> => {
export const getMeasurementCategory = async (id: string): Promise<MeasurementCategory> => {
const { data: receivedCategories } = await axios.get<ApiMeasurementCategoryType>(
makeUrl(API_MEASUREMENTS_CATEGORY_PATH, { id: id }),
{ headers: makeHeader() },
Expand Down Expand Up @@ -106,7 +106,7 @@ export const addMeasurementCategory = async (data: AddMeasurementCategoryParams)
};

export interface editMeasurementCategoryParams {
id: number,
id: string,
name: string;
unit: string;
}
Expand All @@ -124,18 +124,18 @@ export const editMeasurementCategory = async (data: editMeasurementCategoryParam
return MeasurementCategory.fromJson(response.data);
};

export const deleteMeasurementCategory = async (id: number): Promise<void> => {
export const deleteMeasurementCategory = async (id: string): Promise<void> => {
await axios.delete(makeUrl(API_MEASUREMENTS_CATEGORY_PATH, { id: id }), { headers: makeHeader() });
};


export const deleteMeasurementEntry = async (id: number): Promise<void> => {
export const deleteMeasurementEntry = async (id: string): Promise<void> => {
await axios.delete(makeUrl(API_MEASUREMENTS_ENTRY_PATH, { id: id }), { headers: makeHeader() });
};

export interface editMeasurementParams {
id: number,
categoryId?: number,
id: string,
categoryId?: string,
date: Date;
value: number;
notes: string;
Expand All @@ -156,7 +156,7 @@ export const editMeasurementEntry = async (data: editMeasurementParams): Promise
};

export interface AddMeasurementParams {
categoryId: number;
categoryId: string;
date: Date;
value: number;
notes: string;
Expand Down
2 changes: 1 addition & 1 deletion src/components/Measurements/models/Category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export class MeasurementCategory {
entries: MeasurementEntry[] = [];

constructor(
public id: number,
public id: string,
public name: string,
public unit: string,
entries?: MeasurementEntry[]
Expand Down
4 changes: 2 additions & 2 deletions src/components/Measurements/models/Entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { Adapter } from "@/core/lib/Adapter";
export class MeasurementEntry {

constructor(
public id: number | null,
public category: number,
public id: string | null,
public category: string,
public date: Date,
public value: number,
public notes: string
Expand Down
10 changes: 5 additions & 5 deletions src/components/Measurements/queries/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const useAddMeasurementCategoryQuery = () => {
});
};

export const useEditMeasurementCategoryQuery = (id: number) => {
export const useEditMeasurementCategoryQuery = (id: string) => {
const queryClient = useQueryClient();

return useMutation({
Expand All @@ -51,11 +51,11 @@ export const useEditMeasurementCategoryQuery = (id: number) => {
});
};

export const useDeleteMeasurementCategoryQuery = (id: number) => {
export const useDeleteMeasurementCategoryQuery = (id: string) => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (id: number) => deleteMeasurementCategory(id),
mutationFn: (id: string) => deleteMeasurementCategory(id),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [QueryKey.MEASUREMENTS, id]
Expand All @@ -68,7 +68,7 @@ export const useDeleteMeasurementCategoryQuery = (id: number) => {
};


export function useMeasurementsQuery(id: number) {
export function useMeasurementsQuery(id: string) {
return useQuery({
queryKey: [QueryKey.MEASUREMENTS, id],
queryFn: () => getMeasurementCategory(id)
Expand Down Expand Up @@ -111,7 +111,7 @@ export const useDeleteMeasurementsQuery = (/*id: number*/) => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (id: number) => deleteMeasurementEntry(id),
mutationFn: (id: string) => deleteMeasurementEntry(id),
onSuccess: () => queryClient.invalidateQueries({
queryKey: [QueryKey.MEASUREMENTS,]
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { useParams } from "react-router-dom";

export const MeasurementCategoryDetail = () => {
const params = useParams<{ categoryId: string }>();
const categoryId = parseInt(params.categoryId ?? '');
if (Number.isNaN(categoryId)) {
return <p>Please pass an integer as the category id.</p>;
const categoryId = params.categoryId ?? '';
if (!categoryId) {
return <p>Please pass a category id.</p>;
}

// eslint-disable-next-line react-hooks/rules-of-hooks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const CategoryDetailDataGrid = (props: { category: MeasurementCategory })
};

const handleDeleteClick = (id: GridRowId) => async () => {
deleteEntryQuery.mutate(parseInt(id.toString()));
deleteEntryQuery.mutate(id.toString());
};

const handleCancelClick = (id: GridRowId) => () => {
Expand Down
Loading
Loading