Initial commit
This commit is contained in:
103
pages/auth/forgot.vue
Normal file
103
pages/auth/forgot.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<div>
|
||||
<video-background
|
||||
src="/video/bg-video.mp4"
|
||||
style=" height: 100vh;"
|
||||
poster="/images/poster.png"
|
||||
>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-4 bg-img d-none d-lg-block" style="background-image: url('/images/login.svg');background-size: cover;height: 100vh;" />
|
||||
<div class="col-12 col-lg-8 pt-4 px-3 px-sm-5 px-md-5 pb-5">
|
||||
<div class="row">
|
||||
<div class="col-12 py-3 text-center">
|
||||
<img src="/images/logo.svg" height="35" class="img " alt="Mindboost Logo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-lg-0 mt-4" style="height: 90%">
|
||||
<div class="col-12 my-auto">
|
||||
<div class="text-center pt-4 pt-md-2 col-12 col-md-8 col-xl-8 mx-auto">
|
||||
<h1 class="fw-bolder h3" style="color: #585C5E;">
|
||||
{{ t("Forgot Password?") }}
|
||||
</h1>
|
||||
<h2 class="text-muted h5 pt-2">
|
||||
{{ t("We send link") }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="row pt-2">
|
||||
<div class="col-12 col-md-8 col-xl-8 mx-auto">
|
||||
<div v-if="resetSuccess" class="alert alert-success text-center">
|
||||
{{ t('Reset Password Message') }}
|
||||
</div>
|
||||
<form method="POST" @submit.prevent="submit">
|
||||
<div class="row pt-4">
|
||||
<div class="col-12">
|
||||
<label class="form-label">{{ t('Email') }}</label>
|
||||
<input v-model="email" type="email" placeholder="email@mail.com" class="form-control">
|
||||
<div v-if="errors.email" class="invalid-feedback d-block">
|
||||
{{ t(errors.email[0]) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pt-4">
|
||||
<div class="col">
|
||||
<button type="submit" style="min-width:fit-content" class="login-btn col-4">
|
||||
{{ t('Reset Password') }} <div v-if="loading" class="spinner-border spinner-border-sm" role="status">
|
||||
<span class="sr-only">{{ t("Loading...") }}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</video-background>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
setup () {
|
||||
const { t } = useI18n()
|
||||
const localePath = useLocalePath()
|
||||
|
||||
return {
|
||||
t,
|
||||
localePath
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
email: '',
|
||||
resetSuccess: false,
|
||||
errors: [],
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submit () {
|
||||
this.loading = true
|
||||
this.errors = []
|
||||
this.$axios.post('/api/password/email', { email: this.email })
|
||||
.then(({ data }) => {
|
||||
this.loading = false
|
||||
this.$toast.success('Wenn die E-Mail existiert, wurde ein Link zum Zurücksetzen gesendet.')
|
||||
this.resetSuccess = true
|
||||
})
|
||||
.catch((error) => {
|
||||
this.loading = false
|
||||
if (error.response.status === 422) {
|
||||
this.errors = error.response.data.errors
|
||||
} else {
|
||||
this.$toast.error('Ein Fehler ist aufgetreten.')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
214
pages/auth/login.vue
Normal file
214
pages/auth/login.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<div>
|
||||
<video-background
|
||||
src="/video/bg-video.mp4"
|
||||
poster="/images/poster.png"
|
||||
style="height: 100vh;"
|
||||
>
|
||||
<div class="container-fluid">
|
||||
<div class="row ">
|
||||
<div class="col-12 col-lg-4 bg-img d-none d-lg-block" style="background-image: url('/images/login.svg');background-size: cover;height: 100vh;" />
|
||||
<div class="col-12 col-lg-8 pt-4 px-3 px-sm-5 px-md-5 pb-5">
|
||||
<div class="row">
|
||||
<div class="col-12 py-3 text-center">
|
||||
<img src="/images/logo.svg" height="35" class="img " alt="Mindboost Logo">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-lg-0 mt-4" style="height: 90%">
|
||||
<div class="col-12 my-auto">
|
||||
<div class="text-center pt-4 pt-md-2 col-12 col-md-8 col-xl-8 mx-auto">
|
||||
<h1 class="fw-bolder h3" style="color: #585C5E;">
|
||||
{{ t('Log in Header') }}
|
||||
</h1>
|
||||
<h2 class="text-muted h5 pt-2">
|
||||
{{ t('Log in') }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="row pt-2">
|
||||
<div class="col-12 col-md-8 col-xl-8 mx-auto">
|
||||
<form method="POST" @submit.prevent="loginNow">
|
||||
<div class="row pt-4">
|
||||
<div class="col-12">
|
||||
<label class="form-label">{{ t('Email') }}</label>
|
||||
<input v-model="form.email" type="email" autocomplete="email" class="form-control">
|
||||
<div v-if="errors.email" class="invalid-feedback d-block">
|
||||
{{ errors.email[0] }}
|
||||
</div>
|
||||
<div v-if="auth_error" class="invalid-feedback d-block">
|
||||
{{ t(auth_error_message) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pt-3">
|
||||
<div class="col">
|
||||
<label class="form-label">{{ t('Password') }}</label>
|
||||
<input
|
||||
v-model="form.password"
|
||||
type="password"
|
||||
name="password"
|
||||
autocomplete="current-password"
|
||||
class="form-control"
|
||||
>
|
||||
<span class="float-end pt-2">
|
||||
<router-link :to="localePath('/auth/forgot')" class="forgot-link" href="#">{{ t('Forgot Password?') }}</router-link>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pt-4">
|
||||
<div class="col">
|
||||
<button type="submit" style="min-width:fit-content" class="login-btn col-4">
|
||||
{{ t('Sign in') }} <div v-if="loading" class="spinner-border spinner-border-sm" role="status">
|
||||
<span class="sr-only">{{ t('Loading...') }}</span>
|
||||
</div>
|
||||
</button>
|
||||
<p class="pt-3">
|
||||
{{ t('No account?') }} <NuxtLink class="signup-link" :to="localePath('/auth/signup')">
|
||||
{{ t("Sign up now") }}
|
||||
</NuxtLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</video-background>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState, mapActions } from 'pinia'
|
||||
import backgroundImagePath from '~/assets/image/login4.png'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
// import { useBufferStore } from '~/stores/buffer'
|
||||
export default {
|
||||
name: 'Login',
|
||||
setup () {
|
||||
const { t } = useI18n()
|
||||
const localePath = useLocalePath()
|
||||
|
||||
return {
|
||||
t,
|
||||
localePath
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
form: {
|
||||
email: '',
|
||||
password: ''
|
||||
},
|
||||
auth_error: false,
|
||||
auth_error_message: '',
|
||||
backgroundImagePath,
|
||||
errors: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(useUserStore, ['is_login', 'user', 'token']),
|
||||
translate (tag) {
|
||||
return this.t(tag)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.$route.query.verified === '1') {
|
||||
this.$toast.success('Deine E-Mail wurde erfolgreich verifiziert. Bitte melde dich an.')
|
||||
}
|
||||
// useNuxtApp().$logger.log({ nuxtApp })
|
||||
|
||||
// useNuxtApp().$logger.log({ db })
|
||||
|
||||
// if (this.is_login){
|
||||
// this.$router.push('/onboarding');
|
||||
// }
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useUserStore, ['login', 'logout']),
|
||||
loginNow () {
|
||||
this.errors = []
|
||||
this.auth_error = false
|
||||
this.loading = true
|
||||
|
||||
this.$axios.post('/api/login', this.form).then(({ data }) => {
|
||||
this.loading = false
|
||||
|
||||
if (data.status === 'success') {
|
||||
this.$axios.head.Authorization = 'bearer ' + data.authorisation.token
|
||||
this.login(data.user, data.authorisation.token)
|
||||
this.$toast.success('Login successful!')
|
||||
//
|
||||
// If a user already did the onboarding, then the soundscape will be defined, so we can jump
|
||||
// directly to the selected soundscape and start play.
|
||||
//
|
||||
let soundscape = 'Lagoon'
|
||||
if (data.user.settings) { // if the user logged in first time no settings available, so push to onboarding
|
||||
soundscape = data.user.settings.soundscape
|
||||
if (soundscape !== '') {
|
||||
// const soundscape = user.user.settings.soundscape
|
||||
let url
|
||||
switch (soundscape) {
|
||||
case 'Lagoon':
|
||||
url = '/'
|
||||
break
|
||||
case 'Meadow':
|
||||
url = '/'
|
||||
break
|
||||
case 'Tropics':
|
||||
url = '/'
|
||||
break
|
||||
case 'Forest':
|
||||
url = '/'
|
||||
break
|
||||
default:
|
||||
url = '/'
|
||||
break
|
||||
}
|
||||
this.$router.push(this.localePath(url))
|
||||
}
|
||||
} else {
|
||||
this.$router.push(this.localePath('/onboarding'))
|
||||
}
|
||||
} else {
|
||||
this.$toast.error('Email or password is incorrect.')
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.message === 'user is not defined') {
|
||||
this.loading = false
|
||||
} else {
|
||||
this.loading = false
|
||||
if ((error.code === 'ERR_NETWORK' || error.code === 'NS_ERROR_DOM_BAD_URI') && this.user !== null && this.token !== '') {
|
||||
this.$toast.warn(this.translate('Offline mode, please go online soon.'))
|
||||
this.login(this.user, this.token)
|
||||
}
|
||||
this.auth_error = true
|
||||
|
||||
if (error.response.status === 500) {
|
||||
this.auth_error_message = 'Es ist etwas schiefgelaufen. Versuche es später erneut'
|
||||
}
|
||||
if (error.response.status === 401) {
|
||||
this.auth_error_message = 'Email oder Passwort ist falsch.'
|
||||
}
|
||||
if (error.response.status === 403) {
|
||||
this.auth_error_message = 'Bitte bestätige zuerst deine E-Mail-Adresse.'
|
||||
}
|
||||
if (error.response.status === 422) {
|
||||
this.errors = error.response.data.errors
|
||||
this.auth_error = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.videobg-content{
|
||||
overflow: auto !important;
|
||||
}
|
||||
</style>
|
||||
~/stores/audio
|
131
pages/auth/newpassword.vue
Normal file
131
pages/auth/newpassword.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div>
|
||||
<video-background
|
||||
src="/video/bg-video.mp4"
|
||||
poster="/images/poster.png"
|
||||
style="height: 100vh;"
|
||||
>
|
||||
<div class="container-fluid">
|
||||
<div class="row ">
|
||||
<div class="col-12 col-lg-4 bg-img d-none d-lg-block" style="background-image: url('/images/login.svg');background-size: cover;height: 100vh;" />
|
||||
<div class="col-12 col-lg-8 pt-4 px-3 px-sm-5 px-md-5 pb-5">
|
||||
<div class="row">
|
||||
<div class="col-12 py-3 text-center">
|
||||
<img src="/images/logo.svg" height="35" class="img " alt="Mindboost Logo">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-lg-0 mt-4" style="height: 90%">
|
||||
<div class="col-12 my-auto">
|
||||
<div class="text-center pt-4 pt-md-2 col-12 col-md-8 col-xl-8 mx-auto">
|
||||
<h1 class="fw-bolder h3" style="color: #585C5E;">
|
||||
{{ t('New Password') }}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="row pt-2">
|
||||
<div class="col-12 col-md-8 col-xl-8 mx-auto">
|
||||
<form method="POST" @submit.prevent="validateAndReset">
|
||||
<div class="row pt-4">
|
||||
<div class="col-12">
|
||||
<label for="password" class="text-muted ">{{ t("Password") }} </label>
|
||||
<input id="password" v-model="form.password" type="password" class="form-control" placeholder="***">
|
||||
<div v-if="errors.password" class="invalid-feedback d-block">
|
||||
{{ t(errors.password[0]) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pt-3">
|
||||
<div class="col-12">
|
||||
<label for="confirm-password" class="text-muted ">{{ t("Confirm Password") }} </label>
|
||||
<input id="confirm-password" v-model="form.password_confirmation" type="password" class="form-control" placeholder="***">
|
||||
<div v-if="errors.password_confirmation" class="invalid-feedback d-block">
|
||||
{{ t(errors.password_confirmation[0]) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pt-4">
|
||||
<div class="col">
|
||||
<button type="submit" class="login-btn col-4" style="min-width:fit-content">
|
||||
{{ t("Reset Password") }} <div v-if="loading" class="spinner-border spinner-border-sm" role="status">
|
||||
<span class="sr-only">{{ t("Loading...") }}</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</video-background>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
setup () {
|
||||
const { t } = useI18n()
|
||||
const localePath = useLocalePath()
|
||||
const router = useRouter()
|
||||
|
||||
return {
|
||||
t,
|
||||
localePath,
|
||||
router
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
form: {
|
||||
email: '', // wird aus URL-Param geholt
|
||||
token: '', // wird aus URL-Param geholt
|
||||
password: '',
|
||||
password_confirmation: ''
|
||||
},
|
||||
errors: [],
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.form.token = this.$route.query.token || ''
|
||||
this.form.email = this.$route.query.email || ''
|
||||
},
|
||||
methods: {
|
||||
validateAndReset () {
|
||||
this.errors = []
|
||||
|
||||
if (this.form.password.length < 6) {
|
||||
this.errors.password = ['The password must be at least 6 characters.']
|
||||
return
|
||||
}
|
||||
|
||||
if (this.form.password !== this.form.password_confirmation) {
|
||||
this.errors.password_confirmation = ['The passwords do not match.']
|
||||
return
|
||||
}
|
||||
|
||||
this.resetPassword()
|
||||
},
|
||||
|
||||
resetPassword () {
|
||||
this.loading = true
|
||||
this.errors = []
|
||||
this.$axios.post('/api/password/reset', this.form).then(({ data }) => {
|
||||
this.loading = false
|
||||
if (data.success) {
|
||||
this.$toast.success('Password successfully reset!')
|
||||
this.$router.push(this.localePath('/auth/login'))
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.loading = false
|
||||
this.$toast.error('Fehler beim Zurücksetzen des Passworts.')
|
||||
if (error.response && error.response.status === 422) {
|
||||
this.errors = error.response.data.errors
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
216
pages/auth/signup.vue
Normal file
216
pages/auth/signup.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div>
|
||||
<video-background
|
||||
src="/video/bg-video.mp4"
|
||||
poster="/images/poster.png"
|
||||
style=" height: 100vh;"
|
||||
>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div
|
||||
class="col-12 col-lg-4 bg-img d-none d-lg-block"
|
||||
style="background-image: url('/images/login.svg');background-size: cover;height: 100vh;"
|
||||
/>
|
||||
<div class="col-12 col-lg-8 pt-4 px-3 px-sm-5 px-md-5 pb-5">
|
||||
<div class="row">
|
||||
<div class="col-12 py-3 text-center">
|
||||
<img src="/images/logo.svg" height="35" class="img " alt="Mindboost Logo">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-lg-0 mt-4" style="height: 90%">
|
||||
<div class="col-12 my-auto">
|
||||
<div class="text-center pt-4 pt-md-2 col-12 col-md-8 col-xl-8 mx-auto">
|
||||
<h1 class="fw-bolder h3" style="color: #585C5E;">
|
||||
{{ t('Sign up Header') }}
|
||||
</h1>
|
||||
<h2 class="text-muted h5 pt-2">
|
||||
{{ t('Sign up') }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="row pt-2">
|
||||
<div class="col-12 col-xl-10 mx-auto">
|
||||
<div v-if="registrationSuccess" class="alert alert-success text-center mx-0 mx-md-4">
|
||||
{{ t('registersuccess') }}
|
||||
</div>
|
||||
<div
|
||||
v-if="registrationError"
|
||||
class="alert alert-danger text-center mx-0 mx-md-4"
|
||||
>
|
||||
{{ registrationErrorMessage }}
|
||||
</div>
|
||||
<form method="POST" @submit.prevent="submitRegister">
|
||||
<!-- Hidden input for the token -->
|
||||
<input type="hidden" name="token" :value="token">
|
||||
<div class="row px-0 px-md-4 pt-3">
|
||||
<div class="col-12 pt-2 col-md-6 col-lg-6 ">
|
||||
<label class="form-label">{{ t("First Name") }}</label>
|
||||
<input v-model="form.first_name" autocomplete="given-name" type="text" :placeholder="t('Your first name')" class="form-control">
|
||||
<div v-if="errors.first_name" class="invalid-feedback d-block">
|
||||
{{ t(errors.first_name[0]) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 pt-2 col-md-6 col-lg-6 ">
|
||||
<label class="form-label">{{ t("Surname") }}</label>
|
||||
<input v-model="form.surname" autocomplete="family-name" type="text" :placeholder="t('Your surname')" class="form-control">
|
||||
<div v-if="errors.surname" class="invalid-feedback d-block">
|
||||
{{ t(errors.surname[0]) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row px-0 px-md-4 pt-3">
|
||||
<div class="col-12 col-md-6 pt-2 col-lg-6 ">
|
||||
<label class="form-label">{{ t("Email") }}</label>
|
||||
<input v-model="form.email" autocomplete="email" type="email" placeholder="alex@mail.com" class="form-control">
|
||||
<div v-if="errors.email" class="invalid-feedback d-block">
|
||||
{{ t(errors.email[0]) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 pt-2 col-md-6 col-lg-6 ">
|
||||
<label class="form-label">{{ t("Password") }}</label>
|
||||
<input v-model="form.password" type="password" autocomplete="new-password" :placeholder="t('Your password')" class="form-control">
|
||||
<div v-if="errors.password" class="invalid-feedback d-block">
|
||||
{{ t(errors.password[0]) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row px-md-4 pt-5 pb-2">
|
||||
<div class="col-12 col-md-10">
|
||||
<div class="form-check">
|
||||
<input id="flexCheckIndeterminate" v-model="form.privacy" class="form-check-input" type="checkbox" value="">
|
||||
<label class="form-check-label" for="flexCheckIndeterminate">
|
||||
{{ t("I have read") }}
|
||||
<NuxtLink class="signup-link" :to="localePath('/settings/termsandcondition')">
|
||||
{{ t("Terms & Conditions") }}
|
||||
</NuxtLink>
|
||||
{{ t("and the") }}
|
||||
<NuxtLink class="signup-link" :to="localePath('/settings/dataprotection')">
|
||||
{{ t("Privacy Policy") }}
|
||||
</NuxtLink>
|
||||
{{ t("and accept") }}
|
||||
</label>
|
||||
<div v-if="errors.privacy" class="invalid-feedback d-block">
|
||||
{{ t(errors.privacy[0]) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row px-0 px-md-4 pt-4">
|
||||
<div class="col">
|
||||
<button :disabled="loading" type="submit" style="min-width:fit-content" class="login-btn col-4">
|
||||
{{ t("Sign up btn") }}
|
||||
<div v-if="loading" class="spinner-border spinner-border-sm" role="status">
|
||||
<span class="sr-only">{{ t("Loading...") }}</span>
|
||||
</div>
|
||||
</button>
|
||||
<p class="pt-3">
|
||||
{{ t("Already have an Account?") }} <NuxtLink class="signup-link" :to="localePath('/auth/login')">
|
||||
{{ t("Log in now") }}
|
||||
</NuxtLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</video-background>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { useRoute } from 'vue-router'
|
||||
import { mapState, mapActions } from 'pinia'
|
||||
import { ref, watch } from 'vue'
|
||||
import backgroundImagePath from '~/assets/image/login4.png'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
export default {
|
||||
name: 'LoGin',
|
||||
setup () {
|
||||
const { t } = useI18n()
|
||||
const localePath = useLocalePath()
|
||||
|
||||
const route = useRoute() // Get the current route
|
||||
const token = ref(route.query.token) // Extract the token from the query parameters
|
||||
|
||||
// Update form token whenever route.query.token changes
|
||||
watch(() => route.query.token, (newToken) => {
|
||||
token.value = newToken
|
||||
})
|
||||
|
||||
return {
|
||||
t,
|
||||
localePath,
|
||||
token
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
backgroundImagePath,
|
||||
loading: false,
|
||||
errors: [],
|
||||
registrationSuccess: false,
|
||||
registrationError: false,
|
||||
registrationErrorMessage: '',
|
||||
form: {
|
||||
first_name: '',
|
||||
surname: '',
|
||||
email: '',
|
||||
password: '',
|
||||
privacy: false,
|
||||
token: '',
|
||||
language: this.$i18n.locale
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(useUserStore, ['user'])
|
||||
},
|
||||
mounted () {
|
||||
if (this.$route.query.error === 'invalid_or_expired_token') {
|
||||
this.registrationError = true
|
||||
this.registrationErrorMessage = 'Invalid or expired token. Please try again.'
|
||||
}
|
||||
if (this.$route.query.error === 'verification_failed') {
|
||||
this.registrationError = true
|
||||
this.registrationErrorMessage = 'Something went wrong. Please try again later.'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useUserStore, ['login', 'logout']),
|
||||
|
||||
submitRegister () {
|
||||
this.loading = true
|
||||
this.errors = []
|
||||
// Update form with the token from setup
|
||||
this.form.token = this.token
|
||||
|
||||
this.$axios.post('/api/register', this.form)
|
||||
.then(({ data }) => {
|
||||
// useNuxtApp().$logger.log(data)
|
||||
|
||||
this.loading = false
|
||||
this.$toast.success('Signup successful!')
|
||||
this.registrationSuccess = true
|
||||
})
|
||||
.catch((error) => {
|
||||
this.loading = false
|
||||
if (error.response.status === 422) {
|
||||
this.errors = error.response.data.errors
|
||||
this.$toast.error(error.response.data.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
} </script>
|
||||
|
||||
<style scoped>
|
||||
@media only screen and (max-width: 587px) {
|
||||
p{
|
||||
font-size: 16px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
230
pages/auth/testerlogin.vue
Normal file
230
pages/auth/testerlogin.vue
Normal file
@@ -0,0 +1,230 @@
|
||||
<template>
|
||||
<div>
|
||||
<video-background
|
||||
src="/video/bg-video.mp4"
|
||||
style="height: 100vh;"
|
||||
poster="/images/poster.png"
|
||||
>
|
||||
<div class="container-fluid overflow-auto">
|
||||
<div class="row ">
|
||||
<div class="col-12 col-lg-4 bg-img d-none d-lg-block" style="background-image: url('/images/login.svg');background-size: cover;height: 100vh;" />
|
||||
<div class="col-12 col-lg-8 pt-4 px-3 px-sm-5 px-md-5 pb-5">
|
||||
<div class="row">
|
||||
<div class="col-12 py-3 text-center">
|
||||
<img src="/images/logo.svg" height="35" class="img " alt="Mindboost Logo">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="height: 90%">
|
||||
<div class="col-12 my-auto">
|
||||
<div class="text-center pt-1 ">
|
||||
<h2 class="fw-bolder display-6 text-center">
|
||||
{{ t('Welcome, Tester!') }}
|
||||
</h2>
|
||||
<span class="fs-5 pt-1 text-muted">{{ t('Thank you for using and improving Mindboost') }}</span>
|
||||
</div>
|
||||
<div class="row pt-2">
|
||||
<div class="col-12">
|
||||
<form method="POST" class=" pb-5 " @submit.prevent="submitRegister()">
|
||||
<div class="row pt-3 justify-content-center">
|
||||
<div class="col-12 col-sm-8 col-md-8 col-lg-6">
|
||||
<label class="form-label">{{ t('Name') }}</label>
|
||||
<input v-model="form.email" type="input" placeholder="e.g. your name, a tag, date or what ever you like.." class="form-control">
|
||||
<div v-if="errors.email" class="invalid-feedback d-block">
|
||||
{{ t('Please add first a name') }}
|
||||
</div>
|
||||
<div v-if="auth_error" class="invalid-feedback d-block">
|
||||
{{ t('Something is wrong, please try again!') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center pt-3">
|
||||
<div class="col-12 col-sm-8 col-md-8 col-lg-6">
|
||||
<label class="form-label">{{ t('Comments') }}</label>
|
||||
<input v-model="form.password" type="input" :placeholder="t('How did you discover Mindboost?')" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center pb-5">
|
||||
<div class="col-12 col-sm-8 col-md-8 col-lg-6 pt-4 text-center">
|
||||
<button type="submit" class=" login-btn col-12">
|
||||
{{ t('Go') }} <div v-if="loading" class="spinner-border spinner-border-sm" role="status">
|
||||
<span class="sr-only">{{ t('Loading...') }}</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<h5 class="text-center pt-3">
|
||||
{{ t("Want to have an Account?") }} <NuxtLink class="signup-link" :to="localePath('/auth/signup')">
|
||||
{{ t("Sign Up") }}
|
||||
</NuxtLink>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</video-background>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState, mapActions } from 'pinia'
|
||||
import backgroundImagePath from '~/assets/image/login4.png'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
// import { useBufferStore } from '~/stores/buffer'
|
||||
export default {
|
||||
name: 'TestLogin',
|
||||
setup () {
|
||||
const { t } = useI18n()
|
||||
const localePath = useLocalePath()
|
||||
|
||||
return {
|
||||
t,
|
||||
localePath
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
form: {
|
||||
email: '',
|
||||
password: ''
|
||||
},
|
||||
auth_error: false,
|
||||
backgroundImagePath,
|
||||
errors: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(useUserStore, ['is_login', 'user', 'token']),
|
||||
translate (tag) {
|
||||
const { t } = useI18n()
|
||||
return t(tag)
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
||||
// useNuxtApp().$logger.log({ nuxtApp })
|
||||
|
||||
// useNuxtApp().$logger.log({ db })
|
||||
|
||||
// if (this.is_login){
|
||||
// this.$router.push('/onboarding');
|
||||
// }
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useUserStore, ['login', 'logout']),
|
||||
formatToValidEmail (input) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
let email = input.replace(/[^a-zA-Z0-9\._-]/g, '') // Remove invalid characters
|
||||
if (!/@/.test(email)) {
|
||||
email += '@betatester.mindboost.team' // Append a default domain if no '@'
|
||||
} else if (email.split('@')[1] === '') {
|
||||
email += '@betatester.mindboost.team' // Append "example.com" if there is no domain part
|
||||
}
|
||||
return email
|
||||
},
|
||||
submitRegister () {
|
||||
this.loading = true
|
||||
const email = this.formatToValidEmail(this.form.email)
|
||||
this.$axios.post('/api/register', {
|
||||
first_name: this.form.email,
|
||||
surname: this.form.password || 'no comments',
|
||||
email,
|
||||
password: email
|
||||
}).then(({ data }) => {
|
||||
this.loading = false
|
||||
this.$toast.success('Signup successfully....')
|
||||
this.login(data.user, data.authorisation.token)
|
||||
this.$router.push(this.localePath('/onboarding'))
|
||||
}).catch((error) => {
|
||||
this.loading = false
|
||||
if (error.response.status === 422) {
|
||||
this.errors = error.response.data.errors
|
||||
this.$toast.error(error.response.data.message)
|
||||
}
|
||||
})
|
||||
},
|
||||
loginNow () {
|
||||
this.errors = []
|
||||
this.auth_error = false
|
||||
this.loading = true
|
||||
|
||||
this.$axios.post('/api/login', this.form).then(({ data }) => {
|
||||
this.loading = false
|
||||
|
||||
if (data.status === 'success') {
|
||||
this.$axios.head.Authorization = 'bearer ' + data.authorisation.token
|
||||
this.login(data.user, data.authorisation.token)
|
||||
this.$toast.success('Login Successfully....')
|
||||
|
||||
//
|
||||
// If a user already did the onboarding, then the soundscape will be defined, so we can jump
|
||||
// directly to the selected soundscape and start play.
|
||||
//
|
||||
let soundscape = 'Lagoon'
|
||||
if (data.user.settings) { // if the user logged in first time no settings available, so push to onboarding
|
||||
soundscape = data.user.settings.soundscape
|
||||
if (soundscape !== '') {
|
||||
// const soundscape = user.user.settings.soundscape
|
||||
let url
|
||||
switch (soundscape) {
|
||||
case 'Lagoon':
|
||||
url = '/'
|
||||
break
|
||||
case 'Meadow':
|
||||
url = '/'
|
||||
break
|
||||
case 'Tropics':
|
||||
url = '/'
|
||||
break
|
||||
case 'Forest':
|
||||
url = '/'
|
||||
break
|
||||
default:
|
||||
url = '/'
|
||||
break
|
||||
}
|
||||
this.$router.push(this.localePath(url))
|
||||
}
|
||||
} else {
|
||||
this.$router.push(this.localePath('/onboarding'))
|
||||
}
|
||||
} else {
|
||||
this.$toast.error('Email or password is incorrect.')
|
||||
}
|
||||
}).catch((error) => {
|
||||
if (error.message === 'user is not defined') {
|
||||
this.loading = false
|
||||
} else {
|
||||
this.loading = false
|
||||
if ((error.code === 'ERR_NETWORK' || error.code === 'NS_ERROR_DOM_BAD_URI') && this.user !== null && this.token !== '') {
|
||||
this.$toast.warn(this.translate('Offline mode, please go online soon.'))
|
||||
this.login(this.user, this.token)
|
||||
}
|
||||
if (error.response.status === 500) {
|
||||
this.auth_error = true
|
||||
this.$toast.error('Es ist etwas schiefgelaufen. Versuche es später erneut')
|
||||
}
|
||||
if (error.response.status === 401) {
|
||||
this.auth_error = true
|
||||
this.$toast.error('Email or password is incorrect.')
|
||||
}
|
||||
if (error.response.status === 422) {
|
||||
this.errors = error.response.data.errors
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.videobg-content{
|
||||
overflow: auto !important;
|
||||
}
|
||||
</style>
|
||||
~/stores/audio
|
43
pages/auth/verify-email/[token].vue
Normal file
43
pages/auth/verify-email/[token].vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="text-center mt-10">
|
||||
<h1 class="text-2xl font-bold">E-Mail wird verifiziert...</h1>
|
||||
<p>Bitte einen Moment Geduld.</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const localePath = useLocalePath()
|
||||
const config = useRuntimeConfig()
|
||||
const apiBase = config.public.apiUrl
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const token = route.params.token
|
||||
await axios.get(`${apiBase}/api/verify-email/${token}`)
|
||||
|
||||
// Weiterleitung mit Query-Param (z.B. für Erfolgsmeldung im Login)
|
||||
router.push({
|
||||
path: localePath('/auth/login'),
|
||||
query: { verified: '1' }
|
||||
})
|
||||
} catch (error) {
|
||||
if (error.response && (error.response.status === 400 || error.response.status === 404)) {
|
||||
// Token ungültig oder abgelaufen – Weiterleitung zur Registrierung
|
||||
router.push({
|
||||
path: localePath('/auth/signup'),
|
||||
query: { error: 'invalid_or_expired_token' }
|
||||
})
|
||||
} else {
|
||||
router.push({
|
||||
path: localePath('/auth/login'),
|
||||
query: { error: 'verification_failed' }
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
Reference in New Issue
Block a user