From f9ff839b1af7cdae2bc9ff5090844ea6b1fac6ac Mon Sep 17 00:00:00 2001 From: raeno <just.raeno@gmail.com> Date: Wed, 5 Dec 2018 19:17:29 +0400 Subject: [PATCH] Better styling for client-side validation. Add I18n for validation errors. --- src/components/registration/registration.js | 30 +++++++++----- src/components/registration/registration.vue | 41 ++++++++++++++++++-- src/i18n/en.json | 10 ++++- src/i18n/ru.json | 10 ++++- src/modules/users.js | 17 ++++---- 5 files changed, 85 insertions(+), 23 deletions(-) diff --git a/src/components/registration/registration.js b/src/components/registration/registration.js index 69474585..67c052f1 100644 --- a/src/components/registration/registration.js +++ b/src/components/registration/registration.js @@ -1,5 +1,5 @@ import { validationMixin } from 'vuelidate' -import { required } from 'vuelidate/lib/validators' +import { required, sameAs, email } from 'vuelidate/lib/validators' import { mapActions, mapState } from 'vuex' import { SIGN_UP } from '../../mutation_types' @@ -16,24 +16,29 @@ const registration = { }), validations: { user: { - email: { required }, + email: { required, email }, username: { required }, password: { required }, - confirm: { required } + confirm: { + required, + sameAsPassword: sameAs('password') + } } }, created () { - if ((!this.$store.state.instance.registrationOpen && !this.token) || !!this.$store.state.users.currentUser) { + if ((!this.registrationOpen && !this.token) || this.signedIn) { this.$router.push('/main/all') } - // Seems like this doesn't work at first page open for some reason - if (this.$store.state.instance.registrationOpen && this.token) { - this.$router.push('/registration') - } + // // Seems like this doesn't work at first page open for some reason + // if (this.$store.state.instance.registrationOpen && this.token) { + // this.$router.push('/registration') + // } }, computed: { token () { return this.$route.params.token }, ...mapState({ + registrationOpen: (state) => state.instance.registrationOpen, + signedIn: (state) => !!state.users.currentUser, isPending: (state) => state.users[SIGN_UP.isPending], serverValidationErrors: (state) => state.users[SIGN_UP.errors], termsofservice: (state) => state.instance.tos @@ -41,14 +46,19 @@ const registration = { }, methods: { ...mapActions(['signUp']), - submit () { + async submit () { this.user.nickname = this.user.username this.user.token = this.token this.$v.$touch() if (!this.$v.$invalid) { - this.signUp(this.user) + try { + await this.signUp(this.user) + this.$router.push('/main/friends') + } catch (error) { + console.log("Registration failed: " + error) + } } } } diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue index 73200990..5f6357a2 100644 --- a/src/components/registration/registration.vue +++ b/src/components/registration/registration.vue @@ -12,7 +12,11 @@ <input :disabled="isPending" v-model.trim='$v.user.username.$model' class='form-control' id='sign-up-username' placeholder='e.g. lain'> </div> <div class="form-error" v-if="$v.user.username.$dirty"> - <span class="error-required" v-if="!$v.user.username.required">Username is required.</span> + <ul> + <li v-if="!$v.user.username.required"> + <span>{{$t('registration.validations.username_required')}}</span> + </li> + </ul> </div> <div class='form-group'> @@ -25,7 +29,14 @@ <input :disabled="isPending" v-model='$v.user.email.$model' class='form-control' id='email' type="email"> </div> <div class="form-error" v-if="$v.user.email.$dirty"> - <span class="error-required" v-if="!$v.user.email.required">Email is required.</span> + <ul> + <li v-if="!$v.user.email.required"> + <span>{{$t('registration.validations.email_required')}}</span> + </li> + <li v-if="!$v.user.email.email"> + <span>{{$t('registration.validations.email_valid')}}</span> + </li> + </ul> </div> <div class='form-group'> @@ -38,7 +49,11 @@ <input :disabled="isPending" v-model='user.password' class='form-control' id='sign-up-password' type='password'> </div> <div class="form-error" v-if="$v.user.password.$dirty"> - <span class="error-required" v-if="!$v.user.password.required">Password is required.</span> + <ul> + <li v-if="!$v.user.password.required"> + <span>{{$t('registration.validations.password_required')}}</span> + </li> + </ul> </div> <div class='form-group' :class="{ 'form-group--error': $v.user.confirm.$error }"> @@ -46,7 +61,14 @@ <input :disabled="isPending" v-model='user.confirm' class='form-control' id='sign-up-password-confirmation' type='password'> </div> <div class="form-error" v-if="$v.user.confirm.$dirty"> - <span class="error-required" v-if="!$v.user.confirm.required">Password confirmation is required.</span> + <ul> + <li v-if="!$v.user.confirm.required"> + <span>{{$t('registration.validations.password_confirmation_required')}}</span> + </li> + <li v-if="!$v.user.confirm.sameAsPassword"> + <span>{{$t('registration.validations.password_confirmation_match')}}</span> + </li> + </ul> </div> <!-- <div class='form-group'> @@ -132,6 +154,17 @@ span { font-size: 12px; } + + } + + .form-error ul { + list-style: none; + padding: 0 0 0 5px; + margin-top: 0; + + li::before { + content: "• "; + } } form textarea { diff --git a/src/i18n/en.json b/src/i18n/en.json index 8fd546ef..6e13993f 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -72,7 +72,15 @@ "fullname": "Display name", "password_confirm": "Password confirmation", "registration": "Registration", - "token": "Invite token" + "token": "Invite token", + "validations": { + "username_required": "username should not be blank", + "email_required": "should not be blank", + "email_valid": "should be valid email", + "password_required": "should not be blank", + "password_confirmation_required": "should not be blank", + "password_confirmation_match": "should be the same as password" + } }, "settings": { "attachmentRadius": "Attachments", diff --git a/src/i18n/ru.json b/src/i18n/ru.json index 9c28ccf4..de2774ef 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -55,7 +55,15 @@ "fullname": "Отображаемое имя", "password_confirm": "Подтверждение пароля", "registration": "Регистрация", - "token": "Код приглашения" + "token": "Код приглашения", + "validations": { + "username_required": "не должно быть пустым", + "email_required": "не должен быть пустым", + "email_valid": "должен быть корректный email адрес", + "password_required": "не должен быть пустым", + "password_confirmation_required": "не должно быть пустым", + "password_confirmation_match": "должно совпадать с паролем" + } }, "settings": { "attachmentRadius": "Прикреплённые файлы", diff --git a/src/modules/users.js b/src/modules/users.js index 572a62c6..5ed8e46b 100644 --- a/src/modules/users.js +++ b/src/modules/users.js @@ -52,13 +52,14 @@ export const mutations = { }, [SIGN_UP.PENDING] (state) { state[SIGN_UP.isPending] = true + state[SIGN_UP.errors] = [] }, [SIGN_UP.SUCCESS] (state) { state[SIGN_UP.isPending] = false }, [SIGN_UP.FAILURE] (state, errors) { state[SIGN_UP.isPending] = false - state[SIGN_UP.errors] = [...state[SIGN_UP.errors], ...errors] + state[SIGN_UP.errors] = errors } } @@ -97,27 +98,29 @@ const users = { async signUp (store, userInfo) { store.commit(SIGN_UP.PENDING) - let response = await store.rootState.api.backendInteractor.register(userInfo) + let rootState = store.rootState + + let response = await rootState.api.backendInteractor.register(userInfo) if (response.ok) { const data = { - oauth: store.state.oauth, - instance: store.state.instance.server + oauth: rootState.oauth, + instance: rootState.instance.server } let app = await oauthApi.getOrCreateApp(data) let result = await oauthApi.getTokenWithCredentials({ app, instance: data.instance, - username: this.user.username, - password: this.user.password + username: userInfo.username, + password: userInfo.password }) store.commit(SIGN_UP.SUCCESS) store.commit('setToken', result.access_token) store.dispatch('loginUser', result.access_token) - this.$router.push('/main/friends') } else { let data = await response.json() let errors = humanizeErrors(JSON.parse(data.error)) store.commit(SIGN_UP.FAILURE, errors) + throw Error(errors) } }, logout (store) {