diff --git a/.editorconfig b/.editorconfig index ba2e60bb3..8c7b20388 100644 --- a/.editorconfig +++ b/.editorconfig @@ -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 diff --git a/src/components/Calendar/Components/CalendarComponent.test.tsx b/src/components/Calendar/Components/CalendarComponent.test.tsx index 9cebe159f..48a925082 100644 --- a/src/components/Calendar/Components/CalendarComponent.test.tsx +++ b/src/components/Calendar/Components/CalendarComponent.test.tsx @@ -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" + )] ), ])); diff --git a/src/components/Dashboard/NutritionCard.tsx b/src/components/Dashboard/NutritionCard.tsx index ffddd9c7e..7adcfe72e 100644 --- a/src/components/Dashboard/NutritionCard.tsx +++ b/src/components/Dashboard/NutritionCard.tsx @@ -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); diff --git a/src/components/Exercises/api/exerciseTranslation.test.ts b/src/components/Exercises/api/exerciseTranslation.test.ts index 7cf6f4007..85670268c 100644 --- a/src/components/Exercises/api/exerciseTranslation.test.ts +++ b/src/components/Exercises/api/exerciseTranslation.test.ts @@ -18,7 +18,6 @@ describe("Exercise translation service API tests", () => { }); - test('POST a new exercise translation', async () => { // Arrange @@ -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"], }, ], }; diff --git a/src/components/Measurements/api/measurements.test.ts b/src/components/Measurements/api/measurements.test.ts index 913f7f58b..d68606e23 100644 --- a/src/components/Measurements/api/measurements.test.ts +++ b/src/components/Measurements/api/measurements.test.ts @@ -15,6 +15,12 @@ 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, @@ -22,8 +28,8 @@ describe('measurement service tests', () => { previous: null, results: [ { - "id": 1, - "category": 1, + "id": ENTRY_UUID, + "category": CATEGORY_UUID, "value": 80, "date": "2021-01-01", "notes": "" @@ -37,7 +43,7 @@ describe('measurement service tests', () => { previous: null, results: [ { - "id": 1, + "id": CATEGORY_UUID, "name": "Weight", "unit": "kg" } @@ -45,7 +51,7 @@ describe('measurement service tests', () => { }; const measurementDetailResponse = { - "id": 1, + "id": CATEGORY_UUID, "name": "Weight", "unit": "kg" }; @@ -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 }); } }); @@ -88,8 +94,8 @@ 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, "") ]) ]); }); @@ -97,26 +103,26 @@ describe('measurement service tests', () => { 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: "%" }); @@ -126,19 +132,19 @@ 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//', 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"); }); @@ -146,21 +152,21 @@ describe('measurement service tests', () => { test('deleteMeasurementCategory DELETEs /measurement-category//', 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: "", @@ -170,24 +176,24 @@ 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// 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", @@ -195,7 +201,7 @@ describe('measurement service tests', () => { 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" }); @@ -206,10 +212,10 @@ describe('measurement service tests', () => { test('deleteMeasurementEntry DELETEs /measurement//', 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() ); }); diff --git a/src/components/Measurements/api/measurements.ts b/src/components/Measurements/api/measurements.ts index 515915b1e..b39dd0894 100644 --- a/src/components/Measurements/api/measurements.ts +++ b/src/components/Measurements/api/measurements.ts @@ -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; @@ -65,7 +65,7 @@ export const getMeasurementCategories = async (options?: MeasurementQueryOptions return categories; }; -export const getMeasurementCategory = async (id: number): Promise => { +export const getMeasurementCategory = async (id: string): Promise => { const { data: receivedCategories } = await axios.get( makeUrl(API_MEASUREMENTS_CATEGORY_PATH, { id: id }), { headers: makeHeader() }, @@ -106,7 +106,7 @@ export const addMeasurementCategory = async (data: AddMeasurementCategoryParams) }; export interface editMeasurementCategoryParams { - id: number, + id: string, name: string; unit: string; } @@ -124,18 +124,18 @@ export const editMeasurementCategory = async (data: editMeasurementCategoryParam return MeasurementCategory.fromJson(response.data); }; -export const deleteMeasurementCategory = async (id: number): Promise => { +export const deleteMeasurementCategory = async (id: string): Promise => { await axios.delete(makeUrl(API_MEASUREMENTS_CATEGORY_PATH, { id: id }), { headers: makeHeader() }); }; -export const deleteMeasurementEntry = async (id: number): Promise => { +export const deleteMeasurementEntry = async (id: string): Promise => { 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; @@ -156,7 +156,7 @@ export const editMeasurementEntry = async (data: editMeasurementParams): Promise }; export interface AddMeasurementParams { - categoryId: number; + categoryId: string; date: Date; value: number; notes: string; diff --git a/src/components/Measurements/models/Category.ts b/src/components/Measurements/models/Category.ts index 08861e1b3..9af75e341 100644 --- a/src/components/Measurements/models/Category.ts +++ b/src/components/Measurements/models/Category.ts @@ -6,7 +6,7 @@ export class MeasurementCategory { entries: MeasurementEntry[] = []; constructor( - public id: number, + public id: string, public name: string, public unit: string, entries?: MeasurementEntry[] diff --git a/src/components/Measurements/models/Entry.ts b/src/components/Measurements/models/Entry.ts index d2a23a9d3..5a095499e 100644 --- a/src/components/Measurements/models/Entry.ts +++ b/src/components/Measurements/models/Entry.ts @@ -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 diff --git a/src/components/Measurements/queries/index.ts b/src/components/Measurements/queries/index.ts index f958c16fb..9b30de8a1 100644 --- a/src/components/Measurements/queries/index.ts +++ b/src/components/Measurements/queries/index.ts @@ -35,7 +35,7 @@ export const useAddMeasurementCategoryQuery = () => { }); }; -export const useEditMeasurementCategoryQuery = (id: number) => { +export const useEditMeasurementCategoryQuery = (id: string) => { const queryClient = useQueryClient(); return useMutation({ @@ -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] @@ -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) @@ -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,] }) diff --git a/src/components/Measurements/screens/MeasurementCategoryDetail.tsx b/src/components/Measurements/screens/MeasurementCategoryDetail.tsx index 64dff3749..14d32509f 100644 --- a/src/components/Measurements/screens/MeasurementCategoryDetail.tsx +++ b/src/components/Measurements/screens/MeasurementCategoryDetail.tsx @@ -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

Please pass an integer as the category id.

