Skip to content
This repository has been archived by the owner on Oct 31, 2024. It is now read-only.

Commit

Permalink
feat(default-theme): register a customer on first checkout's step (#519)
Browse files Browse the repository at this point in the history
  • Loading branch information
mkucmus authored Mar 22, 2020
1 parent eef6062 commit 0297504
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 40 deletions.
245 changes: 209 additions & 36 deletions packages/default-theme/components/checkout/PersonalDetails.vue
Original file line number Diff line number Diff line change
@@ -1,30 +1,58 @@
<template>
<div>
<div class="sw-checkout__personal_info">
<SfHeading
title="1. Personal details"
class="sf-heading--left sf-heading--no-underline title"
/>
<SfAlert
v-for="(message, index) in useUserErrorMessages"
:key="index"
class="sw-checkout__personal_info__alert"
type="danger"
:message="message"
/>
<div class="form">
<SfSelect
v-if="getMappedSalutations && getMappedSalutations.length > 0"
v-model="customer.salutationId"
label="Salutation"
:valid="!$v.customer.salutationId.$error"
@change="$v.customer.salutationId.$touch()"
error-message="Salutation must be selected"
class="sf-select--underlined form__element form__element--salutation form__select"
>
<SfSelectOption
v-for="salutationOption in getMappedSalutations"
:key="salutationOption.id"
:value="salutationOption.id || ''"
>
{{ salutationOption.name }}
</SfSelectOption>
</SfSelect>
<SfInput
v-model="firstName"
v-model="customer.firstName"
label="First name"
:valid="!$v.customer.firstName.$error"
error-message="First name is required"
name="firstName"
class="form__element form__element--half"
class="form__element form__element--names"
required
/>
<SfInput
v-model="lastName"
v-model="customer.lastName"
label="Last name"
:valid="!$v.customer.lastName.$error"
error-message="last name is required"
name="lastName"
class="form__element form__element--half form__element--half-even"
required
class="form__element form__element--names form__element--names-even"
/>
<SfInput
v-model="email"
v-model="customer.email"
label="Your email"
:valid="!$v.customer.email.$error"
error-message="Proper email is required"
name="email"
class="form__element"
required
/>
<div class="form__element form__group">
<SfCheckbox
Expand All @@ -40,11 +68,18 @@
<transition name="fade">
<SfInput
v-if="createAccount"
v-model="password"
v-model="customer.password"
type="password"
label="Create Password"
:valid="!$v.customer.password.$error"
:error-message="
//handle two different situations - aligned with validation rules
(!$v.customer.password.required && 'Password is required') ||
(!$v.customer.password.minLength &&
'Password should have at least 8 characters') ||
''
"
class="form__element"
required
/>
</transition>
<div class="form__action">
Expand Down Expand Up @@ -96,23 +131,47 @@ import {
SfButton,
SfHeading,
SfModal,
SfCharacteristic
SfCharacteristic,
SfSelect,
SfProductOption,
SfAlert
} from '@storefront-ui/vue'
import { useUserLoginModal } from '@shopware-pwa/composables'
import { validationMixin } from 'vuelidate'
import {
required,
requiredIf,
email,
minLength
} from 'vuelidate/lib/validators'
import { computed } from '@vue/composition-api'
import {
mapSalutations,
getMessagesFromErrorsArray
} from '@shopware-pwa/helpers'
import {
useUser,
useSalutations,
useCountries,
useUserLoginModal
} from '@shopware-pwa/composables'
import SwLoginModal from '@shopware-pwa/default-theme/components/modals/SwLoginModal'
export default {
name: 'PersonalDetails',
mixins: [validationMixin],
components: {
SfInput,
SfCheckbox,
SfButton,
SfHeading,
SfModal,
SfCharacteristic,
SwLoginModal
SfSelect,
SfProductOption,
SfAlert,
SwLoginModal,
},
props: {
order: {
Expand All @@ -122,10 +181,20 @@ export default {
},
data() {
return {
firstName: '',
lastName: '',
email: '',
password: '',
customer: {
salutationId: null,
firstName: '',
lastName: '',
email: '',
password: '',
billingAddress: {
salutationId: null,
street: '-',
zipcode: '-',
city: '-',
countryId: null
}
},
createAccount: false,
accountBenefits: false,
isLoginModalOpen: false,
Expand All @@ -139,39 +208,137 @@ export default {
},
setup() {
const { toggleModal: toggleLoginModal } = useUserLoginModal()
const {
register: registerUser,
login,
error: userError,
isLoggedIn
} = useUser()
const {
getSalutations,
fetchSalutations,
error: salutationsError
} = useSalutations()
const { fetchCountries, getCountries } = useCountries()
const getMappedSalutations = computed(() =>
mapSalutations(getSalutations.value)
)
return {
registerUser,
login,
isLoggedIn,
fetchSalutations,
getMappedSalutations,
userError,
getCountries,
fetchCountries,
getMessagesFromErrorsArray,
toggleLoginModal
}
},
watch: {
order: {
handler(value) {
this.firstName = value.firstName
this.lastName = value.lastName
this.email = value.email
},
immediate: true
},
createAccount(value) {
if (!value) this.password = ''
if (!value) this.customer.password = ''
},
// hack to register user without picking up the salutation in billing address (minimal registration)
// copy the customer's salutation into billing address
'customer.salutationId': function(value) {
if (value && this.customer && this.customer.billingAddress) {
this.customer.billingAddress.salutationId = value
}
}
},
methods: {
toShipping() {
const order = { ...this.order }
order.firstName = this.firstName
order.lastName = this.lastName
order.email = this.email
order.password = this.password
order.createAccount = this.createAccount
this.$emit('update:order', order)
async toShipping() {
// run the validators against the provided data
// consider using $touch event on $blur event in each input
this.$v.$touch()
if (this.$v.$invalid) {
return
}
if (this.createAccount) {
const isRegistered = await this.registerUser(this.customer)
if (!isRegistered) {
return
}
// extra login step won't be necessary once the register has a autologin option
await this.login({
username: this.customer.email,
password: this.customer.password
})
if (!this.isLoggedIn) {
return
}
}
return this.$emit('proceed:shipping')
}
},
computed: {
useUserErrorMessages() {
// all the 400 errors are in a raw format stright from the API - to be extracted easily depeding on needs.
return this.userError && this.getMessagesFromErrorsArray(this.userError)
}
},
async mounted() {
// hack to register user without picking up the country (minimal registration)
await this.fetchCountries()
if (!this.getCountries) {
return
}
const pickedCountry = this.getCountries.find(
({ name }) => name === 'Poland'
)
this.customer.billingAddress.countryId = pickedCountry && pickedCountry.id
// select "not specified" salutation (works for EN) as default salutation
await this.fetchSalutations();
const defaultSalutation = this.getMappedSalutations.find(({id, name}) => name == 'Not specified')
this.customer.salutationId = defaultSalutation && defaultSalutation.id
},
// TODO: move all the rules globally
validations: {
customer: {
salutationId: {
required
},
firstName: {
required
},
lastName: {
required
},
password: {
required: requiredIf(function(password) {
return this.createAccount
}),
minLength: minLength(8)
},
email: {
required,
email
}
}
}
}
</script>
<style lang="scss" scoped>
@import '~@storefront-ui/vue/styles';
.sw-checkout {
&__personal_info {
&__alert {
margin-bottom: 20px;
}
}
}
.title {
margin-bottom: var(--spacer-extra-big);
}
Expand All @@ -186,9 +353,15 @@ export default {
@include for-desktop {
flex: 0 0 100%;
}
&--half {
&--salutation {
@include for-desktop {
flex: 1 1 25%;
padding-right: var(--spacer-extra-big);
}
}
&--names {
@include for-desktop {
flex: 1 1 50%;
flex: 1 1 30%;
}
&-even {
@include for-desktop {
Expand Down
8 changes: 4 additions & 4 deletions packages/default-theme/pages/checkout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
<div class="checkout__main">
<SfSteps :active="currentStep" @change="updateStep($event)">
<SfStep name="Personal Details">
<PersonalDetails
:order="order"
@update:order="updateOrder($event)"
/>
<PersonalDetails :order="order" @proceed:shipping="proceed()" />
</SfStep>
<SfStep name="Shipping">
<Shipping
Expand Down Expand Up @@ -239,6 +236,9 @@ export default {
this.currentStep = next
}
},
proceed() {
this.currentStep++
},
updateOrder(order, next = true) {
this.order = { ...this.order, ...order }
if (next) {
Expand Down

0 comments on commit 0297504

Please sign in to comment.