Explorar el Código

Implemented Payment page (#28)

feat: implement add stripe card

feat: implement login process

Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

feat: implement payment page

feat: implement mail verification screen

Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

feat: prevent registration when the configs.allowRegistration is set to false

fix: weird routing

Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

feat: implement actual pages

Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

feat: api integration for register page

fix: messages

feat: api integration for forget-password page

fix: login title and password placeholder

feat: disable button until get response

Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

feat: integrate api for new login page

feat: implement responsive header

feat: implement checkbox

Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

fix: checkbox event issue

feat: implement register page

feat: implement registration page

Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

feat: implement UI for forgot-password

Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

feat: implement new login page

feat: implement input shared component

Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

feat: implement auth header

Merge branch 'feat/ui-layout' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout

capitalize filename

fix: webpack config to add output default url

Merge branch 'master' into feat/ui-layout

Merge branch 'master' into feat/ui-layout

feat: integrate lazy loading and new page structure

feat: use different layout for new Pages

Co-authored-by: Tyler <everdev0923@gmail.com>
Co-authored-by: Jonathan Cobb <jonathan@kyuss.org>
Co-authored-by: jonathan <jonathan@noreply.git.bubblev.org>
Reviewed-on: https://git.bubblev.org/bubblev/bubble-web/pulls/28
pull/32/head
Tyler Chen hace 4 años
committed by jonathan
padre
commit
942a448586
Se han modificado 20 ficheros con 883 adiciones y 79 borrados
  1. +8
    -0
      package-lock.json
  2. +0
    -6
      src/_assets/messages.json
  3. +7
    -0
      src/_assets/post_auth_messages.json
  4. +29
    -0
      src/_assets/pre_auth_messages.json
  5. +1
    -3
      src/_components/layout/Header.vue
  6. +1
    -1
      src/_components/sections/Features.vue
  7. +2
    -0
      src/_components/shared/Button.vue
  8. +13
    -3
      src/_components/shared/Card.vue
  9. +2
    -2
      src/_pages/auth/ForgotPassword.vue
  10. +9
    -3
      src/_pages/auth/Layout.vue
  11. +3
    -2
      src/_pages/auth/Login.vue
  12. +1
    -38
      src/_pages/auth/Register.vue
  13. +150
    -5
      src/_pages/main/Layout.vue
  14. +56
    -0
      src/_pages/main/account/Layout.vue
  15. +446
    -0
      src/_pages/main/account/Payment.vue
  16. +124
    -0
      src/_pages/main/account/VerifyEmail.vue
  17. +13
    -9
      src/_router/index.js
  18. +2
    -2
      src/_scss/components/_form.scss
  19. +9
    -0
      src/_store/index.js
  20. +7
    -5
      src/_store/system.module.js

+ 8
- 0
package-lock.json Ver fichero

@@ -7751,6 +7751,14 @@
}
}
},
"vue-lottie": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/vue-lottie/-/vue-lottie-0.2.1.tgz",
"integrity": "sha512-zInUX69Ij8MhVR3XArpu4PqqBoufwKxS5UMutWCPm59VUaB5H6GtnaIzf9M+l6aYU+Kr8gF/W9dzWLgRuU6V+Q==",
"requires": {
"lottie-web": "^5.1.9"
}
},
"vue-router": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.6.tgz",


+ 0
- 6
src/_assets/messages.json Ver fichero

@@ -1,6 +0,0 @@
{
"verify_email_title": "Please verify your email address",
"resend_verify_email_label": "Did you not received the email?",
"button_label_resend_verify_email": "Resend it.",
"more_features_label": "More Features"
}

+ 7
- 0
src/_assets/post_auth_messages.json Ver fichero

@@ -0,0 +1,7 @@
{
"payment_page_title": "Please add a payment method",
"payment_page_sub_title": "We need a CC card to secure your place in the Bubblesphere.",

"label_bubble_free_title": "Bubble is free for 30 days.",
"label_bubble_free_description": "We think you'll love the security of being in your Bubble.<br/> If for any reason you want to cancel, that's easy too!"
}

+ 29
- 0
src/_assets/pre_auth_messages.json Ver fichero

