Added submission page and cleaned up time formatting.
This commit is contained in:
parent
7c638d066e
commit
8c2a84755d
|
@ -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(),
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -37,7 +37,7 @@ public class User {
|
|||
)
|
||||
private Set<Role> 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<Role> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public UserPersonalDetails getPersonalDetails() {
|
||||
return personalDetails;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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<object> = response.data;
|
||||
return submissionObjects.map(parseSubmission);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<ExerciseSubmission> {
|
||||
const response = await api.get(`/submissions/${submissionId}`);
|
||||
return response.data;
|
||||
return parseSubmission(response.data);
|
||||
}
|
||||
|
||||
public async createSubmission(
|
||||
|
@ -55,7 +61,7 @@ class SubmissionsModule {
|
|||
): Promise<ExerciseSubmission> {
|
||||
const gymId = getGymCompoundId(gym);
|
||||
const response = await api.post(`/gyms/${gymId}/submissions`, payload);
|
||||
return response.data;
|
||||
return parseSubmission(response.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,36 +1,24 @@
|
|||
<template>
|
||||
<q-expansion-item
|
||||
expand-separator
|
||||
:label="
|
||||
submission.rawWeight +
|
||||
' ' +
|
||||
submission.weightUnit +
|
||||
' x' +
|
||||
submission.reps +
|
||||
' ' +
|
||||
submission.exercise.displayName
|
||||
"
|
||||
:caption="submission.submitterName"
|
||||
>
|
||||
<q-card>
|
||||
<q-card-section class="text-center">
|
||||
<video
|
||||
:src="getFileUrl(submission.videoFileId)"
|
||||
width="600"
|
||||
loop
|
||||
controls
|
||||
autopictureinpicture
|
||||
preload="metadata"
|
||||
/>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-expansion-item>
|
||||
<q-item clickable :to="'/submissions/' + submission.id">
|
||||
<q-item-section>
|
||||
<q-item-label>
|
||||
{{ submission.rawWeight }} {{ WeightUnitUtil.toAbbreviation(submission.weightUnit) }}
|
||||
{{ submission.exercise.displayName }}
|
||||
</q-item-label>
|
||||
<q-item-label caption>
|
||||
{{ submission.submitterName }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side top>
|
||||
{{ submission.createdAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }}
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ExerciseSubmission } from 'src/api/main/submission';
|
||||
import api from 'src/api/main';
|
||||
import { ExerciseSubmission, WeightUnitUtil } from 'src/api/main/submission';
|
||||
import { getFileUrl } from 'src/api/cdn';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
interface Props {
|
||||
submission: ExerciseSubmission;
|
||||
|
|
|
@ -1,16 +1,38 @@
|
|||
<template>
|
||||
<q-page>
|
||||
<standard-centered-page v-if="submission">
|
||||
<h3>Submission: {{ submission.id }}</h3>
|
||||
</standard-centered-page>
|
||||
<StandardCenteredPage v-if="submission">
|
||||
<h3>
|
||||
{{ submission.rawWeight }} {{ WeightUnitUtil.toAbbreviation(submission.weightUnit) }}
|
||||
{{ submission.exercise.displayName }}
|
||||
</h3>
|
||||
<p>{{ submission.reps }} reps</p>
|
||||
<p>by {{ submission.submitterName }}</p>
|
||||
<p>At <router-link :to="getGymRoute(submission.gym)">{{ submission.gym.displayName }}</router-link></p>
|
||||
<p>
|
||||
{{ submission.createdAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }}
|
||||
</p>
|
||||
<video
|
||||
:src="getFileUrl(submission.videoFileId)"
|
||||
width="600"
|
||||
loop
|
||||
controls
|
||||
autopictureinpicture
|
||||
preload="metadata"
|
||||
autoplay
|
||||
/>
|
||||
</StandardCenteredPage>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import api from 'src/api/main';
|
||||
import { ExerciseSubmission } from 'src/api/main/submission';
|
||||
import StandardCenteredPage from 'src/components/StandardCenteredPage.vue';
|
||||
import { ExerciseSubmission, WeightUnitUtil } from 'src/api/main/submission';
|
||||
import { onMounted, ref, Ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { DateTime } from 'luxon';
|
||||
import { getFileUrl } from 'src/api/cdn';
|
||||
import { getGymRoute } from 'src/router/gym-routing';
|
||||
|
||||
const submission: Ref<ExerciseSubmission | undefined> = ref();
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<div v-if="recentSubmissions.length > 0">
|
||||
<h4 class="text-center">{{ $t('gymPage.homePage.recentLifts') }}</h4>
|
||||
<q-list>
|
||||
<q-list separator>
|
||||
<ExerciseSubmissionListItem
|
||||
v-for="sub in recentSubmissions"
|
||||
:submission="sub"
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
emit-value
|
||||
/>
|
||||
</div>
|
||||
<q-list>
|
||||
<q-list separator>
|
||||
<ExerciseSubmissionListItem
|
||||
v-for="sub in submissions"
|
||||
:submission="sub"
|
||||
|
|
Loading…
Reference in New Issue