Implement registration slider e-mail, nonce, and storybook with preset data

This commit is contained in:
Wolfgang Huß 2021-02-09 13:31:59 +01:00
parent e3d3f988ce
commit 9086f99862
6 changed files with 457 additions and 172 deletions

View File

@ -29,10 +29,11 @@
</ds-heading>
</ds-space> -->
<ds-form class="create-user-account"
<ds-form
class="create-user-account"
v-model="formData"
:schema="formSchema"
@submit="submit"
@submit="submit"
@input="handleInput"
@input-valid="handleInputValid"
>
@ -226,6 +227,7 @@ export default {
this.noPolitical = this.sliderData.collectedInputData.noPolitical
? this.sliderData.collectedInputData.noPolitical
: false
this.sendValidation()
})
},
computed: {
@ -284,7 +286,7 @@ export default {
noCommercial,
noPolitical,
} = this
// const locale = this.$i18n.locale()
const locale = this.$i18n.locale()
const value = {
name,
about,
@ -298,7 +300,7 @@ export default {
minimumAge,
noCommercial,
noPolitical,
// locale,
locale,
}
console.log('sendValidation !!!', ' this.valid: ', this.valid, ' value: ', value)
this.sliderData.validateCallback(this.valid, value)

View File

@ -0,0 +1,197 @@
<template>
<ds-space v-if="!data && !error" margin="large">
<ds-form
v-model="formData"
:schema="formSchema"
@input="handleInput"
@input-valid="handleInputValid"
@submit="handleSubmit"
>
<!-- Wolle <h1>
{{
invitation
? $t('profile.invites.title', metadata)
: $t('components.registration.signup.title', metadata)
}}
</h1> -->
<ds-space v-if="token" margin-botton="large">
<ds-text v-html="$t('registration.signup.form.invitation-code', { code: token })" />
</ds-space>
<ds-space margin-botton="large">
<ds-text>
{{
invitation
? $t('profile.invites.description')
: $t('components.registration.signup.form.description')
}}
</ds-text>
</ds-space>
<ds-input
:placeholder="invitation ? $t('profile.invites.emailPlaceholder') : $t('login.email')"
type="email"
id="email"
model="email"
name="email"
icon="envelope"
/>
<!-- <base-button
:disabled="disabled"
:loading="$apollo.loading"
filled
name="submit"
type="submit"
icon="envelope"
>
{{ $t('components.registration.signup.form.submit') }}
</base-button> -->
<slot></slot>
</ds-form>
</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 { 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: {
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'),
},
// email: [
// { type: 'email', required: true },
// {
// validator(rule, value, callback, source, options) {
// const errors = []
// if (currentEmail === normalizeEmail(value)) {
// errors.push(this.$t('common.validations.email'))
// }
// return errors
// },
// },
// ],
},
// disabled: true,
data: null,
error: null,
}
},
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the entire view has been rendered
// console.log('mounted !!! ')
this.formData.email = this.sliderData.collectedInputData.email
? this.sliderData.collectedInputData.email
: ''
})
},
computed: {
submitMessage() {
const { email } = this.data.Signup
return this.$t('components.registration.signup.form.success', { email })
},
// valid() {
// const isValid =
// this.formData.email.XXX === XXX
// return isValid
// },
},
methods: {
handleInput() {
// this.disabled = true
this.sliderData.validateCallback(false)
},
handleInputValid() {
// this.disabemailled = false
const { email } = this.formData
// validate in backend?
// toaster?
console.log('sendValidation !!! email: ', email)
this.sliderData.validateCallback(true, { email })
},
async handleSubmit() {
const mutation = this.token ? SignupByInvitationMutation : SignupMutation
const { token } = this
const { email } = this.formData
try {
const response = await this.$apollo.mutate({ mutation, variables: { email, token } })
this.data = response.data
setTimeout(() => {
this.$emit('submit', { email: this.data.Signup.email })
}, 3000)
} catch (err) {
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)
}
}
},
},
}
</script>
<style>
.space-top {
margin-top: 6ex;
}
</style>

View File