@@ -0,0 +1,29 @@
{
"verify_email_title": "Please verify your email address",
"resend_verify_email_label": "Did you not received the email?",
"button_label_resend_verify_email": "Resend it.",
"more_features_label": "More Features",

"button_label_add_card": "Add Card",
"label_pricing_option_format": "{{messages[`marketing_pricing_${plan}_title`]}}: {{messages.currency_symbol_USD}} {{price}} monthly (free for 30 days)",

"marketing_pricing_options": "personal,power,mega",

"marketing_pricing_personal_title": "Personal Bubble",
"marketing_pricing_personal_users": "1 User Account",
"marketing_pricing_personal_price": "1200",
"marketing_pricing_personal_options": "1 User Account,1TB/Month of Data Transfer",
"marketing_pricing_personal_link": "/register?plan=bubble",

"marketing_pricing_power_title": "Power Plan",
"marketing_pricing_power_users": "5 User Accounts",
"marketing_pricing_power_price": "1900",
"marketing_pricing_power_options": "5 User Accounts,2TB/Month of Data Transfer",
"marketing_pricing_power_link": "/register?plan=bubble_plus",

"marketing_pricing_mega_title": "Mega Plan",
"marketing_pricing_mega_users": "10 User Accounts",
"marketing_pricing_mega_price": "3100",
"marketing_pricing_mega_options": "10 User Accounts,3TB/Month of Data Transfer",
"marketing_pricing_mega_link": "/register?plan=bubble_super"
}

+ 1
- 3
src/_components/layout/Header.vue Ver fichero

