mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge pull request #4168 from Ocelot-Social-Community/4092-redesign-registration-process
feat: 🍰 Redesign Registration Process Frontend
This commit is contained in:
commit
76d3788cf1
@ -16,6 +16,7 @@ describe('rewards', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
await cleanDatabase()
|
||||||
const { server } = createServer({
|
const { server } = createServer({
|
||||||
context: () => {
|
context: () => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
87
webapp/components/ComponentSlider/ComponentSlider.vue
Normal file
87
webapp/components/ComponentSlider/ComponentSlider.vue
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<div class="Sliders">
|
||||||
|
<slot :name="'header'" />
|
||||||
|
|
||||||
|
<ds-heading v-if="sliderData.sliders[sliderIndex].title" size="h3">
|
||||||
|
{{ sliderData.sliders[sliderIndex].title }}
|
||||||
|
</ds-heading>
|
||||||
|
|
||||||
|
<slot :name="sliderData.sliders[sliderIndex].name" />
|
||||||
|
|
||||||
|
<ds-flex>
|
||||||
|
<ds-flex-item :centered="true">
|
||||||
|
<div
|
||||||
|
v-for="(slider, index) in sliderData.sliders"
|
||||||
|
:key="slider.name"
|
||||||
|
:class="['Sliders__slider-selection', index < sliderIndex && '--confirmed']"
|
||||||
|
>
|
||||||
|
<base-button
|
||||||
|
:class="['selection-dot']"
|
||||||
|
style="float: left"
|
||||||
|
:circle="true"
|
||||||
|
size="small"
|
||||||
|
type="submit"
|
||||||
|
filled
|
||||||
|
:loading="false"
|
||||||
|
:disabled="index > sliderIndex"
|
||||||
|
@click="sliderData.sliderSelectorCallback(index)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ds-flex-item>
|
||||||
|
<ds-flex-item>
|
||||||
|
<base-button
|
||||||
|
style="float: right"
|
||||||
|
:icon="sliderData.sliders[sliderIndex].button.icon"
|
||||||
|
type="submit"
|
||||||
|
filled
|
||||||
|
:loading="false"
|
||||||
|
:disabled="!sliderData.sliders[sliderIndex].validated"
|
||||||
|
@click="onNextClick"
|
||||||
|
>
|
||||||
|
{{ sliderData.sliders[sliderIndex].button.title }}
|
||||||
|
</base-button>
|
||||||
|
</ds-flex-item>
|
||||||
|
</ds-flex>
|
||||||
|
|
||||||
|
<slot :name="'footer'" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ComponentSlider',
|
||||||
|
props: {
|
||||||
|
sliderData: { type: Object, required: true },
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sliderIndex() {
|
||||||
|
return this.sliderData.sliderIndex // to have a shorter notation
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async onNextClick() {
|
||||||
|
let success = true
|
||||||
|
if (this.sliderData.sliders[this.sliderIndex].button.sliderCallback) {
|
||||||
|
success = await this.sliderData.sliders[this.sliderIndex].button.sliderCallback()
|
||||||
|
}
|
||||||
|
success = success && this.sliderData.sliders[this.sliderIndex].button.callback(success)
|
||||||
|
if (success && this.sliderIndex < this.sliderData.sliders.length - 1) {
|
||||||
|
this.sliderData.sliderSelectorCallback(this.sliderIndex + 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.Sliders {
|
||||||
|
&__slider-selection {
|
||||||
|
.selection-dot {
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
&.--confirmed {
|
||||||
|
opacity: $opacity-disabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
84
webapp/components/Registration/CreateUserAccount.story.js
Normal file
84
webapp/components/Registration/CreateUserAccount.story.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { storiesOf } from '@storybook/vue'
|
||||||
|
import { withA11y } from '@storybook/addon-a11y'
|
||||||
|
import { action } from '@storybook/addon-actions'
|
||||||
|
import Vuex from 'vuex'
|
||||||
|
import helpers from '~/storybook/helpers'
|
||||||
|
import links from '~/constants/links.js'
|
||||||
|
import metadata from '~/constants/metadata.js'
|
||||||
|
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
||||||
|
import CreateUserAccount from './CreateUserAccount.vue'
|
||||||
|
|
||||||
|
helpers.init()
|
||||||
|
|
||||||
|
const createStore = ({ loginSuccess }) => {
|
||||||
|
return new Vuex.Store({
|
||||||
|
modules: {
|
||||||
|
auth: {
|
||||||
|
namespaced: true,
|
||||||
|
state: () => ({
|
||||||
|
pending: false,
|
||||||
|
}),
|
||||||
|
mutations: {
|
||||||
|
SET_PENDING(state, pending) {
|
||||||
|
state.pending = pending
|
||||||
|
},
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
pending(state) {
|
||||||
|
return !!state.pending
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
async login({ commit, dispatch }, args) {
|
||||||
|
action('Vuex action `auth/login`')(args)
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
commit('SET_PENDING', true)
|
||||||
|
setTimeout(() => {
|
||||||
|
commit('SET_PENDING', false)
|
||||||
|
if (loginSuccess) {
|
||||||
|
resolve(loginSuccess)
|
||||||
|
} else {
|
||||||
|
reject(new Error('Login unsuccessful'))
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
storiesOf('CreateUserAccount', module)
|
||||||
|
.addDecorator(withA11y)
|
||||||
|
.addDecorator(helpers.layout)
|
||||||
|
.add('standard', () => ({
|
||||||
|
components: { LocaleSwitch, CreateUserAccount },
|
||||||
|
store: createStore({ loginSuccess: true }),
|
||||||
|
data: () => ({
|
||||||
|
links,
|
||||||
|
metadata,
|
||||||
|
nonce: 'A34RB56',
|
||||||
|
email: 'user@example.org',
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
handleSuccess() {
|
||||||
|
action('You are logged in!')()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<ds-container width="small">
|
||||||
|
<base-card>
|
||||||
|
<template #imageColumn>
|
||||||
|
<a :href="links.ORGANIZATION" :title="$t('login.moreInfo', metadata)" target="_blank">
|
||||||
|
<img class="image" alt="Sign up" src="/img/custom/sign-up.svg" />
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
<create-user-account @userCreated="handleSuccess" :email="email" :nonce="nonce" />
|
||||||
|
<template #topMenu>
|
||||||
|
<locale-switch offset="5" />
|
||||||
|
</template>
|
||||||
|
</base-card>
|
||||||
|
</ds-container>
|
||||||
|
`,
|
||||||
|
}))
|
||||||
@ -0,0 +1,321 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="response === 'success'">
|
||||||
|
<transition name="ds-transition-fade">
|
||||||
|
<sweetalert-icon icon="success" />
|
||||||
|
</transition>
|
||||||
|
<ds-text align="center" bold color="success">
|
||||||
|
{{ $t('components.registration.create-user-account.success') }}
|
||||||
|
</ds-text>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="response === 'error'">
|
||||||
|
<transition name="ds-transition-fade">
|
||||||
|
<sweetalert-icon icon="error" />
|
||||||
|
</transition>
|
||||||
|
<ds-text align="center" bold color="danger">
|
||||||
|
{{ $t('components.registration.create-user-account.error') }}
|
||||||
|
</ds-text>
|
||||||
|
<ds-text align="center">
|
||||||
|
{{ $t('components.registration.create-user-account.help') }}
|
||||||
|
<a :href="'mailto:' + supportEmail">{{ supportEmail }}</a>
|
||||||
|
</ds-text>
|
||||||
|
<ds-space centered>
|
||||||
|
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
|
||||||
|
</ds-space>
|
||||||
|
</div>
|
||||||
|
<div v-else class="create-account-card">
|
||||||
|
<!-- Wolle <ds-space margin-top="large">
|
||||||
|
<ds-heading size="h3">
|
||||||
|
{{ $t('components.registration.create-user-account.title') }}
|
||||||
|
</ds-heading>
|
||||||
|
</ds-space> -->
|
||||||
|
|
||||||
|
<ds-form
|
||||||
|
class="create-user-account"
|
||||||
|
v-model="formData"
|
||||||
|
:schema="formSchema"
|
||||||
|
@submit="submit"
|
||||||
|
@input="handleInput"
|
||||||
|
@input-valid="handleInputValid"
|
||||||
|
>
|
||||||
|
<!-- leave this here in case the scoped variable is needed in the future nobody would remember this -->
|
||||||
|
<!-- <template v-slot="{ errors }"> -->
|
||||||
|
<template>
|
||||||
|
<ds-input
|
||||||
|
id="name"
|
||||||
|
model="name"
|
||||||
|
icon="user"
|
||||||
|
:label="$t('settings.data.labelName')"
|
||||||
|
:placeholder="$t('settings.data.namePlaceholder')"
|
||||||
|
/>
|
||||||
|
<ds-input
|
||||||
|
id="about"
|
||||||
|
model="about"
|
||||||
|
type="textarea"
|
||||||
|
rows="3"
|
||||||
|
:label="$t('settings.data.labelBio')"
|
||||||
|
:placeholder="$t('settings.data.labelBio')"
|
||||||
|
/>
|
||||||
|
<ds-input
|
||||||
|
id="password"
|
||||||
|
model="password"
|
||||||
|
type="password"
|
||||||
|
autocomplete="off"
|
||||||
|
:label="$t('settings.security.change-password.label-new-password')"
|
||||||
|
/>
|
||||||
|
<ds-input
|
||||||
|
id="passwordConfirmation"
|
||||||
|
model="passwordConfirmation"
|
||||||
|
type="password"
|
||||||
|
autocomplete="off"
|
||||||
|
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
||||||
|
/>
|
||||||
|
<password-strength class="password-strength" :password="formData.password" />
|
||||||
|
|
||||||
|
<ds-text>
|
||||||
|
<!-- Wolle {{ $t('components.enter-nonce.form.description') }} -->
|
||||||
|
Your e-mail address:
|
||||||
|
<b>{{ this.sliderData.collectedInputData.email }}</b>
|
||||||
|
</ds-text>
|
||||||
|
|
||||||
|
<ds-text>
|
||||||
|
<input
|
||||||
|
id="checkbox0"
|
||||||
|
type="checkbox"
|
||||||
|
v-model="termsAndConditionsConfirmed"
|
||||||
|
:checked="termsAndConditionsConfirmed"
|
||||||
|
/>
|
||||||
|
<label for="checkbox0">
|
||||||
|
{{ $t('termsAndConditions.termsAndConditionsConfirmed') }}
|
||||||
|
<br />
|
||||||
|
<nuxt-link to="/terms-and-conditions">{{ $t('site.termsAndConditions') }}</nuxt-link>
|
||||||
|
</label>
|
||||||
|
</ds-text>
|
||||||
|
<ds-text>
|
||||||
|
<input id="checkbox1" type="checkbox" v-model="dataPrivacy" :checked="dataPrivacy" />
|
||||||
|
<label for="checkbox1">
|
||||||
|
{{ $t('components.registration.signup.form.data-privacy') }}
|
||||||
|
<br />
|
||||||
|
<nuxt-link to="/data-privacy">
|
||||||
|
{{ $t('site.data-privacy') }}
|
||||||
|
</nuxt-link>
|
||||||
|
</label>
|
||||||
|
</ds-text>
|
||||||
|
<ds-text>
|
||||||
|
<input id="checkbox2" type="checkbox" v-model="minimumAge" :checked="minimumAge" />
|
||||||
|
<label
|
||||||
|
for="checkbox2"
|
||||||
|
v-html="$t('components.registration.signup.form.minimum-age')"
|
||||||
|
></label>
|
||||||
|
</ds-text>
|
||||||
|
<ds-text>
|
||||||
|
<input id="checkbox3" type="checkbox" v-model="noCommercial" :checked="noCommercial" />
|
||||||
|
<label
|
||||||
|
for="checkbox3"
|
||||||
|
v-html="$t('components.registration.signup.form.no-commercial')"
|
||||||
|
></label>
|
||||||
|
</ds-text>
|
||||||
|
<ds-text>
|
||||||
|
<input id="checkbox4" type="checkbox" v-model="noPolitical" :checked="noPolitical" />
|
||||||
|
<label
|
||||||
|
for="checkbox4"
|
||||||
|
v-html="$t('components.registration.signup.form.no-political')"
|
||||||
|
></label>
|
||||||
|
</ds-text>
|
||||||
|
</template>
|
||||||
|
</ds-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { VERSION } from '~/constants/terms-and-conditions-version.js'
|
||||||
|
import links from '~/constants/links'
|
||||||
|
import emails from '~/constants/emails'
|
||||||
|
import PasswordStrength from '../Password/Strength'
|
||||||
|
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||||
|
import PasswordForm from '~/components/utils/PasswordFormHelper'
|
||||||
|
import { SignupVerificationMutation } from '~/graphql/Registration.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RegistrationItemCreateUserAccount',
|
||||||
|
components: {
|
||||||
|
PasswordStrength,
|
||||||
|
SweetalertIcon,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
sliderData: { type: Object, required: true },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const passwordForm = PasswordForm({ translate: this.$t })
|
||||||
|
return {
|
||||||
|
links,
|
||||||
|
supportEmail: emails.SUPPORT,
|
||||||
|
formData: {
|
||||||
|
name: '',
|
||||||
|
about: '',
|
||||||
|
...passwordForm.formData,
|
||||||
|
},
|
||||||
|
formSchema: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
min: 3,
|
||||||
|
},
|
||||||
|
about: {
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
...passwordForm.formSchema,
|
||||||
|
},
|
||||||
|
response: null, // Wolle
|
||||||
|
// TODO: Our styleguide does not support checkmarks.
|
||||||
|
// Integrate termsAndConditionsConfirmed into `this.formData` once we
|
||||||
|
// have checkmarks available.
|
||||||
|
termsAndConditionsConfirmed: false,
|
||||||
|
dataPrivacy: false,
|
||||||
|
minimumAge: false,
|
||||||
|
noCommercial: false,
|
||||||
|
noPolitical: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
this.$nextTick(function () {
|
||||||
|
// Code that will run only after the entire view has been rendered
|
||||||
|
this.formData.name = this.sliderData.collectedInputData.name
|
||||||
|
? this.sliderData.collectedInputData.name
|
||||||
|
: ''
|
||||||
|
this.formData.about = this.sliderData.collectedInputData.about
|
||||||
|
? this.sliderData.collectedInputData.about
|
||||||
|
: ''
|
||||||
|
this.formData.password = this.sliderData.collectedInputData.password
|
||||||
|
? this.sliderData.collectedInputData.password
|
||||||
|
: ''
|
||||||
|
this.formData.passwordConfirmation = this.sliderData.collectedInputData.passwordConfirmation
|
||||||
|
? this.sliderData.collectedInputData.passwordConfirmation
|
||||||
|
: ''
|
||||||
|
this.termsAndConditionsConfirmed = this.sliderData.collectedInputData
|
||||||
|
.termsAndConditionsConfirmed
|
||||||
|
? this.sliderData.collectedInputData.termsAndConditionsConfirmed
|
||||||
|
: false
|
||||||
|
this.dataPrivacy = this.sliderData.collectedInputData.dataPrivacy
|
||||||
|
? this.sliderData.collectedInputData.dataPrivacy
|
||||||
|
: false
|
||||||
|
this.minimumAge = this.sliderData.collectedInputData.minimumAge
|
||||||
|
? this.sliderData.collectedInputData.minimumAge
|
||||||
|
: false
|
||||||
|
this.noCommercial = this.sliderData.collectedInputData.noCommercial
|
||||||
|
? this.sliderData.collectedInputData.noCommercial
|
||||||
|
: false
|
||||||
|
this.noPolitical = this.sliderData.collectedInputData.noPolitical
|
||||||
|
? this.sliderData.collectedInputData.noPolitical
|
||||||
|
: false
|
||||||
|
this.sendValidation()
|
||||||
|
|
||||||
|
this.sliderData.setSliderValuesCallback(this.validInput, {
|
||||||
|
sliderSettings: { buttonSliderCallback: this.onNextClick },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
validInput() {
|
||||||
|
return (
|
||||||
|
this.formData.name.length >= 3 &&
|
||||||
|
this.formData.password.length >= 1 &&
|
||||||
|
this.formData.password === this.formData.passwordConfirmation &&
|
||||||
|
this.termsAndConditionsConfirmed &&
|
||||||
|
this.dataPrivacy &&
|
||||||
|
this.minimumAge &&
|
||||||
|
this.noCommercial &&
|
||||||
|
this.noPolitical
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
termsAndConditionsConfirmed() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
dataPrivacy() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
minimumAge() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
noCommercial() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
noPolitical() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
sendValidation() {
|
||||||
|
const { name, about, password, passwordConfirmation } = this.formData
|
||||||
|
const {
|
||||||
|
termsAndConditionsConfirmed,
|
||||||
|
dataPrivacy,
|
||||||
|
minimumAge,
|
||||||
|
noCommercial,
|
||||||
|
noPolitical,
|
||||||
|
} = this
|
||||||
|
|
||||||
|
this.sliderData.setSliderValuesCallback(this.validInput, {
|
||||||
|
collectedInputData: {
|
||||||
|
name,
|
||||||
|
about,
|
||||||
|
password,
|
||||||
|
passwordConfirmation,
|
||||||
|
termsAndConditionsConfirmed,
|
||||||
|
dataPrivacy,
|
||||||
|
minimumAge,
|
||||||
|
noCommercial,
|
||||||
|
noPolitical,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async handleInput() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
async handleInputValid() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
async submit() {
|
||||||
|
const { name, password, about } = this.formData
|
||||||
|
const { email, nonce } = this.sliderData.collectedInputData
|
||||||
|
const termsAndConditionsAgreedVersion = VERSION
|
||||||
|
const locale = this.$i18n.locale()
|
||||||
|
try {
|
||||||
|
await this.$apollo.mutate({
|
||||||
|
mutation: SignupVerificationMutation,
|
||||||
|
variables: {
|
||||||
|
name,
|
||||||
|
password,
|
||||||
|
about,
|
||||||
|
email,
|
||||||
|
nonce,
|
||||||
|
termsAndConditionsAgreedVersion,
|
||||||
|
locale,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
this.response = 'success'
|
||||||
|
// Wolle setTimeout(() => {
|
||||||
|
// this.$emit('userCreated', {
|
||||||
|
// email,
|
||||||
|
// password,
|
||||||
|
// })
|
||||||
|
// }, 3000)
|
||||||
|
} catch (err) {
|
||||||
|
this.response = 'error'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onNextClick() {
|
||||||
|
this.submit()
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.password-strength {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
262
webapp/components/Registration/RegistrationItemEnterEmail.vue
Normal file
262
webapp/components/Registration/RegistrationItemEnterEmail.vue
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
<template>
|
||||||
|
<!-- Wolle <ds-space v-if="!data && !error" margin="large"> -->
|
||||||
|
<ds-form
|
||||||
|
v-model="formData"
|
||||||
|
:schema="formSchema"
|
||||||
|
@input="handleInput"
|
||||||
|
@input-valid="handleInputValid"
|
||||||
|
>
|
||||||
|
<!-- Wolle <h1>
|
||||||
|
{{
|
||||||
|
invitation
|
||||||
|
? $t('profile.invites.title', metadata)
|
||||||
|
: $t('components.registration.signup.title', metadata)
|
||||||
|
}}
|
||||||
|
</h1> -->
|
||||||
|
<ds-text
|
||||||
|
v-if="token"
|
||||||
|
v-html="$t('registration.signup.form.invitation-code', { code: token })"
|
||||||
|
/>
|
||||||
|
<ds-text>
|
||||||
|
{{
|
||||||
|
invitation
|
||||||
|
? $t('profile.invites.description')
|
||||||
|
: $t('components.registration.signup.form.description')
|
||||||
|
}}
|
||||||
|
</ds-text>
|
||||||
|
<ds-input
|
||||||
|
:placeholder="invitation ? $t('profile.invites.emailPlaceholder') : $t('login.email')"
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
model="email"
|
||||||
|
name="email"
|
||||||
|
icon="envelope"
|
||||||
|
/>
|
||||||
|
<slot></slot>
|
||||||
|
<ds-text v-if="sliderData.collectedInputData.emailSend">
|
||||||
|
<input id="checkbox" type="checkbox" v-model="sendEmailAgain" :checked="sendEmailAgain" />
|
||||||
|
<label for="checkbox0">
|
||||||
|
<!-- Wolle {{ $t('termsAndConditions.termsAndConditionsConfirmed') }} -->
|
||||||
|
{{ 'Send e-mail again' }}
|
||||||
|
</label>
|
||||||
|
</ds-text>
|
||||||
|
</ds-form>
|
||||||
|
<!-- Wolle </ds-space>
|
||||||
|
<div v-else margin="large">
|
||||||
|
<template v-if="!error">
|
||||||
|
<transition name="ds-transition-fade">
|
||||||
|
<sweetalert-icon icon="info" />
|
||||||
|
</transition>
|
||||||
|
<ds-text align="center" v-html="submitMessage" />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<transition name="ds-transition-fade">
|
||||||
|
<sweetalert-icon icon="error" />
|
||||||
|
</transition>
|
||||||
|
<ds-text align="center">{{ error.message }}</ds-text>
|
||||||
|
<ds-space centered class="space-top">
|
||||||
|
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
|
||||||
|
</ds-space>
|
||||||
|
</template>
|
||||||
|
</div> -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import metadata from '~/constants/metadata'
|
||||||
|
import { isEmail } from 'validator'
|
||||||
|
import normalizeEmail from '~/components/utils/NormalizeEmail'
|
||||||
|
// Wolle import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||||
|
|
||||||
|
export const SignupMutation = gql`
|
||||||
|
mutation($email: String!) {
|
||||||
|
Signup(email: $email) {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
export const SignupByInvitationMutation = gql`
|
||||||
|
mutation($email: String!, $token: String!) {
|
||||||
|
SignupByInvitation(email: $email, token: $token) {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
export default {
|
||||||
|
name: 'RegistrationItemEnterEmail',
|
||||||
|
components: {
|
||||||
|
// Wolle SweetalertIcon,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
sliderData: { type: Object, required: true },
|
||||||
|
token: { type: String, default: null }, // Wolle not used???
|
||||||
|
invitation: { type: Boolean, default: false },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
metadata,
|
||||||
|
formData: {
|
||||||
|
email: '',
|
||||||
|
},
|
||||||
|
formSchema: {
|
||||||
|
email: {
|
||||||
|
type: 'email',
|
||||||
|
required: true,
|
||||||
|
message: this.$t('common.validations.email'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// TODO: Our styleguide does not support checkmarks.
|
||||||
|
// Integrate termsAndConditionsConfirmed into `this.formData` once we
|
||||||
|
// have checkmarks available.
|
||||||
|
sendEmailAgain: false,
|
||||||
|
error: null, // Wolle
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
this.$nextTick(function () {
|
||||||
|
// Code that will run only after the entire view has been rendered
|
||||||
|
this.formData.email = this.sliderData.collectedInputData.email
|
||||||
|
? this.sliderData.collectedInputData.email
|
||||||
|
: ''
|
||||||
|
this.sendValidation()
|
||||||
|
|
||||||
|
this.sliderData.setSliderValuesCallback(this.validInput, {
|
||||||
|
sliderSettings: {
|
||||||
|
...this.buttonValues().sliderSettings,
|
||||||
|
buttonSliderCallback: this.onNextClick,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
sendEmailAgain() {
|
||||||
|
this.setButtonValues()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sliderIndex() {
|
||||||
|
return this.sliderData.sliderIndex // to have a shorter notation
|
||||||
|
},
|
||||||
|
// Wolle submitMessage() {
|
||||||
|
// const { email } = this.data.Signup
|
||||||
|
// return this.$t('components.registration.signup.form.success', { email })
|
||||||
|
// },
|
||||||
|
validInput() {
|
||||||
|
return isEmail(this.formData.email)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async sendValidation() {
|
||||||
|
if (this.formData.email && isEmail(this.formData.email)) {
|
||||||
|
this.formData.email = normalizeEmail(this.formData.email)
|
||||||
|
}
|
||||||
|
const { email } = this.formData
|
||||||
|
|
||||||
|
this.sliderData.setSliderValuesCallback(this.validInput, { collectedInputData: { email } })
|
||||||
|
},
|
||||||
|
async handleInput() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
async handleInputValid() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
buttonValues() {
|
||||||
|
return {
|
||||||
|
sliderSettings: {
|
||||||
|
buttonTitle: this.sliderData.collectedInputData.emailSend
|
||||||
|
? this.sendEmailAgain
|
||||||
|
? 'Resend e-mail'
|
||||||
|
: 'Skip resend'
|
||||||
|
: 'Send e-mail', // Wolle
|
||||||
|
buttonIcon: this.sliderData.collectedInputData.emailSend
|
||||||
|
? this.sendEmailAgain
|
||||||
|
? 'envelope'
|
||||||
|
: 'arrow-right'
|
||||||
|
: 'envelope', // Wolle
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setButtonValues() {
|
||||||
|
this.sliderData.setSliderValuesCallback(this.validInput, this.buttonValues())
|
||||||
|
},
|
||||||
|
async onNextClick() {
|
||||||
|
const mutation = this.token ? SignupByInvitationMutation : SignupMutation
|
||||||
|
const { token } = this
|
||||||
|
const { email } = this.formData
|
||||||
|
const variables = { email, token }
|
||||||
|
|
||||||
|
if (!this.sendEmailAgain && this.sliderData.collectedInputData.emailSend) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.sendEmailAgain ||
|
||||||
|
!this.sliderData.sliders[this.sliderIndex].data.request ||
|
||||||
|
(this.sliderData.sliders[this.sliderIndex].data.request &&
|
||||||
|
(!this.sliderData.sliders[this.sliderIndex].data.request.variables ||
|
||||||
|
(this.sliderData.sliders[this.sliderIndex].data.request.variables &&
|
||||||
|
!this.sliderData.sliders[this.sliderIndex].data.request.variables === variables)))
|
||||||
|
) {
|
||||||
|
this.sliderData.setSliderValuesCallback(
|
||||||
|
this.sliderData.sliders[this.sliderIndex].validated,
|
||||||
|
{ sliderData: { request: { variables }, response: null } },
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.$apollo.mutate({ mutation, variables }) // e-mail is send in emailMiddleware of backend
|
||||||
|
this.sliderData.setSliderValuesCallback(
|
||||||
|
this.sliderData.sliders[this.sliderIndex].validated,
|
||||||
|
{ sliderData: { response: response.data } },
|
||||||
|
)
|
||||||
|
|
||||||
|
if (this.sliderData.sliders[this.sliderIndex].data.response) {
|
||||||
|
this.sliderData.setSliderValuesCallback(this.validInput, {
|
||||||
|
collectedInputData: { emailSend: true },
|
||||||
|
})
|
||||||
|
this.setButtonValues()
|
||||||
|
|
||||||
|
const { email: respnseEmail } =
|
||||||
|
this.sliderData.sliders[this.sliderIndex].data.response.Signup ||
|
||||||
|
this.sliderData.sliders[this.sliderIndex].data.response.SignupByInvitation
|
||||||
|
this.$toast.success(
|
||||||
|
this.$t('components.registration.email.form.success', { email: respnseEmail }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} catch (err) {
|
||||||
|
this.sliderData.setSliderValuesCallback(
|
||||||
|
this.sliderData.sliders[this.sliderIndex].validated,
|
||||||
|
{ sliderData: { request: null, response: null } },
|
||||||
|
)
|
||||||
|
this.sliderData.setSliderValuesCallback(this.validInput, {
|
||||||
|
collectedInputData: { emailSend: false },
|
||||||
|
})
|
||||||
|
this.setButtonValues()
|
||||||
|
|
||||||
|
const { message } = err
|
||||||
|
const mapping = {
|
||||||
|
'A user account with this email already exists': 'email-exists',
|
||||||
|
'Invitation code already used or does not exist': 'invalid-invitation-token',
|
||||||
|
}
|
||||||
|
for (const [pattern, key] of Object.entries(mapping)) {
|
||||||
|
if (message.includes(pattern))
|
||||||
|
this.error = {
|
||||||
|
key,
|
||||||
|
message: this.$t(`components.registration.signup.form.errors.${key}`),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.error) {
|
||||||
|
this.$toast.error(message)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.space-top {
|
||||||
|
margin-top: 6ex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
145
webapp/components/Registration/RegistrationItemEnterInvite.vue
Normal file
145
webapp/components/Registration/RegistrationItemEnterInvite.vue
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<ds-form
|
||||||
|
class="enter-invite"
|
||||||
|
v-model="formData"
|
||||||
|
:schema="formSchema"
|
||||||
|
@input="handleInput"
|
||||||
|
@input-valid="handleInputValid"
|
||||||
|
>
|
||||||
|
<ds-input
|
||||||
|
:placeholder="$t('components.enter-invite.form.invite-code')"
|
||||||
|
model="inviteCode"
|
||||||
|
name="inviteCode"
|
||||||
|
id="inviteCode"
|
||||||
|
icon="question-circle"
|
||||||
|
/>
|
||||||
|
<ds-text>
|
||||||
|
{{ $t('components.enter-invite.form.description') }}
|
||||||
|
</ds-text>
|
||||||
|
<slot></slot>
|
||||||
|
</ds-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
|
export const isValidInviteCodeQuery = gql`
|
||||||
|
query($code: ID!) {
|
||||||
|
isValidInviteCode(code: $code)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
export default {
|
||||||
|
name: 'RegistrationItemEnterInvite',
|
||||||
|
props: {
|
||||||
|
sliderData: { type: Object, required: true },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
inviteCode: '',
|
||||||
|
},
|
||||||
|
formSchema: {
|
||||||
|
inviteCode: {
|
||||||
|
type: 'string',
|
||||||
|
// Wolle min: 6,
|
||||||
|
// max: 6,
|
||||||
|
required: true,
|
||||||
|
message: this.$t('components.enter-invite.form.validations.length'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
this.$nextTick(function () {
|
||||||
|
// Code that will run only after the entire view has been rendered
|
||||||
|
this.formData.inviteCode = this.sliderData.collectedInputData.inviteCode
|
||||||
|
? this.sliderData.collectedInputData.inviteCode
|
||||||
|
: ''
|
||||||
|
this.sendValidation()
|
||||||
|
|
||||||
|
this.sliderData.setSliderValuesCallback(this.validInput, {
|
||||||
|
sliderSettings: { buttonSliderCallback: this.onNextClick },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sliderIndex() {
|
||||||
|
return this.sliderData.sliderIndex // to have a shorter notation
|
||||||
|
},
|
||||||
|
validInput() {
|
||||||
|
return this.formData.inviteCode.length === 6
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async sendValidation() {
|
||||||
|
const { inviteCode } = this.formData
|
||||||
|
|
||||||
|
let dbValidated = false
|
||||||
|
if (this.validInput) {
|
||||||
|
await this.handleSubmitVerify()
|
||||||
|
dbValidated = this.sliderData.sliders[this.sliderIndex].data.response.isValidInviteCode
|
||||||
|
}
|
||||||
|
this.sliderData.setSliderValuesCallback(dbValidated, { collectedInputData: { inviteCode } })
|
||||||
|
},
|
||||||
|
async handleInput() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
async handleInputValid() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
async handleSubmitVerify() {
|
||||||
|
const { inviteCode } = this.formData
|
||||||
|
const variables = { code: inviteCode }
|
||||||
|
|
||||||
|
if (
|
||||||
|
!this.sliderData.sliders[this.sliderIndex].data.request ||
|
||||||
|
(this.sliderData.sliders[this.sliderIndex].data.request &&
|
||||||
|
(!this.sliderData.sliders[this.sliderIndex].data.request.variables ||
|
||||||
|
(this.sliderData.sliders[this.sliderIndex].data.request.variables &&
|
||||||
|
!this.sliderData.sliders[this.sliderIndex].data.request.variables === variables)))
|
||||||
|
) {
|
||||||
|
this.sliderData.setSliderValuesCallback(
|
||||||
|
this.sliderData.sliders[this.sliderIndex].validated,
|
||||||
|
{ sliderData: { request: { variables }, response: null } },
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.$apollo.query({ query: isValidInviteCodeQuery, variables })
|
||||||
|
this.sliderData.setSliderValuesCallback(
|
||||||
|
this.sliderData.sliders[this.sliderIndex].validated,
|
||||||
|
{ sliderData: { response: response.data } },
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.sliderData.sliders[this.sliderIndex].data.response &&
|
||||||
|
this.sliderData.sliders[this.sliderIndex].data.response.isValidInviteCode
|
||||||
|
) {
|
||||||
|
this.$toast.success(
|
||||||
|
this.$t('components.registration.invite-code.form.success', { inviteCode }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.sliderData.setSliderValuesCallback(
|
||||||
|
this.sliderData.sliders[this.sliderIndex].validated,
|
||||||
|
{ sliderData: { response: { isValidInviteCode: false } } },
|
||||||
|
)
|
||||||
|
|
||||||
|
const { message } = err
|
||||||
|
this.$toast.error(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onNextClick() {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.enter-invite {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: $space-large 0 $space-xxx-small 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
107
webapp/components/Registration/RegistrationItemEnterNonce.vue
Normal file
107
webapp/components/Registration/RegistrationItemEnterNonce.vue
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<ds-form
|
||||||
|
class="enter-nonce"
|
||||||
|
v-model="formData"
|
||||||
|
:schema="formSchema"
|
||||||
|
@submit="handleSubmitVerify"
|
||||||
|
@input="handleInput"
|
||||||
|
@input-valid="handleInputValid"
|
||||||
|
>
|
||||||
|
<ds-text>
|
||||||
|
<!-- Wolle {{ $t('components.enter-nonce.form.description') }} -->
|
||||||
|
Your e-mail address:
|
||||||
|
<b>{{ this.sliderData.collectedInputData.email }}</b>
|
||||||
|
</ds-text>
|
||||||
|
<ds-input
|
||||||
|
:placeholder="$t('components.enter-nonce.form.nonce')"
|
||||||
|
model="nonce"
|
||||||
|
name="nonce"
|
||||||
|
id="nonce"
|
||||||
|
icon="question-circle"
|
||||||
|
/>
|
||||||
|
<ds-text>
|
||||||
|
{{ $t('components.enter-nonce.form.description') }}
|
||||||
|
</ds-text>
|
||||||
|
<slot></slot>
|
||||||
|
</ds-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'RegistrationItemEnterNonce',
|
||||||
|
props: {
|
||||||
|
sliderData: { type: Object, required: true },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
nonce: '',
|
||||||
|
},
|
||||||
|
formSchema: {
|
||||||
|
nonce: {
|
||||||
|
type: 'string',
|
||||||
|
// Wolle min: 6,
|
||||||
|
// max: 6,
|
||||||
|
required: true,
|
||||||
|
message: this.$t('components.enter-nonce.form.validations.length'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
this.$nextTick(function () {
|
||||||
|
// Code that will run only after the entire view has been rendered
|
||||||
|
// console.log('mounted !!! ')
|
||||||
|
this.formData.nonce = this.sliderData.collectedInputData.nonce
|
||||||
|
? this.sliderData.collectedInputData.nonce
|
||||||
|
: ''
|
||||||
|
this.sendValidation()
|
||||||
|
|
||||||
|
this.sliderData.setSliderValuesCallback(this.validInput, {
|
||||||
|
sliderSettings: { buttonSliderCallback: this.onNextClick },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
validInput() {
|
||||||
|
return this.formData.nonce.length === 6
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
sendValidation() {
|
||||||
|
const { nonce } = this.formData
|
||||||
|
|
||||||
|
// Wolle shall the nonce be validated in the database?
|
||||||
|
// let dbValidated = false
|
||||||
|
// if (this.validInput) {
|
||||||
|
// await this.handleSubmitVerify()
|
||||||
|
// dbValidated = this.sliderData.sliders[this.sliderIndex].data.response.isValidInviteCode
|
||||||
|
// }
|
||||||
|
// this.sliderData.setSliderValuesCallback(dbValidated, {
|
||||||
|
this.sliderData.setSliderValuesCallback(this.validInput, {
|
||||||
|
collectedInputData: {
|
||||||
|
nonce,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async handleInput() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
async handleInputValid() {
|
||||||
|
this.sendValidation()
|
||||||
|
},
|
||||||
|
handleSubmitVerify() {},
|
||||||
|
onNextClick() {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.enter-nonce {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: $space-large 0 $space-xxx-small 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
160
webapp/components/Registration/RegistrationSlider.story.js
Normal file
160
webapp/components/Registration/RegistrationSlider.story.js
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
import { storiesOf } from '@storybook/vue'
|
||||||
|
import { withA11y } from '@storybook/addon-a11y'
|
||||||
|
import RegistrationSlider from './RegistrationSlider.vue'
|
||||||
|
import helpers from '~/storybook/helpers'
|
||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
|
const plugins = [
|
||||||
|
(app = {}) => {
|
||||||
|
app.$apollo = {
|
||||||
|
mutate: (data) => {
|
||||||
|
if (JSON.stringify(data).includes('UpdateUser')) {
|
||||||
|
return { data: { UpdateUser: { id: data.variables.id, locale: data.variables.locale } } }
|
||||||
|
}
|
||||||
|
if (JSON.stringify(data).includes('Signup')) {
|
||||||
|
return { data: { Signup: { email: data.variables.email } } }
|
||||||
|
}
|
||||||
|
if (JSON.stringify(data).includes('SignupByInvitation')) {
|
||||||
|
return { data: { SignupByInvitation: { email: data.variables.email } } }
|
||||||
|
}
|
||||||
|
if (JSON.stringify(data).includes('SignupVerification')) {
|
||||||
|
return { data: { SignupByInvitation: { ...data.variables } } }
|
||||||
|
}
|
||||||
|
throw new Error(`Mutation name not found!`)
|
||||||
|
},
|
||||||
|
query: (data) => {
|
||||||
|
if (JSON.stringify(data).includes('isValidInviteCode')) {
|
||||||
|
return { data: { isValidInviteCode: true } }
|
||||||
|
}
|
||||||
|
throw new Error(`Query name not found!`)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Vue.prototype.$apollo = app.$apollo
|
||||||
|
return app
|
||||||
|
},
|
||||||
|
]
|
||||||
|
helpers.init({ plugins })
|
||||||
|
|
||||||
|
storiesOf('RegistrationSlider', module)
|
||||||
|
.addDecorator(withA11y)
|
||||||
|
.addDecorator(helpers.layout)
|
||||||
|
.add('invite-code empty', () => ({
|
||||||
|
components: { RegistrationSlider },
|
||||||
|
store: helpers.store,
|
||||||
|
data: () => ({}),
|
||||||
|
template: `
|
||||||
|
<registration-slider registrationType="invite-code" />
|
||||||
|
`,
|
||||||
|
}))
|
||||||
|
.add('invite-code with data', () => ({
|
||||||
|
components: { RegistrationSlider },
|
||||||
|
store: helpers.store,
|
||||||
|
data: () => ({
|
||||||
|
overwriteSliderData: {
|
||||||
|
collectedInputData: {
|
||||||
|
inviteCode: 'IN1T6Y',
|
||||||
|
email: 'wolle.huss@pjannto.com',
|
||||||
|
emailSend: false,
|
||||||
|
nonce: 'NTRSCZ',
|
||||||
|
name: 'Wolle',
|
||||||
|
password: 'Hello',
|
||||||
|
passwordConfirmation: 'Hello',
|
||||||
|
about: `Hey`,
|
||||||
|
termsAndConditionsConfirmed: true,
|
||||||
|
dataPrivacy: true,
|
||||||
|
minimumAge: true,
|
||||||
|
noCommercial: true,
|
||||||
|
noPolitical: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
template: `
|
||||||
|
<registration-slider registrationType="invite-code" :overwriteSliderData="overwriteSliderData" />
|
||||||
|
`,
|
||||||
|
}))
|
||||||
|
.add('public-registration empty', () => ({
|
||||||
|
components: { RegistrationSlider },
|
||||||
|
store: helpers.store,
|
||||||
|
data: () => ({}),
|
||||||
|
template: `
|
||||||
|
<registration-slider registrationType="public-registration" />
|
||||||
|
`,
|
||||||
|
}))
|
||||||
|
.add('public-registration with data', () => ({
|
||||||
|
components: { RegistrationSlider },
|
||||||
|
store: helpers.store,
|
||||||
|
data: () => ({
|
||||||
|
overwriteSliderData: {
|
||||||
|
collectedInputData: {
|
||||||
|
inviteCode: null,
|
||||||
|
email: 'wolle.huss@pjannto.com',
|
||||||
|
emailSend: false,
|
||||||
|
nonce: 'NTRSCZ',
|
||||||
|
name: 'Wolle',
|
||||||
|
password: 'Hello',
|
||||||
|
passwordConfirmation: 'Hello',
|
||||||
|
about: `Hey`,
|
||||||
|
termsAndConditionsConfirmed: true,
|
||||||
|
dataPrivacy: true,
|
||||||
|
minimumAge: true,
|
||||||
|
noCommercial: true,
|
||||||
|
noPolitical: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
template: `
|
||||||
|
<registration-slider registrationType="public-registration" :overwriteSliderData="overwriteSliderData" />
|
||||||
|
`,
|
||||||
|
}))
|
||||||
|
.add('invite-mail empty', () => ({
|
||||||
|
components: { RegistrationSlider },
|
||||||
|
store: helpers.store,
|
||||||
|
data: () => ({
|
||||||
|
overwriteSliderData: {
|
||||||
|
collectedInputData: {
|
||||||
|
inviteCode: null,
|
||||||
|
email: 'wolle.huss@pjannto.com',
|
||||||
|
emailSend: true,
|
||||||
|
nonce: null,
|
||||||
|
name: null,
|
||||||
|
password: null,
|
||||||
|
passwordConfirmation: null,
|
||||||
|
about: null,
|
||||||
|
termsAndConditionsConfirmed: null,
|
||||||
|
dataPrivacy: null,
|
||||||
|
minimumAge: null,
|
||||||
|
noCommercial: null,
|
||||||
|
noPolitical: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
template: `
|
||||||
|
<registration-slider registrationType="invite-mail" :overwriteSliderData="overwriteSliderData" />
|
||||||
|
`,
|
||||||
|
}))
|
||||||
|
.add('invite-mail with data', () => ({
|
||||||
|
components: { RegistrationSlider },
|
||||||
|
store: helpers.store,
|
||||||
|
data: () => ({
|
||||||
|
overwriteSliderData: {
|
||||||
|
collectedInputData: {
|
||||||
|
inviteCode: null,
|
||||||
|
email: 'wolle.huss@pjannto.com',
|
||||||
|
emailSend: true,
|
||||||
|
nonce: 'NTRSCZ',
|
||||||
|
name: 'Wolle',
|
||||||
|
password: 'Hello',
|
||||||
|
passwordConfirmation: 'Hello',
|
||||||
|
about: `Hey`,
|
||||||
|
termsAndConditionsConfirmed: true,
|
||||||
|
dataPrivacy: true,
|
||||||
|
minimumAge: true,
|
||||||
|
noCommercial: true,
|
||||||
|
noPolitical: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
template: `
|
||||||
|
<registration-slider registrationType="invite-mail" :overwriteSliderData="overwriteSliderData" />
|
||||||
|
`,
|
||||||
|
}))
|
||||||
235
webapp/components/Registration/RegistrationSlider.vue
Normal file
235
webapp/components/Registration/RegistrationSlider.vue
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<section class="login-form">
|
||||||
|
<base-card>
|
||||||
|
<template #imageColumn>
|
||||||
|
<a :href="links.ORGANIZATION" :title="$t('login.moreInfo', metadata)" target="_blank">
|
||||||
|
<img class="image" alt="Welcome" src="/img/custom/welcome.svg" />
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<component-slider :sliderData="sliderData">
|
||||||
|
<template #header>
|
||||||
|
<ds-heading size="h2">
|
||||||
|
{{ $t('components.registration.signup.title', metadata) }}
|
||||||
|
</ds-heading>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-if="['invite-code'].includes(registrationType)" #enter-invite>
|
||||||
|
<registration-item-enter-invite :sliderData="sliderData" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template
|
||||||
|
v-if="['invite-code', 'public-registration'].includes(registrationType)"
|
||||||
|
#enter-email
|
||||||
|
>
|
||||||
|
<!-- Wolle !!! may create same source with 'webapp/pages/registration/signup.vue' -->
|
||||||
|
<!-- <signup v-if="publicRegistration" :invitation="false" @submit="handleSubmitted"> -->
|
||||||
|
<registration-item-enter-email :sliderData="sliderData" :invitation="false" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template
|
||||||
|
v-if="['invite-code', 'public-registration', 'invite-mail'].includes(registrationType)"
|
||||||
|
#enter-nonce
|
||||||
|
>
|
||||||
|
<registration-item-enter-nonce :sliderData="sliderData" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #create-user-account>
|
||||||
|
<registration-item-create-user-account :sliderData="sliderData" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<ds-space margin-bottom="xxx-small" margin-top="small" centered>
|
||||||
|
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
|
||||||
|
</ds-space>
|
||||||
|
</template>
|
||||||
|
</component-slider>
|
||||||
|
|
||||||
|
<template #topMenu>
|
||||||
|
<locale-switch offset="5" />
|
||||||
|
</template>
|
||||||
|
</base-card>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import links from '~/constants/links.js'
|
||||||
|
import metadata from '~/constants/metadata.js'
|
||||||
|
import ComponentSlider from '~/components/ComponentSlider/ComponentSlider'
|
||||||
|
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
||||||
|
import RegistrationItemCreateUserAccount from './RegistrationItemCreateUserAccount'
|
||||||
|
import RegistrationItemEnterEmail from '~/components/Registration/RegistrationItemEnterEmail'
|
||||||
|
import RegistrationItemEnterInvite from './RegistrationItemEnterInvite'
|
||||||
|
import RegistrationItemEnterNonce from './RegistrationItemEnterNonce'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RegistrationSlider',
|
||||||
|
components: {
|
||||||
|
ComponentSlider,
|
||||||
|
LocaleSwitch,
|
||||||
|
RegistrationItemCreateUserAccount,
|
||||||
|
RegistrationItemEnterEmail,
|
||||||
|
RegistrationItemEnterInvite,
|
||||||
|
RegistrationItemEnterNonce,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
registrationType: { type: String, required: true },
|
||||||
|
overwriteSliderData: { type: Object, default: () => {} },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const slidersPortfolio = [
|
||||||
|
{
|
||||||
|
name: 'enter-invite',
|
||||||
|
// title: this.$t('components.registration.create-user-account.title'),
|
||||||
|
title: 'Invitation', // Wolle
|
||||||
|
validated: false,
|
||||||
|
data: { request: null, response: { isValidInviteCode: false } },
|
||||||
|
button: {
|
||||||
|
title: 'Next', // Wolle
|
||||||
|
icon: 'arrow-right',
|
||||||
|
callback: this.buttonCallback,
|
||||||
|
sliderCallback: null, // optional set by slot
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'enter-email',
|
||||||
|
title: 'E-Mail', // Wolle
|
||||||
|
validated: false,
|
||||||
|
data: { request: null, response: null },
|
||||||
|
button: {
|
||||||
|
title: '', // set by slider component
|
||||||
|
icon: '', // set by slider component
|
||||||
|
callback: this.buttonCallback,
|
||||||
|
sliderCallback: null, // optional set by slot
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'enter-nonce',
|
||||||
|
title: 'E-Mail Confirmation', // Wolle
|
||||||
|
validated: false,
|
||||||
|
data: { request: null, response: null },
|
||||||
|
button: {
|
||||||
|
title: 'Confirm', // Wolle
|
||||||
|
icon: 'arrow-right',
|
||||||
|
callback: this.buttonCallback,
|
||||||
|
sliderCallback: null, // optional set by slot
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'create-user-account',
|
||||||
|
title: this.$t('components.registration.create-user-account.title'),
|
||||||
|
validated: false,
|
||||||
|
data: { request: null, response: null },
|
||||||
|
button: {
|
||||||
|
// title: this.$t('actions.save'), // Wolle
|
||||||
|
title: 'Create', // Wolle
|
||||||
|
icon: 'check',
|
||||||
|
callback: this.buttonCallback,
|
||||||
|
sliderCallback: null, // optional set by slot
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
let sliders = []
|
||||||
|
switch (this.registrationType) {
|
||||||
|
case 'invite-code':
|
||||||
|
sliders = [
|
||||||
|
slidersPortfolio[0],
|
||||||
|
slidersPortfolio[1],
|
||||||
|
slidersPortfolio[2],
|
||||||
|
slidersPortfolio[3],
|
||||||
|
]
|
||||||
|
break
|
||||||
|
case 'public-registration':
|
||||||
|
sliders = [slidersPortfolio[1], slidersPortfolio[2], slidersPortfolio[3]]
|
||||||
|
break
|
||||||
|
case 'invite-mail':
|
||||||
|
sliders = [slidersPortfolio[2], slidersPortfolio[3]]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
links,
|
||||||
|
metadata,
|
||||||
|
sliderData: {
|
||||||
|
collectedInputData: {
|
||||||
|
inviteCode: null,
|
||||||
|
email: null,
|
||||||
|
emailSend: null,
|
||||||
|
nonce: null,
|
||||||
|
name: null,
|
||||||
|
password: null,
|
||||||
|
passwordConfirmation: null,
|
||||||
|
about: null,
|
||||||
|
termsAndConditionsConfirmed: null,
|
||||||
|
dataPrivacy: null,
|
||||||
|
minimumAge: null,
|
||||||
|
noCommercial: null,
|
||||||
|
noPolitical: null,
|
||||||
|
},
|
||||||
|
sliderIndex: 0,
|
||||||
|
sliders: sliders,
|
||||||
|
sliderSelectorCallback: this.sliderSelectorCallback,
|
||||||
|
setSliderValuesCallback: this.setSliderValuesCallback,
|
||||||
|
...this.overwriteSliderData,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sliderIndex() {
|
||||||
|
return this.sliderData.sliderIndex // to have a shorter notation
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setSliderValuesCallback(isValid, { collectedInputData, sliderData, sliderSettings }) {
|
||||||
|
// all changes of 'this.sliders' has to be filled in from the top to be spread to the component slider and all slider components in the slot
|
||||||
|
|
||||||
|
this.sliderData.sliders[this.sliderIndex].validated = isValid
|
||||||
|
|
||||||
|
if (collectedInputData) {
|
||||||
|
this.sliderData.collectedInputData = {
|
||||||
|
...this.sliderData.collectedInputData,
|
||||||
|
...collectedInputData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sliderData) {
|
||||||
|
if (this.sliderData.sliders[this.sliderIndex].data) {
|
||||||
|
this.sliderData.sliders[this.sliderIndex].data = {
|
||||||
|
request: sliderData.request
|
||||||
|
? sliderData.request
|
||||||
|
: this.sliderData.sliders[this.sliderIndex].data.request,
|
||||||
|
response: sliderData.response
|
||||||
|
? sliderData.response
|
||||||
|
: this.sliderData.sliders[this.sliderIndex].data.response,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sliderSettings) {
|
||||||
|
const { buttonTitle, buttonIcon, buttonSliderCallback } = sliderSettings
|
||||||
|
if (buttonTitle) {
|
||||||
|
this.sliderData.sliders[this.sliderIndex].button.title = buttonTitle
|
||||||
|
}
|
||||||
|
if (buttonIcon) {
|
||||||
|
this.sliderData.sliders[this.sliderIndex].button.icon = buttonIcon
|
||||||
|
}
|
||||||
|
if (buttonSliderCallback) {
|
||||||
|
this.sliderData.sliders[this.sliderIndex].button.sliderCallback = buttonSliderCallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sliderSelectorCallback(selectedIndex) {
|
||||||
|
// all changes of 'this.sliders' has to be filled in from the top to be spread to the component slider and all slider components in the slot
|
||||||
|
|
||||||
|
if (selectedIndex <= this.sliderIndex + 1 && selectedIndex < this.sliderData.sliders.length) {
|
||||||
|
this.sliderData.sliderIndex = selectedIndex
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buttonCallback(success) {
|
||||||
|
// all changes of 'this.sliders' has to be filled in from the top to be spread to the component slider and all slider components in the slot
|
||||||
|
|
||||||
|
return success
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss"></style>
|
||||||
@ -15,7 +15,7 @@ export default function PasswordForm({ translate }) {
|
|||||||
},
|
},
|
||||||
passwordConfirmation: [
|
passwordConfirmation: [
|
||||||
{
|
{
|
||||||
validator(rule, value, callback, source, options) {
|
validator(_rule, value, callback, source, _options) {
|
||||||
var errors = []
|
var errors = []
|
||||||
if (source.password !== value) {
|
if (source.password !== value) {
|
||||||
errors.push(new Error(passwordMismatchMessage))
|
errors.push(new Error(passwordMismatchMessage))
|
||||||
|
|||||||
@ -120,6 +120,16 @@
|
|||||||
"versus": "Versus"
|
"versus": "Versus"
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
|
"enter-invite": {
|
||||||
|
"form": {
|
||||||
|
"description": "Gib den Einladungs-Code ein, den du bekommen hast.",
|
||||||
|
"invite-code": "Einladungs-Code eingeben",
|
||||||
|
"next": "Weiter",
|
||||||
|
"validations": {
|
||||||
|
"length": "muss genau 6 Buchstaben lang sein"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"enter-nonce": {
|
"enter-nonce": {
|
||||||
"form": {
|
"form": {
|
||||||
"description": "Öffne Dein E-Mail Postfach und gib den Code ein, den wir geschickt haben.",
|
"description": "Öffne Dein E-Mail Postfach und gib den Code ein, den wir geschickt haben.",
|
||||||
@ -152,9 +162,19 @@
|
|||||||
"success": "Dein Benutzerkonto wurde erstellt!",
|
"success": "Dein Benutzerkonto wurde erstellt!",
|
||||||
"title": "Benutzerkonto anlegen"
|
"title": "Benutzerkonto anlegen"
|
||||||
},
|
},
|
||||||
|
"email": {
|
||||||
|
"form": {
|
||||||
|
"success": "Verifikations-E-Mail gesendet an <b>{email}</b>!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"invite-code": {
|
||||||
|
"form": {
|
||||||
|
"success": "Gültiger Einladungs-Code <b>{inviteCode}</b>!"
|
||||||
|
}
|
||||||
|
},
|
||||||
"signup": {
|
"signup": {
|
||||||
"form": {
|
"form": {
|
||||||
"data-privacy": "Ich habe die <a href=\"/data-privacy\" target=\"_blank\"><ds-text bold color=\"primary\">Datenschutzerklärung</ds-text></a> gelesen und verstanden",
|
"data-privacy": "Ich habe die Datenschutzerklärung gelesen und verstanden.",
|
||||||
"description": "Um loszulegen, kannst Du Dich hier kostenfrei registrieren:",
|
"description": "Um loszulegen, kannst Du Dich hier kostenfrei registrieren:",
|
||||||
"errors": {
|
"errors": {
|
||||||
"email-exists": "Es gibt schon ein Benutzerkonto mit dieser E-Mail-Adresse!",
|
"email-exists": "Es gibt schon ein Benutzerkonto mit dieser E-Mail-Adresse!",
|
||||||
@ -733,7 +753,7 @@
|
|||||||
"bank": "Bankverbindung",
|
"bank": "Bankverbindung",
|
||||||
"code-of-conduct": "Verhaltenscodex",
|
"code-of-conduct": "Verhaltenscodex",
|
||||||
"contact": "Kontakt",
|
"contact": "Kontakt",
|
||||||
"data-privacy": "Datenschutz",
|
"data-privacy": "Datenschutzerklärung",
|
||||||
"director": "Geschäftsführer",
|
"director": "Geschäftsführer",
|
||||||
"error-occurred": "Ein Fehler ist aufgetreten.",
|
"error-occurred": "Ein Fehler ist aufgetreten.",
|
||||||
"faq": "FAQ",
|
"faq": "FAQ",
|
||||||
|
|||||||
@ -120,6 +120,16 @@
|
|||||||
"versus": "Versus"
|
"versus": "Versus"
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
|
"enter-invite": {
|
||||||
|
"form": {
|
||||||
|
"description": "Enter the invitation code you received.",
|
||||||
|
"invite-code": "Enter your invite code",
|
||||||
|
"next": "Continue",
|
||||||
|
"validations": {
|
||||||
|
"length": "must be 6 characters long"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"enter-nonce": {
|
"enter-nonce": {
|
||||||
"form": {
|
"form": {
|
||||||
"description": "Open your inbox and enter the code that we've sent to you.",
|
"description": "Open your inbox and enter the code that we've sent to you.",
|
||||||
@ -152,9 +162,19 @@
|
|||||||
"success": "Your account has been created!",
|
"success": "Your account has been created!",
|
||||||
"title": "Create user account"
|
"title": "Create user account"
|
||||||
},
|
},
|
||||||
|
"email": {
|
||||||
|
"form": {
|
||||||
|
"success": "Verification e-mail send to <b>{email}</b>!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"invite-code": {
|
||||||
|
"form": {
|
||||||
|
"success": "Valid invite code <b>{inviteCode}</b>!"
|
||||||
|
}
|
||||||
|
},
|
||||||
"signup": {
|
"signup": {
|
||||||
"form": {
|
"form": {
|
||||||
"data-privacy": "I have read and understood the <a href=\"/data-privacy\" target=\"_blank\"><ds-text bold color=\"primary\">Privacy Statement</ds-text></a>.",
|
"data-privacy": "I have read and understood the privacy statement.",
|
||||||
"description": "To get started, you can register here for free:",
|
"description": "To get started, you can register here for free:",
|
||||||
"errors": {
|
"errors": {
|
||||||
"email-exists": "There is already a user account with this e-mail address!",
|
"email-exists": "There is already a user account with this e-mail address!",
|
||||||
@ -762,7 +782,7 @@
|
|||||||
"termsAndConditions": {
|
"termsAndConditions": {
|
||||||
"agree": "I agree!",
|
"agree": "I agree!",
|
||||||
"newTermsAndConditions": "New Terms and Conditions",
|
"newTermsAndConditions": "New Terms and Conditions",
|
||||||
"termsAndConditionsConfirmed": "I have read and confirmed the Terms and Conditions.",
|
"termsAndConditionsConfirmed": "I have read and confirmed the terms and conditions.",
|
||||||
"termsAndConditionsNewConfirm": "I have read and agree to the new terms of conditions.",
|
"termsAndConditionsNewConfirm": "I have read and agree to the new terms of conditions.",
|
||||||
"termsAndConditionsNewConfirmText": "Please read the new terms of use now!"
|
"termsAndConditionsNewConfirmText": "Please read the new terms of use now!"
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user