@ -3,9 +3,9 @@
class="enter-invite"
v-model="formData"
:schema="formSchema"
@submit="handleSubmitVerify"
@input="handleInput"
@input-valid="handleInputValid"
@submit="handleSubmitVerify"
>
<ds-input
:placeholder="$t('components.enter-invite.form.invite-code')"
@ -46,23 +46,41 @@ export default {
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the entire view has been rendered
// console.log('mounted !!! ')
console.log('mounted !!! this.sliderData.collectedInputData.inviteCode: ', this.sliderData.collectedInputData.inviteCode)
this.formData.inviteCode = this.sliderData.collectedInputData.inviteCode
? this.sliderData.collectedInputData.inviteCode
: ''
this.sendValidation()
})
},
computed: {
valid() {
const isValid =
this.formData.inviteCode.length === 6
return isValid
},
},
methods: {
sendValidation() {
const { inviteCode } = this.formData
const value = {
inviteCode,
}
console.log('sendValidation !!! value: ', value)
this.sliderData.validateCallback(this.valid, value)
},
async handleInput() {
// Wolle console.log('handleInput !!!')
this.sliderData.validateCallback(false)
// this.sliderData.validateCallback(false)
this.sendValidation()
},
async handleInputValid() {
// Wolle console.log('handleInputValid !!!')
const { inviteCode } = this.formData
// const { inviteCode } = this.formData
// validate in backend
// toaster
this.sliderData.validateCallback(true, { /* email, */ inviteCode })
// this.sliderData.validateCallback(true, { /* email, */ inviteCode })
this.sendValidation()
},
handleSubmitVerify() {
// Wolle const { nonce } = this.formData

View File

@ -0,0 +1,86 @@
<template>
<ds-form
class="enter-nonce"
v-model="formData"
:schema="formSchema"
@submit="handleSubmitVerify"
@input="handleInput"
@input-valid="handleInputValid"
>
<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>
<!-- <base-button :disabled="disabled" filled name="submit" type="submit">
{{ $t('components.enter-nonce.form.next') }}
</base-button> -->
<slot></slot>
</ds-form>
</template>
<script>
export default {
name: 'RegistrationItemEnterNonce',
props: {
sliderData: { type: Object, required: true },
},
data() {
return {
formData: {
nonce: '',
},
formSchema: {
nonce: {
type: 'string',
min: 6,
max: 6,
required: true,
message: this.$t('components.enter-nonce.form.validations.length'),
},
},
// disabled: true,
}
},
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
: ''
})
},
methods: {
async handleInput() {
// this.disabled = true
this.sliderData.validateCallback(false)
},
async handleInputValid() {
// this.disabled = false
const { nonce } = this.formData
// validate in backend?
// toaster?
this.sliderData.validateCallback(true, { nonce })
},
handleSubmitVerify() {
// const { nonce } = this.formData
// const email = this.email
// this.$emit('nonceEntered', { email, nonce })
},
},
}
</script>
<style lang="scss">
.enter-nonce {
display: flex;
flex-direction: column;
margin: $space-large 0 $space-xxx-small 0;
}
</style>

View File