; + const categoryId = params.categoryId ?? ''; + if (!categoryId) { + return

Please pass a category id.

; } // eslint-disable-next-line react-hooks/rules-of-hooks diff --git a/src/components/Measurements/widgets/CategoryDetailDataGrid.tsx b/src/components/Measurements/widgets/CategoryDetailDataGrid.tsx index 6a4d5bec8..8de749a22 100644 --- a/src/components/Measurements/widgets/CategoryDetailDataGrid.tsx +++ b/src/components/Measurements/widgets/CategoryDetailDataGrid.tsx @@ -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) => () => { diff --git a/src/components/Measurements/widgets/CategoryForm.test.tsx b/src/components/Measurements/widgets/CategoryForm.test.tsx index c8a53f581..907062bd2 100644 --- a/src/components/Measurements/widgets/CategoryForm.test.tsx +++ b/src/components/Measurements/widgets/CategoryForm.test.tsx @@ -65,7 +65,7 @@ describe("Test the CategoryForm component", () => { // Assert await user.click(submitButton); expect(mutate).toHaveBeenCalledWith({ - id: 2, + id: 'cccccccc-cccc-cccc-cccc-000000000002', name: "a better name", unit: 'K/m2', }); diff --git a/src/components/Measurements/widgets/CategoryForm.tsx b/src/components/Measurements/widgets/CategoryForm.tsx index 0d0352660..e94c089b4 100644 --- a/src/components/Measurements/widgets/CategoryForm.tsx +++ b/src/components/Measurements/widgets/CategoryForm.tsx @@ -15,7 +15,7 @@ export const CategoryForm = ({ category, closeFn }: CategoryFormProps) => { const [t] = useTranslation(); const useAddCategoryQuery = useAddMeasurementCategoryQuery(); - const useEditCategoryQuery = useEditMeasurementCategoryQuery(category?.id || 0); + const useEditCategoryQuery = useEditMeasurementCategoryQuery(category?.id || ''); // Match the backend column limits. We do NOT enforce a minimum length: // many users have legitimate 1-2 char names (e.g. CJK abbreviations // like 体重 / 体脂), and the backend allows them. diff --git a/src/components/Measurements/widgets/EntryForm.test.tsx b/src/components/Measurements/widgets/EntryForm.test.tsx index 971dbb902..747722e04 100644 --- a/src/components/Measurements/widgets/EntryForm.test.tsx +++ b/src/components/Measurements/widgets/EntryForm.test.tsx @@ -25,7 +25,7 @@ describe("Test the EntryForm component", () => { const queryClient = new QueryClient(); let mutate = vi.fn(); - const renderComponent = (props: { entry?: MeasurementEntry, categoryId: number }) => { + const renderComponent = (props: { entry?: MeasurementEntry, categoryId: string }) => { return render( @@ -56,7 +56,7 @@ describe("Test the EntryForm component", () => { const entry = TEST_MEASUREMENT_ENTRIES_1[0]; // Act - renderComponent({ entry, categoryId: 1 }); + renderComponent({ entry, categoryId: 'cccccccc-cccc-cccc-cccc-000000000001' }); // Assert expect(screen.getByDisplayValue('10')).toBeInTheDocument(); @@ -74,7 +74,7 @@ describe("Test the EntryForm component", () => { const user = userEvent.setup(); // Act - renderComponent({ entry, categoryId: 1 }); + renderComponent({ entry, categoryId: 'cccccccc-cccc-cccc-cccc-000000000001' }); const submitButton = screen.getByRole('button', { name: 'submit' }); await user.clear(screen.getByLabelText('value')); await user.type(screen.getByLabelText('value'), '25'); @@ -84,7 +84,7 @@ describe("Test the EntryForm component", () => { await user.click(submitButton); expect(mutate).toHaveBeenCalledWith({ date: expect.anything(), - id: 1, + id: 'dddddddd-dddd-dddd-dddd-000000000001', notes: "test note", value: 25, }); @@ -97,7 +97,7 @@ describe("Test the EntryForm component", () => { const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime }); // Act - renderComponent({ categoryId: 11 }); + renderComponent({ categoryId: 'cccccccc-cccc-cccc-cccc-000000000011' }); const valueInput = await screen.findByLabelText('value'); const notesInput = await screen.findByLabelText('notes'); const submitButton = screen.getByRole('button', { name: 'submit' }); @@ -111,7 +111,7 @@ describe("Test the EntryForm component", () => { expect(submitButton).toBeInTheDocument(); await user.click(submitButton); expect(mutate).toHaveBeenCalledWith({ - categoryId: 11, + categoryId: 'cccccccc-cccc-cccc-cccc-000000000011', date: fakeNow, notes: 'The Shiba Inu is a breed of hunting dog from Japan.', value: 42.42, @@ -129,7 +129,7 @@ describe("Test the EntryForm component", () => { i18n.changeLanguage('en'); const entry = TEST_MEASUREMENT_ENTRIES_1[0]; - const { container } = renderComponent({ entry, categoryId: 1 }); + const { container } = renderComponent({ entry, categoryId: 'cccccccc-cccc-cccc-cccc-000000000001' }); const picker = container.querySelector('.MuiPickersInputBase-root'); expect(picker?.textContent).toContain('02/01/2023'); @@ -140,7 +140,7 @@ describe("Test the EntryForm component", () => { i18n.changeLanguage('de'); const entry = TEST_MEASUREMENT_ENTRIES_1[0]; - const { container } = renderComponent({ entry, categoryId: 1 }); + const { container } = renderComponent({ entry, categoryId: 'cccccccc-cccc-cccc-cccc-000000000001' }); const picker = container.querySelector('.MuiPickersInputBase-root'); expect(picker?.textContent).toContain('01.02.2023'); diff --git a/src/components/Measurements/widgets/EntryForm.tsx b/src/components/Measurements/widgets/EntryForm.tsx index a892cb311..2b4c10c62 100644 --- a/src/components/Measurements/widgets/EntryForm.tsx +++ b/src/components/Measurements/widgets/EntryForm.tsx @@ -20,7 +20,7 @@ Settings.defaultZone = TIMEZONE; interface EntryFormProps { entry?: MeasurementEntry, closeFn?: () => void, - categoryId: number, + categoryId: string, } export const EntryForm = ({ entry, closeFn, categoryId }: EntryFormProps) => { diff --git a/src/components/Measurements/widgets/fab.tsx b/src/components/Measurements/widgets/fab.tsx index 79e6a7ef6..4f6f04494 100644 --- a/src/components/Measurements/widgets/fab.tsx +++ b/src/components/Measurements/widgets/fab.tsx @@ -42,7 +42,7 @@ export const AddMeasurementEntryFab = () => { const handleCloseModal = () => setOpenModal(false); const params = useParams<{ categoryId: string }>(); - const categoryId = parseInt(params.categoryId!); + const categoryId = params.categoryId!; return (<> diff --git a/src/components/Nutrition/api/meal.test.ts b/src/components/Nutrition/api/meal.test.ts index e17aeae3c..abe817cbf 100644 --- a/src/components/Nutrition/api/meal.test.ts +++ b/src/components/Nutrition/api/meal.test.ts @@ -1,14 +1,16 @@ -import axios from "axios"; -import { Meal } from "@/components/Nutrition/models/meal"; -import { addMeal, deleteMeal, editMeal, getMealsForPlan } from "@/components/Nutrition/api/meal"; import { getIngredients } from "@/components/Nutrition/api/ingredient"; +import { addMeal, deleteMeal, editMeal, getMealsForPlan } from "@/components/Nutrition/api/meal"; +import { Meal } from "@/components/Nutrition/models/meal"; +import { HHMMToDateTime } from "@/core/lib/date"; +import { TEST_INGREDIENT_1, TEST_INGREDIENT_2 } from "@/tests/ingredientTestdata"; import { + RESPONSE_MEAL_UUID, + RESPONSE_PLAN_UUID_2, responseMealDetail, responseMealItemsForMeal, responseMealsForPlan, } from "@/tests/nutritionTestdata"; -import { TEST_INGREDIENT_1, TEST_INGREDIENT_2 } from "@/tests/ingredientTestdata"; -import { HHMMToDateTime } from "@/core/lib/date"; +import axios from "axios"; import type { Mock } from "vitest"; vi.mock("axios"); @@ -20,7 +22,12 @@ describe("meal service tests", () => { }); test("addMeal POSTs the serialized meal and returns the parsed Meal", async () => { - const meal = new Meal({ planId: 123, order: 2, time: HHMMToDateTime("12:30"), name: "Second breakfast" }); + const meal = new Meal({ + planId: RESPONSE_PLAN_UUID_2, + order: 2, + time: HHMMToDateTime("12:30"), + name: "Second breakfast" + }); (axios.post as Mock).mockResolvedValue({ data: responseMealDetail }); const result = await addMeal(meal); @@ -28,34 +35,40 @@ describe("meal service tests", () => { expect(axios.post).toHaveBeenCalledTimes(1); const [url, body] = (axios.post as Mock).mock.calls[0]; expect(url).toMatch(/\/api\/v2\/meal\/$/); - expect(body).toMatchObject({ plan: 123, name: "Second breakfast", order: 2, time: "12:30" }); + expect(body).toMatchObject({ plan: RESPONSE_PLAN_UUID_2, name: "Second breakfast", order: 2, time: "12:30" }); // toJson omits the id field when it's null - verify expect(body).not.toHaveProperty("id"); expect(result).toBeInstanceOf(Meal); - expect(result.id).toBe(78); + expect(result.id).toBe(RESPONSE_MEAL_UUID); }); test("editMeal PATCHes /meal// and returns the parsed Meal", async () => { - const meal = new Meal({ id: 78, planId: 123, order: 2, time: HHMMToDateTime("12:30"), name: "renamed" }); + const meal = new Meal({ + id: RESPONSE_MEAL_UUID, + planId: RESPONSE_PLAN_UUID_2, + order: 2, + time: HHMMToDateTime("12:30"), + name: "renamed" + }); (axios.patch as Mock).mockResolvedValue({ data: { ...responseMealDetail, name: "renamed" } }); const result = await editMeal(meal); expect(axios.patch).toHaveBeenCalledTimes(1); const [url, body] = (axios.patch as Mock).mock.calls[0]; - expect(url).toMatch(/\/api\/v2\/meal\/78\/$/); - expect(body).toMatchObject({ id: 78, name: "renamed" }); + expect(url).toMatch(new RegExp(`/api/v2/meal/${RESPONSE_MEAL_UUID}/$`)); + expect(body).toMatchObject({ id: RESPONSE_MEAL_UUID, name: "renamed" }); expect(result.name).toBe("renamed"); }); test("deleteMeal DELETEs /meal//", async () => { (axios.delete as Mock).mockResolvedValue({ status: 204 }); - await deleteMeal(78); + await deleteMeal(RESPONSE_MEAL_UUID); expect(axios.delete).toHaveBeenCalledTimes(1); expect(axios.delete).toHaveBeenCalledWith( - expect.stringMatching(/\/api\/v2\/meal\/78\/$/), + expect.stringMatching(new RegExp(`/api/v2/meal/${RESPONSE_MEAL_UUID}/$`)), expect.anything() ); }); @@ -70,12 +83,12 @@ describe("meal service tests", () => { }); (getIngredients as Mock).mockResolvedValue([TEST_INGREDIENT_1, TEST_INGREDIENT_2]); - const result = await getMealsForPlan(123); + const result = await getMealsForPlan(RESPONSE_PLAN_UUID_2); // The list call filters by plan, the item call filters by meal const calledUrls = (axios.get as Mock).mock.calls.map(([u]) => u as string); - expect(calledUrls.some(u => u.includes("/meal/?plan=123"))).toBe(true); - expect(calledUrls.some(u => u.includes("/mealitem/?meal=78"))).toBe(true); + expect(calledUrls.some(u => u.includes(`/meal/?plan=${RESPONSE_PLAN_UUID_2}`))).toBe(true); + expect(calledUrls.some(u => u.includes(`/mealitem/?meal=${RESPONSE_MEAL_UUID}`))).toBe(true); // getIngredients is called once per meal with the deduped ingredient ids expect(getIngredients).toHaveBeenCalledWith([101, 102]); @@ -95,7 +108,7 @@ describe("meal service tests", () => { data: { count: 0, next: null, previous: null, results: [] }, }); - const result = await getMealsForPlan(123); + const result = await getMealsForPlan(RESPONSE_PLAN_UUID_2); expect(result).toEqual([]); // No item calls, no ingredient lookups diff --git a/src/components/Nutrition/api/meal.ts b/src/components/Nutrition/api/meal.ts index bce3ec163..78d63ee46 100644 --- a/src/components/Nutrition/api/meal.ts +++ b/src/components/Nutrition/api/meal.ts @@ -27,14 +27,14 @@ export const editMeal = async (meal: Meal): Promise => { return Meal.fromJson(response.data); }; -export const deleteMeal = async (id: number): Promise => { +export const deleteMeal = async (id: string): Promise => { await axios.delete( makeUrl(ApiPath.MEAL, { id: id }), { headers: makeHeader() }, ); }; -export const getMealsForPlan = async (planId: number): Promise => { +export const getMealsForPlan = async (planId: string): Promise => { let ingredientIds: number[] = []; const { data: receivedMeals } = await axios.get>( diff --git a/src/components/Nutrition/api/mealItem.test.ts b/src/components/Nutrition/api/mealItem.test.ts index ff3833d4e..24cd4f93d 100644 --- a/src/components/Nutrition/api/mealItem.test.ts +++ b/src/components/Nutrition/api/mealItem.test.ts @@ -1,6 +1,6 @@ import { addMealItem, deleteMealItem, editMealItem } from "@/components/Nutrition/api/mealItem"; import { MealItem } from "@/components/Nutrition/models/mealItem"; -import { responseMealItemDetail } from "@/tests/nutritionTestdata"; +import { RESPONSE_MEAL_ITEM_UUID, RESPONSE_MEAL_UUID, responseMealItemDetail, } from "@/tests/nutritionTestdata"; import axios from "axios"; import type { Mock } from "vitest"; @@ -12,7 +12,13 @@ describe("mealItem service tests", () => { }); test("addMealItem POSTs the serialized item and returns the parsed MealItem", async () => { - const item = new MealItem({ mealId: 78, ingredientId: 101, weightUnitId: null, amount: 120, order: 3 }); + const item = new MealItem({ + mealId: RESPONSE_MEAL_UUID, + ingredientId: 101, + weightUnitId: null, + amount: 120, + order: 3 + }); (axios.post as Mock).mockResolvedValue({ data: responseMealItemDetail }); const result = await addMealItem(item); @@ -21,7 +27,7 @@ describe("mealItem service tests", () => { const [url, body] = (axios.post as Mock).mock.calls[0]; expect(url).toMatch(/\/api\/v2\/mealitem\/$/); expect(body).toMatchObject({ - meal: 78, + meal: RESPONSE_MEAL_UUID, ingredient: 101, weight_unit: null, @@ -30,29 +36,36 @@ describe("mealItem service tests", () => { }); expect(body).not.toHaveProperty("id"); expect(result).toBeInstanceOf(MealItem); - expect(result.id).toBe(42); + expect(result.id).toBe(RESPONSE_MEAL_ITEM_UUID); }); test("editMealItem PATCHes /mealitem//", async () => { - const item = new MealItem({ id: 42, mealId: 78, ingredientId: 101, weightUnitId: null, amount: 99, order: 3 }); + const item = new MealItem({ + id: RESPONSE_MEAL_ITEM_UUID, + mealId: RESPONSE_MEAL_UUID, + ingredientId: 101, + weightUnitId: null, + amount: 99, + order: 3 + }); (axios.patch as Mock).mockResolvedValue({ data: { ...responseMealItemDetail, amount: "99.00" } }); const result = await editMealItem(item); expect(axios.patch).toHaveBeenCalledTimes(1); const [url, body] = (axios.patch as Mock).mock.calls[0]; - expect(url).toMatch(/\/api\/v2\/mealitem\/42\/$/); - expect(body).toMatchObject({ id: 42, amount: "99" }); + expect(url).toMatch(new RegExp(`/api/v2/mealitem/${RESPONSE_MEAL_ITEM_UUID}/$`)); + expect(body).toMatchObject({ id: RESPONSE_MEAL_ITEM_UUID, amount: "99" }); expect(result.amount).toBe(99); }); test("deleteMealItem DELETEs /mealitem//", async () => { (axios.delete as Mock).mockResolvedValue({ status: 204 }); - await deleteMealItem(42); + await deleteMealItem(RESPONSE_MEAL_ITEM_UUID); expect(axios.delete).toHaveBeenCalledWith( - expect.stringMatching(/\/api\/v2\/mealitem\/42\/$/), + expect.stringMatching(new RegExp(`/api/v2/mealitem/${RESPONSE_MEAL_ITEM_UUID}/$`)), expect.anything() ); }); diff --git a/src/components/Nutrition/api/mealItem.ts b/src/components/Nutrition/api/mealItem.ts index 5815c2e6e..358305182 100644 --- a/src/components/Nutrition/api/mealItem.ts +++ b/src/components/Nutrition/api/mealItem.ts @@ -24,7 +24,7 @@ export const editMealItem = async (mealItem: MealItem): Promise => { return MealItem.fromJson(response.data); }; -export const deleteMealItem = async (id: number): Promise => { +export const deleteMealItem = async (id: string): Promise => { await axios.delete( makeUrl(ApiPath.MEAL_ITEM, { id: id }), { headers: makeHeader() }, diff --git a/src/components/Nutrition/api/nutritionalDiary.test.ts b/src/components/Nutrition/api/nutritionalDiary.test.ts index 339ef3164..ce109dd1e 100644 --- a/src/components/Nutrition/api/nutritionalDiary.test.ts +++ b/src/components/Nutrition/api/nutritionalDiary.test.ts @@ -7,7 +7,13 @@ import { } from "@/components/Nutrition/api/nutritionalDiary"; import { DiaryEntry } from "@/components/Nutrition/models/diaryEntry"; import { TEST_INGREDIENT_1 } from "@/tests/ingredientTestdata"; -import { responseDiaryEntries, responseDiaryEntryDetail } from "@/tests/nutritionTestdata"; +import { + RESPONSE_DIARY_UUID, + RESPONSE_MEAL_UUID, + RESPONSE_PLAN_UUID, + responseDiaryEntries, + responseDiaryEntryDetail, +} from "@/tests/nutritionTestdata"; import axios from "axios"; import type { Mock } from 'vitest'; @@ -55,7 +61,7 @@ describe("Nutritional diary service tests", () => { count: 2, results: [ responseDiaryEntries.results[0], - { ...responseDiaryEntries.results[0], id: 10 }, + { ...responseDiaryEntries.results[0], id: 'dddddddd-0000-0000-0000-000000000010' }, ], }; (axios.get as Mock).mockResolvedValue({ data: dupedEntries }); @@ -74,8 +80,8 @@ describe("Nutritional diary service tests", () => { test('addNutritionalDiaryEntry POSTs the serialized entry', async () => { const entry = new DiaryEntry({ - planId: 101, - mealId: 78, + planId: RESPONSE_PLAN_UUID, + mealId: RESPONSE_MEAL_UUID, ingredientId: 101, weightUnitId: null, amount: 150, @@ -89,8 +95,8 @@ describe("Nutritional diary service tests", () => { const [url, body] = (axios.post as Mock).mock.calls[0]; expect(url).toMatch(/\/api\/v2\/nutritiondiary\/$/); expect(body).toMatchObject({ - plan: 101, - meal: 78, + plan: RESPONSE_PLAN_UUID, + meal: RESPONSE_MEAL_UUID, ingredient: 101, weight_unit: null, @@ -98,14 +104,14 @@ describe("Nutritional diary service tests", () => { datetime: "2024-08-01T08:00:00.000Z", }); expect(result).toBeInstanceOf(DiaryEntry); - expect(result.id).toBe(9); + expect(result.id).toBe(RESPONSE_DIARY_UUID); }); test('editNutritionalDiaryEntry PATCHes /nutritiondiary//', async () => { const entry = new DiaryEntry({ - id: 9, - planId: 101, - mealId: 78, + id: RESPONSE_DIARY_UUID, + planId: RESPONSE_PLAN_UUID, + mealId: RESPONSE_MEAL_UUID, ingredientId: 101, weightUnitId: null, amount: 99, @@ -119,7 +125,7 @@ describe("Nutritional diary service tests", () => { expect(axios.patch).toHaveBeenCalledTimes(1); const [url, body] = (axios.patch as Mock).mock.calls[0]; - expect(url).toMatch(/\/api\/v2\/nutritiondiary\/9\/$/); + expect(url).toMatch(new RegExp(`/api/v2/nutritiondiary/${RESPONSE_DIARY_UUID}/$`)); expect(body).toMatchObject({ amount: "99" }); expect(result.amount).toBe(99); }); @@ -127,10 +133,10 @@ describe("Nutritional diary service tests", () => { test('deleteNutritionalDiaryEntry DELETEs /nutritiondiary//', async () => { (axios.delete as Mock).mockResolvedValue({ status: 204 }); - await deleteNutritionalDiaryEntry(9); + await deleteNutritionalDiaryEntry(RESPONSE_DIARY_UUID); expect(axios.delete).toHaveBeenCalledWith( - expect.stringMatching(/\/api\/v2\/nutritiondiary\/9\/$/), + expect.stringMatching(new RegExp(`/api/v2/nutritiondiary/${RESPONSE_DIARY_UUID}/$`)), expect.anything() ); }); diff --git a/src/components/Nutrition/api/nutritionalDiary.ts b/src/components/Nutrition/api/nutritionalDiary.ts index 88593afdc..5cba5d0d5 100644 --- a/src/components/Nutrition/api/nutritionalDiary.ts +++ b/src/components/Nutrition/api/nutritionalDiary.ts @@ -67,7 +67,7 @@ export const editNutritionalDiaryEntry = async (diaryEntry: DiaryEntry): Promise return DiaryEntry.fromJson(response.data); }; -export const deleteNutritionalDiaryEntry = async (id: number): Promise => { +export const deleteNutritionalDiaryEntry = async (id: string): Promise => { await axios.delete( makeUrl(ApiPath.NUTRITIONAL_DIARY, { id: id }), { headers: makeHeader() }, diff --git a/src/components/Nutrition/api/nutritionalPlan.test.ts b/src/components/Nutrition/api/nutritionalPlan.test.ts index 12e226bf7..12f8e6d30 100644 --- a/src/components/Nutrition/api/nutritionalPlan.test.ts +++ b/src/components/Nutrition/api/nutritionalPlan.test.ts @@ -10,6 +10,8 @@ import { } from "@/components/Nutrition/api/nutritionalPlan"; import { NutritionalPlan } from "@/components/Nutrition/models/nutritionalPlan"; import { + RESPONSE_MEAL_UUID, + RESPONSE_PLAN_UUID, responseEmptyPlanList, responseNutritionalPlanDetail, responseSinglePlanList, @@ -37,13 +39,17 @@ describe("Nutritional plan service tests", () => { test('GET plans - sparse', async () => { + const PLAN_UUID_A = 'aaaaaaaa-0000-0000-0000-000000072559'; + const PLAN_UUID_B = 'aaaaaaaa-0000-0000-0000-000000060131'; + const PLAN_UUID_C = 'aaaaaaaa-0000-0000-0000-000000024752'; + const planResponse = { count: 2, next: null, previous: null, results: [ { - "id": 72559, + "id": PLAN_UUID_A, "creation_date": "2023-05-26", "start": "2023-06-01", "end": "2023-06-30", @@ -51,7 +57,7 @@ describe("Nutritional plan service tests", () => { "only_logging": true, }, { - "id": 60131, + "id": PLAN_UUID_B, "creation_date": "2022-06-01", "start": "2022-06-01", "end": null, @@ -59,7 +65,7 @@ describe("Nutritional plan service tests", () => { "only_logging": false, }, { - "id": 24752, + "id": PLAN_UUID_C, "creation_date": "2023-08-01", "start": "2023-08-01", "end": "2023-08-31", @@ -76,7 +82,7 @@ describe("Nutritional plan service tests", () => { expect(result).toStrictEqual([ new NutritionalPlan({ - id: 72559, + id: PLAN_UUID_A, creationDate: new Date('2023-05-26'), start: new Date('2023-06-01'), end: new Date('2023-06-30'), @@ -84,13 +90,13 @@ describe("Nutritional plan service tests", () => { onlyLogging: true }), new NutritionalPlan({ - id: 60131, + id: PLAN_UUID_B, creationDate: new Date('2022-06-01'), description: '', onlyLogging: false }), new NutritionalPlan({ - id: 24752, + id: PLAN_UUID_C, creationDate: new Date('2023-08-01'), start: new Date('2023-08-01'), end: new Date('2023-08-31'), @@ -125,11 +131,11 @@ describe("Nutritional plan service tests", () => { const result = await getLastNutritionalPlan(); expect(result).toBeInstanceOf(NutritionalPlan); - expect(result!.id).toBe(101); - expect(getMealsForPlan).toHaveBeenCalledWith(101); + expect(result!.id).toBe(RESPONSE_PLAN_UUID); + expect(getMealsForPlan).toHaveBeenCalledWith(RESPONSE_PLAN_UUID); // Diary entries are filtered by plan id expect(getNutritionalDiaryEntries).toHaveBeenCalledWith({ - filtersetQuery: { plan: 101 }, + filtersetQuery: { plan: RESPONSE_PLAN_UUID }, }); }); @@ -142,24 +148,25 @@ describe("Nutritional plan service tests", () => { }); test('getNutritionalPlanFull wires meals back to their diary entries by mealId', async () => { - // Two diary entries, one for meal 78 and one for an unrelated meal + const OTHER_MEAL_UUID = 'bbbbbbbb-0000-0000-0000-000000000099'; + // Two diary entries, one for the matching meal and one for an unrelated meal const diaryEntries = [ - { mealId: 78 }, - { mealId: 99 }, - { mealId: 78 }, + { mealId: RESPONSE_MEAL_UUID }, + { mealId: OTHER_MEAL_UUID }, + { mealId: RESPONSE_MEAL_UUID }, ]; - const meals = [{ id: 78, diaryEntries: [] as { mealId: number }[] }]; + const meals = [{ id: RESPONSE_MEAL_UUID, diaryEntries: [] as { mealId: string }[] }]; (axios.get as Mock).mockResolvedValue({ data: responseNutritionalPlanDetail }); (getMealsForPlan as Mock).mockResolvedValue(meals); (getNutritionalDiaryEntries as Mock).mockResolvedValue(diaryEntries); - const result = await getNutritionalPlanFull(101); + const result = await getNutritionalPlanFull(RESPONSE_PLAN_UUID); expect(result).not.toBeNull(); // The meal collected only the entries that match its id expect(meals[0].diaryEntries).toHaveLength(2); - expect(meals[0].diaryEntries.every(e => e.mealId === 78)).toBe(true); + expect(meals[0].diaryEntries.every(e => e.mealId === RESPONSE_MEAL_UUID)).toBe(true); // Both data sources are wired into the plan expect(result!.diaryEntries).toBe(diaryEntries); expect(result!.meals).toBe(meals); @@ -170,10 +177,10 @@ describe("Nutritional plan service tests", () => { (getMealsForPlan as Mock).mockResolvedValue([]); (getNutritionalDiaryEntries as Mock).mockResolvedValue([]); - await getNutritionalPlanFull(101, { filtersetQueryLogs: { datetime__date: "2024-08-01" } }); + await getNutritionalPlanFull(RESPONSE_PLAN_UUID, { filtersetQueryLogs: { datetime__date: "2024-08-01" } }); expect(getNutritionalDiaryEntries).toHaveBeenCalledWith({ - filtersetQuery: { plan: 101, datetime__date: "2024-08-01" }, + filtersetQuery: { plan: RESPONSE_PLAN_UUID, datetime__date: "2024-08-01" }, }); }); @@ -203,12 +210,12 @@ describe("Nutritional plan service tests", () => { }); expect(body).not.toHaveProperty("id"); expect(result).toBeInstanceOf(NutritionalPlan); - expect(result.id).toBe(101); + expect(result.id).toBe(RESPONSE_PLAN_UUID); }); test('editNutritionalPlan PATCHes /nutritionplan//', async () => { const plan = new NutritionalPlan({ - id: 101, + id: RESPONSE_PLAN_UUID, description: "edited", start: new Date("2024-06-01"), end: new Date("2024-08-31"), @@ -221,18 +228,18 @@ describe("Nutritional plan service tests", () => { expect(axios.patch).toHaveBeenCalledTimes(1); const [url, body] = (axios.patch as Mock).mock.calls[0]; - expect(url).toMatch(/\/api\/v2\/nutritionplan\/101\/$/); - expect(body).toMatchObject({ id: 101, description: "edited" }); + expect(url).toMatch(new RegExp(`/api/v2/nutritionplan/${RESPONSE_PLAN_UUID}/$`)); + expect(body).toMatchObject({ id: RESPONSE_PLAN_UUID, description: "edited" }); expect(result.description).toBe("edited"); }); test('deleteNutritionalPlan DELETEs /nutritionplan//', async () => { (axios.delete as Mock).mockResolvedValue({ status: 204 }); - await deleteNutritionalPlan(101); + await deleteNutritionalPlan(RESPONSE_PLAN_UUID); expect(axios.delete).toHaveBeenCalledWith( - expect.stringMatching(/\/api\/v2\/nutritionplan\/101\/$/), + expect.stringMatching(new RegExp(`/api/v2/nutritionplan/${RESPONSE_PLAN_UUID}/$`)), expect.anything() ); }); diff --git a/src/components/Nutrition/api/nutritionalPlan.ts b/src/components/Nutrition/api/nutritionalPlan.ts index b1456fd2a..3e2f47c20 100644 --- a/src/components/Nutrition/api/nutritionalPlan.ts +++ b/src/components/Nutrition/api/nutritionalPlan.ts @@ -33,7 +33,7 @@ export const getLastNutritionalPlan = async (): Promise return await getNutritionalPlanFull(receivedPlan.results[0].id); }; -export const getNutritionalPlanFull = async (id: number | null, options?: NutritionalPlanOptions): Promise => { +export const getNutritionalPlanFull = async (id: string | null, options?: NutritionalPlanOptions): Promise => { if (id === null) { return null; } @@ -81,7 +81,7 @@ export const editNutritionalPlan = async (plan: NutritionalPlan): Promise => { +export const deleteNutritionalPlan = async (id: string): Promise => { await axios.delete( makeUrl(API_NUTRITIONAL_PLAN_PATH, { id: id }), { headers: makeHeader() }, diff --git a/src/components/Nutrition/models/diaryEntry.ts b/src/components/Nutrition/models/diaryEntry.ts index a2d7103c7..a78e1b9d3 100644 --- a/src/components/Nutrition/models/diaryEntry.ts +++ b/src/components/Nutrition/models/diaryEntry.ts @@ -6,9 +6,9 @@ import { Adapter } from "@/core/lib/Adapter"; import { numberGramLocale } from "@/core/lib/numbers"; export interface ApiNutritionDiaryType { - id: number, - plan: number, - meal: number | null, + id: string, + plan: string, + meal: string | null, ingredient: number, weight_unit: number, datetime: Date, @@ -16,9 +16,9 @@ export interface ApiNutritionDiaryType { } export type DiaryEntryConstructorParams = { - id?: number; - planId: number; - mealId?: number | null; + id?: string; + planId: string; + mealId?: string | null; ingredientId: number; weightUnitId?: number | null; amount: number; @@ -29,9 +29,9 @@ export type DiaryEntryConstructorParams = { export class DiaryEntry { - public id?: number | null; - public planId: number; - public mealId: number | null; + public id?: string | null; + public planId: string; + public mealId: string | null; public amount: number; public datetime: Date; diff --git a/src/components/Nutrition/models/meal.ts b/src/components/Nutrition/models/meal.ts index 7e5c06a4a..325401610 100644 --- a/src/components/Nutrition/models/meal.ts +++ b/src/components/Nutrition/models/meal.ts @@ -6,8 +6,8 @@ import { Adapter } from "@/core/lib/Adapter"; import { dateTimeToHHMM, dateTimeToLocaleHHMM, HHMMToDateTime, isSameDay } from "@/core/lib/date"; export interface ApiMealType { - id: number, - plan: number, + id: string, + plan: string, order: number, time: string | null, name: string @@ -15,8 +15,8 @@ export interface ApiMealType { export type MealConstructorParams = { - id?: number | null; - planId?: number | null; + id?: string | null; + planId?: string | null; order?: number; time?: Date | null; name: string; @@ -26,8 +26,8 @@ export type MealConstructorParams = { export class Meal { - public id: number | null = null; - public planId: number | null = null; + public id: string | null = null; + public planId: string | null = null; public order: number; public time: Date | null = null; public name: string; diff --git a/src/components/Nutrition/models/mealItem.ts b/src/components/Nutrition/models/mealItem.ts index d8504094b..b3e10a1d9 100644 --- a/src/components/Nutrition/models/mealItem.ts +++ b/src/components/Nutrition/models/mealItem.ts @@ -7,8 +7,8 @@ import { Adapter } from "@/core/lib/Adapter"; import { numberGramLocale } from "@/core/lib/numbers"; export interface ApiMealItemType { - id: number, - meal: number, + id: string, + meal: string, ingredient: number, weight_unit: number, order: number, @@ -16,8 +16,8 @@ export interface ApiMealItemType { } export type MealItemConstructorParams = { - id?: number; - mealId: number; + id?: string; + mealId: string; amount: number; order: number; @@ -29,8 +29,8 @@ export type MealItemConstructorParams = { }; export class MealItem { - public id?: number | null; - public mealId: number; + public id?: string | null; + public mealId: string; public ingredientId: number; public weightUnitId: number | null; public amount: number; @@ -91,7 +91,7 @@ export class MealItem { }); } - diaryEntry(planId: number, date?: Date): DiaryEntry { + diaryEntry(planId: string, date?: Date): DiaryEntry { return new DiaryEntry({ mealId: this.mealId, planId: planId, diff --git a/src/components/Nutrition/models/nutritionalPlan.test.ts b/src/components/Nutrition/models/nutritionalPlan.test.ts index 0ce5fe056..1e3dc6ccf 100644 --- a/src/components/Nutrition/models/nutritionalPlan.test.ts +++ b/src/components/Nutrition/models/nutritionalPlan.test.ts @@ -1,4 +1,4 @@ -import { NutritionalPlan } from "@/components/Nutrition/models/nutritionalPlan"; +import { NutritionalPlan, PSEUDO_MEAL_ID } from "@/components/Nutrition/models/nutritionalPlan"; import { TEST_DIARY_ENTRY_3, TEST_DIARY_ENTRY_4 } from "@/tests/nutritionDiaryTestdata"; import { TEST_MEAL_1, TEST_NUTRITIONAL_PLAN_1 } from "@/tests/nutritionTestdata"; @@ -139,7 +139,11 @@ describe("Test the nutritional plan model", () => { }); test('7-day average returns zero values when no entries exist', () => { - const plan = new NutritionalPlan({ id: 1, creationDate: new Date(), description: 'test' }); + const plan = new NutritionalPlan({ + id: 'aaaaaaaa-0000-0000-0000-000000000001', + creationDate: new Date(), + description: 'test' + }); const values = plan.loggedNutritionalValues7DayAvg; expect(values.energy).toBe(0); @@ -151,7 +155,11 @@ describe("Test the nutritional plan model", () => { test('7-day average divides total by 7 without rounding', () => { // INGREDIENT_3: energy=60, protein=0.89, carbs=14, sugar=11.78, sodium=0.006 per 100g // ENTRY_3: 200g → energy=120; ENTRY_4: 20g → energy=12; total=132, avg=132/7≈18.857 - const plan = new NutritionalPlan({ id: 1, creationDate: new Date(), description: 'test' }); + const plan = new NutritionalPlan({ + id: 'aaaaaaaa-0000-0000-0000-000000000001', + creationDate: new Date(), + description: 'test' + }); plan.diaryEntries = [TEST_DIARY_ENTRY_3, TEST_DIARY_ENTRY_4]; const values = plan.loggedNutritionalValues7DayAvg; @@ -169,7 +177,7 @@ describe("Test the nutritional plan model", () => { const meal = TEST_NUTRITIONAL_PLAN_1.pseudoMealOthers('the name'); // Assert - expect(meal.id).toBe(-1); + expect(meal.id).toBe(PSEUDO_MEAL_ID); expect(meal.name).toBe('the name'); expect(meal.diaryEntries.length).toBe(2); }); @@ -178,7 +186,7 @@ describe("Test the nutritional plan model", () => { // Act const plan = new NutritionalPlan({ - id: 1, + id: '00000000-0000-0000-0000-000000000001', creationDate: new Date(), description: 'test 1', }); diff --git a/src/components/Nutrition/models/nutritionalPlan.ts b/src/components/Nutrition/models/nutritionalPlan.ts index e8d12d38b..263d686c6 100644 --- a/src/components/Nutrition/models/nutritionalPlan.ts +++ b/src/components/Nutrition/models/nutritionalPlan.ts @@ -12,11 +12,11 @@ export type GroupedDiaryEntries = { nutritionalValues: NutritionalValues; } -export const PSEUDO_MEAL_ID = -1; +export const PSEUDO_MEAL_ID = '00000000-0000-0000-0000-000000000000'; type NutritionalPlanConstructorParams = { - id?: number | null, + id?: string | null, creationDate?: Date, start?: Date, end?: Date | null, @@ -33,7 +33,7 @@ type NutritionalPlanConstructorParams = { export class NutritionalPlan { - id: number | null; + id: string | null; creationDate: Date; start: Date; end: Date | null; diff --git a/src/components/Nutrition/queries/diary.ts b/src/components/Nutrition/queries/diary.ts index f450cd69d..710fc24b7 100644 --- a/src/components/Nutrition/queries/diary.ts +++ b/src/components/Nutrition/queries/diary.ts @@ -14,7 +14,7 @@ export const useNutritionDiaryQuery = (options?: NutritionalDiaryEntriesOptions) queryKey: [QueryKey.NUTRITIONAL_PLAN_DIARY, JSON.stringify(options || {})], }); -export const useAddDiaryEntryQuery = (planId: number) => { +export const useAddDiaryEntryQuery = (planId: string) => { const queryClient = useQueryClient(); return useMutation({ @@ -25,7 +25,7 @@ export const useAddDiaryEntryQuery = (planId: number) => { }); }; -export const useAddDiaryEntriesQuery = (planId: number) => { +export const useAddDiaryEntriesQuery = (planId: string) => { const queryClient = useQueryClient(); return useMutation({ @@ -36,18 +36,18 @@ export const useAddDiaryEntriesQuery = (planId: number) => { }); }; -export const useDeleteDiaryEntryQuery = (planId: number) => { +export const useDeleteDiaryEntryQuery = (planId: string) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (id: number) => deleteNutritionalDiaryEntry(id), + mutationFn: (id: string) => deleteNutritionalDiaryEntry(id), onSuccess: () => queryClient.invalidateQueries({ queryKey: [QueryKey.NUTRITIONAL_PLAN, planId] }) }); }; -export const useEditDiaryEntryQuery = (planId: number) => { +export const useEditDiaryEntryQuery = (planId: string) => { const queryClient = useQueryClient(); return useMutation({ diff --git a/src/components/Nutrition/queries/meal.ts b/src/components/Nutrition/queries/meal.ts index 56f9194d9..9df20f48a 100644 --- a/src/components/Nutrition/queries/meal.ts +++ b/src/components/Nutrition/queries/meal.ts @@ -3,7 +3,7 @@ import { Meal } from "@/components/Nutrition/models/meal"; import { addMeal, deleteMeal, editMeal } from "@/components/Nutrition/api/meal"; import { QueryKey } from "@/core/lib/consts"; -export const useAddMealQuery = (planId: number) => { +export const useAddMealQuery = (planId: string) => { const queryClient = useQueryClient(); return useMutation({ @@ -15,11 +15,11 @@ export const useAddMealQuery = (planId: number) => { } }); }; -export const useDeleteMealQuery = (planId: number) => { +export const useDeleteMealQuery = (planId: string) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (id: number) => deleteMeal(id), + mutationFn: (id: string) => deleteMeal(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [QueryKey.NUTRITIONAL_PLAN, planId] @@ -27,7 +27,7 @@ export const useDeleteMealQuery = (planId: number) => { } }); }; -export const useEditMealQuery = (planId: number) => { +export const useEditMealQuery = (planId: string) => { const queryClient = useQueryClient(); return useMutation({ diff --git a/src/components/Nutrition/queries/mealItem.ts b/src/components/Nutrition/queries/mealItem.ts index 380fcf5e8..007a12f70 100644 --- a/src/components/Nutrition/queries/mealItem.ts +++ b/src/components/Nutrition/queries/mealItem.ts @@ -3,7 +3,7 @@ import { MealItem } from "@/components/Nutrition/models/mealItem"; import { addMealItem, deleteMealItem, editMealItem } from "@/components/Nutrition/api/mealItem"; import { QueryKey } from "@/core/lib/consts"; -export const useAddMealItemQuery = (planId: number) => { +export const useAddMealItemQuery = (planId: string) => { const queryClient = useQueryClient(); return useMutation({ @@ -16,7 +16,7 @@ export const useAddMealItemQuery = (planId: number) => { }); }; -export const useEditMealItemQuery = (planId: number) => { +export const useEditMealItemQuery = (planId: string) => { const queryClient = useQueryClient(); return useMutation({ @@ -29,11 +29,11 @@ export const useEditMealItemQuery = (planId: number) => { }); }; -export const useDeleteMealItemQuery = (planId: number) => { +export const useDeleteMealItemQuery = (planId: string) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (id: number) => deleteMealItem(id), + mutationFn: (id: string) => deleteMealItem(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [QueryKey.NUTRITIONAL_PLAN, planId] diff --git a/src/components/Nutrition/queries/plan.ts b/src/components/Nutrition/queries/plan.ts index 6a386f351..632dd9942 100644 --- a/src/components/Nutrition/queries/plan.ts +++ b/src/components/Nutrition/queries/plan.ts @@ -26,7 +26,7 @@ export function useFetchLastNutritionalPlanQuery() { }); } -export function useFetchNutritionalPlanQuery(planId: number) { +export function useFetchNutritionalPlanQuery(planId: string) { return useQuery({ queryKey: [QueryKey.NUTRITIONAL_PLAN, planId], queryFn: () => getNutritionalPlanFull(planId) @@ -37,7 +37,7 @@ export function useFetchNutritionalPlanQuery(planId: number) { * Fetches the full nutritional plan (meals, etc.), but only the diary entries for * the given date */ -export function useFetchNutritionalPlanDateQuery(planId: number | null, dateStr: string, enabled = true) { +export function useFetchNutritionalPlanDateQuery(planId: string | null, dateStr: string, enabled = true) { return useQuery({ queryKey: [QueryKey.NUTRITIONAL_PLAN, planId, dateStr], queryFn: () => getNutritionalPlanFull(planId, { filtersetQueryLogs: { "datetime__eq": dateToYYYYMMDD(new Date(dateStr)) } }), @@ -61,11 +61,11 @@ export const useAddNutritionalPlanQuery = () => { }); }; -export const useDeleteNutritionalPlanQuery = (id: number) => { +export const useDeleteNutritionalPlanQuery = (id: string) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (id: number) => deleteNutritionalPlan(id), + mutationFn: (id: string) => deleteNutritionalPlan(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [QueryKey.NUTRITIONAL_PLANS,] @@ -76,7 +76,7 @@ export const useDeleteNutritionalPlanQuery = (id: number) => { } }); }; -export const useEditNutritionalPlanQuery = (id: number) => { +export const useEditNutritionalPlanQuery = (id: string) => { const queryClient = useQueryClient(); return useMutation({ diff --git a/src/components/Nutrition/screens/NutritionDiaryOverview.tsx b/src/components/Nutrition/screens/NutritionDiaryOverview.tsx index 6be974dc9..66e58e297 100644 --- a/src/components/Nutrition/screens/NutritionDiaryOverview.tsx +++ b/src/components/Nutrition/screens/NutritionDiaryOverview.tsx @@ -16,9 +16,9 @@ export const NutritionDiaryOverview = () => { const [t] = useTranslation(); const params = useParams<{ planId: string, date: string }>(); - const planId = parseInt(params.planId!); - if (Number.isNaN(planId)) { - return

