Ocelot-Social/webapp/components/Registration/RegistrationSlideCreate.vue

488 lines
15 KiB
Vue
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div v-if="response === 'success'">
<transition name="ds-transition-fade">
<sweetalert-icon icon="success" />
</transition>
<p class="ds-text ds-text-center ds-text-bold ds-text-success">
{{ $t('components.registration.create-user-account.success') }}
</p>
<div class="ds-my-xxx-small"></div>
</div>
<div v-else-if="response === 'error'">
<transition name="ds-transition-fade">
<sweetalert-icon icon="error" />
</transition>
<p class="ds-text ds-text-center ds-text-bold ds-text-danger">
{{ $t('components.registration.create-user-account.error') }}
</p>
<p class="ds-text ds-text-center">
{{ $t('components.registration.create-user-account.help') }}
<a :href="'mailto:' + supportEmail">{{ supportEmail }}</a>
</p>
<div class="ds-mb-large ds-space-centered">
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
</div>
<div class="ds-my-xxx-small"></div>
</div>
<div v-else class="create-account-card">
<ds-form
class="create-user-account"
v-model="formData"
:schema="formSchema"
@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>
<email-display-and-verify :email="sliderData.collectedInputData.email" />
<div v-if="askForRealName" class="full-name">
<!-- <p>{{ $t('settings.data.realNamePlease') }}</p>-->
<ds-input
id="givenName"
model="givenName"
:label="$t('settings.data.givenName')"
:placeholder="$t('settings.data.givenNamePlaceholder')"
/>
<ds-input
id="surName"
model="surName"
:label="$t('settings.data.surName')"
:placeholder="$t('settings.data.surNamePlaceholder')"
/>
</div>
<ds-input
v-else
id="name"
model="name"
:label="$t('settings.data.labelName')"
:placeholder="$t('settings.data.namePlaceholder')"
/>
<div class="password-wrapper">
<ds-input
id="password"
model="password"
:type="showPassword ? 'text' : 'password'"
:label="$t('settings.security.change-password.label-new-password')"
autocomplete="off"
class="password-field"
ref="password"
/>
<show-password
class="show-password-toggle with-label"
@show-password="toggleShowPassword('password')"
:icon="passwordIcon"
/>
</div>
<div class="password-wrapper">
<ds-input
id="passwordConfirmation"
model="passwordConfirmation"
:type="showPasswordConfirm ? 'text' : 'password'"
:label="$t('settings.security.change-password.label-new-password-confirm')"
autocomplete="off"
class="password-field"
ref="confirmPassword"
/>
<show-password
class="show-password-toggle with-label"
@show-password="toggleShowPassword('confirmPassword')"
:icon="passwordConfirmIcon"
/>
</div>
<password-strength
class="password-strength"
:password="formData.password"
style="margin: 30px 0 20px 0"
/>
<!-- location -->
<location-select
v-if="locationRequired"
v-model="locationName"
:canBeCleared="false"
:showPreviousLocation="false"
/>
<div class="checkbox-group">
<div class="checkbox-item">
<input
id="checkbox0"
type="checkbox"
v-model="termsAndConditionsConfirmed"
:checked="termsAndConditionsConfirmed"
class="checkbox-input"
/>
<label for="checkbox0" class="checkbox-label">
<span class="checkbox-text">
{{ $t('components.registration.create-user-account.termsAndCondsEtcConfirmed') }}
</span>
<div class="checkbox-links">
<page-params-link :pageParams="links.TERMS_AND_CONDITIONS" forceTargetBlank>
{{ $t('site.termsAndConditions') }}
</page-params-link>
<span class="separator"></span>
<page-params-link :pageParams="links.DATA_PRIVACY" forceTargetBlank>
{{ $t('site.data-privacy') }}
</page-params-link>
</div>
</label>
</div>
<div class="checkbox-item">
<input
id="checkbox1"
type="checkbox"
v-model="receiveCommunicationAsEmailsEtcConfirmed"
:checked="receiveCommunicationAsEmailsEtcConfirmed"
class="checkbox-input"
/>
<label for="checkbox1" class="checkbox-label">
<span class="checkbox-text">
{{
$t(
'components.registration.create-user-account.receiveCommunicationAsEmailsEtcConfirmed',
)
}}
</span>
</label>
</div>
</div>
</template>
<div class="ds-my-xxx-small"></div>
</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 { SignupVerificationMutation } from '~/graphql/Registration.js'
import { SweetalertIcon } from 'vue-sweetalert-icons'
import PasswordStrength from '~/components/Password/Strength'
import EmailDisplayAndVerify from './EmailDisplayAndVerify'
import PageParamsLink from '~/components/_new/features/PageParamsLink/PageParamsLink'
import PasswordForm from '~/components/utils/PasswordFormHelper'
import { iconRegistry } from '~/utils/iconRegistry'
import ShowPassword from '../ShowPassword/ShowPassword.vue'
import LocationSelect from '~/components/Select/LocationSelect'
const threePerEmSpace = '' // unicode u+2004;
export default {
name: 'RegistrationSlideCreate',
components: {
EmailDisplayAndVerify,
PageParamsLink,
PasswordStrength,
ShowPassword,
SweetalertIcon,
LocationSelect,
},
props: {
sliderData: { type: Object, required: true },
},
data() {
const passwordForm = PasswordForm({ translate: this.$t })
return {
links,
supportEmail: emails.SUPPORT_EMAIL,
formData: {
givenName: '',
surName: '',
name: '',
...passwordForm.formData,
},
formSchema: {
givenName: {
type: 'string',
required: this.$env.ASK_FOR_REAL_NAME,
min: 2,
},
surName: {
type: 'string',
required: this.$env.ASK_FOR_REAL_NAME,
min: 2,
},
name: {
type: 'string',
required: !this.$env.ASK_FOR_REAL_NAME,
min: 3,
},
...passwordForm.formSchema,
},
response: null,
// TODO: Our styleguide does not support checkmarks.
// Integrate termsAndConditionsConfirmed into `this.formData` once we
// have checkmarks available.
locationName: '',
termsAndConditionsConfirmed: false,
receiveCommunicationAsEmailsEtcConfirmed: false,
showPassword: false,
showPasswordConfirm: false,
}
},
mounted: function () {
if (this.askForRealName) {
if (this.sliderData.collectedInputData.name) {
const split = this.sliderData.collectedInputData.name.split(threePerEmSpace)
this.formData.givenName = split[0]
this.formData.surName = split[1] || ''
} else {
this.formData.surName = ''
this.formData.givenName = ''
}
} else {
this.formData.name = this.sliderData.collectedInputData.name || ''
}
this.formData.password = this.sliderData.collectedInputData.password || ''
this.formData.passwordConfirmation =
this.sliderData.collectedInputData.passwordConfirmation || ''
this.termsAndConditionsConfirmed =
this.sliderData.collectedInputData.termsAndConditionsConfirmed || false
this.receiveCommunicationAsEmailsEtcConfirmed =
this.sliderData.collectedInputData.receiveCommunicationAsEmailsEtcConfirmed || false
this.locationName = this.sliderData.collectedInputData.locationName || ''
this.sendValidation()
this.sliderData.setSliderValuesCallback(this.validInput, {
sliderSettings: { buttonSliderCallback: this.onNextClick },
})
},
computed: {
formLocationName() {
// toDo: Mixin or move it to location select component
const isNestedValue =
typeof this.locationName === 'object' && typeof this.locationName.value === 'string'
const isDirectString = typeof this.locationName === 'string'
return isNestedValue ? this.locationName.value : isDirectString ? this.locationName : ''
},
locationRequired() {
return this.$env.REQUIRE_LOCATION
},
askForRealName() {
return this.$env.ASK_FOR_REAL_NAME
},
validInput() {
return (
(this.askForRealName
? this.formData.givenName.length >= 2 && this.formData.surName.length >= 2
: this.formData.name.length >= 3) &&
this.formData.password.length >= 1 &&
this.formData.password === this.formData.passwordConfirmation &&
this.termsAndConditionsConfirmed &&
this.receiveCommunicationAsEmailsEtcConfirmed &&
(this.locationRequired ? this.formLocationName : true)
)
},
passwordIcon() {
return this.showPassword ? this.icons.eyeSlash : this.icons.eye
},
passwordConfirmIcon() {
return this.showPasswordConfirm ? this.icons.eyeSlash : this.icons.eye
},
},
watch: {
termsAndConditionsConfirmed() {
this.sendValidation()
},
receiveCommunicationAsEmailsEtcConfirmed() {
this.sendValidation()
},
locationName() {
this.sendValidation()
},
},
created() {
this.icons = iconRegistry
},
methods: {
buildName(data) {
if (this.askForRealName) return `${data.givenName}${threePerEmSpace}${data.surName}`
return data.name
},
sendValidation() {
const { password, passwordConfirmation } = this.formData
const name = this.buildName(this.formData)
const { termsAndConditionsConfirmed, receiveCommunicationAsEmailsEtcConfirmed } = this
const locationName = this.formLocationName
this.sliderData.setSliderValuesCallback(this.validInput, {
collectedInputData: {
name,
password,
passwordConfirmation,
termsAndConditionsConfirmed,
receiveCommunicationAsEmailsEtcConfirmed,
locationName,
},
})
},
async handleInput() {
this.sendValidation()
},
async handleInputValid() {
this.sendValidation()
},
async submit() {
const { password } = this.formData
const name = this.buildName(this.formData).replace(threePerEmSpace, ' ')
const { email, inviteCode = null, nonce } = this.sliderData.collectedInputData
const termsAndConditionsAgreedVersion = VERSION
const locale = this.$i18n.locale()
try {
this.sliderData.setSliderValuesCallback(null, {
sliderSettings: { buttonLoading: true },
})
await this.$apollo.mutate({
mutation: SignupVerificationMutation,
variables: {
name,
password,
email,
inviteCode,
nonce,
termsAndConditionsAgreedVersion,
locale,
locationName: this.formLocationName,
},
})
this.response = 'success'
setTimeout(async () => {
await this.$store.dispatch('auth/login', { email, password })
this.$toast.success(this.$t('login.success'))
const { validateInviteCode } = this.sliderData.sliders[0].data.response
if (
validateInviteCode &&
validateInviteCode.invitedTo &&
validateInviteCode.invitedTo.groupType === 'public'
) {
const { invitedTo } = validateInviteCode
this.$router.push(`/groups/${invitedTo.slug}`)
} else {
this.$router.push('/')
}
this.sliderData.setSliderValuesCallback(null, {
sliderSettings: { buttonLoading: false },
})
}, 3000)
} catch (err) {
this.sliderData.setSliderValuesCallback(null, {
sliderSettings: { buttonLoading: false },
})
this.response = 'error'
}
},
onNextClick() {
this.submit()
return true
},
toggleShowPassword(e) {
if (e === 'password') {
this.showPassword = !this.showPassword
this.$nextTick(() => {
this.$refs.password.$el.children[1].children[0].focus()
this.$emit('focus')
})
} else {
this.showPasswordConfirm = !this.showPasswordConfirm
this.$nextTick(() => {
this.$refs.confirmPassword.$el.children[1].children[0].focus()
this.$emit('focus')
})
}
},
},
}
</script>
<style lang="scss" scoped>
.password-strength {
margin-bottom: 14px;
}
.password-wrapper {
display: flex;
width: 100%;
align-items: center;
padding: 0;
height: $input-height;
margin-top: 40px;
margin-bottom: 16px;
.password-field {
width: 100%;
padding: 0;
}
::v-deep .ds-input-wrap {
bottom: 4px;
left: -1px;
}
}
.full-name {
padding-bottom: $space-small;
}
.checkbox-group {
margin: 20px 0;
}
.checkbox-item {
display: flex;
align-items: flex-start;
margin-bottom: 16px;
padding: 16px;
background-color: #f8f9fa; // Light gray background
border-radius: 8px;
transition: background-color 0.2s ease;
&:hover {
background-color: #f0f2f5; // Slightly darker on hover
}
&:last-child {
margin-bottom: 0;
}
}
.checkbox-input {
flex-shrink: 0;
width: 18px;
height: 18px;
margin-right: 12px;
margin-top: 2px; // Align with first line of text
cursor: pointer;
}
.checkbox-label {
flex: 1;
cursor: pointer;
line-height: 1.5;
color: #374151; // Dark gray text
}
.checkbox-text {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.checkbox-links {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
font-size: 0.875rem; // Slightly smaller text for links
.separator {
color: #9ca3af; // Light gray separator
font-size: 0.75rem;
}
}
</style>