All unauthenticated pages such as Login, Register or any special public pages should be saved as classic pages in your base router file in src/router/index.js. This method allows you to use any private or public layout. Authenticated private pages should use the special src/router/admin.js file. This file is a simple admin redirect object that takes advantage of the entire admin layout with the app bar header and sidebar.
In order for the login page to work, it must have a classic login form. Then all you need to do is send it to the login authentication action, which will pass the credentials to the login authentication provider method.
src/views/Login.vue
import { mapActions } from "vuex";
export default {
data() {
return {
username: null,
password: null,
};
},
methods: {
...mapActions({
login: "auth/login",
}),
async validate() {
await this.login({
username: this.username,
password: this.password,
});
},
},
};
For unauthenticated login redirection, to localize the login URL path, Olobase Admin looks for a route called login; Therefore, make sure that your login route named dashboard is set in the configuration in your admin plugin.
src/plugins/admin.js
export default {
install: (app, http, resources) => {
// console.error(app.config.globalProperties)
admin.setOptions({
app,
router,
resources,
store,
i18n,
dashboard: "dashboard",
/*
...
*/
});
}
}
As explained above, this authenticated page must be registered in src/router/admin.js for the admin layout to be inherited. In this way, Olobase Admin can obtain the authenticated user's information and pre-fill the form in the user account.
The this.admin.http() method should be used to make authenticated API requests as saving account information may differ depending on the project context. The default api url for the current user's account update is /api/account/update.
src/views/Account.vue
<template>
<v-row>
<v-col sm="12">
<v-form>
<v-card flat>
<v-card-text>
<div class="d-flex align-center mb-2">
<h1 class="h1">{{ $t("resources.account.title") }}</h1>
</div>
<v-row>
<v-col lg="3" md="3" sm="12">
<va-avatar-input v-model="avatar.image" :set-label="$t('resources.account.fields.avatar.set')" :del-label="$t('resources.account.fields.avatar.delete')">
</va-avatar-input>
<va-text-input source="firstname" resource="account" v-model="firstname" :error-messages="firstnameErrors"></va-text-input>
<va-text-input source="lastname" resource="account" v-model="lastname" :error-messages="lastnameErrors"></va-text-input>
<va-text-input source="email" resource="account" v-model="email" type="email" :error-messages="emailErrors"></va-text-input>
<va-color-picker-input source="themeColor" resource="account" dot-size="25" mode="hexa" show-swatches swatches-max-height="150" v-model="themeColor" :error-messages="themeColorErrors"></va-color-picker-input>
</v-col>
</v-row>
<v-btn color="primary" :loading="accountUpdating" type="submit">
{{ $t("va.actions.save") }}
</v-btn>
</v-card-text>
</v-card>
</v-form>
</v-col>
</v-row>
</template>
<script>
import { mapActions } from "vuex";
import { useVuelidate } from "@vuelidate/core";
import { required, email, minLength, maxLength } from "@vuelidate/validators";
export default {
inject: ['admin', 'vuetify'],
setup() {
return { v$: useVuelidate() };
},
data() {
return {
accountUpdating: false,
themeColor: "#F26522",
firstname: null,
lastname: null,
email: null,
avatarImage: null,
};
},
validations() {
return {
firstname: {
required,
maxLength: maxLength(120),
},
lastname: {
required,
maxLength: maxLength(120),
},
email: {
required,
email,
},
themeColor: {
required,
minLength: minLength(7),
maxLength: maxLength(7),
},
}
},
computed: {
firstnameErrors() {
const errors = [];
const field = "firstname";
if (!this.v$[field].$dirty) return errors;
this.v$[field].required.$invalid &&
errors.push(this.$t("v.text.required"));
this.v$[field].maxLength.$invalid &&
errors.push(this.$t("v.string.maxLength", { max: "120" }));
return errors;
},
lastnameErrors() {
const errors = [];
const field = "lastname";
if (!this.v$[field].$dirty) return errors;
this.v$[field].required.$invalid &&
errors.push(this.$t("v.text.required"));
this.v$[field].maxLength.$invalid &&
errors.push(this.$t("v.string.maxLength", { max: "120" }));
return errors;
},
emailErrors() {
const errors = [];
const field = "email";
if (!this.v$[field].$dirty) return errors;
this.v$[field].required.$invalid &&
errors.push(this.$t("v.email.required"));
this.v$[field].email.$invalid && errors.push(this.$t("v.email.invalid"));
return errors;
},
themeColorErrors() {
const errors = [];
const field = "themeColor";
if (!this.v$[field].$dirty) return errors;
this.v$[field].required.$invalid &&
errors.push(this.$t("v.text.required"));
this.v$[field].minLength.$invalid &&
errors.push(this.$t("v.string.minLength", { min: "7" }));
this.v$[field].maxLength.$invalid &&
errors.push(this.$t("v.string.maxLength", { max: "7" }));
return errors;
},
},
created() {
this.initializeAccount();
},
methods: {
...mapActions({
checkAuth: "auth/checkAuth",
}),
async initializeAccount() {
let response = await this.admin.http.get("/account/findMe");
let data = response.data.data;
this.firstname = data.firstname;
this.lastname = data.lastname;
this.email = data.email;
this.avatar.image = data.avatar.image;
this.themeColor = data.themeColor ? data.themeColor : this.vuetify.theme.themes.value.defaultTheme.colors.primary;
},
async updateAccount() {
this.v$.$touch();
if (this.v$.$invalid) {
return false;
}
this.accountUpdating = true;
try {
let data = {
firstname: this.firstname,
lastname: this.lastname,
email: this.email,
locale: this.locale,
avatar: {
image: this.avatar.image,
},
themeColor: this.themeColor
};
let Self = this;
let user = await this.checkAuth();
if (user) {
this.admin.http({ method: "PUT", url: "/account/update", data: data }).then(async function(response){
if (response && response.status == 200) {
Self.vuetify.theme.themes.value.defaultTheme.colors.primary = Self.themeColor;
localStorage.setItem("themeColor", Self.themeColor);
Self.$router.push({ name: "dashboard"});
setTimeout(function(){
Self.admin.message("success", Self.$t("form.saved"));
}, 1);
}
});
}
} catch (e) {
// console.log(e.message);
} finally {
this.accountUpdating = false;
}
},
},
};
</script>
When Olobase Admin form elements are used on pages other than CRUD operations, the name of the relevant resource must be entered in the resource attribute for tag translation operations, as seen above, since the resource is unknown, unlike CRUD numbers. Otherwise, the translation process for the element will not occur.
If you need to add these features, the home page is the perfect place to do it. Simply add the relevant forms, then call your API-specific registration or password reset endpoint using the global admin this.admin.http() method as shown in the next section of the profile page.