Please pass an integer as the nutritional plan id.

; + const planId = params.planId ?? ''; + if (planId === '') { + return

Please pass a UUID as the nutritional plan id.

; } const date = new Date(params.date!); diff --git a/src/components/Nutrition/screens/PlanDetail.tsx b/src/components/Nutrition/screens/PlanDetail.tsx index 32e36488a..ca298fbc0 100644 --- a/src/components/Nutrition/screens/PlanDetail.tsx +++ b/src/components/Nutrition/screens/PlanDetail.tsx @@ -21,9 +21,9 @@ import { useParams } from "react-router-dom"; export const PlanDetail = () => { const [t] = useTranslation(); const params = useParams<{ planId: string }>(); - const planId = parseInt(params.planId ?? ''); - if (Number.isNaN(planId)) { - return

Please pass an integer as the nutritional plan id.

; + const planId = params.planId ?? ''; + if (planId === '') { + return

Please pass a UUID as the nutritional plan id.

; } // eslint-disable-next-line react-hooks/rules-of-hooks diff --git a/src/components/Nutrition/widgets/DiaryOverview.tsx b/src/components/Nutrition/widgets/DiaryOverview.tsx index c16a9b69e..eca650436 100644 --- a/src/components/Nutrition/widgets/DiaryOverview.tsx +++ b/src/components/Nutrition/widgets/DiaryOverview.tsx @@ -9,7 +9,7 @@ import { numberLocale } from "@/core/lib/numbers"; import { makeLink, WgerLink } from "@/core/lib/url"; export const DiaryOverview = (props: { - planId: number, + planId: string, logged: Map, planned: NutritionalValues }) => { diff --git a/src/components/Nutrition/widgets/MealDetail.tsx b/src/components/Nutrition/widgets/MealDetail.tsx index 9a4c9c635..bd58a46ff 100644 --- a/src/components/Nutrition/widgets/MealDetail.tsx +++ b/src/components/Nutrition/widgets/MealDetail.tsx @@ -29,7 +29,7 @@ import { MealDetailDropdown } from "@/components/Nutrition/widgets/MealDetailDro import React, { useState } from "react"; import { useTranslation } from "react-i18next"; -const MealItemListItem = (props: { mealItem: MealItem, planId: number, mealId: number }) => { +const MealItemListItem = (props: { mealItem: MealItem, planId: string, mealId: string }) => { const [expandForm, setExpandForm] = useState(false); const handleToggleForm = () => setExpandForm(!expandForm); @@ -63,7 +63,7 @@ const MealItemListItem = (props: { mealItem: MealItem, planId: number, mealId: n ; }; -export const MealDetail = (props: { meal: Meal, planId: number, onlyLogging: boolean }) => { +export const MealDetail = (props: { meal: Meal, planId: string, onlyLogging: boolean }) => { const theme = useTheme(); const [t] = useTranslation(); const [expandViewStats, setExpandViewStats] = useState(false); diff --git a/src/components/Nutrition/widgets/MealDetailDropdown.tsx b/src/components/Nutrition/widgets/MealDetailDropdown.tsx index 16cddf30a..d239ff0cb 100644 --- a/src/components/Nutrition/widgets/MealDetailDropdown.tsx +++ b/src/components/Nutrition/widgets/MealDetailDropdown.tsx @@ -17,7 +17,7 @@ import { SNACKBAR_AUTO_HIDE_DURATION } from "@/core/lib/consts"; export const MealDetailDropdown = (props: { meal: Meal, - planId: number, + planId: string, onlyLogging: boolean, isExpanded: boolean, handleExpanded: () => void, diff --git a/src/components/Nutrition/widgets/forms/MealForm.test.tsx b/src/components/Nutrition/widgets/forms/MealForm.test.tsx index 26a2a1385..66c28a7e9 100644 --- a/src/components/Nutrition/widgets/forms/MealForm.test.tsx +++ b/src/components/Nutrition/widgets/forms/MealForm.test.tsx @@ -31,7 +31,7 @@ describe('Test the MealForm component', () => { // Act render( - + ); @@ -47,7 +47,7 @@ describe('Test the MealForm component', () => { expect(closeFnMock).toHaveBeenCalled(); expect(mutateAddMock).toHaveBeenCalledWith(new Meal({ name: '2nd breakfast', - planId: 987, + planId: 'aaaaaaaa-0000-0000-0000-000000000987', time: expect.any(Date), })); }); @@ -59,7 +59,7 @@ describe('Test the MealForm component', () => { // Act render( - + ); @@ -75,10 +75,10 @@ describe('Test the MealForm component', () => { expect(closeFnMock).toHaveBeenCalled(); expect(mutateEditMock).toHaveBeenCalledWith( expect.objectContaining({ - id: 78, + id: 'bbbbbbbb-0000-0000-0000-000000000078', name: '2nd breakfast', order: 2, - planId: 123, + planId: 'aaaaaaaa-0000-0000-0000-000000000123', time: TEST_MEAL_1.time }) ); diff --git a/src/components/Nutrition/widgets/forms/MealForm.tsx b/src/components/Nutrition/widgets/forms/MealForm.tsx index 4a89bf308..f5f83ae1b 100644 --- a/src/components/Nutrition/widgets/forms/MealForm.tsx +++ b/src/components/Nutrition/widgets/forms/MealForm.tsx @@ -10,7 +10,7 @@ import { useTranslation } from "react-i18next"; import * as yup from "yup"; interface MealFormProps { - planId: number, + planId: string, meal?: Meal, closeFn?: () => void, } diff --git a/src/components/Nutrition/widgets/forms/MealItemForm.test.tsx b/src/components/Nutrition/widgets/forms/MealItemForm.test.tsx index 7e3a5de40..d2ecaea8b 100644 --- a/src/components/Nutrition/widgets/forms/MealItemForm.test.tsx +++ b/src/components/Nutrition/widgets/forms/MealItemForm.test.tsx @@ -66,7 +66,8 @@ describe('Test the MealItemForm component', () => { // Act render( - + ); await fillInEntry(user); @@ -78,7 +79,7 @@ describe('Test the MealItemForm component', () => { expect(closeFnMock).toHaveBeenCalled(); expect(mutateAddMock).toHaveBeenCalledWith( expect.objectContaining({ - mealId: 123, + mealId: 'bbbbbbbb-0000-0000-0000-000000000123', amount: 120, ingredientId: 101, weightUnitId: null, @@ -92,7 +93,8 @@ describe('Test the MealItemForm component', () => { // Act render( - + ); await fillInEntry(user); @@ -104,8 +106,8 @@ describe('Test the MealItemForm component', () => { expect(closeFnMock).toHaveBeenCalled(); expect(mutateEditMock).toHaveBeenCalledWith( expect.objectContaining({ - id: 42, - mealId: 1001, + id: 'cccccccc-0000-0000-0000-000000000042', + mealId: 'bbbbbbbb-0000-0000-0000-000000001001', amount: 120, order: 3, ingredientId: 101, diff --git a/src/components/Nutrition/widgets/forms/MealItemForm.tsx b/src/components/Nutrition/widgets/forms/MealItemForm.tsx index 3e55f0b8c..4a91f7854 100644 --- a/src/components/Nutrition/widgets/forms/MealItemForm.tsx +++ b/src/components/Nutrition/widgets/forms/MealItemForm.tsx @@ -16,8 +16,8 @@ import * as yup from "yup"; const GRAM_UNIT_VALUE = 'g'; type MealItemFormProps = - | { planId: number; item: MealItem; closeFn?: () => void; mealId?: number } - | { planId: number; mealId: number; item?: undefined; closeFn?: () => void }; + | { planId: string; item: MealItem; closeFn?: () => void; mealId?: string } + | { planId: string; mealId: string; item?: undefined; closeFn?: () => void }; export const MealItemForm = ({ planId, item, mealId, closeFn }: MealItemFormProps) => { @@ -83,7 +83,7 @@ export const MealItemForm = ({ planId, item, mealId, closeFn }: MealItemFormProp } else { // Add addMealItemQuery.mutate(new MealItem({ - mealId: mealId, + mealId: mealId!, amount: newAmount, ingredientId: values.ingredient, weightUnitId: selectedUnit?.id ?? null, diff --git a/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.test.tsx b/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.test.tsx index e83f81f1d..e73648f0f 100644 --- a/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.test.tsx +++ b/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.test.tsx @@ -63,7 +63,7 @@ describe('Test the NutritionDiaryEntryForm component', () => { // Act render( - + ); await fillInEntry(user); @@ -78,7 +78,7 @@ describe('Test the NutritionDiaryEntryForm component', () => { amount: 120, datetime: expect.any(Date), ingredientId: 101, - planId: 123, + planId: 'aaaaaaaa-0000-0000-0000-000000000123', mealId: null, weightUnitId: null, }) @@ -91,7 +91,8 @@ describe('Test the NutritionDiaryEntryForm component', () => { // Act render( - + ); await fillInEntry(user); @@ -106,8 +107,8 @@ describe('Test the NutritionDiaryEntryForm component', () => { amount: 120, datetime: expect.any(Date), ingredientId: 101, - planId: 123, - mealId: 456, + planId: 'aaaaaaaa-0000-0000-0000-000000000123', + mealId: 'bbbbbbbb-0000-0000-0000-000000000456', weightUnitId: null, }) @@ -121,7 +122,8 @@ describe('Test the NutritionDiaryEntryForm component', () => { // Act render( - + ); await fillInEntry(user); @@ -131,10 +133,10 @@ describe('Test the NutritionDiaryEntryForm component', () => { expect(closeFnMock).toHaveBeenCalled(); expect(mutateEditMock).toHaveBeenCalledWith( expect.objectContaining({ - id: 42, + id: 'dddddddd-0000-0000-0000-000000000042', amount: 120, - mealId: 78, - planId: 123, + mealId: 'bbbbbbbb-0000-0000-0000-000000000078', + planId: 'aaaaaaaa-0000-0000-0000-000000000123', ingredientId: 101, }) ); @@ -147,7 +149,9 @@ describe('Test the NutritionDiaryEntryForm component', () => { // Act render( - + ); await fillInEntry(user); @@ -157,9 +161,9 @@ describe('Test the NutritionDiaryEntryForm component', () => { expect(closeFnMock).toHaveBeenCalled(); expect(mutateEditMock).toHaveBeenCalledWith( expect.objectContaining({ - id: 42, - planId: 123, - mealId: 456, + id: 'dddddddd-0000-0000-0000-000000000042', + planId: 'aaaaaaaa-0000-0000-0000-000000000123', + mealId: 'bbbbbbbb-0000-0000-0000-000000000456', amount: 120, ingredientId: 101, weightUnitId: null, diff --git a/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.tsx b/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.tsx index 9a26b8de1..cf81a03e5 100644 --- a/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.tsx +++ b/src/components/Nutrition/widgets/forms/NutritionDiaryEntryForm.tsx @@ -17,9 +17,9 @@ import * as yup from "yup"; const GRAM_UNIT_VALUE = 'g'; type NutritionDiaryEntryFormProps = { - planId: number, + planId: string, entry?: DiaryEntry, - mealId?: number | null, + mealId?: string | null, meals?: Meal[], closeFn?: () => void, } @@ -33,7 +33,7 @@ export const NutritionDiaryEntryForm = ({ planId, entry, mealId, meals, closeFn const addDiaryQuery = useAddDiaryEntryQuery(planId); const editDiaryQuery = useEditDiaryEntryQuery(planId); const [dateValue, setDateValue] = useState(entry ? DateTime.fromJSDate(entry.datetime) : DateTime.now()); - const [selectedMeal, setSelectedMeal] = useState(meal); + const [selectedMeal, setSelectedMeal] = useState(meal); const [selectedUnit, setSelectedUnit] = useState(entry?.weightUnit ?? null); const [weightUnits, setWeightUnits] = useState(entry?.ingredient?.weightUnits ?? []); diff --git a/src/components/Nutrition/widgets/forms/PlanForm.test.tsx b/src/components/Nutrition/widgets/forms/PlanForm.test.tsx index 7e720c7b2..4d98847b1 100644 --- a/src/components/Nutrition/widgets/forms/PlanForm.test.tsx +++ b/src/components/Nutrition/widgets/forms/PlanForm.test.tsx @@ -59,7 +59,7 @@ describe("Test the PlanForm component", () => { // Assert await user.click(screen.getByRole('button', { name: 'submit' })); expect(mutate).toHaveBeenCalledWith(expect.objectContaining({ - id: 101, + id: 'aaaaaaaa-0000-0000-0000-000000000101', description: "a better name", // the existing plan's dates must be preserved when only the description changes start: TEST_NUTRITIONAL_PLAN_1.start, diff --git a/src/components/Nutrition/widgets/forms/PlanForm.tsx b/src/components/Nutrition/widgets/forms/PlanForm.tsx index 588ad6b00..9a570ab0c 100644 --- a/src/components/Nutrition/widgets/forms/PlanForm.tsx +++ b/src/components/Nutrition/widgets/forms/PlanForm.tsx @@ -32,7 +32,7 @@ export const PlanForm = ({ plan, closeFn }: PlanFormProps) => { const [t] = useTranslation(); const addPlanQuery = useAddNutritionalPlanQuery(); - const editPlanQuery = useEditNutritionalPlanQuery(plan?.id || 0); + const editPlanQuery = useEditNutritionalPlanQuery(plan?.id ?? ''); const [useGoals, setUseGoals] = useState(plan?.hasAnyGoals); const [startDateValue, setStartDateValue] = useState(plan ? DateTime.fromJSDate(plan.start) : DateTime.now); const [endDateValue, setEndDateValue] = useState(plan && plan?.end !== null ? DateTime.fromJSDate(plan!.end) : null); diff --git a/src/components/Routines/api/routine.test.ts b/src/components/Routines/api/routine.test.ts index 6483b3149..521c1e0ef 100644 --- a/src/components/Routines/api/routine.test.ts +++ b/src/components/Routines/api/routine.test.ts @@ -115,7 +115,7 @@ describe("workout routine service tests", () => { expect(axios.get).toHaveBeenCalledTimes(1); expect(result).toStrictEqual([ new WorkoutLog({ - id: 2, + id: 'aaaaaaaa-aaaa-aaaa-aaaa-000000000002', routineId: 1, date: new Date("2023-05-10"), iteration: 1, @@ -132,7 +132,7 @@ describe("workout routine service tests", () => { }), new WorkoutLog({ - id: 1, + id: 'aaaaaaaa-aaaa-aaaa-aaaa-000000000001', routineId: 1, date: new Date("2023-05-13"), iteration: 1, diff --git a/src/components/Routines/api/session.test.ts b/src/components/Routines/api/session.test.ts index f4d43d0a5..5f7ea4ceb 100644 --- a/src/components/Routines/api/session.test.ts +++ b/src/components/Routines/api/session.test.ts @@ -1,13 +1,19 @@ -import axios from "axios"; +import * as exerciseService from "@/components/Exercises/api/exercise"; import { addSession, editSession, getSessions, searchSession } from "@/components/Routines/api/session"; import { WorkoutSession } from "@/components/Routines/models/WorkoutSession"; -import * as exerciseService from "@/components/Exercises/api/exercise"; import { testExerciseBenchPress, testExerciseSquats } from "@/tests/exerciseTestdata"; +import axios from "axios"; import type { Mock } from 'vitest'; vi.mock("axios"); vi.mock("@/components/Exercises/api/exercise"); +// Recognisable test-marker UUIDs matching the Django fixtures convention +const SESSION_UUID = 'bbbbbbbb-bbbb-bbbb-bbbb-000000024284'; +const SESSION_UUID_2 = 'bbbbbbbb-bbbb-bbbb-bbbb-000000000001'; +const LOG_UUID_1 = 'aaaaaaaa-aaaa-aaaa-aaaa-000004072327'; +const LOG_UUID_2 = 'aaaaaaaa-aaaa-aaaa-aaaa-000004072329'; + describe("Session service tests", () => { beforeEach(() => { vi.resetAllMocks(); @@ -23,9 +29,9 @@ describe("Session service tests", () => { previous: null, results: [ { - "id": 4072327, + "id": LOG_UUID_1, "date": "2025-08-07T00:00:00+02:00", - "session": 24284, + "session": SESSION_UUID, "routine": 39764, "iteration": 1, "slot_entry": 316691, @@ -43,9 +49,9 @@ describe("Session service tests", () => { "rest_target": null }, { - "id": 4072329, + "id": LOG_UUID_2, "date": "2025-08-07T00:00:00+02:00", - "session": 24284, + "session": SESSION_UUID, "routine": 39764, "iteration": 1, "slot_entry": 316693, @@ -75,7 +81,7 @@ describe("Session service tests", () => { previous: null, results: [ { - "id": 24284, + "id": SESSION_UUID, "routine": 39764, "day": null, "date": "2025-08-07", @@ -96,7 +102,7 @@ describe("Session service tests", () => { // Assert expect(sessions).toHaveLength(1); - expect(sessions[0].id).toBe(24284); + expect(sessions[0].id).toBe(SESSION_UUID); expect(sessions[0].logs).toHaveLength(2); expect(sessions[0].logs[0].exerciseId).toBe(345); expect(sessions[0].logs[0].exerciseObj).toBeDefined(); @@ -155,11 +161,11 @@ describe("Session service tests", () => { count: 1, next: null, previous: null, results: [ { - id: 24284, routine: 39764, day: 5, + id: SESSION_UUID, routine: 39764, day: 5, date: "2025-08-07", notes: "ok", impression: "3", - time_start: "20:10:58", time_end: "23:28:21", + time_start: "20:10:58", time_end: "23:28:21", }, ], }; @@ -172,7 +178,7 @@ describe("Session service tests", () => { expect(url).toContain("routine=39764"); expect(url).toContain("date=2025-08-07"); expect(result).toBeInstanceOf(WorkoutSession); - expect(result?.id).toBe(24284); + expect(result?.id).toBe(SESSION_UUID); }); test('searchSession returns null when count !== 1', async () => { @@ -198,9 +204,9 @@ describe("Session service tests", () => { test('addSession POSTs the params and returns the parsed session', async () => { (axios.post as Mock).mockResolvedValue({ data: { - id: 1, routine: 39764, day: 5, date: "2025-08-07", + id: SESSION_UUID_2, routine: 39764, day: 5, date: "2025-08-07", notes: null, impression: "3", - time_start: null, time_end: null, + time_start: null, time_end: null, }, }); @@ -221,27 +227,27 @@ describe("Session service tests", () => { impression: "3", }); expect(result).toBeInstanceOf(WorkoutSession); - expect(result.id).toBe(1); + expect(result.id).toBe(SESSION_UUID_2); }); test('editSession PATCHes /workoutsession// with the params', async () => { (axios.patch as Mock).mockResolvedValue({ data: { - id: 24284, routine: 39764, day: 5, date: "2025-08-07", + id: SESSION_UUID, routine: 39764, day: 5, date: "2025-08-07", notes: "edited", impression: "3", - time_start: null, time_end: null, + time_start: null, time_end: null, }, }); const result = await editSession({ - id: 24284, + id: SESSION_UUID, notes: "edited", }); expect(axios.patch).toHaveBeenCalledTimes(1); const [url, body] = (axios.patch as Mock).mock.calls[0]; - expect(url).toMatch(/\/api\/v2\/workoutsession\/24284\/$/); - expect(body).toEqual({ id: 24284, notes: "edited" }); + expect(url).toMatch(new RegExp(`/api/v2/workoutsession/${SESSION_UUID}/$`)); + expect(body).toEqual({ id: SESSION_UUID, notes: "edited" }); expect(result.notes).toBe("edited"); }); }); \ No newline at end of file diff --git a/src/components/Routines/api/workoutLogs.test.ts b/src/components/Routines/api/workoutLogs.test.ts index 06cdd80df..696da8128 100644 --- a/src/components/Routines/api/workoutLogs.test.ts +++ b/src/components/Routines/api/workoutLogs.test.ts @@ -38,7 +38,7 @@ describe("workout logs service tests", () => { expect(axios.get).toHaveBeenCalledTimes(1); expect(result).toStrictEqual([ new WorkoutLog({ - id: 2, + id: 'aaaaaaaa-aaaa-aaaa-aaaa-000000000002', routineId: 1, date: new Date("2023-05-10"), iteration: 1, @@ -60,7 +60,7 @@ describe("workout logs service tests", () => { }), new WorkoutLog({ - id: 1, + id: 'aaaaaaaa-aaaa-aaaa-aaaa-000000000001', routineId: 1, date: new Date("2023-05-13"), iteration: 1, @@ -100,7 +100,7 @@ describe("workout logs service tests", () => { expect(axios.get).toHaveBeenCalledTimes(1); expect(result).toStrictEqual([ new WorkoutLog({ - id: 2, + id: 'aaaaaaaa-aaaa-aaaa-aaaa-000000000002', routineId: 1, date: new Date("2023-05-10"), iteration: 1, @@ -124,7 +124,7 @@ describe("workout logs service tests", () => { }), new WorkoutLog({ - id: 1, + id: 'aaaaaaaa-aaaa-aaaa-aaaa-000000000001', routineId: 1, date: new Date("2023-05-13"), iteration: 1, @@ -163,21 +163,23 @@ describe("workout logs service tests", () => { }); test('deleteLog DELETEs /workoutlog// and returns the response status', async () => { + const LOG_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-000000000007'; (axios.delete as Mock).mockResolvedValue({ status: 204 }); - const result = await deleteLog(7); + const result = await deleteLog(LOG_UUID); expect(axios.delete).toHaveBeenCalledTimes(1); expect(axios.delete).toHaveBeenCalledWith( - expect.stringMatching(/\/api\/v2\/workoutlog\/7\/$/), + expect.stringMatching(new RegExp(`/api/v2/workoutlog/${LOG_UUID}/$`)), expect.anything() ); expect(result).toBe(204); }); test('editLog PATCHes /workoutlog// with the snake_case body and returns the parsed log', async () => { + const LOG_UUID = 'aaaaaaaa-aaaa-aaaa-aaaa-000000000005'; const apiResponse = { - id: 5, + id: LOG_UUID, iteration: 1, date: "2024-08-01T08:00:00.000Z", exercise: 100, @@ -197,7 +199,7 @@ describe("workout logs service tests", () => { (axios.patch as Mock).mockResolvedValue({ data: apiResponse }); const log = new WorkoutLog({ - id: 5, + id: LOG_UUID, routineId: 1, slotEntryId: 2, exerciseId: 100, @@ -217,9 +219,9 @@ describe("workout logs service tests", () => { expect(axios.patch).toHaveBeenCalledTimes(1); const [url, body] = (axios.patch as Mock).mock.calls[0]; - expect(url).toMatch(/\/api\/v2\/workoutlog\/5\/$/); + expect(url).toMatch(new RegExp(`/api/v2/workoutlog/${LOG_UUID}/$`)); expect(body).toMatchObject({ - id: 5, + id: LOG_UUID, exercise: 100, slot_entry: 2, @@ -228,13 +230,15 @@ describe("workout logs service tests", () => { repetitions_unit: 1, }); expect(result).toBeInstanceOf(WorkoutLog); - expect(result.id).toBe(5); + expect(result.id).toBe(LOG_UUID); }); test('addLogs POSTs each entry sequentially and collects the parsed responses', async () => { + const LOG_UUID_1 = 'aaaaaaaa-aaaa-aaaa-aaaa-000000000001'; + const LOG_UUID_2 = 'aaaaaaaa-aaaa-aaaa-aaaa-000000000002'; const responses = [ { - id: 1, + id: LOG_UUID_1, iteration: 1, date: "2024-08-01T00:00:00Z", exercise: 100, @@ -252,7 +256,7 @@ describe("workout logs service tests", () => { rest_target: null }, { - id: 2, + id: LOG_UUID_2, iteration: 1, date: "2024-08-02T00:00:00Z", exercise: 100, @@ -284,7 +288,7 @@ describe("workout logs service tests", () => { expect(url).toMatch(/\/api\/v2\/workoutlog\/$/); expect(result).toHaveLength(2); expect(result[0]).toBeInstanceOf(WorkoutLog); - expect(result.map(l => l.id)).toEqual([1, 2]); + expect(result.map(l => l.id)).toEqual([LOG_UUID_1, LOG_UUID_2]); }); test('addLogs returns [] when given an empty list (no requests fire)', async () => { diff --git a/src/components/Routines/api/workoutLogs.ts b/src/components/Routines/api/workoutLogs.ts index 303836188..e7dc042a5 100644 --- a/src/components/Routines/api/workoutLogs.ts +++ b/src/components/Routines/api/workoutLogs.ts @@ -7,7 +7,7 @@ import { fetchPaginated } from "@/core/lib/requests"; import { makeHeader, makeUrl } from "@/core/lib/url"; import axios from "axios"; -export const deleteLog = async (id: number): Promise => { +export const deleteLog = async (id: string): Promise => { const response = await axios.delete(makeUrl(ApiPath.WORKOUT_LOG, { id: id }), { headers: makeHeader(), }); diff --git a/src/components/Routines/models/WorkoutLog.ts b/src/components/Routines/models/WorkoutLog.ts index 8a3987cf0..19a00beb6 100644 --- a/src/components/Routines/models/WorkoutLog.ts +++ b/src/components/Routines/models/WorkoutLog.ts @@ -20,12 +20,12 @@ export interface LogEntryForm { export class WorkoutLog { - public id: number; + public id: string; public date: Date; public iteration: number | null; public exerciseId: number; public slotEntryId: number | null; - public sessionId: number | null; + public sessionId: string | null; public routineId: number | null; public repetitionUnitObj: RepetitionUnit | null; @@ -48,11 +48,11 @@ export class WorkoutLog { public exerciseObj?: Exercise; constructor(data: { - id: number; + id: string; date: Date | string; iteration: number | null; slotEntryId: number | null; - sessionId?: number | null; + sessionId?: string | null; routineId?: number | null; exercise?: Exercise; diff --git a/src/components/Routines/models/WorkoutSession.ts b/src/components/Routines/models/WorkoutSession.ts index 41a37e260..1a6d3de78 100644 --- a/src/components/Routines/models/WorkoutSession.ts +++ b/src/components/Routines/models/WorkoutSession.ts @@ -21,11 +21,11 @@ export interface AddSessionParams { } export interface EditSessionParams extends Partial { - id: number, + id: string, } interface WorkoutSessionParams { - id: number; + id: string; dayId: number; routineId: number; date: Date; @@ -39,7 +39,7 @@ interface WorkoutSessionParams { export class WorkoutSession { - id: number; + id: string; dayId: number; routineId: number; date: Date; diff --git a/src/components/Routines/queries/logs.ts b/src/components/Routines/queries/logs.ts index 350f2507b..4c820a06e 100644 --- a/src/components/Routines/queries/logs.ts +++ b/src/components/Routines/queries/logs.ts @@ -7,7 +7,7 @@ export function useDeleteRoutineLogQuery(routineId: number) { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (logId: number) => deleteLog(logId), + mutationFn: (logId: string) => deleteLog(logId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [QueryKey.ROUTINE_LOG_DATA, routineId] }); queryClient.invalidateQueries({ queryKey: [QueryKey.ROUTINE_STATS, routineId] }); diff --git a/src/components/Routines/queries/sessions.ts b/src/components/Routines/queries/sessions.ts index 7b09649d3..c70d485c2 100644 --- a/src/components/Routines/queries/sessions.ts +++ b/src/components/Routines/queries/sessions.ts @@ -31,7 +31,7 @@ export const useSessionsQuery = (options?: SessionQueryOptions) => useQuery({ }); -export const useEditSessionQuery = (id: number) => { +export const useEditSessionQuery = (id: string) => { const queryClient = useQueryClient(); return useMutation({ diff --git a/src/components/Routines/widgets/LogWidgets.test.tsx b/src/components/Routines/widgets/LogWidgets.test.tsx index 8d824398e..21f9afa4f 100644 --- a/src/components/Routines/widgets/LogWidgets.test.tsx +++ b/src/components/Routines/widgets/LogWidgets.test.tsx @@ -55,7 +55,7 @@ describe('ExerciseLog', () => { ); await user.click(screen.getAllByRole('menuitem', { name: /delete/i })[0]); - expect(mockDeleteMutate).toHaveBeenCalledWith(5); + expect(mockDeleteMutate).toHaveBeenCalledWith('aaaaaaaa-aaaa-aaaa-aaaa-000000000005'); expect(mockEditMutate).not.toHaveBeenCalled(); }); diff --git a/src/components/Routines/widgets/LogWidgets.tsx b/src/components/Routines/widgets/LogWidgets.tsx index 4ef71d138..7c33104d7 100644 --- a/src/components/Routines/widgets/LogWidgets.tsx +++ b/src/components/Routines/widgets/LogWidgets.tsx @@ -74,7 +74,7 @@ export const ExerciseLog = (props: { exercise: Exercise, routineId: number, logE }; const handleDeleteClick = (id: GridRowId) => () => { - deleteLogQuery.mutate(id as number); + deleteLogQuery.mutate(id.toString()); setRows(rows.filter((row) => row.id !== id)); }; diff --git a/src/components/Routines/widgets/forms/SessionForm.test.tsx b/src/components/Routines/widgets/forms/SessionForm.test.tsx index b3f0db088..84d820934 100644 --- a/src/components/Routines/widgets/forms/SessionForm.test.tsx +++ b/src/components/Routines/widgets/forms/SessionForm.test.tsx @@ -33,7 +33,7 @@ describe('SessionForm', () => { // Arrange const user = userEvent.setup(); const mockSession = new WorkoutSession({ - id: 0, + id: 'bbbbbbbb-bbbb-bbbb-bbbb-000000000000', dayId: 0, routineId: 0, date: new Date(), @@ -93,7 +93,7 @@ describe('SessionForm', () => { const timeEndFormatted = timeEnd.toLocaleString(DateTime.TIME_SIMPLE, { locale: 'en-us' }); const mockSession = new WorkoutSession({ - id: 1, + id: 'bbbbbbbb-bbbb-bbbb-bbbb-000000000001', dayId: dayId, routineId: routineId, date: date.toJSDate(), diff --git a/src/components/Routines/widgets/forms/SessionForm.tsx b/src/components/Routines/widgets/forms/SessionForm.tsx index 7e72744f5..158e1c1e4 100644 --- a/src/components/Routines/widgets/forms/SessionForm.tsx +++ b/src/components/Routines/widgets/forms/SessionForm.tsx @@ -43,7 +43,7 @@ export const SessionForm = ({ initialSession, dayId, routineId, selectedDate, se const [session, setSession] = React.useState(initialSession); const addSessionQuery = useAddSessionQuery(); - const editSessionQuery = useEditSessionQuery(session?.id || 0); + const editSessionQuery = useEditSessionQuery(session?.id || ''); const findSessionQuery = useFindSessionQuery( routineId, { diff --git a/src/core/lib/url.ts b/src/core/lib/url.ts index ccb6ec742..0c6551a88 100644 --- a/src/core/lib/url.ts +++ b/src/core/lib/url.ts @@ -4,7 +4,7 @@ import i18n from "@/i18n"; import slug from "slug"; interface makeUrlInterface { - id?: number, + id?: number | string, server?: string, objectMethod?: string, // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -100,7 +100,7 @@ export enum WgerLink { TROPHIES, } -type UrlParams = { id: number, id2?: number, slug?: string, date?: string }; +type UrlParams = { id: number | string, id2?: number | string, slug?: string, date?: string }; /* diff --git a/src/tests/measurementsTestData.ts b/src/tests/measurementsTestData.ts index 425d2f4e6..40df955ef 100644 --- a/src/tests/measurementsTestData.ts +++ b/src/tests/measurementsTestData.ts @@ -1,31 +1,36 @@ import { MeasurementCategory } from "@/components/Measurements/models/Category"; import { MeasurementEntry } from "@/components/Measurements/models/Entry"; +// Recognisable test-marker UUIDs matching the Django fixtures convention +const CATEGORY_1 = 'cccccccc-cccc-cccc-cccc-000000000001'; +const CATEGORY_2 = 'cccccccc-cccc-cccc-cccc-000000000002'; +const entry = (n: number) => `dddddddd-dddd-dddd-dddd-${n.toString().padStart(12, '0')}`; + export const TEST_MEASUREMENT_ENTRIES_1 = [ - new MeasurementEntry(1, 1, new Date(2023, 1, 1, 8, 0), 10, "test note"), - new MeasurementEntry(2, 1, new Date(2023, 1, 1, 18, 30), 12, "evening measurement"), - new MeasurementEntry(3, 1, new Date(2023, 1, 2, 7, 45), 20, ""), - new MeasurementEntry(4, 1, new Date(2023, 1, 3, 9, 15), 30, "important note"), - new MeasurementEntry(5, 1, new Date(2023, 1, 4, 12, 0), 40, "this day was good"), - new MeasurementEntry(6, 1, new Date(2023, 1, 5, 8, 0), 50, ""), - new MeasurementEntry(7, 1, new Date(2023, 1, 6, 8, 0), 60, ""), - new MeasurementEntry(8, 1, new Date(2023, 1, 7, 8, 0), 70, ""), + new MeasurementEntry(entry(1), CATEGORY_1, new Date(2023, 1, 1, 8, 0), 10, "test note"), + new MeasurementEntry(entry(2), CATEGORY_1, new Date(2023, 1, 1, 18, 30), 12, "evening measurement"), + new MeasurementEntry(entry(3), CATEGORY_1, new Date(2023, 1, 2, 7, 45), 20, ""), + new MeasurementEntry(entry(4), CATEGORY_1, new Date(2023, 1, 3, 9, 15), 30, "important note"), + new MeasurementEntry(entry(5), CATEGORY_1, new Date(2023, 1, 4, 12, 0), 40, "this day was good"), + new MeasurementEntry(entry(6), CATEGORY_1, new Date(2023, 1, 5, 8, 0), 50, ""), + new MeasurementEntry(entry(7), CATEGORY_1, new Date(2023, 1, 6, 8, 0), 60, ""), + new MeasurementEntry(entry(8), CATEGORY_1, new Date(2023, 1, 7, 8, 0), 70, ""), ]; export const TEST_MEASUREMENT_ENTRIES_2 = [ - new MeasurementEntry(1, 2, new Date(2023, 3, 1, 7, 0), 11, ""), - new MeasurementEntry(2, 2, new Date(2023, 3, 1, 19, 0), 13, ""), - new MeasurementEntry(3, 2, new Date(2023, 3, 2, 8, 30), 22, ""), - new MeasurementEntry(4, 2, new Date(2023, 3, 3, 9, 0), 33, ""), - new MeasurementEntry(5, 2, new Date(2023, 3, 4, 10, 15), 44, ""), - new MeasurementEntry(6, 2, new Date(2023, 3, 5, 7, 45), 55, ""), - new MeasurementEntry(7, 2, new Date(2023, 3, 6, 8, 0), 66, ""), - new MeasurementEntry(8, 2, new Date(2023, 3, 7, 8, 0), 77, ""), + new MeasurementEntry(entry(101), CATEGORY_2, new Date(2023, 3, 1, 7, 0), 11, ""), + new MeasurementEntry(entry(102), CATEGORY_2, new Date(2023, 3, 1, 19, 0), 13, ""), + new MeasurementEntry(entry(103), CATEGORY_2, new Date(2023, 3, 2, 8, 30), 22, ""), + new MeasurementEntry(entry(104), CATEGORY_2, new Date(2023, 3, 3, 9, 0), 33, ""), + new MeasurementEntry(entry(105), CATEGORY_2, new Date(2023, 3, 4, 10, 15), 44, ""), + new MeasurementEntry(entry(106), CATEGORY_2, new Date(2023, 3, 5, 7, 45), 55, ""), + new MeasurementEntry(entry(107), CATEGORY_2, new Date(2023, 3, 6, 8, 0), 66, ""), + new MeasurementEntry(entry(108), CATEGORY_2, new Date(2023, 3, 7, 8, 0), 77, ""), ]; export const TEST_MEASUREMENT_CATEGORY_1 = new MeasurementCategory( - 1, + CATEGORY_1, "Biceps", "cm", TEST_MEASUREMENT_ENTRIES_1, @@ -33,8 +38,8 @@ export const TEST_MEASUREMENT_CATEGORY_1 = new MeasurementCategory( export const TEST_MEASUREMENT_CATEGORY_2 = new MeasurementCategory( - 2, + CATEGORY_2, "Body fat", "%", TEST_MEASUREMENT_ENTRIES_2 -); \ No newline at end of file +); diff --git a/src/tests/nutritionDiaryTestdata.ts b/src/tests/nutritionDiaryTestdata.ts index dc916b70f..70674f0c7 100644 --- a/src/tests/nutritionDiaryTestdata.ts +++ b/src/tests/nutritionDiaryTestdata.ts @@ -3,9 +3,9 @@ import { TEST_INGREDIENT_1, TEST_INGREDIENT_2, TEST_INGREDIENT_3, TEST_INGREDIEN export const TEST_DIARY_ENTRY_1 = new DiaryEntry({ - id: 42, - planId: 1, - mealId: 78, + id: 'dddddddd-0000-0000-0000-000000000042', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000078', ingredientId: 101, weightUnitId: null, amount: 120, @@ -14,9 +14,9 @@ export const TEST_DIARY_ENTRY_1 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_2 = new DiaryEntry({ - id: 44, - planId: 1, - mealId: 78, + id: 'dddddddd-0000-0000-0000-000000000044', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000078', ingredientId: 102, weightUnitId: null, amount: 50, @@ -25,9 +25,9 @@ export const TEST_DIARY_ENTRY_2 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_3 = new DiaryEntry({ - id: 45, - planId: 1, - mealId: 78, + id: 'dddddddd-0000-0000-0000-000000000045', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000078', ingredientId: 103, weightUnitId: null, amount: 200, @@ -36,9 +36,9 @@ export const TEST_DIARY_ENTRY_3 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_4 = new DiaryEntry({ - id: 46, - planId: 1, - mealId: 78, + id: 'dddddddd-0000-0000-0000-000000000046', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000078', ingredientId: 103, weightUnitId: null, amount: 20, @@ -47,9 +47,9 @@ export const TEST_DIARY_ENTRY_4 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_5 = new DiaryEntry({ - id: 47, - planId: 1, - mealId: 78, + id: 'dddddddd-0000-0000-0000-000000000047', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000078', ingredientId: 103, weightUnitId: null, amount: 20, @@ -58,9 +58,9 @@ export const TEST_DIARY_ENTRY_5 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_6 = new DiaryEntry({ - id: 48, - planId: 1, - mealId: 78, + id: 'dddddddd-0000-0000-0000-000000000048', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000078', ingredientId: 103, weightUnitId: null, amount: 50, @@ -69,9 +69,9 @@ export const TEST_DIARY_ENTRY_6 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_7 = new DiaryEntry({ - id: 49, - planId: 1, - mealId: 78, + id: 'dddddddd-0000-0000-0000-000000000049', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000078', ingredientId: 103, weightUnitId: null, amount: 50, @@ -80,9 +80,9 @@ export const TEST_DIARY_ENTRY_7 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_8 = new DiaryEntry({ - id: 50, - planId: 1, - mealId: 78, + id: 'dddddddd-0000-0000-0000-000000000050', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000078', ingredientId: 103, weightUnitId: null, amount: 100, @@ -91,9 +91,9 @@ export const TEST_DIARY_ENTRY_8 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_9 = new DiaryEntry({ - id: 51, - planId: 1, - mealId: 999, + id: 'dddddddd-0000-0000-0000-000000000051', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000999', ingredientId: 103, weightUnitId: null, amount: 80, @@ -102,9 +102,9 @@ export const TEST_DIARY_ENTRY_9 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_10 = new DiaryEntry({ - id: 51, - planId: 1, - mealId: 78, + id: 'dddddddd-0000-0000-0000-000000000051', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000078', ingredientId: 103, weightUnitId: null, amount: 80, @@ -113,9 +113,9 @@ export const TEST_DIARY_ENTRY_10 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_11 = new DiaryEntry({ - id: 52, - planId: 1, - mealId: 999, + id: 'dddddddd-0000-0000-0000-000000000052', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000999', ingredientId: 103, weightUnitId: null, amount: 500, @@ -124,9 +124,9 @@ export const TEST_DIARY_ENTRY_11 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_12 = new DiaryEntry({ - id: 52, - planId: 1, - mealId: 999, + id: 'dddddddd-0000-0000-0000-000000000052', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000999', ingredientId: 104, weightUnitId: null, amount: 500, @@ -135,9 +135,9 @@ export const TEST_DIARY_ENTRY_12 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_13 = new DiaryEntry({ - id: 53, - planId: 1, - mealId: 78, + id: 'dddddddd-0000-0000-0000-000000000053', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', + mealId: 'bbbbbbbb-0000-0000-0000-000000000078', ingredientId: 104, weightUnitId: null, amount: 500, @@ -146,8 +146,8 @@ export const TEST_DIARY_ENTRY_13 = new DiaryEntry({ }); export const TEST_DIARY_ENTRY_14 = new DiaryEntry({ - id: 54, - planId: 1, + id: 'dddddddd-0000-0000-0000-000000000054', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', mealId: null, ingredientId: 104, weightUnitId: null, @@ -156,8 +156,8 @@ export const TEST_DIARY_ENTRY_14 = new DiaryEntry({ ingredient: TEST_INGREDIENT_4 }); export const TEST_DIARY_ENTRY_15 = new DiaryEntry({ - id: 54, - planId: 1, + id: 'dddddddd-0000-0000-0000-000000000054', + planId: 'aaaaaaaa-0000-0000-0000-000000000001', mealId: null, ingredientId: 103, weightUnitId: null, diff --git a/src/tests/nutritionTestdata.ts b/src/tests/nutritionTestdata.ts index 80b197027..c155b7aec 100644 --- a/src/tests/nutritionTestdata.ts +++ b/src/tests/nutritionTestdata.ts @@ -41,8 +41,8 @@ export const TEST_WEIGHT_UNIT_CUP = new NutritionWeightUnit( export const TEST_MEAL_ITEM_1 = new MealItem({ - id: 42, - mealId: 1001, + id: 'cccccccc-0000-0000-0000-000000000042', + mealId: 'bbbbbbbb-0000-0000-0000-000000001001', amount: 120, order: 3, ingredient: TEST_INGREDIENT_1, @@ -50,8 +50,8 @@ export const TEST_MEAL_ITEM_1 = new MealItem({ weightUnitId: null, }); export const TEST_MEAL_ITEM_2 = new MealItem({ - id: 43, - mealId: 1001, + id: 'cccccccc-0000-0000-0000-000000000043', + mealId: 'bbbbbbbb-0000-0000-0000-000000001001', amount: 220, order: 1, ingredientId: 102, @@ -59,8 +59,8 @@ export const TEST_MEAL_ITEM_2 = new MealItem({ ingredient: TEST_INGREDIENT_2 }); export const TEST_MEAL_ITEM_3 = new MealItem({ - id: 45, - mealId: 1001, + id: 'cccccccc-0000-0000-0000-000000000045', + mealId: 'bbbbbbbb-0000-0000-0000-000000001001', amount: 320, order: 2, ingredientId: 104, @@ -69,8 +69,8 @@ export const TEST_MEAL_ITEM_3 = new MealItem({ }); export const TEST_MEAL_ITEM_4 = new MealItem({ - id: 46, - mealId: 999, + id: 'cccccccc-0000-0000-0000-000000000046', + mealId: 'bbbbbbbb-0000-0000-0000-000000000999', amount: 320, order: 1, ingredientId: 105, @@ -79,8 +79,8 @@ export const TEST_MEAL_ITEM_4 = new MealItem({ }); export const TEST_MEAL_ITEM_5 = new MealItem({ - id: 47, - mealId: 1, + id: 'cccccccc-0000-0000-0000-000000000047', + mealId: 'bbbbbbbb-0000-0000-0000-000000000001', amount: 320, order: 1, ingredientId: 101, @@ -89,8 +89,8 @@ export const TEST_MEAL_ITEM_5 = new MealItem({ }); export const TEST_MEAL_ITEM_6 = new MealItem({ - id: 48, - mealId: 123, + id: 'cccccccc-0000-0000-0000-000000000048', + mealId: 'bbbbbbbb-0000-0000-0000-000000000123', amount: 100, order: 2, ingredientId: 102, @@ -98,8 +98,8 @@ export const TEST_MEAL_ITEM_6 = new MealItem({ ingredient: TEST_INGREDIENT_2 }); export const TEST_MEAL_ITEM_7 = new MealItem({ - id: 103, - mealId: 2345, + id: 'cccccccc-0000-0000-0000-000000000103', + mealId: 'bbbbbbbb-0000-0000-0000-000000002345', amount: 100, order: 1, ingredientId: 109, @@ -108,8 +108,8 @@ export const TEST_MEAL_ITEM_7 = new MealItem({ }); export const TEST_MEAL_ITEM_8 = new MealItem({ - id: 104, - mealId: 2222, + id: 'cccccccc-0000-0000-0000-000000000104', + mealId: 'bbbbbbbb-0000-0000-0000-000000002222', amount: 120, order: 1, ingredientId: 110, @@ -119,8 +119,8 @@ export const TEST_MEAL_ITEM_8 = new MealItem({ export const TEST_MEAL_1 = new Meal({ - id: 78, - planId: 123, + id: 'bbbbbbbb-0000-0000-0000-000000000078', + planId: 'aaaaaaaa-0000-0000-0000-000000000123', order: 2, time: HHMMToDateTime('12:30'), name: 'Second breakfast', @@ -129,7 +129,7 @@ TEST_MEAL_1.items = [TEST_MEAL_ITEM_1, TEST_MEAL_ITEM_2, TEST_MEAL_ITEM_3]; export const TEST_MEAL_2 = new Meal({ - id: 999, + id: 'bbbbbbbb-0000-0000-0000-000000000999', order: 3, time: HHMMToDateTime('22:30'), name: 'evening snack', @@ -137,7 +137,7 @@ export const TEST_MEAL_2 = new Meal({ TEST_MEAL_2.items = [TEST_MEAL_ITEM_4]; export const TEST_MEAL_3 = new Meal({ - id: 1, + id: 'bbbbbbbb-0000-0000-0000-000000000001', order: 1, time: HHMMToDateTime('6:30'), name: 'breakfast', @@ -145,7 +145,7 @@ export const TEST_MEAL_3 = new Meal({ TEST_MEAL_3.items = [TEST_MEAL_ITEM_5, TEST_MEAL_ITEM_6]; export const TEST_MEAL_4 = new Meal({ - id: 2, + id: 'bbbbbbbb-0000-0000-0000-000000000002', order: 1, time: HHMMToDateTime('7:45'), name: 'Cake time', @@ -153,7 +153,7 @@ export const TEST_MEAL_4 = new Meal({ TEST_MEAL_4.items = [TEST_MEAL_ITEM_7]; export const TEST_MEAL_5 = new Meal({ - id: 22, + id: 'bbbbbbbb-0000-0000-0000-000000000022', order: 2, time: HHMMToDateTime('12:00'), name: 'Time to visit McDonalds', @@ -161,7 +161,7 @@ export const TEST_MEAL_5 = new Meal({ TEST_MEAL_5.items = [TEST_MEAL_ITEM_8]; export const TEST_NUTRITIONAL_PLAN_1 = new NutritionalPlan({ - id: 101, + id: 'aaaaaaaa-0000-0000-0000-000000000101', creationDate: new Date('2023-01-01'), description: 'Summer body!!!', }); @@ -191,17 +191,25 @@ TEST_NUTRITIONAL_PLAN_1.diaryEntries = [ export const TEST_NUTRITIONAL_PLAN_2 = new NutritionalPlan({ - id: 222, + id: 'aaaaaaaa-0000-0000-0000-000000000222', creationDate: new Date('2023-08-01'), description: 'Bulking till we puke', }); TEST_NUTRITIONAL_PLAN_2.meals = [TEST_MEAL_4, TEST_MEAL_5]; +// Recognisable test-marker UUIDs for the API response envelopes below +export const RESPONSE_PLAN_UUID = 'aaaaaaaa-0000-0000-0000-000000000101'; +export const RESPONSE_PLAN_UUID_2 = 'aaaaaaaa-0000-0000-0000-000000000123'; +export const RESPONSE_MEAL_UUID = 'bbbbbbbb-0000-0000-0000-000000000078'; +export const RESPONSE_MEAL_ITEM_UUID = 'cccccccc-0000-0000-0000-000000000042'; +export const RESPONSE_MEAL_ITEM_UUID_2 = 'cccccccc-0000-0000-0000-000000000043'; +export const RESPONSE_DIARY_UUID = 'dddddddd-0000-0000-0000-000000000009'; + // API envelope for a single meal (POST/PATCH /meal/ response). export const responseMealDetail = { - id: 78, - plan: 123, + id: RESPONSE_MEAL_UUID, + plan: RESPONSE_PLAN_UUID_2, order: 2, time: '12:30', name: 'Second breakfast', @@ -222,16 +230,16 @@ export const responseMealItemsForMeal = { previous: null, results: [ { - id: 42, - meal: 78, + id: RESPONSE_MEAL_ITEM_UUID, + meal: RESPONSE_MEAL_UUID, ingredient: 101, weight_unit: null, order: 3, amount: '120.00', }, { - id: 43, - meal: 78, + id: RESPONSE_MEAL_ITEM_UUID_2, + meal: RESPONSE_MEAL_UUID, ingredient: 102, weight_unit: null, order: 1, @@ -242,8 +250,8 @@ export const responseMealItemsForMeal = { // Single mealitem envelope (POST/PATCH /mealitem/ response) export const responseMealItemDetail = { - id: 42, - meal: 78, + id: RESPONSE_MEAL_ITEM_UUID, + meal: RESPONSE_MEAL_UUID, ingredient: 101, weight_unit: null, order: 3, @@ -252,7 +260,7 @@ export const responseMealItemDetail = { // Single nutritional plan envelope (POST/PATCH /nutritionplan/ response) export const responseNutritionalPlanDetail = { - id: 101, + id: RESPONSE_PLAN_UUID, creation_date: '2023-01-01', start: '2023-01-01', end: '2023-12-31', @@ -288,9 +296,9 @@ export const responseDiaryEntries = { previous: null, results: [ { - id: 9, - plan: 101, - meal: 78, + id: RESPONSE_DIARY_UUID, + plan: RESPONSE_PLAN_UUID, + meal: RESPONSE_MEAL_UUID, ingredient: 101, weight_unit: null, amount: '150.00', @@ -301,9 +309,9 @@ export const responseDiaryEntries = { // Single diary entry envelope (POST/PATCH /nutritiondiary/ response) export const responseDiaryEntryDetail = { - id: 9, - plan: 101, - meal: 78, + id: RESPONSE_DIARY_UUID, + plan: RESPONSE_PLAN_UUID, + meal: RESPONSE_MEAL_UUID, ingredient: 101, weight_unit: null, amount: '150.00', diff --git a/src/tests/workoutLogsRoutinesTestData.ts b/src/tests/workoutLogsRoutinesTestData.ts index afc210422..94338bde5 100644 --- a/src/tests/workoutLogsRoutinesTestData.ts +++ b/src/tests/workoutLogsRoutinesTestData.ts @@ -4,7 +4,7 @@ import { testExerciseSquats } from "@/tests/exerciseTestdata"; import { testRepUnitRepetitions, testWeightUnitKg } from "@/tests/workoutRoutinesTestData"; const testWorkoutLog1 = new WorkoutLog({ - id: 5, + id: 'aaaaaaaa-aaaa-aaaa-aaaa-000000000005', routineId: 1, date: new Date(2023, 1, 1), iteration: 345, @@ -21,7 +21,7 @@ const testWorkoutLog1 = new WorkoutLog({ }); const testWorkoutLog2 = new WorkoutLog({ - id: 6, + id: 'aaaaaaaa-aaaa-aaaa-aaaa-000000000006', routineId: 1, date: new Date(2023, 1, 2), iteration: 345, @@ -38,7 +38,7 @@ const testWorkoutLog2 = new WorkoutLog({ }); const testWorkoutLog3 = new WorkoutLog({ - id: 7, + id: 'aaaaaaaa-aaaa-aaaa-aaaa-000000000007', routineId: 1, date: new Date(2023, 1, 3), iteration: 345, @@ -55,7 +55,7 @@ const testWorkoutLog3 = new WorkoutLog({ }); const testWorkoutLog4 = new WorkoutLog({ - id: 8, + id: 'aaaaaaaa-aaaa-aaaa-aaaa-000000000008', routineId: 1, date: new Date(2023, 1, 10), iteration: 345, @@ -79,7 +79,7 @@ export const testWorkoutLogs = [ ]; export const testWorkoutSession = new WorkoutSession({ - id: 1, + id: 'bbbbbbbb-bbbb-bbbb-bbbb-000000000001', dayId: 2, routineId: 3, date: new Date(2025, 1, 10), diff --git a/src/tests/workoutRoutinesTestData.ts b/src/tests/workoutRoutinesTestData.ts index 562081ad1..f27abb6d1 100644 --- a/src/tests/workoutRoutinesTestData.ts +++ b/src/tests/workoutRoutinesTestData.ts @@ -205,7 +205,7 @@ export const testRoutineDayData1 = [ export const testRoutineLogData = [ new RoutineLogData( new WorkoutSession({ - id: 111, + id: 'bbbbbbbb-bbbb-bbbb-bbbb-000000000111', dayId: 2, routineId: 1, date: new Date('2024-07-01'), @@ -308,7 +308,7 @@ export const responseRoutineLogs = { "previous": null, "results": [ { - "id": 2, + "id": "aaaaaaaa-aaaa-aaaa-aaaa-000000000002", "iteration": 1, "date": "2023-05-10", "exercise": 345, @@ -327,7 +327,7 @@ export const responseRoutineLogs = { "rir_target": null }, { - "id": 1, + "id": "aaaaaaaa-aaaa-aaaa-aaaa-000000000001", "iteration": 1, "date": "2023-05-13", "exercise": 345, diff --git a/src/types.ts b/src/types.ts index 1f57da219..a78dad30e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -48,7 +48,7 @@ export interface ApiAliasType { } export interface ApiMeasurementCategoryType { - id: number, + id: string, name: string, unit: string } @@ -112,7 +112,7 @@ export type ApiIngredientWeightUnitType = { } export interface ApiNutritionalPlanType { - id: number, + id: string, creation_date: string, start: string, end: string | null, @@ -127,8 +127,8 @@ export interface ApiNutritionalPlanType { export interface ApiMeasurementEntryType { - id: number, - category: number, + id: string, + category: string, date: Date, value: number, notes: string