Files
nuxt-frontend/components/Auth/Auth.vue
Joris Slagter 791aebc346
Some checks failed
continuous-integration/drone/push Build is failing
Initial Nuxt frontend import
- Complete GGZ Ecademy Nuxt.js user portal
- Learning products browser and management
- Member management interface
- User authentication and roles
- Multi-language support (NL/EN)
- Vuex store for state management
- Component-based architecture
2025-12-02 17:48:48 +01:00

286 lines
7.6 KiB
Vue

<template>
<v-form ref="form" v-model="valid" lazy-validation>
<v-card tile flat class="mb-6 d-flex flex-column fill-height" width="400">
<v-card-title class="txt--text">
<h2 class="mt-4" v-if="isLogin">{{ $t('auth.login.title') }}</h2>
<h2 class="mt-4" v-if="isForgotten">
{{ $t('auth.password_forgotten.title') }}
</h2>
<h2 class="mt-4" v-if="isReset">
{{ $t('auth.password_reset.title') }}
</h2>
</v-card-title>
<v-card-text class="txt--text">
<span v-if="isLogin">{{ $t('auth.login.text') }}</span>
<span v-if="isForgotten">{{ $t('auth.password_forgotten.text') }}</span>
<span v-if="isReset">{{ $t('auth.password_reset.text') }}</span>
</v-card-text>
<v-card-text class="secondary--text" v-if="errors">
<errors-list :errors="errors" />
</v-card-text>
<v-card-text>
<v-text-field
name="email"
prepend-inner-icon="icon-user"
type="text"
color="accent"
outlined
required
lines
:placeholder="$t('auth.email')"
v-model="email"
:rules="rules.email"
/>
<v-text-field
id="password"
name="password"
:placeholder="$t('auth.password')"
prepend-inner-icon="icon-password"
type="password"
color="accent"
outlined
v-model="password"
:rules="rules.password"
v-if="isLogin || isReset"
/>
<v-text-field
id="password_confirmation"
name="password_confirmation"
placeholder="Wachtwoord bevestiging"
prepend-inner-icon="icon-password"
type="password"
outlined
v-model="passwordConfirmation"
:rules="rules.password_confirmation"
v-if="isReset"
/>
</v-card-text>
<v-card-actions>
<v-btn
block
color="accent"
rounded
large
depressed
v-if="isLogin"
@click="validate"
:disabled="!valid"
>{{ $t('auth.login.cta') }}</v-btn
>
<v-btn
block
color="accent"
rounded
large
depressed
v-if="isForgotten"
@click="validate"
:disabled="!valid"
>{{ $t('auth.password_forgotten.cta') }}</v-btn
>
<v-btn
block
color="accent"
rounded
large
depressed
v-if="isReset"
@click="validate"
:disabled="!valid || !isValidToken"
>{{ $t('auth.password_reset.cta') }}</v-btn
>
</v-card-actions>
<v-card-actions class="d-flex justify-space-between" v-if="isLogin">
<!-- <v-checkbox
on-icon="icon-selectionbox-checked"
off-icon="icon-selectionbox"
style="margin-left: 10px;"
v-model="check"
:label="$t('auth.keep_logged')"
value="secondary"
rounded
></v-checkbox> -->
<nuxt-link
class="info--text"
:to="localePath('/auth/password-forgotten')"
>{{ $t('auth.password_forgotten.question') }}</nuxt-link
>
</v-card-actions>
<!-- <v-card-actions class="d-flex justify-space-between" v-if="!isLogin">
<nuxt-link
class="info--text"
:to="localePath('/auth/login')"
>{{ $t('auth.account.question') }}</nuxt-link>
</v-card-actions> -->
</v-card>
</v-form>
</template>
<script>
import errorsList from '@/components/UI/ErrorsList/ErrorsList'
export default {
components: {
errorsList,
},
props: {
mode: {
type: String,
default: 'login',
},
},
data() {
return {
valid: true,
check: true,
email: '',
errors: null,
password: '',
passwordConfirmation: '',
rules: {
email: [
(v) => !!v || this.$t('auth.validation.email.required'),
(v) => v.length <= 50 || this.$t('auth.validation.email.max_length'),
(v) =>
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
v
) || this.$t('auth.validation.email.invalid'),
],
password: [
(v) => !!v || this.$t('auth.validation.password.required'),
(v) =>
(v && v.length >= 8) ||
this.$t('auth.validation.password.min_length'),
(v) =>
(v && v.length <= 20) ||
this.$t('auth.validation.password.max_min_length'),
],
password_confirmation: [
(v) =>
v === this.password ||
this.$t('auth.validation.password.confirmation'),
],
},
}
},
computed: {
isLogin() {
return this.mode === 'login'
},
isForgotten() {
return this.mode === 'password-forgotten'
},
isReset() {
return this.mode === 'password-reset'
},
isValidToken() {
return this.$route.query.token.length === 64 && this.$route.query.token
},
hasErrors() {
return this.backendErrors.length > 0
},
},
methods: {
async validate() {
if (!this.$refs.form.validate()) return
if (this.isLogin) await this.login()
if (this.isForgotten) await this.askToResetPassword()
if (this.isReset) await this.resetPassword()
},
// reset() {
// this.$refs.form.reset()
// },
// resetValidation() {
// this.$refs.form.resetValidation()
// },
async login() {
try {
await this.$auth.loginWith('local', {
data: {
email: this.email,
password: this.password,
},
})
this.$router.push('/manager')
} catch (error) {
this.errors = error.response.data.errors
this.$notifier.showMessage({
content: error.response.data.message,
color: 'error',
icon: 'icon-message',
})
}
},
async askToResetPassword() {
if (!this.isForgotten) return
try {
const response = await this.$axios.post('auth/password/email', {
email: this.email,
})
this.$notifier.showMessage({
content: this.$t('auth.notifications.request_accepted'),
color: 'success',
icon: 'icon-checkmark',
})
} catch (error) {
this.errors = error.response.data.errors
this.$notifier.showMessage({
content: error.response.data.error,
color: 'error',
icon: 'icon-message',
})
}
},
async resetPassword() {
if (!this.isReset) return
if (!this.isValidToken) return
try {
const response = await this.$axios.post('auth/password/reset', {
email: this.email,
password: this.password,
password_confirmation: this.passwordConfirmation,
token: this.$route.query.token,
})
this.$notifier.showMessage({
content: this.$t('auth.notifications.password_changed'),
color: 'success',
icon: 'icon-checkmark',
})
this.$router.push('/auth/login')
} catch (error) {
this.errors = error.response.data.errors
this.$notifier.showMessage({
content: error.response.data.error,
color: 'error',
icon: 'icon-message',
})
}
},
},
}
</script>
<style lang="scss" scoped>
.info--text {
text-decoration: none;
}
.v-card__title,
.v-card__text {
word-break: normal;
}
</style>