Updated front-end to follow updated APIs.
This commit is contained in:
parent
67504d0883
commit
1134eef3ad
|
@ -44,9 +44,9 @@ public class GymService {
|
||||||
query.orderBy(criteriaBuilder.desc(root.get("createdAt")));
|
query.orderBy(criteriaBuilder.desc(root.get("createdAt")));
|
||||||
query.distinct(true);
|
query.distinct(true);
|
||||||
|
|
||||||
|
// TODO: Filter to only verified submissions.
|
||||||
return PredicateBuilder.and(criteriaBuilder)
|
return PredicateBuilder.and(criteriaBuilder)
|
||||||
.with(criteriaBuilder.equal(root.get("gym"), gym))
|
.with(criteriaBuilder.equal(root.get("gym"), gym))
|
||||||
.with(criteriaBuilder.isTrue(root.get("complete")))
|
|
||||||
.build();
|
.build();
|
||||||
}, PageRequest.of(0, 10))
|
}, PageRequest.of(0, 10))
|
||||||
.map(ExerciseSubmissionResponse::new)
|
.map(ExerciseSubmissionResponse::new)
|
||||||
|
|
|
@ -111,9 +111,7 @@ module.exports = configure(function (ctx) {
|
||||||
// directives: [],
|
// directives: [],
|
||||||
|
|
||||||
// Quasar plugins
|
// Quasar plugins
|
||||||
plugins: [
|
plugins: ['Notify'],
|
||||||
'Notify'
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// animations: 'all', // --- includes all animations
|
// animations: 'all', // --- includes all animations
|
||||||
|
|
|
@ -41,30 +41,49 @@ class AuthModule {
|
||||||
clearTimeout(this.tokenRefreshTimer);
|
clearTimeout(this.tokenRefreshTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async register(payload: UserCreationPayload) {
|
public async register(payload: UserCreationPayload): Promise<User> {
|
||||||
const response = await api.post('/auth/register', payload);
|
const response = await api.post('/auth/register', payload);
|
||||||
console.log(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async activateUser(code: string): Promise<User> {
|
|
||||||
const response = await api.post('/auth/activate', {code: code});
|
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetchNewToken(credentials: TokenCredentials): Promise<string> {
|
public async activateUser(code: string): Promise<User> {
|
||||||
|
const response = await api.post('/auth/activate', { code: code });
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fetchNewToken(credentials: TokenCredentials): Promise<string> {
|
||||||
const response = await api.post('/auth/token', credentials);
|
const response = await api.post('/auth/token', credentials);
|
||||||
return response.data.token;
|
return response.data.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async refreshToken(authStore: AuthStoreType) {
|
public async refreshToken(authStore: AuthStoreType) {
|
||||||
const response = await api.get('/auth/token', authStore.axiosConfig);
|
const response = await api.get('/auth/token', authStore.axiosConfig);
|
||||||
authStore.token = response.data.token;
|
authStore.token = response.data.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetchMyUser(authStore: AuthStoreType): Promise<User> {
|
public async fetchMyUser(authStore: AuthStoreType): Promise<User> {
|
||||||
const response = await api.get('/auth/me', authStore.axiosConfig);
|
const response = await api.get('/auth/me', authStore.axiosConfig);
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async updatePassword(newPassword: string, authStore: AuthStoreType) {
|
||||||
|
await api.post(
|
||||||
|
'/auth/me/password',
|
||||||
|
{ newPassword: newPassword },
|
||||||
|
authStore.axiosConfig
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async generatePasswordResetCode(email: string) {
|
||||||
|
await api.get('/auth/reset-password', { params: { email: email } });
|
||||||
|
}
|
||||||
|
|
||||||
|
public async resetPassword(resetCode: string, newPassword: string) {
|
||||||
|
await api.post('/auth/reset-password', {
|
||||||
|
code: resetCode,
|
||||||
|
newPassword: newPassword,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AuthModule;
|
export default AuthModule;
|
||||||
|
|
|
@ -13,7 +13,7 @@ export interface ExerciseSubmissionPayload {
|
||||||
weight: number;
|
weight: number;
|
||||||
weightUnit: string;
|
weightUnit: string;
|
||||||
reps: number;
|
reps: number;
|
||||||
videoId: number;
|
videoFileId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExerciseSubmission {
|
export interface ExerciseSubmission {
|
||||||
|
@ -21,7 +21,7 @@ export interface ExerciseSubmission {
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
gym: SimpleGym;
|
gym: SimpleGym;
|
||||||
exercise: Exercise;
|
exercise: Exercise;
|
||||||
status: ExerciseSubmissionStatus;
|
videoFileId: string;
|
||||||
submitterName: string;
|
submitterName: string;
|
||||||
rawWeight: number;
|
rawWeight: number;
|
||||||
weightUnit: string;
|
weightUnit: string;
|
||||||
|
@ -29,14 +29,6 @@ export interface ExerciseSubmission {
|
||||||
reps: number;
|
reps: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ExerciseSubmissionStatus {
|
|
||||||
WAITING = 'WAITING',
|
|
||||||
PROCESSING = 'PROCESSING',
|
|
||||||
FAILED = 'FAILED',
|
|
||||||
COMPLETED = 'COMPLETED',
|
|
||||||
VERIFIED = 'VERIFIED',
|
|
||||||
}
|
|
||||||
|
|
||||||
class SubmissionsModule {
|
class SubmissionsModule {
|
||||||
public async getSubmission(
|
public async getSubmission(
|
||||||
submissionId: string
|
submissionId: string
|
||||||
|
@ -45,16 +37,6 @@ class SubmissionsModule {
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSubmissionVideoUrl(submission: ExerciseSubmission): string | null {
|
|
||||||
if (
|
|
||||||
submission.status !== ExerciseSubmissionStatus.COMPLETED &&
|
|
||||||
submission.status !== ExerciseSubmissionStatus.VERIFIED
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return BASE_URL + `/submissions/${submission.id}/video`;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async createSubmission(
|
public async createSubmission(
|
||||||
gym: GymRoutable,
|
gym: GymRoutable,
|
||||||
payload: ExerciseSubmissionPayload
|
payload: ExerciseSubmissionPayload
|
||||||
|
@ -63,49 +45,6 @@ class SubmissionsModule {
|
||||||
const response = await api.post(`/gyms/${gymId}/submissions`, payload);
|
const response = await api.post(`/gyms/${gymId}/submissions`, payload);
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async uploadVideoFile(gym: GymRoutable, file: File): Promise<number> {
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('file', file);
|
|
||||||
const gymId = getGymCompoundId(gym);
|
|
||||||
const response = await api.post(
|
|
||||||
`/gyms/${gymId}/submissions/upload`,
|
|
||||||
formData,
|
|
||||||
{
|
|
||||||
headers: { 'Content-Type': 'multipart/form-data' },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return response.data.id as number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asynchronous method that waits until a submission is done processing.
|
|
||||||
* @param submissionId The submission's id.
|
|
||||||
*/
|
|
||||||
public async waitUntilSubmissionProcessed(
|
|
||||||
submissionId: string
|
|
||||||
): Promise<ExerciseSubmission> {
|
|
||||||
let failureCount = 0;
|
|
||||||
let attemptCount = 0;
|
|
||||||
while (failureCount < 5 && attemptCount < 60) {
|
|
||||||
await sleep(1000);
|
|
||||||
attemptCount++;
|
|
||||||
try {
|
|
||||||
const response = await this.getSubmission(submissionId);
|
|
||||||
failureCount = 0;
|
|
||||||
if (
|
|
||||||
response.status !== ExerciseSubmissionStatus.WAITING &&
|
|
||||||
response.status !== ExerciseSubmissionStatus.PROCESSING
|
|
||||||
) {
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
failureCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Error('Failed to wait for submission to complete.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SubmissionsModule;
|
export default SubmissionsModule;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {boot} from 'quasar/wrappers';
|
import { boot } from 'quasar/wrappers';
|
||||||
import {createI18n} from 'vue-i18n';
|
import { createI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import messages from 'src/i18n';
|
import messages from 'src/i18n';
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAuthStore } from 'stores/auth-store';
|
import { useAuthStore } from 'stores/auth-store';
|
||||||
import api from 'src/api/main';
|
import api from 'src/api/main';
|
||||||
import {useRoute, useRouter} from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -39,8 +39,8 @@ async function goToLoginPage() {
|
||||||
await router.push({
|
await router.push({
|
||||||
path: '/login',
|
path: '/login',
|
||||||
query: {
|
query: {
|
||||||
next: encodeURIComponent(route.path)
|
next: encodeURIComponent(route.path),
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section class="text-center">
|
<q-card-section class="text-center">
|
||||||
<video
|
<video
|
||||||
:src="api.gyms.submissions.getSubmissionVideoUrl(submission)"
|
:src="getFileUrl(submission.videoFileId)"
|
||||||
width="600"
|
width="600"
|
||||||
loop
|
loop
|
||||||
controls
|
controls
|
||||||
|
@ -30,6 +30,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ExerciseSubmission } from 'src/api/main/submission';
|
import { ExerciseSubmission } from 'src/api/main/submission';
|
||||||
import api from 'src/api/main';
|
import api from 'src/api/main';
|
||||||
|
import { getFileUrl } from 'src/api/cdn';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
submission: ExerciseSubmission;
|
submission: ExerciseSubmission;
|
||||||
|
|
|
@ -25,7 +25,7 @@ const i18n = useI18n({ useScope: 'global' });
|
||||||
const localeOptions = [
|
const localeOptions = [
|
||||||
{ value: 'en-US', label: 'English' },
|
{ value: 'en-US', label: 'English' },
|
||||||
{ value: 'nl-NL', label: 'Nederlands' },
|
{ value: 'nl-NL', label: 'Nederlands' },
|
||||||
{ value: 'de', label: 'Deutsch' }
|
{ value: 'de', label: 'Deutsch' },
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,27 @@
|
||||||
export default {
|
export default {
|
||||||
mainLayout: {
|
mainLayout: {
|
||||||
language: 'Sprache',
|
language: 'Sprache',
|
||||||
pages: 'Seiten',
|
pages: 'Seiten',
|
||||||
|
},
|
||||||
|
indexPage: {
|
||||||
|
searchHint: 'Suche nach einem Gym',
|
||||||
|
},
|
||||||
|
gymPage: {
|
||||||
|
home: 'Home',
|
||||||
|
submit: 'Einreichen',
|
||||||
|
leaderboard: 'Bestenliste',
|
||||||
|
homePage: {
|
||||||
|
overview: 'Überblick über dieses Fitnessstudio:',
|
||||||
|
recentLifts: 'Letzten Aufzüge',
|
||||||
},
|
},
|
||||||
indexPage: {
|
submitPage: {
|
||||||
searchHint: 'Suche nach einem Gym',
|
name: 'Dein Name',
|
||||||
},
|
exercise: 'Übung',
|
||||||
gymPage: {
|
weight: 'Gewicht',
|
||||||
home: 'Home',
|
reps: 'Wiederholungen',
|
||||||
|
date: 'Datum',
|
||||||
|
upload: 'Videodatei zum Hochladen',
|
||||||
submit: 'Einreichen',
|
submit: 'Einreichen',
|
||||||
leaderboard: 'Bestenliste',
|
|
||||||
homePage: {
|
|
||||||
overview: 'Überblick über dieses Fitnessstudio:',
|
|
||||||
recentLifts: 'Letzten Aufzüge'
|
|
||||||
},
|
|
||||||
submitPage: {
|
|
||||||
name: 'Dein Name',
|
|
||||||
exercise: 'Übung',
|
|
||||||
weight: 'Gewicht',
|
|
||||||
reps: 'Wiederholungen',
|
|
||||||
date: 'Datum',
|
|
||||||
upload: 'Videodatei zum Hochladen',
|
|
||||||
submit: 'Einreichen',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
},
|
||||||
|
};
|
||||||
|
|
|
@ -9,14 +9,14 @@ export default {
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
password: 'Password',
|
password: 'Password',
|
||||||
register: 'Register',
|
register: 'Register',
|
||||||
error: 'An error occurred.'
|
error: 'An error occurred.',
|
||||||
},
|
},
|
||||||
loginPage: {
|
loginPage: {
|
||||||
title: 'Login to Gymboard',
|
title: 'Login to Gymboard',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
password: 'Password',
|
password: 'Password',
|
||||||
logIn: 'Log in',
|
logIn: 'Log in',
|
||||||
createAccount: 'Create an account'
|
createAccount: 'Create an account',
|
||||||
},
|
},
|
||||||
indexPage: {
|
indexPage: {
|
||||||
searchHint: 'Search for a Gym',
|
searchHint: 'Search for a Gym',
|
||||||
|
@ -41,6 +41,6 @@ export default {
|
||||||
},
|
},
|
||||||
accountMenuItem: {
|
accountMenuItem: {
|
||||||
logIn: 'Login',
|
logIn: 'Login',
|
||||||
logOut: 'Log out'
|
logOut: 'Log out',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,5 +5,5 @@ import de from './de';
|
||||||
export default {
|
export default {
|
||||||
'en-US': enUS,
|
'en-US': enUS,
|
||||||
'nl-NL': nlNL,
|
'nl-NL': nlNL,
|
||||||
'de': de,
|
de: de,
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,14 +9,14 @@ export default {
|
||||||
email: 'E-mail',
|
email: 'E-mail',
|
||||||
password: 'Wachtwoord',
|
password: 'Wachtwoord',
|
||||||
register: 'Registreren',
|
register: 'Registreren',
|
||||||
error: 'Er is een fout opgetreden.'
|
error: 'Er is een fout opgetreden.',
|
||||||
},
|
},
|
||||||
loginPage: {
|
loginPage: {
|
||||||
title: 'Inloggen bij Gymboard',
|
title: 'Inloggen bij Gymboard',
|
||||||
email: 'E-mail',
|
email: 'E-mail',
|
||||||
password: 'Wachtwoord',
|
password: 'Wachtwoord',
|
||||||
logIn: 'Inloggen',
|
logIn: 'Inloggen',
|
||||||
createAccount: 'Account aanmaken'
|
createAccount: 'Account aanmaken',
|
||||||
},
|
},
|
||||||
indexPage: {
|
indexPage: {
|
||||||
searchHint: 'Zoek een sportschool',
|
searchHint: 'Zoek een sportschool',
|
||||||
|
@ -41,6 +41,6 @@ export default {
|
||||||
},
|
},
|
||||||
accountMenuItem: {
|
accountMenuItem: {
|
||||||
logIn: 'Inloggen',
|
logIn: 'Inloggen',
|
||||||
logOut: 'Uitloggen'
|
logOut: 'Uitloggen',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
<StandardCenteredPage>
|
<StandardCenteredPage>
|
||||||
<h3 class="text-center">About Gymboard</h3>
|
<h3 class="text-center">About Gymboard</h3>
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Error fugit quia laboriosam eaque? Deserunt, accusantium dicta assumenda debitis incidunt eius provident magnam, est quasi officia voluptas, nam neque omnis reiciendis.
|
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Error fugit quia
|
||||||
|
laboriosam eaque? Deserunt, accusantium dicta assumenda debitis incidunt
|
||||||
|
eius provident magnam, est quasi officia voluptas, nam neque omnis
|
||||||
|
reiciendis.
|
||||||
</p>
|
</p>
|
||||||
</StandardCenteredPage>
|
</StandardCenteredPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,19 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="fullscreen text-center q-pa-md flex flex-center">
|
||||||
class="fullscreen text-center q-pa-md flex flex-center"
|
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<div style="font-size: 30vh">404</div>
|
<div style="font-size: 30vh">404</div>
|
||||||
|
|
||||||
<div class="text-h2" style="opacity: 0.4">Page not found.</div>
|
<div class="text-h2" style="opacity: 0.4">Page not found.</div>
|
||||||
|
|
||||||
<q-btn
|
<q-btn class="q-mt-xl" unelevated to="/" label="Go Home" no-caps />
|
||||||
class="q-mt-xl"
|
|
||||||
unelevated
|
|
||||||
to="/"
|
|
||||||
label="Go Home"
|
|
||||||
no-caps
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
<StandardCenteredPage>
|
<StandardCenteredPage>
|
||||||
<h3>Testing Page</h3>
|
<h3>Testing Page</h3>
|
||||||
<p>
|
<p>
|
||||||
Use this page to test new functionality, before adding it to the main app.
|
Use this page to test new functionality, before adding it to the main
|
||||||
This page should be hidden on production.
|
app. This page should be hidden on production.
|
||||||
</p>
|
</p>
|
||||||
<div style="border: 3px solid red">
|
<div style="border: 3px solid red">
|
||||||
<h4>Auth Test</h4>
|
<h4>Auth Test</h4>
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import StandardCenteredPage from 'components/StandardCenteredPage.vue';
|
import StandardCenteredPage from 'components/StandardCenteredPage.vue';
|
||||||
import {onMounted, ref} from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import {useRoute, useRouter} from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import api from 'src/api/main';
|
import api from 'src/api/main';
|
||||||
import {sleep} from 'src/utils';
|
import { sleep } from 'src/utils';
|
||||||
import {useI18n} from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -35,6 +35,4 @@ onMounted(async () => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -28,11 +28,20 @@
|
||||||
</q-input>
|
</q-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<q-btn type="submit" :label="$t('loginPage.logIn')" color="primary" class="q-mt-md col-12" no-caps/>
|
<q-btn
|
||||||
|
type="submit"
|
||||||
|
:label="$t('loginPage.logIn')"
|
||||||
|
color="primary"
|
||||||
|
class="q-mt-md col-12"
|
||||||
|
no-caps
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<router-link
|
<router-link
|
||||||
:to="{ path: '/register', query: route.query.next ? { next: route.query.next } : {} }"
|
:to="{
|
||||||
|
path: '/register',
|
||||||
|
query: route.query.next ? { next: route.query.next } : {},
|
||||||
|
}"
|
||||||
class="q-mt-md text-primary text-center col-12"
|
class="q-mt-md text-primary text-center col-12"
|
||||||
>
|
>
|
||||||
{{ $t('loginPage.createAccount') }}
|
{{ $t('loginPage.createAccount') }}
|
||||||
|
@ -46,10 +55,10 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import StandardCenteredPage from 'components/StandardCenteredPage.vue';
|
import StandardCenteredPage from 'components/StandardCenteredPage.vue';
|
||||||
import SlimForm from 'components/SlimForm.vue';
|
import SlimForm from 'components/SlimForm.vue';
|
||||||
import {ref} from 'vue';
|
import { ref } from 'vue';
|
||||||
import api from 'src/api/main';
|
import api from 'src/api/main';
|
||||||
import {useAuthStore} from 'stores/auth-store';
|
import { useAuthStore } from 'stores/auth-store';
|
||||||
import {useRoute, useRouter} from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -57,14 +66,16 @@ const route = useRoute();
|
||||||
|
|
||||||
const loginModel = ref({
|
const loginModel = ref({
|
||||||
email: '',
|
email: '',
|
||||||
password: ''
|
password: '',
|
||||||
});
|
});
|
||||||
const passwordVisible = ref(false);
|
const passwordVisible = ref(false);
|
||||||
|
|
||||||
async function tryLogin() {
|
async function tryLogin() {
|
||||||
try {
|
try {
|
||||||
await api.auth.login(authStore, loginModel.value);
|
await api.auth.login(authStore, loginModel.value);
|
||||||
const dest = route.query.next ? decodeURIComponent(route.query.next as string) : '/';
|
const dest = route.query.next
|
||||||
|
? decodeURIComponent(route.query.next as string)
|
||||||
|
: '/';
|
||||||
await router.push(dest);
|
await router.push(dest);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -77,6 +88,4 @@ function resetLogin() {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -53,17 +53,17 @@
|
||||||
import SlimForm from 'components/SlimForm.vue';
|
import SlimForm from 'components/SlimForm.vue';
|
||||||
import StandardCenteredPage from 'components/StandardCenteredPage.vue';
|
import StandardCenteredPage from 'components/StandardCenteredPage.vue';
|
||||||
import api from 'src/api/main';
|
import api from 'src/api/main';
|
||||||
import {useRouter} from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import {ref} from 'vue';
|
import { ref } from 'vue';
|
||||||
import {useQuasar} from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import {useI18n} from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const registerModel = ref({
|
const registerModel = ref({
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
password: ''
|
password: '',
|
||||||
});
|
});
|
||||||
const passwordVisible = ref(false);
|
const passwordVisible = ref(false);
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ async function tryRegister() {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
quasar.notify({
|
quasar.notify({
|
||||||
message: t('registerPage.error'),
|
message: t('registerPage.error'),
|
||||||
type: 'negative'
|
type: 'negative',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,4 @@ function resetForm() {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -10,6 +10,4 @@
|
||||||
import StandardCenteredPage from 'components/StandardCenteredPage.vue';
|
import StandardCenteredPage from 'components/StandardCenteredPage.vue';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -69,7 +69,6 @@ onMounted(async () => {
|
||||||
function initMap() {
|
function initMap() {
|
||||||
if (!gym.value) return;
|
if (!gym.value) return;
|
||||||
const g: Gym = gym.value;
|
const g: Gym = gym.value;
|
||||||
console.log(mapContainer);
|
|
||||||
|
|
||||||
const tiles = new TileLayer(TILE_URL, {
|
const tiles = new TileLayer(TILE_URL, {
|
||||||
attribution: ATTRIBUTION,
|
attribution: ATTRIBUTION,
|
||||||
|
|
|
@ -100,6 +100,7 @@ import { Gym } from 'src/api/main/gyms';
|
||||||
import { Exercise } from 'src/api/main/exercises';
|
import { Exercise } from 'src/api/main/exercises';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { sleep } from 'src/utils';
|
import { sleep } from 'src/utils';
|
||||||
|
import { uploadVideoToCDN, VideoProcessingStatus, waitUntilVideoProcessingComplete } from 'src/api/cdn';
|
||||||
|
|
||||||
interface Option {
|
interface Option {
|
||||||
value: string;
|
value: string;
|
||||||
|
@ -117,7 +118,7 @@ let submissionModel = ref({
|
||||||
weight: 100,
|
weight: 100,
|
||||||
weightUnit: 'Kg',
|
weightUnit: 'Kg',
|
||||||
reps: 1,
|
reps: 1,
|
||||||
videoId: -1,
|
videoFileId: '',
|
||||||
videoFile: null,
|
videoFile: null,
|
||||||
date: new Date().toLocaleDateString('en-CA'),
|
date: new Date().toLocaleDateString('en-CA'),
|
||||||
});
|
});
|
||||||
|
@ -159,10 +160,7 @@ async function onSubmitted() {
|
||||||
try {
|
try {
|
||||||
infoMessage.value = 'Uploading video...';
|
infoMessage.value = 'Uploading video...';
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
submissionModel.value.videoId = await api.gyms.submissions.uploadVideoFile(
|
submissionModel.value.videoFileId = await uploadVideoToCDN(selectedVideoFile.value);
|
||||||
gym.value,
|
|
||||||
selectedVideoFile.value
|
|
||||||
);
|
|
||||||
infoMessage.value = 'Creating submission...';
|
infoMessage.value = 'Creating submission...';
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
const submission = await api.gyms.submissions.createSubmission(
|
const submission = await api.gyms.submissions.createSubmission(
|
||||||
|
@ -170,11 +168,14 @@ async function onSubmitted() {
|
||||||
submissionModel.value
|
submissionModel.value
|
||||||
);
|
);
|
||||||
infoMessage.value = 'Submission processing...';
|
infoMessage.value = 'Submission processing...';
|
||||||
const completedSubmission =
|
const finalStatus = await waitUntilVideoProcessingComplete(submission.videoFileId);
|
||||||
await api.gyms.submissions.waitUntilSubmissionProcessed(submission.id);
|
if (finalStatus === VideoProcessingStatus.COMPLETED) {
|
||||||
console.log(completedSubmission);
|
infoMessage.value = 'Submission complete!';
|
||||||
infoMessage.value = 'Submission complete!';
|
await sleep(1000);
|
||||||
await router.push(getGymRoute(gym.value));
|
await router.push(getGymRoute(gym.value));
|
||||||
|
} else {
|
||||||
|
infoMessage.value = 'Submission processing failed. Please try again later.';
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
submitting.value = false;
|
submitting.value = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ import GymHomePage from 'pages/gym/GymHomePage.vue';
|
||||||
import GymLeaderboardsPage from 'pages/gym/GymLeaderboardsPage.vue';
|
import GymLeaderboardsPage from 'pages/gym/GymLeaderboardsPage.vue';
|
||||||
import TestingPage from 'pages/TestingPage.vue';
|
import TestingPage from 'pages/TestingPage.vue';
|
||||||
import LoginPage from 'pages/auth/LoginPage.vue';
|
import LoginPage from 'pages/auth/LoginPage.vue';
|
||||||
import RegisterPage from "pages/auth/RegisterPage.vue";
|
import RegisterPage from 'pages/auth/RegisterPage.vue';
|
||||||
import RegistrationSuccessPage from "pages/auth/RegistrationSuccessPage.vue";
|
import RegistrationSuccessPage from 'pages/auth/RegistrationSuccessPage.vue';
|
||||||
import ActivationPage from "pages/auth/ActivationPage.vue";
|
import ActivationPage from 'pages/auth/ActivationPage.vue';
|
||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
// Auth-related pages, which live outside the main layout.
|
// Auth-related pages, which live outside the main layout.
|
||||||
|
@ -35,7 +35,7 @@ const routes: RouteRecordRaw[] = [
|
||||||
{ path: 'leaderboard', component: GymLeaderboardsPage },
|
{ path: 'leaderboard', component: GymLeaderboardsPage },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ path: 'about', component: AboutPage }
|
{ path: 'about', component: AboutPage },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,12 @@ import nl.andrewlalis.gymboardcdn.util.ULID;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.Order;
|
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
import org.springframework.web.cors.CorsConfiguration;
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
import org.springframework.web.cors.CorsConfigurationSource;
|
|
||||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
import org.springframework.web.filter.CorsFilter;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
|
@ -18,24 +19,17 @@ public class Config {
|
||||||
@Value("${app.api-origin}")
|
@Value("${app.api-origin}")
|
||||||
private String apiOrigin;
|
private String apiOrigin;
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines the CORS configuration for this API, which is to say that we
|
|
||||||
* allow cross-origin requests ONLY from the web app for the vast majority
|
|
||||||
* of endpoints.
|
|
||||||
* @return The CORS configuration source.
|
|
||||||
*/
|
|
||||||
@Bean
|
@Bean
|
||||||
@Order(1)
|
public CorsFilter corsFilter() {
|
||||||
public CorsConfigurationSource corsConfigurationSource() {
|
|
||||||
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
final CorsConfiguration config = new CorsConfiguration();
|
final CorsConfiguration config = new CorsConfiguration();
|
||||||
config.setAllowCredentials(true);
|
config.setAllowCredentials(true);
|
||||||
config.addAllowedOriginPattern(webOrigin);
|
config.addAllowedOriginPattern(webOrigin);
|
||||||
config.addAllowedOriginPattern(apiOrigin);
|
config.addAllowedOriginPattern(apiOrigin);
|
||||||
config.addAllowedHeader("*");
|
config.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept"));
|
||||||
config.addAllowedMethod("*");
|
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH"));
|
||||||
source.registerCorsConfiguration("/**", config);
|
source.registerCorsConfiguration("/**", config);
|
||||||
return source;
|
return new CorsFilter(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|
Loading…
Reference in New Issue