@@ -7,7 +7,7 @@
<div class="navbar d-none d-md-flex">
<!--- If not logged in --->
<div
v-if="status.loggedIn === false"
v-if="status.loggedIn !== true"
class="d-flex justify-content-center align-items-center"
>
<router-link to="/help" class="d-flex align-items-center">
@@ -146,11 +146,9 @@ export default {

methods: {
toggleNavbar() {
console.log('toggleNavbar');
this.menuVisible = !this.prevVisibleState;
},
hide() {
console.log('hide');
this.prevVisibleState = this.menuVisible;
this.menuVisible = false;
},


+ 1
- 1
src/_components/sections/Features.vue Ver fichero

@@ -11,7 +11,7 @@
:key="index"
class="col-lg-3 col-md-6 col-sm-12 my-4 px-3"
>
<Card>
<Card class="h-100">
<div class="card-content">
<span
class="card-icon"


+ 2
- 0
src/_components/shared/Button.vue Ver fichero

@@ -49,10 +49,12 @@

&.block {
display: block;
width: 100%;
}

.btn--text {
text-transform: uppercase;
white-space: nowrap;
padding: 5px 20px;
}



+ 13
- 3
src/_components/shared/Card.vue Ver fichero

@@ -1,6 +1,6 @@
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ -->
<template>
<div class="card-container h-100">
<div class="card-container" :class="{ 'rounded-corner': roundCorner }">
<slot></slot>
</div>
</template>
@@ -9,8 +9,11 @@
.card-container {
background-color: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0px 10px 50px #dee1ec;

&.rounded-corner {
border-radius: 10px;
}
}

.card-content {
@@ -47,5 +50,12 @@
</style>

<script>
export default {};
export default {
props: {
roundCorner: {
type: Boolean,
default: true,
},
},
};
</script>

+ 2
- 2
src/_pages/auth/ForgotPassword.vue Ver fichero

@@ -8,7 +8,7 @@
{{ messages.forgot_password_blurb }}
</h4>

<form class="auth-form" @submit.prevent="handleSubmit">
<form class="bubble-form" @submit.prevent="handleSubmit">
<div class="form-group">
<Input
class="form-control"
@@ -30,7 +30,7 @@
</div>
<Button
color="default"
class="auth-form-submit"
class="bubble-form-submit"
@click="handleSubmit"
:disabled="status.sendingResetPasswordMessage"
>


+ 9
- 3
src/_pages/auth/Layout.vue Ver fichero

@@ -42,13 +42,19 @@ export default {
computed: {
backgroundClass() {
const urlNodes = this.$route.fullPath.split('/');
switch (urlNodes[1]) {
case 'login':
case 'forgotPassword':
return 'background1';
}

// new pages in progress
switch (urlNodes[2]) {
case 'login':
case 'forgot-password':
case 'forgotPassword':
return 'background1';
default:
return 'background2';
}

return 'background2';
},
},


+ 3
- 2
src/_pages/auth/Login.vue Ver fichero

@@ -10,7 +10,7 @@
{{ messages.login_blurb }}
</h4>

<form class="auth-form" @submit.prevent="submit">
<form class="bubble-form" @submit.prevent="submit">
<h4
v-if="submitted && errors.has('approvalToken')"
class="invalid-feedback d-block"
@@ -131,8 +131,9 @@
{{ messages.button_label_forgotPassword }}
</router-link>
<Button
block
color="default"
class="auth-form-submit"
class="bubble-form-submit"
@click="submit"
:disabled="status.loggingIn"
>


+ 1
- 38
src/_pages/auth/Register.vue Ver fichero

@@ -8,7 +8,7 @@
{{ messages.register_blurb }}
</h4>

<form class="auth-form" @submit.prevent="handleSubmit">
<form class="bubble-form" @submit.prevent="handleSubmit">
<div class="form-group">
<Input
class="form-control"
@@ -157,43 +157,6 @@
>
{{ messages.marketing_pricing_title }}
</a>
<!-- <div class="row px-5 mx-5">
<div class="col-12 d-flex">
<div
class="plan flex-grow-1"
v-for="(plan, index) in messages.marketing_pricing_options.split(',')"
:key="index"
>
<p class="plan-name">
{{ messages[`marketing_pricing_${plan}_title`] }}
</p>
<p class="plan-users">
{{ messages[`marketing_pricing_${plan}_users`] }}
</p>
<p class="plan-pricing">
{{ messages[`marketing_pricing_${plan}_users`] }}
</p>
<p
class="plan-common-features"
v-for="option in messages[`marketing_pricing_common_options`].split(
','
)"
:key="option"
>
{{ option }}
</p>
<p
class="plan-features"
v-for="option in messages[
`marketing_pricing_${plan}_options`
].split(',')"
:key="option"
>
{{ option }}
</p>
</div>
</div>
</div> -->
</div>
</template>



+ 150
- 5
src/_pages/main/Layout.vue Ver fichero

@@ -1,11 +1,156 @@
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ -->
<template>
<div>
Main Layout
<router-view></router-view>
</div>
<router-view></router-view>
</template>

<script>
export default {};
import { mapState, mapActions } from 'vuex';
import { util } from '~/_helpers';
import { isAuthenticator, isNotAuthenticator } from '~/_store/users.module';

export default {
data: () => ({
verifiedContacts: null,
verifiedContactRefresher: null,
accountPlan: {
name: '',
domain: '',
locale: util.currentUser().locale,
timezone: '',
plan: 'bubble',
footprint: 'Worldwide',
paymentMethodObject: {
uuid: null,
paymentMethodType: null,
paymentInfo: null,
},
sshKey: '',
forkHost: '',
syncPassword: true,
launchLock: false,
sendErrors: true,
sendMetrics: true,
},
payMethods: null,
}),

computed: {
...mapState('users', ['policy']),
...mapState('paymentMethods', ['accountPaymentMethods']),
},

mounted() {
this.initDefaults();
},

methods: {
...mapActions('users', ['getPolicyByUserId']),
...mapActions('paymentMethods', ['getAllAccountPaymentMethods']),

initDefaults() {
const currentUser = util.currentUser();
const selectedLocale =
currentUser !== null &&
typeof currentUser.locale !== 'undefined' &&
currentUser.locale !== null
? currentUser.locale
: 'detect';
this.getPolicyByUserId({
userId: currentUser.uuid,
messages: this.messages,
errors: this.errors,
});
},

hasVerifiedContact(policy) {
if (policy && policy.accountContacts) {
const contacts = policy.accountContacts;
for (let i = 0; i < contacts.length; i++) {
if (contacts[i].verified && isNotAuthenticator(contacts[i]))
return true;
}
return false;
}
return false;
},

navigateToVerifyEmail() {
if (this.$route.path !== '/new_pages/verify-email') {
this.$router.push('/new_pages/verify-email');
}
},

navigateToPaymentPage() {
if (this.$route.path !== '/new_pages/payment') {
this.$router.push('/new_pages/payment');
}
},

navigateToDashboard() {
if (
this.$route.path === '/new_pages/payment' ||
this.$route.path === '/new_pages/verify-email'
) {
this.$router.push('/new_pages/test');
}
},
},

watch: {
policy(p) {
this.verifiedContacts = this.hasVerifiedContact(p);
if (!this.verifiedContacts) {
this.navigateToVerifyEmail();
if (this.verifiedContactRefresher === null) {
const vue = this;
const currentUser = util.currentUser();
this.verifiedContactRefresher = window.setInterval(() => {
vue.getPolicyByUserId({
userId: currentUser.uuid,
messages: vue.messages,
errors: vue.errors,
});
}, 5000);
}
} else {
console.log('hasVerified Contact');
const currentUser = util.currentUser();
this.getAllAccountPaymentMethods({
userId: currentUser.uuid,
messages: this.messages,
errors: this.errors,
});
if (this.verifiedContactRefresher !== null) {
window.clearInterval(this.verifiedContactRefresher);
this.verifiedContactRefresher = null;
}
}
},

accountPaymentMethods(pms) {
if (pms) {
const payMethods = [];
for (let i = 0; i < pms.length; i++) {
const pm = pms[i];
if (
(typeof pm.promotion === 'undefined' ||
pm.promotion === null ||
!pm.promotion) &&
(typeof pm.deleted === 'undefined' || pm.deleted === null)
) {
payMethods.push(pm);
}
}
if (
this.accountPlan.paymentMethodObject.uuid === null &&
payMethods.length > 0
) {
this.navigateToDashboard();
} else {
this.navigateToPaymentPage();
}
}
},
},
};
</script>

+ 56
- 0
src/_pages/main/account/Layout.vue Ver fichero

@@ -0,0 +1,56 @@
<template>
<div :class="backgroundClass" class="auth-layout d-flex flex-column content">
<router-view></router-view>
</div>
</template>

<style lang="scss" scoped>
@import '../../../_scss/breakpoints';

.content {
background-repeat: no-repeat;
background-size: 100% auto;
background-position-x: center;

padding: 80px;

@include respond-below(sm) {
background-size: 200%, auto;
padding: 20px;
}
}

.background1 {
background-image: url('/background1.svg');
}

.background2 {
background-image: url('/background2.svg');
}
</style>

<script>
import { Header } from '~/_components/layout';
export default {
components: {
Header,
},
computed: {
backgroundClass() {
const urlNodes = this.$route.fullPath.split('/');
switch (urlNodes[1]) {
case 'payment':
return 'background1';
}

// new pages in progress
switch (urlNodes[2]) {
case 'payment':
return 'background1';
}

return 'background2';
},
},
};
</script>

+ 446
- 0
src/_pages/main/account/Payment.vue Ver fichero

@@ -0,0 +1,446 @@
<template>
<div class="wrapper">
<h1 class="text-center white-text form-title">
{{ messages.payment_page_title }}
</h1>
<h4
class="d-flex align-items-center justify-content-center white-text form-sub-title"
>
{{ messages.payment_page_sub_title }}
</h4>

<form class="bubble-form" @submit.prevent="authorizeCard">
<select
v-validate="'required'"
v-if="planObjects"
v-model="bubblePlan"
name="plan"
class="form-control"
:class="{ 'is-invalid': submitted && errors.has('plan') }"
>
<option
v-for="(plan, index) in planObjects"
:value="plan.name"
:key="index"
>
{{ messages['plan_name_' + plan.name] }} -
{{
messages.price_format.parseExpression({
messages: messages,
...plan,
})
}}
</option>
</select>

<div id="card-number" class="form-control mt-3" />

<div class="mt-3 d-flex">
<div id="card-expiry" class="form-control mr-1" />
<div id="card-cvc" class="form-control" />
<div class="flex-grow-1"></div>
<div id="card-zip" class="form-control" />
</div>

<p class="text-center mt-3">
<small>
{{ messages['label_promotion_FirstMonthFree_description'] }}
</small>
</p>

<p v-if="stripeError" class="invalid-feedback d-block">
{{ stripeError }}
</p>
<div
v-if="submitted && errors.has('purchase')"
class="invalid-feedback d-block"
>
{{ errors.first('purchase') }}
</div>
<div
v-if="submitted && errors.has('paymentMethod')"
class="invalid-feedback d-block"
>
{{ errors.first('paymentMethod') }}
</div>
<div
v-if="submitted && errors.has('paymentMethodInfo')"
class="invalid-feedback d-block"
>
{{ errors.first('paymentMethodInfo') }}
</div>
<div
v-if="submitted && errors.has('paymentMethodType')"
class="invalid-feedback d-block"
>
{{ errors.first('paymentMethodType') }}
</div>
<div
v-if="submitted && errors.has('paymentMethodService')"
class="invalid-feedback d-block"
>
{{ errors.first('paymentMethodService') }}
</div>
<div
v-if="submitted && errors.has('paymentInfo')"
class="invalid-feedback d-block"
>
{{ errors.first('paymentInfo') }}
</div>
<div
v-if="submitted && errors.has('plan')"
class="invalid-feedback d-block"
>
{{ errors.first('plan') }}
</div>

<Button
block
color="default"
class="bubble-form-submit"
@click="authorizeCard"
v-if="
paymentStatus.addingPaymentMethod || !paymentStatus.addedPaymentMethod
"
:disabled="paymentStatus.addingPaymentMethod"
>
{{ messages.button_label_add_card }}
</Button>
</form>

<!--- Pricing Section --->
<div class="mt-5">
<h2 class="text-center">
{{ messages.label_bubble_free_title }}
</h2>
<h5
class="text-center"
v-html="messages.label_bubble_free_description"
></h5>
</div>

<div
class="row px-5 mx-5 mt-5"
v-if="messages && messages.marketing_pricing_common_options"
>
<div class="col-12 d-flex plan-section">
<Card
class="plan flex-grow-1"
v-for="(plan, index) in messages.marketing_pricing_options.split(',')"
:round-corner="false"
:key="index"
>
<p class="plan-name">
{{ messages[`marketing_pricing_${plan}_title`] }}
</p>
<p class="plan-users">
{{ messages[`marketing_pricing_${plan}_users`] }}
</p>
<p class="plan-pricing">
<span class="symbol">
{{ messages.currency_symbol_USD }}
</span>
<span class="price">
{{ messages[`marketing_pricing_${plan}_price`].parsePrice() }}
</span>
<span class="period">
{{ messages.marketing_pricing_period }}
</span>
</p>
<p
class="plan-common-features text-center"
v-for="option in messages.marketing_pricing_common_options.split(
','
)"
:key="option"
>
{{ option }}
</p>
<p
class="plan-features text-center"
v-for="option in messages[
`marketing_pricing_${plan}_options`
].split(',')"
:key="option"
>
{{ option }}
</p>
</Card>
</div>
</div>
</div>
</template>

<style lang="scss" scoped>
@import '../../../_scss/components/form';

.features-section-link {
color: $vivid-navy;
font-size: 16px;
margin-top: 25px;
}

.plan-section {
@include respond-below(md) {
flex-direction: column;
}
}

#card-expiry,
#card-cvc,
#card-zip {
max-width: 25%;
}

.plan {
display: flex;
flex-direction: column;
align-items: center;

padding: 20px;
background-color: white;

&:nth-child(2n + 1) {
margin-top: 30px;
color: $vivid-navy;
}
&:nth-child(2n) {
z-index: 1;
color: $strong-purple-1;
}

.plan-users {
color: inherit;
font-weight: 700;
font-size: 0.9em;
}

.plan-pricing {
color: inherit;
display: flex;

margin: 2em 0;

.symbol {
font-size: 1.5em;
}
.price {
font-size: 5em;
line-height: 1em;
font-weight: bold;
}
.period {
font-size: 1.5em;
align-self: flex-end;
}
}

.plan-name {
color: #2e2545;
font-size: 1.5em;
font-weight: 500;
}

.plan-common-features {
color: #8585bd;
}
.plan-features {
color: #8585bd;
font-weight: bold;
}
}
</style>

<script>
import { mapState, mapActions } from 'vuex';
import { util } from '~/_helpers';

import { Button, Card } from '~/_components/shared';

export default {
components: {
Button,
Card,
},

data() {
return {
user: util.currentUser(),
bubblePlan: '',
card: {
cvc: '',
number: '',
expiry: '',
zip: '',
},
cardNumber: '',
cardExpiry: '',
cardCvc: '',
cardZip: '',
brand: '',
stripe: null,
stripeError: '',
submitted: false,
};
},

computed: {
...mapState('system', ['messages']),
...mapState('paymentMethods', ['paymentMethods', 'paymentStatus']),
...mapState('plans', ['plans']),

planObjects: function() {
const plans_array = [];
if (this.plans) {
for (let i = 0; i < this.plans.length; i++) {
plans_array.push({
...this.plans[i],
localName: this.messages['plan_name_' + this.plans[i].name],
description: this.messages[
'plan_description_' + this.plans[i].name
],
priceMajorUnits: this.plans[i].price / 100,
priceMinorUnits: this.plans[i].price % 100,
});
}
this.bubblePlan = this.plans[0].name;
}
return plans_array;
},
},

created() {
this.initDefaults();
},

mounted() {},

methods: {
...mapActions('paymentMethods', [
'getAllPaymentMethods',
'setPaymentMethod',
'addAccountPaymentMethod',
'getAllAccountPaymentMethods',
]),
...mapActions('plans', ['getAllPlans']),

setUpStripe() {
if (window.Stripe === undefined) {
alert('Stripe V3 library not loaded!');
} else {
const stripe = window.Stripe(
this.paymentMethods[0].driverConfig.publicApiKey
);
this.stripe = stripe;

const style = {
base: {
padding: '.375rem .75rem',
fontSize: '1rem',
fontWeight: '400',
lineHeight: '1.5',
color: '#495057',
},
};

const elements = stripe.elements();
this.cardCvc = elements.create('cardCvc', { style });
this.cardExpiry = elements.create('cardExpiry', { style });
this.cardNumber = elements.create('cardNumber', { style });
this.cardZip = elements.create('postalCode', {
placeholder: 'Zip',
style,
});
this.cardNumber.update({
placeholder: 'CC Card #',
showIcon: true,
});

this.cardCvc.mount('#card-cvc');
this.cardExpiry.mount('#card-expiry');
this.cardNumber.mount('#card-number');
this.cardZip.mount('#card-zip');

this.listenForErrors();
}
},

listenForErrors() {
this.cardNumber.addEventListener('change', (event) => {
this.toggleError(event);
this.cardNumberError = '';
this.card.number = event.complete ? true : false;
if (this.card.number) {
this.brand = event.brand;
}
});

this.cardExpiry.addEventListener('change', (event) => {
this.toggleError(event);
this.cardExpiryError = '';
this.card.expiry = event.complete ? true : false;
});

this.cardCvc.addEventListener('change', (event) => {
this.toggleError(event);
this.cardCvcError = '';
this.card.cvc = event.complete ? true : false;
});
},

toggleError(event) {
if (event.error) {
this.stripeError = event.error.message;
} else {
this.stripeError = '';
}
},

initDefaults() {
this.getAllPlans(this.messages, this.errors);
this.getAllPaymentMethods(this.messages, this.errors);
},

authorizeCard(e) {
util.setSkipRegistration();

this.errors.clear();

this.stripe.createToken(this.cardNumber).then((result) => {
if (result.error) {
this.$snotify.error(result.error.message);
} else {
this.submitted = true;
this.addAccountPaymentMethod({
userId: this.user && this.user.uuid ? this.user.uuid : null,
paymentMethod: {
paymentMethodType: 'credit',
paymentInfo: result.token.id,
},
messages: this.messages,
errors: this.errors,
});
}
});
return false;
},
},

watch: {
paymentMethods() {
this.setUpStripe();
},

paymentStatus(ps) {
if (ps && ps.addedPaymentMethod) {
// refresh account payment methods
this.getAllAccountPaymentMethods({
userId: this.user.uuid,
messages: this.messages,
errors: this.errors,
});
}
},
},
};
</script>

+ 124
- 0
src/_pages/main/account/VerifyEmail.vue Ver fichero

@@ -0,0 +1,124 @@
<template>
<div class="wrapper">
<h1 class="text-center white-text form-title">
{{ messages.verify_email_title }}
</h1>
<h4 class="d-flex align-items-center justify-content-center form-sub-title">
<span class="text-center white-text">
{{ messages.resend_verify_email_label }}
</span>
<a class="resend-btn" href="#" @click="resendVerification(firstContact)">
{{ messages.button_label_resend_verify_email }}
</a>
</h4>

<div class="d-flex justify-content-center mt-5">
<div ref="lottie" class="lottie"></div>
</div>

<Features></Features>

<a
class="features-section-link text-center d-block"
href="https://getbubblenow.com/features/"
>
{{ messages.more_features_label }}
</a>
</div>
</template>

<style lang="scss" scoped>
@import '../../../_scss/components/form';

.features-section-link {
color: $vivid-navy;
font-size: 16px;
margin-top: 25px;
}

.sub-title {
font-size: 24px;
}

.lottie {
width: 400px;
}

.resend-btn {
text-decoration: underline;
color: white;
margin-left: 10px;
}
</style>

<script>
import { mapState, mapActions } from 'vuex';
import Lottie from 'lottie-web';

import { util } from '~/_helpers'
import { Features } from '~/_components/sections';

// convenience methods
import { isAuthenticator, isNotAuthenticator } from '~/_store/users.module';
window.isAuthenticator = isAuthenticator;
window.isNotAuthenticator = isNotAuthenticator;

export default {
components: {
Features,
},

data() {
return {
firstContact: null,
};
},

computed: {
...mapState('system', ['messages']),
...mapState('users', ['policy']),
},

mounted() {
Lottie.loadAnimation({
container: this.$refs.lottie,
renderer: '',
loop: true,
autoplay: true,
path: '/green_email_sent.json',
});
},

methods: {
...mapActions('account', ['resendVerificationCode']),

getFirstContact(policy) {
if (policy && policy.accountContacts) {
const contacts = policy.accountContacts;
for (let i = 0; i < contacts.length; i++) {
if (isNotAuthenticator(contacts[i])) return contacts[i];
}
return null;
}
return null;
},

resendVerification(contact) {
console.log(contact);
this.resendVerificationCode({
userId: util.currentUser().uuid,
contact: contact,
messages: this.messages,
errors: this.errors,
});
return false; // do not follow the click
},
},

watch: {
policy(p) {
this.firstContact = this.getFirstContact(p);
},
},
};
</script>

+ 13
- 9
src/_router/index.js Ver fichero

@@ -73,18 +73,22 @@ export const router = new Router({
children: [
{
path: '',
component: () => import('~/_pages/auth/Layout'),
component: () => import('~/_pages/main/Layout'),
children: [
{
path: 'verify-email',
component: () => import('~/_pages/auth/VerifyEmail'),
path: '',
component: () => import('~/_pages/main/account/Layout'),
children: [
{
path: 'verify-email',
component: () => import('~/_pages/main/account/VerifyEmail'),
},
{
path: 'payment',
component: () => import('~/_pages/main/account/Payment'),
},
],
},
],
},
{
path: '',
component: () => import('~/_pages/main/Layout'),
children: [
{
path: 'test',
component: () => import('~/_pages/main/Test'),


+ 2
- 2
src/_scss/components/_form.scss Ver fichero

@@ -14,7 +14,7 @@ $form-border-radius: 2px;
margin-top: 16px;
}

.auth-form {
.bubble-form {
background-color: white;
box-shadow: $form-box-shadow;

@@ -34,7 +34,7 @@ $form-border-radius: 2px;
}
}

.auth-form-submit {
.bubble-form-submit {
margin-top: 3rem;
}



+ 9
- 0
src/_store/index.js Ver fichero

@@ -106,3 +106,12 @@ String.prototype.parseDateMessage = function (millis, messages) {
return evalInContext(context, expression)
}) : '';
};

String.prototype.parsePrice = function () {
const price = +this;
if (price % 100 === 0) {
return (price / 100).toString();
} else {
return (price / 100).toFixed(2);
}
}

+ 7
- 5
src/_store/system.module.js Ver fichero

@@ -8,7 +8,8 @@ import { router } from '~/_router';

import { account } from './account.module';

import staticMessages from '~/_assets/messages.json';
import preAuthStaticMessages from '~/_assets/pre_auth_messages.json';
import postAuthStaticMessages from '~/_assets/post_auth_messages.json';

const state = {
configs: {
@@ -68,7 +69,8 @@ const state = {
}
return { count: parseInt(ms), units: '' };
},
...staticMessages,
...preAuthStaticMessages,
...postAuthStaticMessages,
},
messageGroupsLoaded: [],
countries: [],
@@ -337,9 +339,9 @@ const getters = {
promoCodeRequired: function() {
return state.promoCodePolicy === 'required';
},
configs: function () {
configs: function() {
return state.configs.loaded === true ? state.configs : {};
}
},
};

const mutations = {
@@ -368,7 +370,7 @@ const mutations = {

loadSystemConfigsRequest(state) {},
loadSystemConfigsSuccess(state, configs) {
state.configs = {...configs, loaded: true};
state.configs = { ...configs, loaded: true };
},
loadSystemConfigsFailure(state, error) {
state.error = error;


Cargando…
Cancelar
Guardar