@ -5,123 +5,63 @@ import helpers from '~/storybook/helpers'
helpers.init()
// Wolle export const searchResults = [
// {
// id: 'post-by-jenny',
// __typename: 'Post',
// slug: 'user-post-by-jenny',
// title: 'User Post by Jenny',
// value: 'User Post by Jenny',
// shoutedCount: 0,
// commentsCount: 4,
// createdAt: '2019-11-13T03:03:16.155Z',
// author: {
// id: 'u3',
// name: 'Jenny Rostock',
// slug: 'jenny-rostock',
// },
// },
// {
// id: 'f48f00a0-c412-432f-8334-4276a4e15d1c',
// __typename: 'Post',
// slug: 'eum-quos-est-molestiae-enim-magni-consequuntur-sed-commodi-eos',
// title: 'Eum quos est molestiae enim magni consequuntur sed commodi eos.',
// value: 'Eum quos est molestiae enim magni consequuntur sed commodi eos.',
// shoutedCount: 0,
// commentsCount: 0,
// createdAt: '2019-11-13T03:00:45.478Z',
// author: {
// id: 'u6',
// name: 'Louie',
// slug: 'louie',
// },
// },
// {
// id: 'p7',
// __typename: 'Post',
// slug: 'this-is-post-7',
// title: 'This is post #7',
// value: 'This is post #7',
// shoutedCount: 1,
// commentsCount: 1,
// createdAt: '2019-11-13T03:00:23.098Z',
// author: {
// id: 'u6',
// name: 'Louie',
// slug: 'louie',
// },
// },
// {
// id: 'p12',
// __typename: 'Post',
// slug: 'this-is-post-12',
// title: 'This is post #12',
// value: 'This is post #12',
// shoutedCount: 0,
// commentsCount: 12,
// createdAt: '2019-11-13T03:00:23.098Z',
// author: {
// id: 'u6',
// name: 'Louie',
// slug: 'louie',
// },
// },
// {
// id: 'u1',
// __typename: 'User',
// avatar: {
// url:
// 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg',
// },
// name: 'Peter Lustig',
// slug: 'peter-lustig',
// },
// {
// id: 'cdbca762-0632-4564-b646-415a0c42d8b8',
// __typename: 'User',
// avatar: {
// url:
// 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg',
// },
// name: 'Herbert Schultz',
// slug: 'herbert-schultz',
// },
// {
// id: 'u2',
// __typename: 'User',
// avatar: {
// url:
// 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg',
// },
// name: 'Bob der Baumeister',
// slug: 'bob-der-baumeister',
// },
// {
// id: '7b654f72-f4da-4315-8bed-39de0859754b',
// __typename: 'User',
// avatar: {
// url:
// 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg',
// },
// name: 'Tonya Mohr',
// slug: 'tonya-mohr',
// },
// {
// id: 'Hashtag',
// __typename: 'Tag',
// },
// ]
storiesOf('RegistrationSlider', module)
.addDecorator(withA11y)
.addDecorator(helpers.layout)
.add('standard', () => ({
.add('invite-code empty', () => ({
components: { RegistrationSlider },
store: helpers.store,
data: () => ({
// Wolle searchResults,
}),
template: `
<registration-slider />
<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',
nonce: 'NTRSCZ',
name: 'Wolle',
password: 'Hello',
passwordConfirmation: 'Hello',
about: `Hey`,
termsAndConditionsAgreedVersion: null,
termsAndConditionsConfirmed: true,
dataPrivacy: true,
minimumAge: true,
noCommercial: true,
noPolitical: true,
locale: null,
},
},
}),
template: `
<registration-slider registrationType="invite-code" :overwriteSliderData="overwriteSliderData" />
`,
}))
.add('public-registration', () => ({
components: { RegistrationSlider },
store: helpers.store,
data: () => ({
// Wolle searchResults,
}),
template: `
<registration-slider registrationType="public-registration" />
`,
}))
.add('invite-mail', () => ({
components: { RegistrationSlider },
store: helpers.store,
data: () => ({
// Wolle searchResults,
}),
template: `
<registration-slider registrationType="invite-mail" />
`,
}))

View File

@ -14,23 +14,25 @@
</ds-heading>
</template>
<template #enter-invite>
<template v-if="['invite-code'].includes(registrationType)" #enter-invite>
<registration-item-enter-invite :sliderData="sliderData" />
</template>
<!-- <template #enter-email> -->
<!-- Wolle !!! may create same source with 'webapp/pages/registration/signup.vue' -->
<!-- <signup v-if="publicRegistration" :invitation="false" @submit="handleSubmitted"> -->
<!-- <signup :invitation="false" @submit="handleSubmitted">
<ds-space centered margin-top="large">
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
</ds-space>
</signup> -->
<!-- <ds-space v-else centered>
<hc-empty icon="events" :message="$t('components.registration.signup.unavailable')" />
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
</ds-space> -->
<!-- </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 :invitation="false" :sliderData="sliderData" />
</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
@ -58,11 +60,12 @@
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 Signup from '~/components/Registration/Signup'
import HcEmpty from '~/components/Empty/Empty'
import RegistrationItemEnterInvite from './RegistrationItemEnterInvite'
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',
@ -70,11 +73,80 @@ export default {
ComponentSlider,
HcEmpty,
LocaleSwitch,
RegistrationItemEnterInvite,
RegistrationItemCreateUserAccount,
Signup,
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,
button: {
title: 'Next', // Wolle
icon: 'arrow-right',
callback: this.buttonCallback,
},
},
{
name: 'enter-email',
title: 'E-Mail', // Wolle
validated: false,
button: {
title: 'Send E-Mail', // Wolle
icon: 'envelope',
callback: this.buttonCallback,
},
},
{
name: 'enter-nonce',
title: 'E-Mail Confirmation', // Wolle
validated: false,
button: {
title: 'Confirm', // Wolle
icon: 'arrow-right',
callback: this.buttonCallback,
},
},
{
name: 'create-user-account',
title: this.$t('components.registration.create-user-account.title'),
validated: false,
button: {
// title: this.$t('actions.save'), // Wolle
title: 'Create', // Wolle
icon: 'check',
callback: this.buttonCallback,
},
},
]
// Wolle console.log('this.registrationType: ', this.registrationType)
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
}
// let sliders = slidersPortfolio
return {
links,
metadata,
@ -84,7 +156,8 @@ export default {
email: null,
nonce: null,
name: null,
// password: null,
password: null,
passwordConfirmation: null,
about: null,
termsAndConditionsAgreedVersion: null,
termsAndConditionsConfirmed: null,
@ -92,44 +165,13 @@ export default {
minimumAge: null,
noCommercial: null,
noPolitical: null,
// locale: null, // Wolle: what to do? Is collected in the last slider?! and gets stored in the database
locale: null, // Wolle: what to do? Is collected in the last slider?! and gets stored in the database
},
sliderIndex: 0,
sliders: [
{
name: 'enter-invite',
// title: this.$t('components.registration.create-user-account.title'),
title: 'Invitation', // Wolle
validated: false,
button: {
title: 'Next', // Wolle
icon: 'arrow-right',
callback: this.buttonCallback,
},
},
// {
// name: 'enter-email',
// validated: false,
// title: 'E-Mail', // Wolle
// button: {
// title: 'Next', // Wolle
// icon: :icon="(sliderIndex < sliderData.sliders.length - 1 && 'arrow-right') || (sliderIndex === sliderData.sliders.length - 1 && 'check')"
// callback: this.buttonCallback,
// },
// },
{
name: 'create-user-account',
title: this.$t('components.registration.create-user-account.title'),
validated: false,
button: {
title: this.$t('actions.save'), // Wolle
icon: 'check',
callback: this.buttonCallback,
},
},
],
sliders: sliders,
sliderSelectorCallback: this.sliderSelectorCallback,
validateCallback: this.validateCallback,
...this.overwriteSliderData,
},
}
},