mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge pull request #994 from Human-Connection/signup_as_admin
Signup new users as admin
This commit is contained in:
commit
e6e9b1b7d7
@ -276,9 +276,9 @@ When("I fill the password form with:", table => {
|
|||||||
table = table.rowsHash();
|
table = table.rowsHash();
|
||||||
cy.get("input[id=oldPassword]")
|
cy.get("input[id=oldPassword]")
|
||||||
.type(table["Your old password"])
|
.type(table["Your old password"])
|
||||||
.get("input[id=newPassword]")
|
.get("input[id=password]")
|
||||||
.type(table["Your new passsword"])
|
.type(table["Your new passsword"])
|
||||||
.get("input[id=confirmPassword]")
|
.get("input[id=passwordConfirmation]")
|
||||||
.type(table["Confirm new password"]);
|
.type(table["Confirm new password"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -54,7 +54,7 @@ describe('ChangePassword.vue', () => {
|
|||||||
describe('match', () => {
|
describe('match', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper.find('input#oldPassword').setValue('some secret')
|
wrapper.find('input#oldPassword').setValue('some secret')
|
||||||
wrapper.find('input#newPassword').setValue('some secret')
|
wrapper.find('input#password').setValue('some secret')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('invalid', () => {
|
it('invalid', () => {
|
||||||
@ -90,8 +90,8 @@ describe('ChangePassword.vue', () => {
|
|||||||
describe('given valid input', () => {
|
describe('given valid input', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper.find('input#oldPassword').setValue('supersecret')
|
wrapper.find('input#oldPassword').setValue('supersecret')
|
||||||
wrapper.find('input#newPassword').setValue('superdupersecret')
|
wrapper.find('input#password').setValue('superdupersecret')
|
||||||
wrapper.find('input#confirmPassword').setValue('superdupersecret')
|
wrapper.find('input#passwordConfirmation').setValue('superdupersecret')
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('submit form', () => {
|
describe('submit form', () => {
|
||||||
@ -109,8 +109,8 @@ describe('ChangePassword.vue', () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
variables: {
|
variables: {
|
||||||
oldPassword: 'supersecret',
|
oldPassword: 'supersecret',
|
||||||
newPassword: 'superdupersecret',
|
password: 'superdupersecret',
|
||||||
confirmPassword: 'superdupersecret',
|
passwordConfirmation: 'superdupersecret',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -135,8 +135,8 @@ describe('ChangePassword.vue', () => {
|
|||||||
/* describe('mutation rejects', () => {
|
/* describe('mutation rejects', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await wrapper.find('input#oldPassword').setValue('supersecret')
|
await wrapper.find('input#oldPassword').setValue('supersecret')
|
||||||
await wrapper.find('input#newPassword').setValue('supersecret')
|
await wrapper.find('input#password').setValue('supersecret')
|
||||||
await wrapper.find('input#confirmPassword').setValue('supersecret')
|
await wrapper.find('input#passwordConfirmation').setValue('supersecret')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('displays error message', async () => {
|
it('displays error message', async () => {
|
||||||
|
|||||||
@ -1,12 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<ds-form
|
<ds-form v-model="formData" :schema="formSchema" @submit="handleSubmit">
|
||||||
v-model="formData"
|
<template slot-scope="{ errors }">
|
||||||
:schema="formSchema"
|
|
||||||
@submit="handleSubmit"
|
|
||||||
@input="handleInput"
|
|
||||||
@input-valid="handleInputValid"
|
|
||||||
>
|
|
||||||
<template>
|
|
||||||
<ds-input
|
<ds-input
|
||||||
id="oldPassword"
|
id="oldPassword"
|
||||||
model="oldPassword"
|
model="oldPassword"
|
||||||
@ -15,22 +9,22 @@
|
|||||||
:label="$t('settings.security.change-password.label-old-password')"
|
:label="$t('settings.security.change-password.label-old-password')"
|
||||||
/>
|
/>
|
||||||
<ds-input
|
<ds-input
|
||||||
id="newPassword"
|
id="password"
|
||||||
model="newPassword"
|
model="password"
|
||||||
type="password"
|
type="password"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:label="$t('settings.security.change-password.label-new-password')"
|
:label="$t('settings.security.change-password.label-new-password')"
|
||||||
/>
|
/>
|
||||||
<ds-input
|
<ds-input
|
||||||
id="confirmPassword"
|
id="passwordConfirmation"
|
||||||
model="confirmPassword"
|
model="passwordConfirmation"
|
||||||
type="password"
|
type="password"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
||||||
/>
|
/>
|
||||||
<password-strength :password="formData.newPassword" />
|
<password-strength :password="formData.password" />
|
||||||
<ds-space margin-top="base">
|
<ds-space margin-top="base">
|
||||||
<ds-button :loading="loading" :disabled="disabled" primary>
|
<ds-button :loading="loading" :disabled="errors" primary>
|
||||||
{{ $t('settings.security.change-password.button') }}
|
{{ $t('settings.security.change-password.button') }}
|
||||||
</ds-button>
|
</ds-button>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
@ -41,6 +35,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import PasswordStrength from './Strength'
|
import PasswordStrength from './Strength'
|
||||||
|
import PasswordForm from '~/components/utils/PasswordFormHelper'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ChangePassword',
|
name: 'ChangePassword',
|
||||||
@ -48,11 +43,11 @@ export default {
|
|||||||
PasswordStrength,
|
PasswordStrength,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const passwordForm = PasswordForm({ translate: this.$t })
|
||||||
return {
|
return {
|
||||||
formData: {
|
formData: {
|
||||||
oldPassword: '',
|
oldPassword: '',
|
||||||
newPassword: '',
|
...passwordForm.formData,
|
||||||
confirmPassword: '',
|
|
||||||
},
|
},
|
||||||
formSchema: {
|
formSchema: {
|
||||||
oldPassword: {
|
oldPassword: {
|
||||||
@ -60,38 +55,18 @@ export default {
|
|||||||
required: true,
|
required: true,
|
||||||
message: this.$t('settings.security.change-password.message-old-password-required'),
|
message: this.$t('settings.security.change-password.message-old-password-required'),
|
||||||
},
|
},
|
||||||
newPassword: {
|
...passwordForm.formSchema,
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
message: this.$t('settings.security.change-password.message-new-password-required'),
|
|
||||||
},
|
|
||||||
confirmPassword: [
|
|
||||||
{ validator: this.matchPassword },
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
message: this.$t(
|
|
||||||
'settings.security.change-password.message-new-password-confirm-required',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
loading: false,
|
loading: false,
|
||||||
disabled: true,
|
disabled: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async handleInput(data) {
|
|
||||||
this.disabled = true
|
|
||||||
},
|
|
||||||
async handleInputValid(data) {
|
|
||||||
this.disabled = false
|
|
||||||
},
|
|
||||||
async handleSubmit(data) {
|
async handleSubmit(data) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
const mutation = gql`
|
const mutation = gql`
|
||||||
mutation($oldPassword: String!, $newPassword: String!) {
|
mutation($oldPassword: String!, $password: String!) {
|
||||||
changePassword(oldPassword: $oldPassword, newPassword: $newPassword)
|
changePassword(oldPassword: $oldPassword, newPassword: $password)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const variables = this.formData
|
const variables = this.formData
|
||||||
@ -102,8 +77,8 @@ export default {
|
|||||||
this.$toast.success(this.$t('settings.security.change-password.success'))
|
this.$toast.success(this.$t('settings.security.change-password.success'))
|
||||||
this.formData = {
|
this.formData = {
|
||||||
oldPassword: '',
|
oldPassword: '',
|
||||||
newPassword: '',
|
password: '',
|
||||||
confirmPassword: '',
|
passwordConfirmation: '',
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$toast.error(err.message)
|
this.$toast.error(err.message)
|
||||||
@ -111,15 +86,6 @@ export default {
|
|||||||
this.loading = false
|
this.loading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
matchPassword(rule, value, callback, source, options) {
|
|
||||||
var errors = []
|
|
||||||
if (this.formData.newPassword !== value) {
|
|
||||||
errors.push(
|
|
||||||
new Error(this.$t('settings.security.change-password.message-new-password-missmatch')),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
callback(errors)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -47,8 +47,8 @@ describe('ChangePassword ', () => {
|
|||||||
describe('submitting new password', () => {
|
describe('submitting new password', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper = Wrapper()
|
wrapper = Wrapper()
|
||||||
wrapper.find('input#newPassword').setValue('supersecret')
|
wrapper.find('input#password').setValue('supersecret')
|
||||||
wrapper.find('input#confirmPassword').setValue('supersecret')
|
wrapper.find('input#passwordConfirmation').setValue('supersecret')
|
||||||
wrapper.find('form').trigger('submit')
|
wrapper.find('form').trigger('submit')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ describe('ChangePassword ', () => {
|
|||||||
|
|
||||||
it('delivers new password to backend', () => {
|
it('delivers new password to backend', () => {
|
||||||
const expected = expect.objectContaining({
|
const expected = expect.objectContaining({
|
||||||
variables: { code: '123456', email: 'mail@example.org', newPassword: 'supersecret' },
|
variables: { code: '123456', email: 'mail@example.org', password: 'supersecret' },
|
||||||
})
|
})
|
||||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
|
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,54 +1,52 @@
|
|||||||
<template>
|
<template>
|
||||||
<ds-card class="verify-code">
|
<ds-card class="verify-code">
|
||||||
<ds-space margin="large">
|
<ds-space margin="large">
|
||||||
<template>
|
<ds-form
|
||||||
<ds-form
|
v-if="!changePasswordResult"
|
||||||
v-if="!changePasswordResult"
|
v-model="formData"
|
||||||
v-model="formData"
|
:schema="formSchema"
|
||||||
:schema="formSchema"
|
@submit="handleSubmitPassword"
|
||||||
@submit="handleSubmitPassword"
|
class="change-password"
|
||||||
@input="handleInput"
|
>
|
||||||
@input-valid="handleInputValid"
|
<template slot-scope="{ errors }">
|
||||||
class="change-password"
|
|
||||||
>
|
|
||||||
<ds-input
|
<ds-input
|
||||||
id="newPassword"
|
id="password"
|
||||||
model="newPassword"
|
model="password"
|
||||||
type="password"
|
type="password"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:label="$t('settings.security.change-password.label-new-password')"
|
:label="$t('settings.security.change-password.label-new-password')"
|
||||||
/>
|
/>
|
||||||
<ds-input
|
<ds-input
|
||||||
id="confirmPassword"
|
id="passwordConfirmation"
|
||||||
model="confirmPassword"
|
model="passwordConfirmation"
|
||||||
type="password"
|
type="password"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
||||||
/>
|
/>
|
||||||
<password-strength :password="formData.newPassword" />
|
<password-strength :password="formData.password" />
|
||||||
<ds-space margin-top="base">
|
<ds-space margin-top="base">
|
||||||
<ds-button :loading="$apollo.loading" :disabled="disabled" primary>
|
<ds-button :loading="$apollo.loading" :disabled="errors" primary>
|
||||||
{{ $t('settings.security.change-password.button') }}
|
{{ $t('settings.security.change-password.button') }}
|
||||||
</ds-button>
|
</ds-button>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
</ds-form>
|
</template>
|
||||||
<ds-text v-else>
|
</ds-form>
|
||||||
<template v-if="changePasswordResult === 'success'">
|
<ds-text v-else>
|
||||||
<sweetalert-icon icon="success" />
|
<template v-if="changePasswordResult === 'success'">
|
||||||
<ds-text>
|
<sweetalert-icon icon="success" />
|
||||||
{{ $t(`verify-code.form.change-password.success`) }}
|
<ds-text>
|
||||||
</ds-text>
|
{{ $t(`verify-code.form.change-password.success`) }}
|
||||||
</template>
|
</ds-text>
|
||||||
<template v-else>
|
</template>
|
||||||
<sweetalert-icon icon="error" />
|
<template v-else>
|
||||||
<ds-text align="left">
|
<sweetalert-icon icon="error" />
|
||||||
{{ $t(`verify-code.form.change-password.error`) }}
|
<ds-text align="left">
|
||||||
{{ $t('verify-code.form.change-password.help') }}
|
{{ $t(`verify-code.form.change-password.error`) }}
|
||||||
</ds-text>
|
{{ $t('verify-code.form.change-password.help') }}
|
||||||
<a href="mailto:support@human-connection.org">support@human-connection.org</a>
|
</ds-text>
|
||||||
</template>
|
<a href="mailto:support@human-connection.org">support@human-connection.org</a>
|
||||||
</ds-text>
|
</template>
|
||||||
</template>
|
</ds-text>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
</ds-card>
|
</ds-card>
|
||||||
</template>
|
</template>
|
||||||
@ -57,6 +55,7 @@
|
|||||||
import PasswordStrength from '../Password/Strength'
|
import PasswordStrength from '../Password/Strength'
|
||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||||
|
import PasswordForm from '~/components/utils/PasswordFormHelper'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@ -68,48 +67,28 @@ export default {
|
|||||||
code: { type: String, required: true },
|
code: { type: String, required: true },
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
const passwordForm = PasswordForm({ translate: this.$t })
|
||||||
return {
|
return {
|
||||||
formData: {
|
formData: {
|
||||||
newPassword: '',
|
...passwordForm.formData,
|
||||||
confirmPassword: '',
|
|
||||||
},
|
},
|
||||||
formSchema: {
|
formSchema: {
|
||||||
newPassword: {
|
...passwordForm.formSchema,
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
message: this.$t('settings.security.change-password.message-new-password-required'),
|
|
||||||
},
|
|
||||||
confirmPassword: [
|
|
||||||
{ validator: this.matchPassword },
|
|
||||||
{
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
message: this.$t(
|
|
||||||
'settings.security.change-password.message-new-password-confirm-required',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
disabled: true,
|
disabled: true,
|
||||||
changePasswordResult: null,
|
changePasswordResult: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async handleInput() {
|
|
||||||
this.disabled = true
|
|
||||||
},
|
|
||||||
async handleInputValid() {
|
|
||||||
this.disabled = false
|
|
||||||
},
|
|
||||||
async handleSubmitPassword() {
|
async handleSubmitPassword() {
|
||||||
const mutation = gql`
|
const mutation = gql`
|
||||||
mutation($code: String!, $email: String!, $newPassword: String!) {
|
mutation($code: String!, $email: String!, $password: String!) {
|
||||||
resetPassword(code: $code, email: $email, newPassword: $newPassword)
|
resetPassword(code: $code, email: $email, newPassword: $password)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const { newPassword } = this.formData
|
const { password } = this.formData
|
||||||
const { email, code } = this
|
const { email, code } = this
|
||||||
const variables = { newPassword, email, code }
|
const variables = { password, email, code }
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
data: { resetPassword },
|
data: { resetPassword },
|
||||||
@ -119,22 +98,13 @@ export default {
|
|||||||
this.$emit('passwordResetResponse', this.changePasswordResult)
|
this.$emit('passwordResetResponse', this.changePasswordResult)
|
||||||
}, 3000)
|
}, 3000)
|
||||||
this.formData = {
|
this.formData = {
|
||||||
newPassword: '',
|
password: '',
|
||||||
confirmPassword: '',
|
passwordConfirmation: '',
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$toast.error(err.message)
|
this.$toast.error(err.message)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
matchPassword(rule, value, callback, source, options) {
|
|
||||||
var errors = []
|
|
||||||
if (this.formData.newPassword !== value) {
|
|
||||||
errors.push(
|
|
||||||
new Error(this.$t('settings.security.change-password.message-new-password-missmatch')),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
callback(errors)
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
132
webapp/components/Registration/CreateUserAccount.spec.js
Normal file
132
webapp/components/Registration/CreateUserAccount.spec.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import { mount, createLocalVue } from '@vue/test-utils'
|
||||||
|
import CreateUserAccount, { SignupVerificationMutation } from './CreateUserAccount'
|
||||||
|
import Styleguide from '@human-connection/styleguide'
|
||||||
|
|
||||||
|
const localVue = createLocalVue()
|
||||||
|
|
||||||
|
localVue.use(Styleguide)
|
||||||
|
|
||||||
|
describe('CreateUserAccount', () => {
|
||||||
|
let wrapper
|
||||||
|
let Wrapper
|
||||||
|
let mocks
|
||||||
|
let propsData
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks = {
|
||||||
|
$toast: {
|
||||||
|
success: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
},
|
||||||
|
$t: jest.fn(),
|
||||||
|
$apollo: {
|
||||||
|
loading: false,
|
||||||
|
mutate: jest.fn(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
propsData = {}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
Wrapper = () => {
|
||||||
|
return mount(CreateUserAccount, {
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
localVue,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('given email and nonce', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
propsData.nonce = '666777'
|
||||||
|
propsData.email = 'sixseven@example.org'
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders a form to create a new user', () => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.find('.create-user-account').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('submit', () => {
|
||||||
|
let action
|
||||||
|
beforeEach(() => {
|
||||||
|
action = async () => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
wrapper.find('input#name').setValue('John Doe')
|
||||||
|
wrapper.find('input#password').setValue('hellopassword')
|
||||||
|
wrapper.find('input#passwordConfirmation').setValue('hellopassword')
|
||||||
|
await wrapper.find('form').trigger('submit')
|
||||||
|
await wrapper.html()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls CreateUserAccount graphql mutation', async () => {
|
||||||
|
await action()
|
||||||
|
const expected = expect.objectContaining({ mutation: SignupVerificationMutation })
|
||||||
|
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('delivers data to backend', async () => {
|
||||||
|
await action()
|
||||||
|
const expected = expect.objectContaining({
|
||||||
|
variables: {
|
||||||
|
about: '',
|
||||||
|
name: 'John Doe',
|
||||||
|
email: 'sixseven@example.org',
|
||||||
|
nonce: '666777',
|
||||||
|
password: 'hellopassword',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('in case mutation resolves', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks.$apollo.mutate = jest.fn().mockResolvedValue({
|
||||||
|
data: {
|
||||||
|
SignupVerification: {
|
||||||
|
id: 'u1',
|
||||||
|
name: 'John Doe',
|
||||||
|
slug: 'john-doe',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('displays success', async () => {
|
||||||
|
await action()
|
||||||
|
expect(mocks.$t).toHaveBeenCalledWith('registration.create-user-account.success')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('after timeout', () => {
|
||||||
|
beforeEach(jest.useFakeTimers)
|
||||||
|
|
||||||
|
it('emits `userCreated` with { password, email }', async () => {
|
||||||
|
await action()
|
||||||
|
jest.runAllTimers()
|
||||||
|
expect(wrapper.emitted('userCreated')).toEqual([
|
||||||
|
[
|
||||||
|
{
|
||||||
|
email: 'sixseven@example.org',
|
||||||
|
password: 'hellopassword',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('in case mutation rejects', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks.$apollo.mutate = jest.fn().mockRejectedValue(new Error('Invalid nonce'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('displays form errors', async () => {
|
||||||
|
await action()
|
||||||
|
expect(wrapper.find('.backendErrors').text()).toContain('Invalid nonce')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
142
webapp/components/Registration/CreateUserAccount.vue
Normal file
142
webapp/components/Registration/CreateUserAccount.vue
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<template>
|
||||||
|
<ds-card v-if="success" class="success">
|
||||||
|
<ds-space>
|
||||||
|
<sweetalert-icon icon="success" />
|
||||||
|
<ds-text align="center" bold color="success">
|
||||||
|
{{ $t('registration.create-user-account.success') }}
|
||||||
|
</ds-text>
|
||||||
|
</ds-space>
|
||||||
|
</ds-card>
|
||||||
|
<ds-form
|
||||||
|
v-else
|
||||||
|
class="create-user-account"
|
||||||
|
v-model="formData"
|
||||||
|
:schema="formSchema"
|
||||||
|
@submit="submit"
|
||||||
|
>
|
||||||
|
<template slot-scope="{ errors }">
|
||||||
|
<ds-card :header="$t('registration.create-user-account.title')">
|
||||||
|
<ds-input
|
||||||
|
id="name"
|
||||||
|
model="name"
|
||||||
|
icon="user"
|
||||||
|
:label="$t('settings.data.labelName')"
|
||||||
|
:placeholder="$t('settings.data.namePlaceholder')"
|
||||||
|
/>
|
||||||
|
<ds-input
|
||||||
|
id="bio"
|
||||||
|
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 :password="formData.password" />
|
||||||
|
<template slot="footer">
|
||||||
|
<ds-space class="backendErrors" v-if="backendErrors">
|
||||||
|
<ds-text align="center" bold color="danger">
|
||||||
|
{{ backendErrors.message }}
|
||||||
|
</ds-text>
|
||||||
|
</ds-space>
|
||||||
|
<ds-button
|
||||||
|
style="float: right;"
|
||||||
|
icon="check"
|
||||||
|
type="submit"
|
||||||
|
:loading="$apollo.loading"
|
||||||
|
:disabled="errors"
|
||||||
|
primary
|
||||||
|
>
|
||||||
|
{{ $t('actions.save') }}
|
||||||
|
</ds-button>
|
||||||
|
</template>
|
||||||
|
</ds-card>
|
||||||
|
</template>
|
||||||
|
</ds-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import PasswordStrength from '../Password/Strength'
|
||||||
|
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||||
|
import PasswordForm from '~/components/utils/PasswordFormHelper'
|
||||||
|
|
||||||
|
export const SignupVerificationMutation = gql`
|
||||||
|
mutation($nonce: String!, $name: String!, $email: String!, $password: String!) {
|
||||||
|
SignupVerification(nonce: $nonce, email: $email, name: $name, password: $password) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
PasswordStrength,
|
||||||
|
SweetalertIcon,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const passwordForm = PasswordForm({ translate: this.$t })
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
name: '',
|
||||||
|
about: '',
|
||||||
|
...passwordForm.formData,
|
||||||
|
},
|
||||||
|
formSchema: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
min: 3,
|
||||||
|
},
|
||||||
|
about: {
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
...passwordForm.formSchema,
|
||||||
|
},
|
||||||
|
disabled: true,
|
||||||
|
success: null,
|
||||||
|
backendErrors: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
nonce: { type: String, required: true },
|
||||||
|
email: { type: String, required: true },
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async submit() {
|
||||||
|
const { name, password, about } = this.formData
|
||||||
|
const { email, nonce } = this
|
||||||
|
try {
|
||||||
|
await this.$apollo.mutate({
|
||||||
|
mutation: SignupVerificationMutation,
|
||||||
|
variables: { name, password, about, email, nonce },
|
||||||
|
})
|
||||||
|
this.success = true
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$emit('userCreated', {
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
})
|
||||||
|
}, 3000)
|
||||||
|
} catch (err) {
|
||||||
|
this.backendErrors = err
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
146
webapp/components/Registration/Signup.spec.js
Normal file
146
webapp/components/Registration/Signup.spec.js
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import { mount, createLocalVue } from '@vue/test-utils'
|
||||||
|
import Signup, { SignupMutation, SignupByInvitationMutation } from './Signup'
|
||||||
|
import Styleguide from '@human-connection/styleguide'
|
||||||
|
|
||||||
|
const localVue = createLocalVue()
|
||||||
|
|
||||||
|
localVue.use(Styleguide)
|
||||||
|
|
||||||
|
describe('Signup', () => {
|
||||||
|
let wrapper
|
||||||
|
let Wrapper
|
||||||
|
let mocks
|
||||||
|
let propsData
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks = {
|
||||||
|
$toast: {
|
||||||
|
success: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
},
|
||||||
|
$t: jest.fn(),
|
||||||
|
$apollo: {
|
||||||
|
loading: false,
|
||||||
|
mutate: jest.fn().mockResolvedValue({ data: { Signup: { email: 'mail@example.org' } } }),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
propsData = {}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('mount', () => {
|
||||||
|
beforeEach(jest.useFakeTimers)
|
||||||
|
|
||||||
|
Wrapper = () => {
|
||||||
|
return mount(Signup, {
|
||||||
|
mocks,
|
||||||
|
propsData,
|
||||||
|
localVue,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('without invitation code', () => {
|
||||||
|
it('renders signup form', () => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.find('.signup').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('submit', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
wrapper.find('input#email').setValue('mail@example.org')
|
||||||
|
await wrapper.find('form').trigger('submit')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('calls Signup graphql mutation', () => {
|
||||||
|
const expected = expect.objectContaining({ mutation: SignupMutation })
|
||||||
|
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('delivers email to backend', () => {
|
||||||
|
const expected = expect.objectContaining({
|
||||||
|
variables: { email: 'mail@example.org', token: null },
|
||||||
|
})
|
||||||
|
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('hides form to avoid re-submission', () => {
|
||||||
|
expect(wrapper.find('form').exists()).not.toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('displays a message that a mail for email verification was sent', () => {
|
||||||
|
const expected = ['registration.signup.form.success', { email: 'mail@example.org' }]
|
||||||
|
expect(mocks.$t).toHaveBeenCalledWith(...expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('after animation', () => {
|
||||||
|
beforeEach(jest.runAllTimers)
|
||||||
|
|
||||||
|
it('emits `handleSubmitted`', () => {
|
||||||
|
expect(wrapper.emitted('handleSubmitted')).toEqual([[{ email: 'mail@example.org' }]])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with invitation code', () => {
|
||||||
|
let action
|
||||||
|
beforeEach(() => {
|
||||||
|
propsData.token = '666777'
|
||||||
|
action = async () => {
|
||||||
|
wrapper = Wrapper()
|
||||||
|
wrapper.find('input#email').setValue('mail@example.org')
|
||||||
|
await wrapper.find('form').trigger('submit')
|
||||||
|
await wrapper.html()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('submit', () => {
|
||||||
|
it('calls SignupByInvitation graphql mutation', async () => {
|
||||||
|
await action()
|
||||||
|
const expected = expect.objectContaining({ mutation: SignupByInvitationMutation })
|
||||||
|
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('delivers invitation token to backend', async () => {
|
||||||
|
await action()
|
||||||
|
const expected = expect.objectContaining({
|
||||||
|
variables: { email: 'mail@example.org', token: '666777' },
|
||||||
|
})
|
||||||
|
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('in case a user account with the email already exists', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks.$apollo.mutate = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(
|
||||||
|
new Error('UserInputError: User account with this email already exists.'),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('explains the error', async () => {
|
||||||
|
await action()
|
||||||
|
expect(mocks.$t).toHaveBeenCalledWith('registration.signup.form.errors.email-exists')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('in case the invitation code was incorrect', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks.$apollo.mutate = jest
|
||||||
|
.fn()
|
||||||
|
.mockRejectedValue(
|
||||||
|
new Error('UserInputError: Invitation code already used or does not exist.'),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('explains the error', async () => {
|
||||||
|
await action()
|
||||||
|
expect(mocks.$t).toHaveBeenCalledWith(
|
||||||
|
'registration.signup.form.errors.invalid-invitation-token',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
141
webapp/components/Registration/Signup.vue
Normal file
141
webapp/components/Registration/Signup.vue
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<ds-card class="signup">
|
||||||
|
<ds-space margin="large">
|
||||||
|
<ds-form
|
||||||
|
v-if="!success && !error"
|
||||||
|
@input="handleInput"
|
||||||
|
@input-valid="handleInputValid"
|
||||||
|
v-model="formData"
|
||||||
|
:schema="formSchema"
|
||||||
|
@submit="handleSubmit"
|
||||||
|
>
|
||||||
|
<h1>{{ $t('registration.signup.title') }}</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>
|
||||||
|
{{ $t('registration.signup.form.description') }}
|
||||||
|
</ds-text>
|
||||||
|
</ds-space>
|
||||||
|
<ds-input
|
||||||
|
:placeholder="$t('login.email')"
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
model="email"
|
||||||
|
name="email"
|
||||||
|
icon="envelope"
|
||||||
|
/>
|
||||||
|
<ds-button
|
||||||
|
:disabled="disabled"
|
||||||
|
:loading="$apollo.loading"
|
||||||
|
primary
|
||||||
|
fullwidth
|
||||||
|
name="submit"
|
||||||
|
type="submit"
|
||||||
|
icon="envelope"
|
||||||
|
>
|
||||||
|
{{ $t('registration.signup.form.submit') }}
|
||||||
|
</ds-button>
|
||||||
|
</ds-form>
|
||||||
|
<div v-else>
|
||||||
|
<template v-if="!error">
|
||||||
|
<sweetalert-icon icon="info" />
|
||||||
|
<ds-text align="center" v-html="submitMessage" />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<sweetalert-icon icon="error" />
|
||||||
|
<ds-text align="center">
|
||||||
|
{{ error.message }}
|
||||||
|
</ds-text>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</ds-space>
|
||||||
|
</ds-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
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 {
|
||||||
|
components: {
|
||||||
|
SweetalertIcon,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
token: { type: String, default: null },
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
email: '',
|
||||||
|
},
|
||||||
|
formSchema: {
|
||||||
|
email: {
|
||||||
|
type: 'email',
|
||||||
|
required: true,
|
||||||
|
message: this.$t('common.validations.email'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
disabled: true,
|
||||||
|
success: false,
|
||||||
|
error: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
submitMessage() {
|
||||||
|
const { email } = this.formData
|
||||||
|
return this.$t('registration.signup.form.success', { email })
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleInput() {
|
||||||
|
this.disabled = true
|
||||||
|
},
|
||||||
|
handleInputValid() {
|
||||||
|
this.disabled = false
|
||||||
|
},
|
||||||
|
async handleSubmit() {
|
||||||
|
const mutation = this.token ? SignupByInvitationMutation : SignupMutation
|
||||||
|
const { email } = this.formData
|
||||||
|
const { token } = this
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.$apollo.mutate({ mutation, variables: { email, token } })
|
||||||
|
this.success = true
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$emit('handleSubmitted', { email })
|
||||||
|
}, 3000)
|
||||||
|
} catch (err) {
|
||||||
|
const { message } = err
|
||||||
|
const mapping = {
|
||||||
|
'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(`registration.signup.form.errors.${key}`) }
|
||||||
|
}
|
||||||
|
if (!this.error) {
|
||||||
|
this.$toast.error(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
36
webapp/components/utils/PasswordFormHelper.js
Normal file
36
webapp/components/utils/PasswordFormHelper.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export default function PasswordForm({ translate }) {
|
||||||
|
const passwordMismatchMessage = translate(
|
||||||
|
'settings.security.change-password.message-new-password-missmatch',
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
password: '',
|
||||||
|
passwordConfirmation: '',
|
||||||
|
},
|
||||||
|
formSchema: {
|
||||||
|
password: {
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
message: translate('settings.security.change-password.message-new-password-required'),
|
||||||
|
},
|
||||||
|
passwordConfirmation: [
|
||||||
|
{
|
||||||
|
validator(rule, value, callback, source, options) {
|
||||||
|
var errors = []
|
||||||
|
if (source.password !== value) {
|
||||||
|
errors.push(new Error(passwordMismatchMessage))
|
||||||
|
}
|
||||||
|
callback(errors)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
message: translate(
|
||||||
|
'settings.security.change-password.message-new-password-confirm-required',
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -29,7 +29,8 @@
|
|||||||
"moreInfo": "Was ist Human Connection?",
|
"moreInfo": "Was ist Human Connection?",
|
||||||
"moreInfoURL": "https://human-connection.org",
|
"moreInfoURL": "https://human-connection.org",
|
||||||
"moreInfoHint": "zur Präsentationsseite",
|
"moreInfoHint": "zur Präsentationsseite",
|
||||||
"hello": "Hallo"
|
"hello": "Hallo",
|
||||||
|
"success": "Du bist eingeloggt!"
|
||||||
},
|
},
|
||||||
"password-reset": {
|
"password-reset": {
|
||||||
"title": "Passwort zurücksetzen",
|
"title": "Passwort zurücksetzen",
|
||||||
@ -39,6 +40,24 @@
|
|||||||
"submitted": "Eine E-Mail mit weiteren Instruktionen wurde verschickt an <b>{email}</b>"
|
"submitted": "Eine E-Mail mit weiteren Instruktionen wurde verschickt an <b>{email}</b>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"registration": {
|
||||||
|
"signup": {
|
||||||
|
"title": "Mach mit bei Human Connection!",
|
||||||
|
"form": {
|
||||||
|
"description": "Um loszulegen, gib deine E-Mail Adresse ein:",
|
||||||
|
"errors": {
|
||||||
|
"email-exists": "Es gibt schon ein Benutzerkonto mit dieser E-Mail Adresse!",
|
||||||
|
"invalid-invitation-token": "Es sieht so aus, als ob der Einladungscode schon eingelöst wurde. Jeder Code kann nur einmalig benutzt werden."
|
||||||
|
},
|
||||||
|
"submit": "Konto erstellen",
|
||||||
|
"success": "Eine Mail mit einem Bestätigungslink für die Registrierung wurde an <b>{email}</b> geschickt"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"create-user-account": {
|
||||||
|
"title": "Benutzerkonto anlegen",
|
||||||
|
"success": "Dein Benutzerkonto wurde erstellt!"
|
||||||
|
}
|
||||||
|
},
|
||||||
"verify-code": {
|
"verify-code": {
|
||||||
"form": {
|
"form": {
|
||||||
"code": "Code eingeben",
|
"code": "Code eingeben",
|
||||||
@ -189,6 +208,11 @@
|
|||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"name": "Einstellungen"
|
"name": "Einstellungen"
|
||||||
|
},
|
||||||
|
"invites": {
|
||||||
|
"name": "Benutzer einladen",
|
||||||
|
"title": "Benutzer als Admin anmelden",
|
||||||
|
"description": "Dieses Anmeldeformular ist zu sehen sobald die Anmeldung öffentlich zugänglich ist."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"post": {
|
"post": {
|
||||||
|
|||||||
@ -29,7 +29,8 @@
|
|||||||
"moreInfo": "What is Human Connection?",
|
"moreInfo": "What is Human Connection?",
|
||||||
"moreInfoURL": "https://human-connection.org/en/",
|
"moreInfoURL": "https://human-connection.org/en/",
|
||||||
"moreInfoHint": "to the presentation page",
|
"moreInfoHint": "to the presentation page",
|
||||||
"hello": "Hello"
|
"hello": "Hello",
|
||||||
|
"success": "You are logged in!"
|
||||||
},
|
},
|
||||||
"password-reset": {
|
"password-reset": {
|
||||||
"title": "Reset your password",
|
"title": "Reset your password",
|
||||||
@ -39,6 +40,25 @@
|
|||||||
"submitted": "A mail with further instruction has been sent to <b>{email}</b>"
|
"submitted": "A mail with further instruction has been sent to <b>{email}</b>"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"registration": {
|
||||||
|
"signup": {
|
||||||
|
"title": "Join Human Connection!",
|
||||||
|
"form": {
|
||||||
|
"description": "To get started, enter your email address:",
|
||||||
|
"invitation-code": "Your invitation code is: <b>{code}</b>",
|
||||||
|
"errors": {
|
||||||
|
"email-exists": "There is already a user account with this email address!",
|
||||||
|
"invalid-invitation-token": "It looks like as if the invitation has been used already. Invitation links can only be used once."
|
||||||
|
},
|
||||||
|
"submit": "Create an account",
|
||||||
|
"success": "A mail with a link to complete your registration has been sent to <b>{email}</b>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"create-user-account": {
|
||||||
|
"title": "Create user account",
|
||||||
|
"success": "Your account has been created!"
|
||||||
|
}
|
||||||
|
},
|
||||||
"verify-code": {
|
"verify-code": {
|
||||||
"form": {
|
"form": {
|
||||||
"code": "Enter your code",
|
"code": "Enter your code",
|
||||||
@ -189,6 +209,11 @@
|
|||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"name": "Settings"
|
"name": "Settings"
|
||||||
|
},
|
||||||
|
"invites": {
|
||||||
|
"name": "Invite users",
|
||||||
|
"title": "Signup users as admin",
|
||||||
|
"description": "This registration form will be visible as soon as the registration is open to the public."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"post": {
|
"post": {
|
||||||
|
|||||||
@ -31,10 +31,10 @@ module.exports = {
|
|||||||
'password-reset-request',
|
'password-reset-request',
|
||||||
'password-reset-verify-code',
|
'password-reset-verify-code',
|
||||||
'password-reset-change-password',
|
'password-reset-change-password',
|
||||||
'register',
|
// 'registration-signup', TODO: uncomment to open public registration
|
||||||
'signup',
|
'registration-signup-by-invitation-code',
|
||||||
'reset',
|
'registration-verify-code',
|
||||||
'reset-token',
|
'registration-create-user-account',
|
||||||
'pages-slug',
|
'pages-slug',
|
||||||
],
|
],
|
||||||
// pages to keep alive
|
// pages to keep alive
|
||||||
|
|||||||
@ -52,6 +52,10 @@ export default {
|
|||||||
name: this.$t('admin.tags.name'),
|
name: this.$t('admin.tags.name'),
|
||||||
path: `/admin/tags`,
|
path: `/admin/tags`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: this.$t('admin.invites.name'),
|
||||||
|
path: `/admin/invite`,
|
||||||
|
},
|
||||||
// TODO implement
|
// TODO implement
|
||||||
/* {
|
/* {
|
||||||
name: this.$t('admin.settings.name'),
|
name: this.$t('admin.settings.name'),
|
||||||
|
|||||||
23
webapp/pages/admin/invite.vue
Normal file
23
webapp/pages/admin/invite.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<ds-section>
|
||||||
|
<ds-space>
|
||||||
|
<ds-heading size="h3">
|
||||||
|
{{ $t('admin.invites.title') }}
|
||||||
|
</ds-heading>
|
||||||
|
<ds-text>
|
||||||
|
{{ $t('admin.invites.description') }}
|
||||||
|
</ds-text>
|
||||||
|
</ds-space>
|
||||||
|
<signup />
|
||||||
|
</ds-section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Signup from '~/components/Registration/Signup'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Signup,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -115,7 +115,7 @@ export default {
|
|||||||
async onSubmit() {
|
async onSubmit() {
|
||||||
try {
|
try {
|
||||||
await this.$store.dispatch('auth/login', { ...this.form })
|
await this.$store.dispatch('auth/login', { ...this.form })
|
||||||
this.$toast.success('You are logged in!')
|
this.$toast.success(this.$t('login.success'))
|
||||||
this.$router.replace(this.$route.query.path || '/')
|
this.$router.replace(this.$route.query.path || '/')
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.$toast.error(err.message)
|
this.$toast.error(err.message)
|
||||||
|
|||||||
14
webapp/pages/registration.vue
Normal file
14
webapp/pages/registration.vue
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<nuxt-child />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
layout: 'default',
|
||||||
|
asyncData({ store, redirect }) {
|
||||||
|
if (store.getters['auth/isLoggedIn']) {
|
||||||
|
redirect('/')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
27
webapp/pages/registration/create-user-account.vue
Normal file
27
webapp/pages/registration/create-user-account.vue
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<create-user-account @userCreated="handleUserCreated" :email="email" :nonce="nonce" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CreateUserAccount from '~/components/Registration/CreateUserAccount'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
const { nonce = '', email = '' } = this.$route.query
|
||||||
|
return { nonce, email }
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
CreateUserAccount,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async handleUserCreated({ email, password }) {
|
||||||
|
try {
|
||||||
|
await this.$store.dispatch('auth/login', { email, password })
|
||||||
|
this.$toast.success('You are logged in!')
|
||||||
|
this.$router.push('/')
|
||||||
|
} catch (err) {
|
||||||
|
this.$toast.error(err.message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
Loading…
x
Reference in New Issue
Block a user