From 8c2a84755da57eab79dfa72de91ac1862aaa990a Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sun, 5 Feb 2023 12:48:30 +0100 Subject: [PATCH] Added submission page and cleaned up time formatting. --- .../api/dto/ExerciseSubmissionResponse.java | 3 +- .../domains/api/dto/GymResponse.java | 3 +- .../gymboard_api/domains/auth/model/User.java | 7 ++- .../util/StandardDateFormatter.java | 15 +++++++ gymboard-app/package-lock.json | 27 ++++++++++++ gymboard-app/package.json | 2 + gymboard-app/src/api/main/gyms.ts | 5 ++- gymboard-app/src/api/main/leaderboards.ts | 4 +- gymboard-app/src/api/main/submission.ts | 16 ++++--- .../components/ExerciseSubmissionListItem.vue | 44 +++++++------------ gymboard-app/src/pages/SubmissionPage.vue | 30 +++++++++++-- gymboard-app/src/pages/gym/GymHomePage.vue | 2 +- .../src/pages/gym/GymLeaderboardsPage.vue | 2 +- 13 files changed, 114 insertions(+), 46 deletions(-) create mode 100644 gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/StandardDateFormatter.java diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseSubmissionResponse.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseSubmissionResponse.java index 67854db..fb5e91d 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseSubmissionResponse.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseSubmissionResponse.java @@ -1,6 +1,7 @@ package nl.andrewlalis.gymboard_api.domains.api.dto; import nl.andrewlalis.gymboard_api.domains.api.model.exercise.ExerciseSubmission; +import nl.andrewlalis.gymboard_api.util.StandardDateFormatter; import java.time.format.DateTimeFormatter; @@ -19,7 +20,7 @@ public record ExerciseSubmissionResponse( public ExerciseSubmissionResponse(ExerciseSubmission submission) { this( submission.getId(), - submission.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), + StandardDateFormatter.format(submission.getCreatedAt()), new GymSimpleResponse(submission.getGym()), new ExerciseResponse(submission.getExercise()), submission.getVideoFileId(), diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/GymResponse.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/GymResponse.java index 400fe86..e5c3640 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/GymResponse.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/GymResponse.java @@ -1,6 +1,7 @@ package nl.andrewlalis.gymboard_api.domains.api.dto; import nl.andrewlalis.gymboard_api.domains.api.model.Gym; +import nl.andrewlalis.gymboard_api.util.StandardDateFormatter; import java.time.format.DateTimeFormatter; @@ -22,7 +23,7 @@ public record GymResponse ( gym.getCity().getCountry().getName(), gym.getCity().getShortName(), gym.getCity().getName(), - gym.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), + StandardDateFormatter.format(gym.getCreatedAt()), gym.getShortName(), gym.getDisplayName(), gym.getWebsiteUrl(), diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/model/User.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/model/User.java index 4d747ff..80b8b03 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/model/User.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/model/User.java @@ -37,7 +37,7 @@ public class User { ) private Set roles; - @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, optional = false) + @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, optional = false, fetch = FetchType.LAZY) private UserPersonalDetails personalDetails; public User() {} @@ -49,6 +49,7 @@ public class User { this.passwordHash = passwordHash; this.name = name; this.roles = new HashSet<>(); + this.personalDetails = new UserPersonalDetails(this); } public String getId() { @@ -86,4 +87,8 @@ public class User { public Set getRoles() { return roles; } + + public UserPersonalDetails getPersonalDetails() { + return personalDetails; + } } diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/StandardDateFormatter.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/StandardDateFormatter.java new file mode 100644 index 0000000..0f81d72 --- /dev/null +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/StandardDateFormatter.java @@ -0,0 +1,15 @@ +package nl.andrewlalis.gymboard_api.util; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; + +/** + * The API-standard formatter for date-time objects that are sent as responses + * where we need to enforce a specific format. + */ +public class StandardDateFormatter { + public static String format(LocalDateTime utcTimestamp) { + return utcTimestamp.atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } +} diff --git a/gymboard-app/package-lock.json b/gymboard-app/package-lock.json index 1fb0ff1..a2f394c 100644 --- a/gymboard-app/package-lock.json +++ b/gymboard-app/package-lock.json @@ -11,6 +11,7 @@ "@quasar/cli": "^2.0.0", "@quasar/extras": "^1.0.0", "axios": "^0.21.1", + "luxon": "^3.2.1", "moment": "^2.29.4", "pinia": "^2.0.11", "quasar": "^2.6.0", @@ -22,6 +23,7 @@ "@intlify/vite-plugin-vue-i18n": "^3.3.1", "@quasar/app-vite": "^1.0.0", "@types/leaflet": "^1.9.0", + "@types/luxon": "^3.2.0", "@types/node": "^12.20.21", "@typescript-eslint/eslint-plugin": "^5.10.0", "@typescript-eslint/parser": "^5.10.0", @@ -598,6 +600,12 @@ "@types/geojson": "*" } }, + "node_modules/@types/luxon": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.2.0.tgz", + "integrity": "sha512-lGmaGFoaXHuOLXFvuju2bfvZRqxAqkHPx9Y9IQdQABrinJJshJwfNCKV+u7rR3kJbiqfTF/NhOkcxxAFrObyaA==", + "dev": true + }, "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -4237,6 +4245,14 @@ "yallist": "^2.0.0" } }, + "node_modules/luxon": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz", + "integrity": "sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg==", + "engines": { + "node": ">=12" + } + }, "node_modules/magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -6828,6 +6844,12 @@ "@types/geojson": "*" } }, + "@types/luxon": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.2.0.tgz", + "integrity": "sha512-lGmaGFoaXHuOLXFvuju2bfvZRqxAqkHPx9Y9IQdQABrinJJshJwfNCKV+u7rR3kJbiqfTF/NhOkcxxAFrObyaA==", + "dev": true + }, "@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -9347,6 +9369,11 @@ "yallist": "^2.0.0" } }, + "luxon": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.2.1.tgz", + "integrity": "sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg==" + }, "magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", diff --git a/gymboard-app/package.json b/gymboard-app/package.json index 9e31681..f714341 100644 --- a/gymboard-app/package.json +++ b/gymboard-app/package.json @@ -14,6 +14,7 @@ "@quasar/cli": "^2.0.0", "@quasar/extras": "^1.0.0", "axios": "^0.21.1", + "luxon": "^3.2.1", "moment": "^2.29.4", "pinia": "^2.0.11", "quasar": "^2.6.0", @@ -25,6 +26,7 @@ "@intlify/vite-plugin-vue-i18n": "^3.3.1", "@quasar/app-vite": "^1.0.0", "@types/leaflet": "^1.9.0", + "@types/luxon": "^3.2.0", "@types/node": "^12.20.21", "@typescript-eslint/eslint-plugin": "^5.10.0", "@typescript-eslint/parser": "^5.10.0", diff --git a/gymboard-app/src/api/main/gyms.ts b/gymboard-app/src/api/main/gyms.ts index 62c3d6a..a58844f 100644 --- a/gymboard-app/src/api/main/gyms.ts +++ b/gymboard-app/src/api/main/gyms.ts @@ -1,5 +1,5 @@ import { GeoPoint } from 'src/api/main/models'; -import SubmissionsModule, { ExerciseSubmission } from 'src/api/main/submission'; +import SubmissionsModule, { ExerciseSubmission, parseSubmission } from 'src/api/main/submission'; import { api } from 'src/api/main/index'; import { GymRoutable } from 'src/router/gym-routing'; @@ -55,7 +55,8 @@ class GymsModule { const response = await api.get( `/gyms/${gym.countryCode}_${gym.cityShortName}_${gym.shortName}/recent-submissions` ); - return response.data; + const submissionObjects: Array = response.data; + return submissionObjects.map(parseSubmission); } } diff --git a/gymboard-app/src/api/main/leaderboards.ts b/gymboard-app/src/api/main/leaderboards.ts index b876bd3..a2997bd 100644 --- a/gymboard-app/src/api/main/leaderboards.ts +++ b/gymboard-app/src/api/main/leaderboards.ts @@ -1,4 +1,4 @@ -import { ExerciseSubmission } from 'src/api/main/submission'; +import { ExerciseSubmission, parseSubmission } from 'src/api/main/submission'; import { getGymCompoundId, GymRoutable } from 'src/router/gym-routing'; import { api } from 'src/api/main/index'; @@ -46,7 +46,7 @@ class LeaderboardsModule { if (params.size) requestParams.size = params.size; const response = await api.get('/leaderboards', { params: requestParams }); - return response.data.content; + return response.data.content.map(parseSubmission); } } diff --git a/gymboard-app/src/api/main/submission.ts b/gymboard-app/src/api/main/submission.ts index 90cd975..93ea35c 100644 --- a/gymboard-app/src/api/main/submission.ts +++ b/gymboard-app/src/api/main/submission.ts @@ -1,8 +1,8 @@ import { SimpleGym } from 'src/api/main/gyms'; import { Exercise } from 'src/api/main/exercises'; -import { api, BASE_URL } from 'src/api/main/index'; +import { api } from 'src/api/main/index'; import { getGymCompoundId, GymRoutable } from 'src/router/gym-routing'; -import { sleep } from 'src/utils'; +import { DateTime } from 'luxon'; /** * The data that's sent when creating a submission. @@ -30,7 +30,7 @@ export class WeightUnitUtil { export interface ExerciseSubmission { id: string; - createdAt: string; + createdAt: DateTime; gym: SimpleGym; exercise: Exercise; videoFileId: string; @@ -41,12 +41,18 @@ export interface ExerciseSubmission { reps: number; } +export function parseSubmission(data: any): ExerciseSubmission { + data.createdAt = DateTime.fromISO(data.createdAt); + console.log(data); + return data as ExerciseSubmission; +} + class SubmissionsModule { public async getSubmission( submissionId: string ): Promise { const response = await api.get(`/submissions/${submissionId}`); - return response.data; + return parseSubmission(response.data); } public async createSubmission( @@ -55,7 +61,7 @@ class SubmissionsModule { ): Promise { const gymId = getGymCompoundId(gym); const response = await api.post(`/gyms/${gymId}/submissions`, payload); - return response.data; + return parseSubmission(response.data); } } diff --git a/gymboard-app/src/components/ExerciseSubmissionListItem.vue b/gymboard-app/src/components/ExerciseSubmissionListItem.vue index ec5bf75..a5efdfc 100644 --- a/gymboard-app/src/components/ExerciseSubmissionListItem.vue +++ b/gymboard-app/src/components/ExerciseSubmissionListItem.vue @@ -1,36 +1,24 @@