Added env files, completed user settings page.
This commit is contained in:
parent
88089f2e11
commit
0663296052
|
@ -0,0 +1,3 @@
|
||||||
|
API_URL=http://localhost:8080
|
||||||
|
CDN_URL=http://localhost:8082
|
||||||
|
SEARCH_URL=http://localhost:8081
|
|
@ -0,0 +1,3 @@
|
||||||
|
API_URL=https://api.gymboard.com
|
||||||
|
CDN_URL=https://cdn.gymboard.com
|
||||||
|
SEARCH_URL=https://search.gymboard.com
|
|
@ -11,6 +11,7 @@
|
||||||
"@quasar/cli": "^2.0.0",
|
"@quasar/cli": "^2.0.0",
|
||||||
"@quasar/extras": "^1.0.0",
|
"@quasar/extras": "^1.0.0",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
"luxon": "^3.2.1",
|
"luxon": "^3.2.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"pinia": "^2.0.11",
|
"pinia": "^2.0.11",
|
||||||
|
@ -2166,6 +2167,15 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dotenv": {
|
||||||
|
"version": "16.0.3",
|
||||||
|
"resolved": "http://192.168.88.248:8081/repository/npm-public/dotenv/-/dotenv-16.0.3.tgz",
|
||||||
|
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eastasianwidth": {
|
"node_modules/eastasianwidth": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
|
@ -7937,6 +7947,11 @@
|
||||||
"is-obj": "^2.0.0"
|
"is-obj": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dotenv": {
|
||||||
|
"version": "16.0.3",
|
||||||
|
"resolved": "http://192.168.88.248:8081/repository/npm-public/dotenv/-/dotenv-16.0.3.tgz",
|
||||||
|
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="
|
||||||
|
},
|
||||||
"eastasianwidth": {
|
"eastasianwidth": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"@quasar/cli": "^2.0.0",
|
"@quasar/cli": "^2.0.0",
|
||||||
"@quasar/extras": "^1.0.0",
|
"@quasar/extras": "^1.0.0",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
"luxon": "^3.2.1",
|
"luxon": "^3.2.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"pinia": "^2.0.11",
|
"pinia": "^2.0.11",
|
||||||
|
|
|
@ -12,6 +12,8 @@ const { configure } = require('quasar/wrappers');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { withCtx } = require('vue');
|
const { withCtx } = require('vue');
|
||||||
|
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
module.exports = configure(function (ctx) {
|
module.exports = configure(function (ctx) {
|
||||||
return {
|
return {
|
||||||
eslint: {
|
eslint: {
|
||||||
|
@ -65,7 +67,9 @@ module.exports = configure(function (ctx) {
|
||||||
// publicPath: '/',
|
// publicPath: '/',
|
||||||
// analyze: true,
|
// analyze: true,
|
||||||
env: {
|
env: {
|
||||||
API: ctx.dev ? 'http://localhost:8080' : 'https://api.gymboard.com',
|
API_URL: process.env.API_URL,
|
||||||
|
CDN_URL: process.env.CDN_URL,
|
||||||
|
SEARCH_URL: process.env.SEARCH_URL,
|
||||||
},
|
},
|
||||||
// rawDefine: {}
|
// rawDefine: {}
|
||||||
// ignorePublicFolder: true,
|
// ignorePublicFolder: true,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {api} from 'src/api/main/index';
|
import {api} from 'src/api/main/index';
|
||||||
import {AuthStoreType} from 'stores/auth-store';
|
import {AuthStoreType} from 'stores/auth-store';
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
|
import {WeightUnit} from 'src/api/main/submission';
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -21,7 +22,7 @@ export interface UserPersonalDetails {
|
||||||
userId: string;
|
userId: string;
|
||||||
birthDate?: string;
|
birthDate?: string;
|
||||||
currentWeight?: number;
|
currentWeight?: number;
|
||||||
currentWeightUnit?: number;
|
currentWeightUnit?: WeightUnit;
|
||||||
currentMetricWeight?: number;
|
currentMetricWeight?: number;
|
||||||
sex: PersonSex;
|
sex: PersonSex;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,9 @@ import GymsModule from 'src/api/main/gyms';
|
||||||
import ExercisesModule from 'src/api/main/exercises';
|
import ExercisesModule from 'src/api/main/exercises';
|
||||||
import LeaderboardsModule from 'src/api/main/leaderboards';
|
import LeaderboardsModule from 'src/api/main/leaderboards';
|
||||||
import AuthModule from 'src/api/main/auth';
|
import AuthModule from 'src/api/main/auth';
|
||||||
|
console.log(process.env);
|
||||||
export const BASE_URL = 'http://localhost:8080';
|
|
||||||
|
|
||||||
// TODO: Figure out how to get the base URL from environment.
|
|
||||||
export const api = axios.create({
|
export const api = axios.create({
|
||||||
baseURL: BASE_URL,
|
baseURL: process.env.API_URL,
|
||||||
});
|
});
|
||||||
|
|
||||||
class GymboardApi {
|
class GymboardApi {
|
||||||
|
|
|
@ -37,7 +37,7 @@ export default boot(({ app }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary override if you want to test a particular locale.
|
// Temporary override if you want to test a particular locale.
|
||||||
i18n.global.locale.value = 'nl-NL';
|
// i18n.global.locale.value = 'nl-NL';
|
||||||
|
|
||||||
// Set i18n instance on app
|
// Set i18n instance on app
|
||||||
app.use(i18n);
|
app.use(i18n);
|
||||||
|
|
|
@ -2,11 +2,12 @@
|
||||||
<div class="row justify-between">
|
<div class="row justify-between">
|
||||||
<span class="property-label">{{ label }}</span>
|
<span class="property-label">{{ label }}</span>
|
||||||
|
|
||||||
<div v-if="typeof modelValue === 'string'">
|
<div v-if="(typeof modelValue === 'string' || typeof modelValue === 'number') && inputType !== 'select'">
|
||||||
<q-input
|
<q-input
|
||||||
:model-value="modelValue"
|
:model-value="modelValue"
|
||||||
@update:modelValue="onValueUpdated"
|
@update:modelValue="onValueUpdated"
|
||||||
:type="inputType"
|
:type="inputType"
|
||||||
|
:step="numberInputStep"
|
||||||
dense
|
dense
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,6 +15,16 @@
|
||||||
<div v-if="typeof modelValue === 'boolean'">
|
<div v-if="typeof modelValue === 'boolean'">
|
||||||
<q-toggle :model-value="modelValue" @update:modelValue="onValueUpdated"/>
|
<q-toggle :model-value="modelValue" @update:modelValue="onValueUpdated"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="inputType === 'select'">
|
||||||
|
<q-select
|
||||||
|
:model-value="modelValue"
|
||||||
|
:options="selectOptions"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
@update:modelValue="onValueUpdated"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -24,6 +35,8 @@ interface Props {
|
||||||
label: string;
|
label: string;
|
||||||
inputType?: any;
|
inputType?: any;
|
||||||
modelValue: string | number | boolean | DateTime;
|
modelValue: string | number | boolean | DateTime;
|
||||||
|
selectOptions?: Array<object>;
|
||||||
|
numberInputStep?: number;
|
||||||
}
|
}
|
||||||
defineProps<Props>();
|
defineProps<Props>();
|
||||||
const emits = defineEmits(['update:modelValue']);
|
const emits = defineEmits(['update:modelValue']);
|
||||||
|
|
|
@ -48,11 +48,25 @@ export default {
|
||||||
},
|
},
|
||||||
userSettingsPage: {
|
userSettingsPage: {
|
||||||
title: 'Account Settings',
|
title: 'Account Settings',
|
||||||
|
password: 'Password',
|
||||||
|
passwordHint: 'Set a new password for your account.',
|
||||||
|
updatePassword: 'Update Password',
|
||||||
|
passwordUpdated: 'Password updated.',
|
||||||
|
passwordInvalid: 'Invalid password.',
|
||||||
personalDetails: {
|
personalDetails: {
|
||||||
birthDate: 'Date of Birth'
|
title: 'Personal Details',
|
||||||
|
birthDate: 'Date of Birth',
|
||||||
|
sex: 'Sex',
|
||||||
|
sexMale: 'Male',
|
||||||
|
sexFemale: 'Female',
|
||||||
|
sexUnknown: 'Prefer not to say',
|
||||||
|
currentWeight: 'Current Weight',
|
||||||
|
currentWeightUnit: 'Current Weight Unit'
|
||||||
},
|
},
|
||||||
preferences: {
|
preferences: {
|
||||||
accountPrivate: 'Private Account'
|
title: 'Preferences',
|
||||||
|
accountPrivate: 'Private',
|
||||||
|
language: 'Language'
|
||||||
},
|
},
|
||||||
save: 'Save',
|
save: 'Save',
|
||||||
undo: 'Undo'
|
undo: 'Undo'
|
||||||
|
@ -65,5 +79,9 @@ export default {
|
||||||
},
|
},
|
||||||
generalErrors: {
|
generalErrors: {
|
||||||
apiError: 'An API error occurred. Please try again later.'
|
apiError: 'An API error occurred. Please try again later.'
|
||||||
|
},
|
||||||
|
weightUnit: {
|
||||||
|
kilograms: 'Kilograms',
|
||||||
|
pounds: 'Pounds'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,6 +17,7 @@ export default {
|
||||||
password: 'Wachtwoord',
|
password: 'Wachtwoord',
|
||||||
logIn: 'Inloggen',
|
logIn: 'Inloggen',
|
||||||
createAccount: 'Account aanmaken',
|
createAccount: 'Account aanmaken',
|
||||||
|
authFailed: 'Ongeldige inloggegevens.',
|
||||||
},
|
},
|
||||||
indexPage: {
|
indexPage: {
|
||||||
searchHint: 'Zoek een sportschool',
|
searchHint: 'Zoek een sportschool',
|
||||||
|
@ -39,8 +40,48 @@ export default {
|
||||||
submit: 'Sturen',
|
submit: 'Sturen',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
userPage: {
|
||||||
|
notFound: {
|
||||||
|
title: 'Gebruiker niet gevonden',
|
||||||
|
description: 'Wij konden de gebruiker voor wie jij zoekt niet vinden, helaas.'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
userSettingsPage: {
|
||||||
|
title: 'Account instellingen',
|
||||||
|
password: 'Wachtwoord',
|
||||||
|
passwordHint: 'Stel een nieuw wachtwoord voor je account in.',
|
||||||
|
updatePassword: 'Wachtwoord bijwerken',
|
||||||
|
passwordUpdated: 'Wachtwoord succesvol bijgewerkt.',
|
||||||
|
passwordInvalid: 'Ongeldig wachtwoord.',
|
||||||
|
personalDetails: {
|
||||||
|
title: 'Persoonlijke gegevens',
|
||||||
|
birthDate: 'Geboortedatum',
|
||||||
|
sex: 'Geslacht',
|
||||||
|
sexMale: 'Mannelijk',
|
||||||
|
sexFemale: 'Vrouwelijk',
|
||||||
|
sexUnknown: 'Liever niet zeggen',
|
||||||
|
currentWeight: 'Huidige gewicht',
|
||||||
|
currentWeightUnit: 'Eenheden van huidige gewicht'
|
||||||
|
},
|
||||||
|
preferences: {
|
||||||
|
title: 'Voorkeuren',
|
||||||
|
accountPrivate: 'Privaat',
|
||||||
|
language: 'Taal'
|
||||||
|
},
|
||||||
|
save: 'Opslaan',
|
||||||
|
undo: 'Terugzetten'
|
||||||
|
},
|
||||||
accountMenuItem: {
|
accountMenuItem: {
|
||||||
logIn: 'Inloggen',
|
logIn: 'Inloggen',
|
||||||
|
profile: 'Profile',
|
||||||
|
settings: 'Instellingen',
|
||||||
logOut: 'Uitloggen',
|
logOut: 'Uitloggen',
|
||||||
},
|
},
|
||||||
|
generalErrors: {
|
||||||
|
apiError: 'Er is een API fout opgetreden. Probeer het nogmals later.'
|
||||||
|
},
|
||||||
|
weightUnit: {
|
||||||
|
kilograms: 'Kilogram',
|
||||||
|
pounds: 'Ponden'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
<router-link
|
<router-link
|
||||||
:to="{
|
:to="{
|
||||||
path: '/register',
|
path: '/register',
|
||||||
query: route.query.next ? { next: route.query.next } : {},
|
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"
|
||||||
>
|
>
|
||||||
|
@ -87,7 +87,7 @@ async function tryLogin() {
|
||||||
await api.auth.login(authStore, loginModel.value);
|
await api.auth.login(authStore, loginModel.value);
|
||||||
|
|
||||||
// Set the locale to the user's preferred locale.
|
// Set the locale to the user's preferred locale.
|
||||||
i18n.locale.value = resolveLocale(authStore.user?.preferences?.locale);
|
i18n.locale.value = resolveLocale(authStore.user?.preferences?.locale).value;
|
||||||
|
|
||||||
// Redirect back to whatever was set as the next URL.
|
// Redirect back to whatever was set as the next URL.
|
||||||
const dest = route.query.next
|
const dest = route.query.next
|
||||||
|
|
|
@ -1,17 +1,58 @@
|
||||||
|
<!--
|
||||||
|
The page where users can edit their personal information and preferences.
|
||||||
|
-->
|
||||||
<template>
|
<template>
|
||||||
<q-page>
|
<q-page>
|
||||||
<StandardCenteredPage>
|
<StandardCenteredPage>
|
||||||
<h3>{{ $t('userSettingsPage.title') }}</h3>
|
<h3>{{ $t('userSettingsPage.title') }}</h3>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
<div class="row justify-between">
|
||||||
|
<span class="property-label">{{ $t('userSettingsPage.password') }}</span>
|
||||||
|
<q-input
|
||||||
|
type="password"
|
||||||
|
v-model="newPassword"
|
||||||
|
:hint="$t('userSettingsPage.passwordHint')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="canUpdatePassword">
|
||||||
|
<q-btn :label="$t('userSettingsPage.updatePassword')" color="positive" @click="updatePassword"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="personalDetails">
|
<div v-if="personalDetails">
|
||||||
<h4>Personal Information</h4>
|
<h4>{{ $t('userSettingsPage.personalDetails.title') }}</h4>
|
||||||
<EditablePropertyRow
|
<EditablePropertyRow
|
||||||
v-model="personalDetails.birthDate"
|
v-model="personalDetails.birthDate"
|
||||||
:label="$t('userSettingsPage.personalDetails.birthDate')"
|
:label="$t('userSettingsPage.personalDetails.birthDate')"
|
||||||
input-type="date"
|
input-type="date"
|
||||||
/>
|
/>
|
||||||
|
<EditablePropertyRow
|
||||||
|
v-model="personalDetails.sex"
|
||||||
|
:label="$t('userSettingsPage.personalDetails.sex')"
|
||||||
|
input-type="select"
|
||||||
|
:select-options="[
|
||||||
|
{ label: $t('userSettingsPage.personalDetails.sexMale'), value: 'MALE' },
|
||||||
|
{ label: $t('userSettingsPage.personalDetails.sexFemale'), value: 'FEMALE' },
|
||||||
|
{ label: $t('userSettingsPage.personalDetails.sexUnknown'), value: 'UNKNOWN' },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
<EditablePropertyRow
|
||||||
|
v-model="personalDetails.currentWeight"
|
||||||
|
:label="$t('userSettingsPage.personalDetails.currentWeight')"
|
||||||
|
input-type="number"
|
||||||
|
:number-input-step="0.1"
|
||||||
|
/>
|
||||||
|
<EditablePropertyRow
|
||||||
|
v-model="personalDetails.currentWeightUnit"
|
||||||
|
:label="$t('userSettingsPage.personalDetails.currentWeightUnit')"
|
||||||
|
input-type="select"
|
||||||
|
:select-options="[
|
||||||
|
{ label: $t('weightUnit.kilograms'), value: WeightUnit.KILOGRAMS },
|
||||||
|
{ label: $t('weightUnit.pounds'), value: WeightUnit.POUNDS },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
|
||||||
<div v-if="personalDetailsChanged">
|
<div v-if="personalDetailsChanged" class="save-button-row">
|
||||||
<q-btn :label="$t('userSettingsPage.save')" color="positive" @click="savePersonalDetails"/>
|
<q-btn :label="$t('userSettingsPage.save')" color="positive" @click="savePersonalDetails"/>
|
||||||
<q-btn :label="$t('userSettingsPage.undo')" color="secondary" @click="undoPersonalDetailsChanges"/>
|
<q-btn :label="$t('userSettingsPage.undo')" color="secondary" @click="undoPersonalDetailsChanges"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,14 +60,21 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="preferences">
|
<div v-if="preferences">
|
||||||
<h4>Preferences</h4>
|
<h4>{{ $t('userSettingsPage.preferences.title') }}</h4>
|
||||||
|
|
||||||
<EditablePropertyRow
|
<EditablePropertyRow
|
||||||
v-model="preferences.accountPrivate"
|
v-model="preferences.accountPrivate"
|
||||||
:label="$t('userSettingsPage.preferences.accountPrivate')"
|
:label="$t('userSettingsPage.preferences.accountPrivate')"
|
||||||
/>
|
/>
|
||||||
|
<EditablePropertyRow
|
||||||
|
v-model="preferences.locale"
|
||||||
|
:label="$t('userSettingsPage.preferences.language')"
|
||||||
|
input-type="select"
|
||||||
|
:select-options="supportedLocales"
|
||||||
|
@update:modelValue="updateLocale"
|
||||||
|
/>
|
||||||
|
|
||||||
<div v-if="preferencesChanged">
|
<div v-if="preferencesChanged" class="save-button-row">
|
||||||
<q-btn :label="$t('userSettingsPage.save')" color="positive" @click="savePreferences"/>
|
<q-btn :label="$t('userSettingsPage.save')" color="positive" @click="savePreferences"/>
|
||||||
<q-btn :label="$t('userSettingsPage.undo')" color="secondary" @click="undoPreferencesChanges"/>
|
<q-btn :label="$t('userSettingsPage.undo')" color="secondary" @click="undoPreferencesChanges"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -44,10 +92,16 @@ import {computed, onMounted, ref, Ref, toRaw} from 'vue';
|
||||||
import {UserPersonalDetails, UserPreferences} from 'src/api/main/auth';
|
import {UserPersonalDetails, UserPreferences} from 'src/api/main/auth';
|
||||||
import api from 'src/api/main';
|
import api from 'src/api/main';
|
||||||
import EditablePropertyRow from 'components/EditablePropertyRow.vue';
|
import EditablePropertyRow from 'components/EditablePropertyRow.vue';
|
||||||
|
import {WeightUnit} from 'src/api/main/submission';
|
||||||
|
import {resolveLocale, supportedLocales} from 'src/i18n';
|
||||||
|
import {useI18n} from 'vue-i18n';
|
||||||
|
import {useQuasar} from 'quasar';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const quasar = useQuasar();
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
const i18n = useI18n({useScope: 'global'});
|
||||||
|
|
||||||
const personalDetails: Ref<UserPersonalDetails | undefined> = ref();
|
const personalDetails: Ref<UserPersonalDetails | undefined> = ref();
|
||||||
const preferences: Ref<UserPreferences | undefined> = ref();
|
const preferences: Ref<UserPreferences | undefined> = ref();
|
||||||
|
@ -55,6 +109,8 @@ const preferences: Ref<UserPreferences | undefined> = ref();
|
||||||
let initialPersonalDetails: UserPersonalDetails | null = null;
|
let initialPersonalDetails: UserPersonalDetails | null = null;
|
||||||
let initialPreferences: UserPreferences | null = null;
|
let initialPreferences: UserPreferences | null = null;
|
||||||
|
|
||||||
|
const newPassword = ref('');
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// Redirect away from the page if the user isn't viewing their own settings.
|
// Redirect away from the page if the user isn't viewing their own settings.
|
||||||
const userId = route.params.userId as string;
|
const userId = route.params.userId as string;
|
||||||
|
@ -67,6 +123,8 @@ onMounted(async () => {
|
||||||
|
|
||||||
preferences.value = await api.auth.getMyPreferences(authStore);
|
preferences.value = await api.auth.getMyPreferences(authStore);
|
||||||
initialPreferences = structuredClone(toRaw(preferences.value));
|
initialPreferences = structuredClone(toRaw(preferences.value));
|
||||||
|
|
||||||
|
newPassword.value = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
const personalDetailsChanged = computed(() => {
|
const personalDetailsChanged = computed(() => {
|
||||||
|
@ -79,10 +137,23 @@ const preferencesChanged = computed(() => {
|
||||||
JSON.stringify(initialPreferences) !== JSON.stringify(preferences.value);
|
JSON.stringify(initialPreferences) !== JSON.stringify(preferences.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const canUpdatePassword = computed(() => {
|
||||||
|
const p = newPassword.value;
|
||||||
|
return p.length >= 8;
|
||||||
|
});
|
||||||
|
|
||||||
async function savePersonalDetails() {
|
async function savePersonalDetails() {
|
||||||
if (personalDetails.value) {
|
if (personalDetails.value) {
|
||||||
|
try {
|
||||||
personalDetails.value = await api.auth.updateMyPersonalDetails(authStore, personalDetails.value);
|
personalDetails.value = await api.auth.updateMyPersonalDetails(authStore, personalDetails.value);
|
||||||
initialPersonalDetails = structuredClone(toRaw(personalDetails.value));
|
initialPersonalDetails = structuredClone(toRaw(personalDetails.value));
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.response && error.response.status === 400) {
|
||||||
|
console.warn('bad request');
|
||||||
|
} else {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,14 +165,61 @@ async function savePreferences() {
|
||||||
if (preferences.value) {
|
if (preferences.value) {
|
||||||
preferences.value = await api.auth.updateMyPreferences(authStore, preferences.value);
|
preferences.value = await api.auth.updateMyPreferences(authStore, preferences.value);
|
||||||
initialPreferences = structuredClone(toRaw(preferences.value));
|
initialPreferences = structuredClone(toRaw(preferences.value));
|
||||||
|
updateLocale();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateLocale() {
|
||||||
|
const chosenLocale = resolveLocale(preferences.value?.locale);
|
||||||
|
i18n.locale.value = chosenLocale.value;
|
||||||
|
}
|
||||||
|
|
||||||
function undoPreferencesChanges() {
|
function undoPreferencesChanges() {
|
||||||
preferences.value = structuredClone(initialPreferences);
|
preferences.value = structuredClone(initialPreferences);
|
||||||
|
updateLocale();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updatePassword() {
|
||||||
|
try {
|
||||||
|
await api.auth.updatePassword(newPassword.value, authStore);
|
||||||
|
newPassword.value = '';
|
||||||
|
quasar.notify({
|
||||||
|
message: i18n.t('userSettingsPage.passwordUpdated'),
|
||||||
|
type: 'positive',
|
||||||
|
position: 'top'
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.response && error.response.status === 400) {
|
||||||
|
newPassword.value = '';
|
||||||
|
quasar.notify({
|
||||||
|
message: i18n.t('userSettingsPage.passwordInvalid'),
|
||||||
|
type: 'warning',
|
||||||
|
position: 'top'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
quasar.notify({
|
||||||
|
message: i18n.t('generalErrors.apiError'),
|
||||||
|
type: 'danger',
|
||||||
|
position: 'top'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.property-label {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-button-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
column-gap: 10px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue