Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout feat: implement legal page feat: app login page feat: update header feat: change header to have more links Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout feat: implement Restore page 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 my bubble page Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout feat: replace old pages with new pages feat: implement devices page 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 account policy page Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout feat: implement payment and bills page Merge branch 'feat/ui-layout' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout feat: change config to accept env files Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout feat: make a stripe element component Merge branch 'master' into feat/ui-layout Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout feat: implement manage ssh key page feat: set password page feat: implement change password page fix: change design feat: implement my account page feat: implement animation control for launching bubble screen fix: navigating to network page feat: implement Launching bubble page feat: integrate launch bubble api 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 MFA in login Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout feat: implement adding ssh key Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout fix: showing default values Merge branch 'master' of git.bubblev.org:bubblev/bubble-web into feat/ui-layout feat: implement selector placeholders and default values Merge branch 'feat/ui-layout' 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 fix: getting user information after setting payment method Merge branch 'master' into feat/ui-layout feat: implement setting payment plan in payment page feat: populate options for advanced settings modal fix: remove showing advanced settings modal when loading the page feat: implement Advanced Settings Modal layout Co-authored-by: Tyler <everdev0923@gmail.com> Co-authored-by: jonathan <jonathan@noreply.git.bubblev.org> Reviewed-on: https://git.bubblev.org/bubblev/bubble-web/pulls/49pull/56/head
@@ -1,43 +1,2 @@ | |||||
{ | { | ||||
"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!", | |||||
"label_welcome_message": "Welcome, {{user.name}}", | |||||
"label_time_to_launch_bubble": "It's time to launch your Bubble.", | |||||
"button_label_launch_bubble": "Launch Bubble", | |||||
"button_label_advanced_bubble_settings": "Launch with Advanced Settings", | |||||
"how_it_works_title": "How does it work?", | |||||
"field_label_bubble_name": "Custom Bubble Name", | |||||
"button_label_launch": "Launch", | |||||
"label_launching_bubble_title": "Your Bubble is Launching!", | |||||
"label_launching_bubble_description": "Once ready, use your Bubble by adding the app to your devices", | |||||
"button_cancel_lauch_bubble": "Cancel Launch", | |||||
"label_get_bubble_for_devices": "Get the Bubble Apps for Your Devices!", | |||||
"available_devices": "iphone,android,mac_computer,windows_computer", | |||||
"label_device_iphone": "iPhone", | |||||
"label_device_android": "Android", | |||||
"label_device_mac_computer": "Mac Computer", | |||||
"label_device_windows_computer": "Windows Computer", | |||||
"title_my_account": "My Account", | |||||
"manage_account_actions": "change_password,account_data,billing,manage_ssh,payment,delete_backup", | |||||
"manage_account_change_password_title": "Change Password", | |||||
"manage_account_account_data_title": "Account Data", | |||||
"manage_account_billing_title": "Billing", | |||||
"manage_account_manage_ssh_title": "Manage SSH Keys", | |||||
"manage_account_payment_title": "Payment Methods and Credits", | |||||
"manage_account_delete_backup_title": "Delete or Backup Bubble", | |||||
"manage_account_change_password_link": "/me/change_password", | |||||
"manage_account_account_data_link": "/me/policy", | |||||
"manage_account_billing_link": "/me/bills", | |||||
"manage_account_manage_ssh_link": "/me/keys", | |||||
"manage_account_payment_link": "/me/payment", | |||||
"manage_account_delete_backup_link": "/me/policy" | |||||
} | } |
@@ -1,29 +1,2 @@ | |||||
{ | { | ||||
"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" | |||||
} | } |
@@ -0,0 +1,86 @@ | |||||
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ --> | |||||
<template> | |||||
<div> | |||||
<h1 class="text-center white-text form-title"> | |||||
{{ messages.message_authenticating_app_login }} | |||||
</h1> | |||||
</div> | |||||
</template> | |||||
<script> | |||||
import { mapState, mapGetters, mapActions } from 'vuex'; | |||||
import { util } from '~/_helpers'; | |||||
export default { | |||||
computed: { | |||||
...mapState('account', { | |||||
currentUser: (state) => state.user, | |||||
}), | |||||
...mapState('users', ['user']), | |||||
...mapState('system', ['messages']), | |||||
}, | |||||
created() { | |||||
let locale = null; | |||||
if (util.userLoggedIn() && util.currentUser().locale) | |||||
locale = util.currentUser().locale; | |||||
this.loadMessages('pre_auth', locale === null ? 'detect' : locale); | |||||
let session = this.$route.query.session; | |||||
let uri = this.$route.query.uri; | |||||
if ( | |||||
!this.$route.query.hasOwnProperty('session') || | |||||
typeof session === 'undefined' || | |||||
session === null | |||||
) { | |||||
console.warn( | |||||
'AppLoginPage.created: session parameter is empty, sending to login page' | |||||
); | |||||
if (util.userLoggedIn()) { | |||||
this.logout({ messages: this.messages, errors: this.errors }); | |||||
} | |||||
this.$router.replace('/login'); | |||||
return; | |||||
} | |||||
if ( | |||||
typeof uri === 'undefined' || | |||||
uri === null || | |||||
(uri.length > 0 && uri[0] !== '/') | |||||
) { | |||||
console.warn( | |||||
'AppLoginPage.created: uri parameter is not empty, sending to login page' | |||||
); | |||||
this.$router.replace('/login'); | |||||
return; | |||||
} | |||||
let user = util.currentUser(); | |||||
if (user !== null && session !== user.token) { | |||||
this.logout({ messages: this.messages, errors: this.errors }); | |||||
} | |||||
if (uri.startsWith('/appLogin')) { | |||||
uri = '/'; | |||||
} | |||||
localStorage.setItem(util.USER_KEY, JSON.stringify(user)); | |||||
this.appLogin({ | |||||
session: session, | |||||
uri: uri, | |||||
messages: this.messages, | |||||
errors: this.errors, | |||||
}); | |||||
}, | |||||
methods: { | |||||
...mapActions('account', ['login', 'logout', 'appLogin']), | |||||
...mapActions('system', ['loadSystemConfigs', 'loadMessages']), | |||||
}, | |||||
watch: { | |||||
user(u) { | |||||
if (u.token) { | |||||
this.loadMessages('post_auth', u.locale); | |||||
this.loadMessages('apps', u.locale); | |||||
} | |||||
}, | |||||
}, | |||||
}; | |||||
</script> |
@@ -49,6 +49,7 @@ export default { | |||||
const urlNodes = this.$route.fullPath.split('/'); | const urlNodes = this.$route.fullPath.split('/'); | ||||
switch (urlNodes[1]) { | switch (urlNodes[1]) { | ||||
case 'login': | case 'login': | ||||
case 'appLogin': | |||||
case 'forgotPassword': | case 'forgotPassword': | ||||
return 'background1'; | return 'background1'; | ||||
} | } | ||||
@@ -56,6 +57,7 @@ export default { | |||||
// new pages in progress | // new pages in progress | ||||
switch (urlNodes[2]) { | switch (urlNodes[2]) { | ||||
case 'login': | case 'login': | ||||
case 'appLogin': | |||||
case 'forgotPassword': | case 'forgotPassword': | ||||
return 'background1'; | return 'background1'; | ||||
} | } | ||||
@@ -75,23 +75,23 @@ export default { | |||||
}, | }, | ||||
navigateToVerifyEmail() { | navigateToVerifyEmail() { | ||||
if (this.$route.path !== '/new_pages/verify-email') { | |||||
this.$router.push('/new_pages/verify-email'); | |||||
if (this.$route.path !== '/verify-email') { | |||||
this.$router.push('/verify-email'); | |||||
} | } | ||||
}, | }, | ||||
navigateToPaymentPage() { | navigateToPaymentPage() { | ||||
if (this.$route.path !== '/new_pages/payment') { | |||||
this.$router.push('/new_pages/payment'); | |||||
if (this.$route.path !== '/payment') { | |||||
this.$router.push('/payment'); | |||||
} | } | ||||
}, | }, | ||||
navigateToDashboard() { | navigateToDashboard() { | ||||
if ( | if ( | ||||
this.$route.path === '/new_pages/payment' || | |||||
this.$route.path === '/new_pages/verify-email' | |||||
this.$route.path === '/payment' || | |||||
this.$route.path === '/verify-email' | |||||
) { | ) { | ||||
this.$router.push('/new_pages/test'); | |||||
this.$router.push('/'); | |||||
} | } | ||||
}, | }, | ||||
}, | }, | ||||
@@ -113,7 +113,6 @@ export default { | |||||
}, 5000); | }, 5000); | ||||
} | } | ||||
} else { | } else { | ||||
console.log('hasVerified Contact'); | |||||
const currentUser = util.currentUser(); | const currentUser = util.currentUser(); | ||||
this.getAllAccountPaymentMethods({ | this.getAllAccountPaymentMethods({ | ||||
userId: currentUser.uuid, | userId: currentUser.uuid, | ||||
@@ -127,7 +126,7 @@ export default { | |||||
} | } | ||||
}, | }, | ||||
accountPaymentMethods(pms) { | |||||
accountPaymentMethods(pms, oldpms) { | |||||
if (pms) { | if (pms) { | ||||
const payMethods = []; | const payMethods = []; | ||||
for (let i = 0; i < pms.length; i++) { | for (let i = 0; i < pms.length; i++) { | ||||
@@ -141,6 +140,7 @@ export default { | |||||
payMethods.push(pm); | payMethods.push(pm); | ||||
} | } | ||||
} | } | ||||
if ( | if ( | ||||
this.accountPlan.paymentMethodObject.uuid === null && | this.accountPlan.paymentMethodObject.uuid === null && | ||||
payMethods.length > 0 | payMethods.length > 0 | ||||
@@ -0,0 +1,70 @@ | |||||
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ --> | |||||
<template> | |||||
<div> | |||||
<!--- We've Got You Covered Section ---> | |||||
<h1 class="title text-center white-text"> | |||||
{{ messages.title_legal_topics }} | |||||
</h1> | |||||
<div class="row" v-if="messages && messages.legal_topics"> | |||||
<div | |||||
v-for="(item, index) in messages.legal_topics.split(',')" | |||||
:key="index" | |||||
class="col-lg-6 col-md-6 col-sm-12 my-4 px-3" | |||||
> | |||||
<a :href="messages[`legal_${item}_link`]"> | |||||
<div class="h-100 card-container"> | |||||
<div class="d-flex justify-content-between align-items-center"> | |||||
<span>{{ messages[`legal_${item}`] }}</span> | |||||
<span class="icon"> | |||||
<i class="fa fa-plus"></i> | |||||
</span> | |||||
</div> | |||||
</div> | |||||
</a> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</template> | |||||
<style lang="scss" scoped> | |||||
.title { | |||||
margin-top: 64px; | |||||
margin-bottom: 20px; | |||||
} | |||||
.card-container { | |||||
padding: 24px; | |||||
margin: 0 20px; | |||||
box-shadow: 0px 10px 30px rgba(0, 0, 0, 0.1); | |||||
border-radius: 10px; | |||||
background: #fff; | |||||
color: #17aea6; | |||||
} | |||||
.icon { | |||||
font-size: 14px; | |||||
width: 24px; | |||||
height: 24px; | |||||
border-radius: 50%; | |||||
background: #eeeef0; | |||||
display: flex; | |||||
justify-content: center; | |||||
align-items: center; | |||||
} | |||||
</style> | |||||
<script> | |||||
import { mapState } from 'vuex'; | |||||
import { Card } from '~/_components/shared'; | |||||
export default { | |||||
components: { | |||||
Card, | |||||
}, | |||||
computed: { | |||||
...mapState('system', ['messages']), | |||||
}, | |||||
}; | |||||
</script> |
@@ -337,7 +337,11 @@ export default { | |||||
watch: { | watch: { | ||||
paymentMethods() { | paymentMethods() { | ||||
this.$refs.stripeElement.setUpStripe(this.paymentMethods[0].driverConfig.publicApiKey); | |||||
if (this.paymentMethods[0]) { | |||||
this.$refs.stripeElement.setUpStripe( | |||||
this.paymentMethods[0].driverConfig.publicApiKey | |||||
); | |||||
} | |||||
}, | }, | ||||
paymentStatus(ps) { | paymentStatus(ps) { | ||||
@@ -71,6 +71,16 @@ export const router = new Router({ | |||||
path: '/new_pages', | path: '/new_pages', | ||||
component: () => import('~/_pages/Layout'), | component: () => import('~/_pages/Layout'), | ||||
children: [ | children: [ | ||||
{ | |||||
path: '', | |||||
component: () => import('~/_pages/auth/Layout'), | |||||
children: [ | |||||
{ | |||||
path: 'appLogin', | |||||
component: () => import('~/_pages/auth/AppLogin'), | |||||
}, | |||||
], | |||||
}, | |||||
{ | { | ||||
path: '', | path: '', | ||||
component: () => import('~/_pages/main/Layout'), | component: () => import('~/_pages/main/Layout'), | ||||
@@ -87,6 +97,14 @@ export const router = new Router({ | |||||
path: 'restore', | path: 'restore', | ||||
component: () => import('~/_pages/main/bubble/Restore'), | component: () => import('~/_pages/main/bubble/Restore'), | ||||
}, | }, | ||||
{ | |||||
path: 'legal', | |||||
component: () => import('~/_pages/main/account/Legal'), | |||||
}, | |||||
{ | |||||
path: 'restore', | |||||
component: () => import('~/_pages/main/bubble/Restore'), | |||||
}, | |||||
], | ], | ||||
}, | }, | ||||
{ | { | ||||
@@ -182,7 +182,7 @@ export default { | |||||
return this.configs ? this.configs.awaitingRestore : undefined; | return this.configs ? this.configs.awaitingRestore : undefined; | ||||
}, | }, | ||||
isNewPage() { | isNewPage() { | ||||
const newPages = ['/new_pages', '/login', '/forgotPassword', '/register', '/me', '/devices', '/launch-bubble', '/launching-bubble']; | |||||
const newPages = ['/new_pages', '/login', '/forgotPassword', '/register', '/me', '/devices', '/launch-bubble', '/launching-bubble', '/payment', '/verify-email']; | |||||
return ( | return ( | ||||
newPages.includes(this.$route.path) || | newPages.includes(this.$route.path) || | ||||
newPages.filter((p) => this.$route.path.startsWith(p)).length | newPages.filter((p) => this.$route.path.startsWith(p)).length | ||||