@@ -1280,6 +1280,11 @@ | |||
"integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", | |||
"dev": true | |||
}, | |||
"blueimp-md5": { | |||
"version": "2.18.0", | |||
"resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.18.0.tgz", | |||
"integrity": "sha512-vE52okJvzsVWhcgUHOv+69OG3Mdg151xyn41aVQN/5W5S+S43qZhxECtYLAEHMSFWX6Mv5IZrzj3T5+JqXfj5Q==" | |||
}, | |||
"bn.js": { | |||
"version": "5.1.2", | |||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", | |||
@@ -11,6 +11,7 @@ | |||
"dev": "webpack-dev-server --open --env.server=$MY_BUBBLE_API" | |||
}, | |||
"dependencies": { | |||
"blueimp-md5": "^2.18.0", | |||
"lottie-web": "^5.7.1", | |||
"luxon": "^1.21.3", | |||
"qrcode": "^1.4.4", | |||
@@ -1,2 +1,5 @@ | |||
{ | |||
"label_homepage_welcome": "Hello", | |||
"label_menu_help": "Help", | |||
"label_menu_settings": "Settings" | |||
} |
@@ -1,2 +1,9 @@ | |||
{ | |||
"footer_links": "get_help,legal_stuff,about_bubble", | |||
"title_get_help": "Get Help", | |||
"title_legal_stuff": "Legal Stuff", | |||
"title_about_bubble": "About Bubble", | |||
"link_get_help": "https://example.com/get_help", | |||
"link_legal_stuff": "https://example.com/legal_stuff", | |||
"link_about_bubble": "https://example.com/about_bubble" | |||
} |
@@ -0,0 +1,117 @@ | |||
<template> | |||
<footer class="footer"> | |||
<p class="text-white text-center"> | |||
{{ messages.label_get_bubble_for_devices }} | |||
</p> | |||
<div class="devices"> | |||
<a | |||
class="device" | |||
v-for="device in availableDevices" | |||
:key="device" | |||
download | |||
href="" | |||
> | |||
<img :src="`/${device}.png`" /> | |||
<span>{{ messages[`label_device_${device}`] }}</span> | |||
</a> | |||
</div> | |||
<div class="links"> | |||
<a | |||
class="text-white link" | |||
v-for="item in footerLinks" | |||
:key="item" | |||
:to="messages[`link_${item}`]" | |||
> | |||
{{ messages[`title_${item}`] }} | |||
</a> | |||
</div> | |||
</footer> | |||
</template> | |||
<style lang="scss" scoped> | |||
@import '../../_scss/breakpoints'; | |||
.footer { | |||
z-index: 1; | |||
& > * { | |||
margin: 20px 0; | |||
} | |||
p { | |||
font-size: 26px; | |||
line-height: 30px; | |||
} | |||
@include respond-below(sm) { | |||
padding: 0 70px; | |||
} | |||
} | |||
.devices, | |||
.links { | |||
display: flex; | |||
flex-wrap: wrap; | |||
justify-content: center; | |||
} | |||
.device { | |||
width: 165px; | |||
padding: 10px; | |||
@include respond-below(lg) { | |||
width: 120px; | |||
padding: 10px 0; | |||
} | |||
margin: 10px; | |||
border-radius: 10px; | |||
background-color: white; | |||
box-shadow: 0px 5px 30px #1551ab; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
justify-content: center; | |||
span { | |||
font-size: 12px; | |||
color: #7c7c7c; | |||
margin-top: 16px; | |||
} | |||
} | |||
.link { | |||
font-size: 14px; | |||
&:not(:last-child) { | |||
display: inline-flex; | |||
align-items: center; | |||
&:after { | |||
content: ''; | |||
width: 3px; | |||
height: 3px; | |||
background-color: white; | |||
margin: 0 12px; | |||
display: inline-flex; | |||
border-radius: 3px; | |||
} | |||
} | |||
} | |||
</style> | |||
<script> | |||
import { mapState } from 'vuex'; | |||
export default { | |||
computed: { | |||
...mapState('system', ['messages']), | |||
availableDevices() { | |||
return this.messages.available_devices ? this.messages.available_devices.split(',') : []; | |||
}, | |||
footerLinks() { | |||
return this.messages.footer_links ? this.messages.footer_links.split(',') : []; | |||
}, | |||
}, | |||
}; | |||
</script> |
@@ -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="header d-flex align-items-center justify-content-center"> | |||
<header class="header d-flex align-items-center justify-content-center"> | |||
<div class="d-flex justify-content-center align-items-center container "> | |||
<router-link to="/"> | |||
<img src="/small-BubbleLogo-Horizontal-BlackText.png" height="40" /> | |||
@@ -139,7 +139,7 @@ | |||
</router-link> | |||
</div> | |||
</div> | |||
</div> | |||
</header> | |||
</template> | |||
<style lang="scss" scoped> | |||
@@ -0,0 +1,27 @@ | |||
<template> | |||
<div class="notification"> | |||
<i class="fa fa-bell"></i> | |||
</div> | |||
</template> | |||
<style lang="scss" scoped> | |||
.notification { | |||
position: absolute; | |||
top: 10px; | |||
right: 20px; | |||
width: 48px; | |||
height: 48px; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
border: 1px solid #eaeaea; | |||
border-radius: 50%; | |||
color: #c4c4c4; | |||
} | |||
</style> | |||
<script> | |||
export default {}; | |||
</script> |
@@ -0,0 +1,245 @@ | |||
<template> | |||
<aside class="sidebar"> | |||
<!-- Logo --> | |||
<div class="d-flex align-items-center logo"> | |||
<router-link to="/"> | |||
<img src="/small-BubbleLogo-Horizontal-BlackText.png" height="40" /> | |||
</router-link> | |||
<a @click="toggleMenu" class="toggle-menu"> | |||
<i class="fa fa-bars"></i> | |||
</a> | |||
</div> | |||
<div | |||
class="flex-grow-1 d-flex flex-column menu" | |||
:class="{ 'menu-invisible': !menuVisible }" | |||
> | |||
<!-- Profile --> | |||
<router-link to="/me"> | |||
<div class="profile"> | |||
<img | |||
:src="`https://www.gravatar.com/avatar/${userHash}?r=pg`" | |||
class="profile-image" | |||
/> | |||
<div class="profile-info"> | |||
<strong>{{ currentUser.name }}</strong> | |||
<br /> | |||
<span>{{ currentUser.email }}</span> | |||
</div> | |||
<i class="fa fa-chevron-right"></i> | |||
</div> | |||
</router-link> | |||
<!-- Navigation --> | |||
<div class="navigation"> | |||
<div class="navigation-item active"> | |||
<i class="fa fa-home icon icon-home"></i> | |||
<span>{{ messages.label_menu_network }}</span> | |||
</div> | |||
<div class="navigation-item"> | |||
<i class="fa fa-tablet icon icon-devices"></i> | |||
<span>{{ messages.label_menu_devices }}</span> | |||
</div> | |||
<div class="navigation-item"> | |||
<i class="fa fa-cog icon icon-settings"></i> | |||
<span>{{ messages.label_menu_settings }}</span> | |||
</div> | |||
<div class="navigation-item"> | |||
<i class="fa fa-question-circle icon icon-help"></i> | |||
<span>{{ messages.label_menu_help }}</span> | |||
</div> | |||
</div> | |||
<!-- Upgrade Plan --> | |||
<div class="flex-grow-1"></div> | |||
<!-- Logout --> | |||
<Button color="outline" class="logout-button"> | |||
{{ messages.log_out }} | |||
</Button> | |||
</div> | |||
</aside> | |||
</template> | |||
<style lang="scss" scoped> | |||
@import '../../_scss/breakpoints'; | |||
.sidebar { | |||
z-index: 1; | |||
padding: 20px 0; | |||
display: flex; | |||
flex-direction: column; | |||
} | |||
.logout-button { | |||
margin-top: 30px; | |||
} | |||
.profile { | |||
color: #172239; | |||
margin-top: 30px; | |||
padding: 24px; | |||
border: 1px solid #e0e2f0; | |||
border-radius: 32px; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
@include respond-between(sm, lg) { | |||
margin-top: 0px; | |||
border: none; | |||
> *:not(.profile-image) { | |||
display: none; | |||
} | |||
} | |||
} | |||
.logo { | |||
padding: 0 20px; | |||
} | |||
.profile-image { | |||
height: 48px; | |||
width: 48px; | |||
border-radius: 50%; | |||
} | |||
.profile-info { | |||
width: 156px; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
margin: 0 20px; | |||
font-size: 12px; | |||
strong { | |||
font-size: 16px; | |||
} | |||
} | |||
.toggle-menu { | |||
display: none; | |||
@include respond-below(sm) { | |||
display: block; | |||
} | |||
} | |||
.menu { | |||
max-height: 10000px; | |||
padding: 0 20px 20px; | |||
transition: max-height 0.5s; | |||
overflow: hidden; | |||
background-color: white; | |||
@include respond-below(sm) { | |||
position: absolute; | |||
left: 0; | |||
right: 0; | |||
top: 60px; | |||
&.menu-invisible { | |||
max-height: 0px; | |||
} | |||
} | |||
} | |||
.navigation { | |||
margin-top: 30px; | |||
border: 1px solid #e0e2f0; | |||
border-radius: 32px; | |||
padding: 32px; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
justify-content: center; | |||
@include respond-between(sm, lg) { | |||
margin-top: 12px; | |||
span { | |||
display: none; | |||
} | |||
} | |||
} | |||
.navigation-item { | |||
color: #6d6d78; | |||
padding: 16px; | |||
margin: 10px 0; | |||
width: 240px; | |||
display: flex; | |||
align-items: center; | |||
span { | |||
margin-left: 20px; | |||
} | |||
&.active { | |||
background-color: #66cda4; | |||
color: white; | |||
border-radius: 20px; | |||
.icon { | |||
color: white; | |||
} | |||
} | |||
@include respond-between(sm, lg) { | |||
width: auto; | |||
} | |||
} | |||
.toggle-menu { | |||
margin-left: 20px; | |||
font-size: 20px; | |||
font-weight: bold; | |||
color: #1930d7 !important; | |||
} | |||
.icon { | |||
font-size: 20px; | |||
} | |||
.icon-home { | |||
color: #5055bd; | |||
} | |||
.icon-devices { | |||
color: #4b53df; | |||
} | |||
.icon-settings { | |||
color: #e6458a; | |||
} | |||
.icon-help { | |||
color: #57c8e9; | |||
} | |||
</style> | |||
<script> | |||
import { mapState } from 'vuex'; | |||
import md5 from 'blueimp-md5'; | |||
import { util } from '~/_helpers'; | |||
import { Button } from '~/_components/shared'; | |||
export default { | |||
components: { | |||
Button, | |||
}, | |||
data: () => ({ | |||
currentUser: util.currentUser(), | |||
menuVisible: false, | |||
}), | |||
computed: { | |||
...mapState('system', ['messages']), | |||
userHash() { | |||
return md5(this.currentUser.email); | |||
}, | |||
}, | |||
methods: { | |||
toggleMenu() { | |||
this.menuVisible = !this.menuVisible; | |||
}, | |||
}, | |||
}; | |||
</script> |
@@ -3,3 +3,6 @@ | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
export { default as Header } from './Header'; | |||
export { default as Footer } from './Footer'; | |||
export { default as Notification } from './Notification'; | |||
export { default as Sidebar } from './Sidebar'; |
@@ -1,8 +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="w-100"> | |||
<router-view></router-view> | |||
</div> | |||
<router-view></router-view> | |||
</template> | |||
<script> | |||
@@ -1,6 +1,8 @@ | |||
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ --> | |||
<template> | |||
<div class="background w-100 h-100"> | |||
<Header></Header> | |||
<div | |||
:class="backgroundClass" | |||
class="auth-layout d-flex flex-column content" | |||
@@ -163,7 +163,7 @@ | |||
</template> | |||
<style lang="scss" scoped> | |||
@import '../../../_scss/components/form'; | |||
@import '../../_scss/components/form'; | |||
.features-section-link { | |||
color: $vivid-navy; |
@@ -8,7 +8,7 @@ | |||
<span class="text-center white-text"> | |||
{{ messages.resend_verify_email_label }} | |||
</span> | |||
<a class="resend-btn" href="#"> | |||
<a class="resend-btn" href="#" @click="resendVerification(firstContact)"> | |||
{{ messages.button_label_resend_verify_email }} | |||
</a> | |||
</h4> | |||
@@ -53,18 +53,31 @@ | |||
</style> | |||
<script> | |||
import { mapState } from 'vuex'; | |||
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() { | |||
@@ -76,5 +89,37 @@ export default { | |||
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> |
@@ -1,21 +1,102 @@ | |||
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ --> | |||
<template> | |||
<router-view></router-view> | |||
<div class="container-fluid"> | |||
<img src="/bubble_bkgrnd.png" alt="" class="background-image" /> | |||
<div class="content"> | |||
<Sidebar /> | |||
<div class="flex-grow-1 p-4"> | |||
<header> | |||
<p class="mb-0">{{ messages.label_homepage_welcome }},</p> | |||
<p class="name">{{ currentUser.name }} 👋</p> | |||
</header> | |||
<main> | |||
<router-view></router-view> | |||
</main> | |||
</div> | |||
<Notification /> | |||
</div> | |||
<Footer /> | |||
</div> | |||
</template> | |||
<style lang="scss" scoped> | |||
@import '../../_scss/breakpoints'; | |||
.container-fluid { | |||
background: linear-gradient(26.64deg, #1933dd -10.01%, #18d59d 86.47%); | |||
height: 100%; | |||
padding: 30px 20px; | |||
@include respond-below(lg) { | |||
padding: 0; | |||
.content { | |||
border-radius: 0; | |||
} | |||
} | |||
display: flex; | |||
flex-direction: column; | |||
} | |||
.background-image { | |||
position: absolute; | |||
bottom: 20px; | |||
left: 100px; | |||
pointer-events: none; | |||
@include respond-below(lg) { | |||
bottom: -20px; | |||
left: 0px; | |||
} | |||
@include respond-below(sm) { | |||
bottom: -20px; | |||
left: 0px; | |||
width: 100%; | |||
} | |||
} | |||
.content { | |||
position: relative; | |||
z-index: 2; | |||
flex-grow: 1; | |||
background: #fafafd; | |||
border-radius: 10px; | |||
box-shadow: 0px 4px 25px rgba(149, 149, 149, 0.25); | |||
display: flex; | |||
@include respond-below(sm) { | |||
flex-direction: column; | |||
} | |||
} | |||
header { | |||
.name { | |||
color: #172239; | |||
font-size: 3rem; | |||
} | |||
} | |||
</style> | |||
<script> | |||
import { mapState, mapActions } from 'vuex'; | |||
import { util } from '~/_helpers'; | |||
import { isAuthenticator, isNotAuthenticator } from '~/_store/users.module'; | |||
import { Footer, Notification, Sidebar } from '~/_components/layout'; | |||
export default { | |||
components: { | |||
Footer, | |||
Notification, | |||
Sidebar, | |||
}, | |||
data: () => ({ | |||
verifiedContacts: null, | |||
verifiedContactRefresher: null, | |||
accountPlan: { | |||
currentUser: null, | |||
name: '', | |||
domain: '', | |||
locale: util.currentUser().locale, | |||
locale: null, | |||
timezone: '', | |||
plan: 'bubble', | |||
footprint: 'Worldwide', | |||
@@ -37,7 +118,12 @@ export default { | |||
computed: { | |||
...mapState('users', ['policy']), | |||
...mapState('paymentMethods', ['accountPaymentMethods']), | |||
...mapState('system', ['configs']), | |||
...mapState('system', ['configs', 'messages']), | |||
}, | |||
created() { | |||
this.currentUser = util.currentUser(); | |||
this.locale = this.currentUser.locale; | |||
}, | |||
mounted() { | |||
@@ -49,16 +135,15 @@ export default { | |||
...mapActions('paymentMethods', ['getAllAccountPaymentMethods']), | |||
initDefaults() { | |||
const currentUser = util.currentUser(); | |||
const selectedLocale = | |||
currentUser !== null && | |||
typeof currentUser.locale !== 'undefined' && | |||
currentUser.locale !== null | |||
? currentUser.locale | |||
this.currentUser !== null && | |||
typeof this.currentUser.locale !== 'undefined' && | |||
this.currentUser.locale !== null | |||
? this.currentUser.locale | |||
: 'detect'; | |||
if (!currentUser.admin) { | |||
if (!this.currentUser.admin) { | |||
this.getPolicyByUserId({ | |||
userId: currentUser.uuid, | |||
userId: this.currentUser.uuid, | |||
messages: this.messages, | |||
errors: this.errors, | |||
}); | |||
@@ -1,125 +0,0 @@ | |||
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ --> | |||
<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> |
@@ -62,23 +62,12 @@ const newNetworkChildren = [ | |||
export const router = new Router({ | |||
mode: 'history', | |||
routes: [ | |||
// existing pages | |||
{ path: '', component: DashboardPage }, | |||
{ path: '/', component: DashboardPage }, | |||
// { path: '/legal', component: LegalPage }, | |||
// { path: '/support', component: SupportPage }, | |||
// { path: '/me/old', component: ProfilePage }, | |||
// { path: '/me/policy', component: PolicyPage }, | |||
{ | |||
path: '/me/download/:uuid', | |||
redirect: (r) => ({ | |||
path: '/me/policy', | |||
query: { download: r.params.uuid }, | |||
}), | |||
}, | |||
{ path: '/me/action', component: ActionPage }, | |||
// { path: '/me/changePassword/old', component: ChangePasswordPage }, | |||
// { path: '/me/setPassword/:code', component: SetPasswordPage }, | |||
// { path: '/me/keys', component: SshKeysPage }, | |||
@@ -89,64 +78,17 @@ export const router = new Router({ | |||
// children: paymentMethodsChildren, | |||
// }, | |||
// { path: '/devices', component: DevicesPage }, | |||
{ path: '/apps', component: AppsPage }, | |||
{ path: '/app/:app', component: AppPage }, | |||
{ path: '/app/:app/config/:view', component: AppConfigPage }, | |||
{ path: '/app/:app/config/:view/:item', component: AppConfigPage }, | |||
{ path: '/app/:app/view/:view', component: AppDataViewPage }, | |||
{ path: '/app/:app/site/:site', component: AppSitePage }, | |||
{ path: '/app/:app/site/:site/view/:view', component: AppDataViewPage }, | |||
{ path: '/notifications', component: NotificationsPage }, | |||
{ | |||
path: '/bubbles', | |||
component: NetworksPage, | |||
children: [ | |||
{ | |||
path: '', | |||
component: NewNetworkPage, | |||
children: newNetworkChildren, | |||
}, | |||
], | |||
}, | |||
{ | |||
path: '/new_bubble', | |||
component: NewNetworkPage, | |||
children: newNetworkChildren, | |||
}, | |||
{ path: '/bubble/:id', component: NetworkPage }, | |||
{ path: '/action', component: ActionPage }, | |||
{ path: '/resetPassword/:code', component: SetPasswordPage }, | |||
{ path: '/activate', component: ActivationPage }, | |||
// { | |||
// path: '/register', | |||
// component: RegisterPage, | |||
// children: paymentMethodsChildren, | |||
// }, | |||
{ path: '/auth', component: MultifactorAuthPage }, | |||
// { path: '/login', component: LoginPage }, | |||
{ path: '/logout', component: LogoutPage }, | |||
// { path: '/forgotPassword', component: ForgotPasswordPage }, | |||
// { path: '/appLogin', component: AppLoginPage }, | |||
// { path: '/restore', component: RestorePage }, | |||
{ path: '/admin/accounts', component: AccountsPage }, | |||
{ path: '/admin/new_account', component: ProfilePage }, | |||
{ path: '/admin/accounts/:id', component: ProfilePage }, | |||
{ path: '/admin/accounts/:id/policy', component: PolicyPage }, | |||
{ | |||
path: '/admin/accounts/:id/changePassword', | |||
component: ChangePasswordPage, | |||
}, | |||
{ path: '/admin/accounts/:id/keys', component: SshKeysPage }, | |||
{ path: '/admin/accounts/:id/bills', component: BillsPage }, | |||
{ | |||
path: '/admin/accounts/:id/payment', | |||
component: PaymentMethodsPage, | |||
children: paymentMethodsChildren, | |||
}, | |||
{ path: '/admin/model', component: ModelSetupPage }, | |||
// new route | |||
{ | |||
path: '', | |||
@@ -154,100 +96,123 @@ export const router = new Router({ | |||
children: [ | |||
{ | |||
path: '', | |||
component: () => import('~/_pages/auth/Layout'), | |||
component: () => import('~/_pages/main/Layout'), | |||
children: [ | |||
{ path: '', component: DashboardPage }, | |||
{ | |||
path: 'login', | |||
component: () => import('~/_pages/auth/Login'), | |||
path: 'me/download/:uuid', | |||
redirect: (r) => ({ | |||
path: 'me/policy', | |||
query: { download: r.params.uuid }, | |||
}), | |||
}, | |||
{ path: 'me/action', component: ActionPage }, | |||
{ path: 'apps', component: AppsPage }, | |||
{ path: 'app/:app', component: AppPage }, | |||
{ path: 'app/:app/config/:view', component: AppConfigPage }, | |||
{ | |||
path: 'forgotPassword', | |||
component: () => import('~/_pages/auth/ForgotPassword'), | |||
path: 'app/:app/config/:view/:item', | |||
component: AppConfigPage, | |||
}, | |||
{ path: 'app/:app/view/:view', component: AppDataViewPage }, | |||
{ path: 'app/:app/site/:site', component: AppSitePage }, | |||
{ | |||
path: 'register', | |||
component: () => import('~/_pages/auth/Register'), | |||
path: 'app/:app/site/:site/view/:view', | |||
component: AppDataViewPage, | |||
}, | |||
{ path: 'notifications', component: NotificationsPage }, | |||
{ | |||
path: 'appLogin', | |||
component: () => import('~/_pages/auth/AppLogin'), | |||
}, | |||
], | |||
}, | |||
{ | |||
path: '', | |||
component: () => import('~/_pages/main/Layout'), | |||
children: [ | |||
{ | |||
path: '', | |||
component: () => import('~/_pages/main/account/Layout'), | |||
path: 'bubbles', | |||
component: NetworksPage, | |||
children: [ | |||
{ | |||
path: 'verifyEmail', | |||
component: () => import('~/_pages/main/account/VerifyEmail'), | |||
}, | |||
{ | |||
path: 'payment', | |||
component: () => import('~/_pages/main/account/Payment'), | |||
}, | |||
{ | |||
path: 'me', | |||
exact: true, | |||
component: () => import('~/_pages/main/account/MyAccount'), | |||
}, | |||
{ | |||
path: 'me/changePassword', | |||
component: () => | |||
import('~/_pages/main/account/ChangePassword'), | |||
}, | |||
{ | |||
path: 'me/setPassword/:code', | |||
component: () => import('~/_pages/main/account/SetPassword'), | |||
}, | |||
{ | |||
path: 'me/keys', | |||
component: () => import('~/_pages/main/account/ManageSSH'), | |||
}, | |||
{ | |||
path: 'me/payment', | |||
component: () => | |||
import('~/_pages/main/account/PaymentMethods'), | |||
}, | |||
{ | |||
path: 'me/bills', | |||
component: () => import('~/_pages/main/account/Bills'), | |||
}, | |||
{ | |||
path: 'me/policy', | |||
component: () => import('~/_pages/main/account/Policy'), | |||
}, | |||
{ | |||
path: 'me/delete', | |||
component: () => import('~/_pages/main/account/Delete'), | |||
path: '', | |||
component: NewNetworkPage, | |||
children: newNetworkChildren, | |||
}, | |||
], | |||
}, | |||
{ | |||
path: 'new_bubble', | |||
component: NewNetworkPage, | |||
children: newNetworkChildren, | |||
}, | |||
{ path: 'bubble/:id', component: NetworkPage }, | |||
{ path: 'action', component: ActionPage }, | |||
{ path: 'resetPassword/:code', component: SetPasswordPage }, | |||
{ | |||
path: 'devices', | |||
component: () => import('~/_pages/main/account/Devices'), | |||
}, | |||
{ path: 'activate', component: ActivationPage }, | |||
{ path: 'auth', component: MultifactorAuthPage }, | |||
{ path: 'admin/accounts', component: AccountsPage }, | |||
{ path: 'admin/new_account', component: ProfilePage }, | |||
{ path: 'admin/accounts/:id', component: ProfilePage }, | |||
{ path: 'admin/accounts/:id/policy', component: PolicyPage }, | |||
{ | |||
path: 'admin/accounts/:id/changePassword', | |||
component: ChangePasswordPage, | |||
}, | |||
{ path: 'admin/accounts/:id/keys', component: SshKeysPage }, | |||
{ path: 'admin/accounts/:id/bills', component: BillsPage }, | |||
{ | |||
path: 'admin/accounts/:id/payment', | |||
component: PaymentMethodsPage, | |||
children: paymentMethodsChildren, | |||
}, | |||
{ path: 'admin/model', component: ModelSetupPage }, | |||
{ | |||
path: 'me', | |||
exact: true, | |||
component: () => import('~/_pages/main/account/MyAccount'), | |||
}, | |||
{ | |||
path: 'me/changePassword', | |||
component: () => import('~/_pages/main/account/ChangePassword'), | |||
}, | |||
{ | |||
path: 'me/setPassword/:code', | |||
component: () => import('~/_pages/main/account/SetPassword'), | |||
}, | |||
{ | |||
path: 'me/keys', | |||
component: () => import('~/_pages/main/account/ManageSSH'), | |||
}, | |||
{ | |||
path: 'me/payment', | |||
component: () => import('~/_pages/main/account/PaymentMethods'), | |||
}, | |||
{ | |||
path: 'me/bills', | |||
component: () => import('~/_pages/main/account/Bills'), | |||
}, | |||
{ | |||
path: 'me/policy', | |||
component: () => import('~/_pages/main/account/Policy'), | |||
}, | |||
{ | |||
path: 'me/delete', | |||
component: () => import('~/_pages/main/account/Delete'), | |||
}, | |||
{ | |||
path: 'bubble/:id', | |||
component: () => import('~/_pages/main/bubble/Network'), | |||
}, | |||
{ | |||
path: 'restore', | |||
component: () => import('~/_pages/main/bubble/Restore'), | |||
}, | |||
{ | |||
path: 'legal', | |||
component: () => import('~/_pages/main/account/Legal'), | |||
}, | |||
{ | |||
path: 'support', | |||
component: () => import('~/_pages/main/account/Support'), | |||
}, | |||
], | |||
{ | |||
path: 'devices', | |||
component: () => import('~/_pages/main/account/Devices'), | |||
}, | |||
{ | |||
path: 'bubble/:id', | |||
component: () => import('~/_pages/main/bubble/Network'), | |||
}, | |||
{ | |||
path: 'restore', | |||
component: () => import('~/_pages/main/bubble/Restore'), | |||
}, | |||
{ | |||
path: 'legal', | |||
component: () => import('~/_pages/main/account/Legal'), | |||
}, | |||
{ | |||
path: 'support', | |||
component: () => import('~/_pages/main/account/Support'), | |||
}, | |||
{ | |||
path: 'launch-bubble', | |||
@@ -259,11 +224,42 @@ export const router = new Router({ | |||
}, | |||
], | |||
}, | |||
{ | |||
path: '', | |||
component: () => import('~/_pages/auth/Layout'), | |||
children: [ | |||
{ | |||
path: 'login', | |||
component: () => import('~/_pages/auth/Login'), | |||
}, | |||
{ | |||
path: 'forgotPassword', | |||
component: () => import('~/_pages/auth/ForgotPassword'), | |||
}, | |||
{ | |||
path: 'register', | |||
component: () => import('~/_pages/auth/Register'), | |||
}, | |||
{ | |||
path: 'appLogin', | |||
component: () => import('~/_pages/auth/AppLogin'), | |||
}, | |||
{ | |||
path: 'verifyEmail', | |||
component: () => import('~/_pages/auth/VerifyEmail'), | |||
}, | |||
{ | |||
path: 'payment', | |||
component: () => import('~/_pages/auth/Payment'), | |||
}, | |||
{ path: 'logout', component: () => import('~/auth/LogoutPage') }, | |||
], | |||
}, | |||
], | |||
}, | |||
// otherwise redirect to dashboard | |||
{ path: '*', redirect: '/' }, | |||
{ path: '*', redirect: '' }, | |||
], | |||
}); | |||
@@ -281,10 +277,10 @@ const publicPages = [ | |||
'/legal', | |||
// new Pages | |||
'/new_pages', | |||
]; | |||
router.beforeEach((to, from, next) => { | |||
console.log('to', to); | |||
const authRequired = | |||
!publicPages.includes(to.path) && | |||
publicPages.filter((p) => to.path.startsWith(p)).length === 0; | |||
@@ -1,18 +1,15 @@ | |||
<!-- Copyright (c) 2020 Bubble, Inc. All rights reserved. For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ --> | |||
<template> | |||
<div v-if="isNewPage" class="page-container"> | |||
<Header></Header> | |||
<div class="page-container"> | |||
<router-view></router-view> | |||
<vue-snotify></vue-snotify> | |||
<totp-modal /> | |||
</div> | |||
<div v-else> | |||
<!-- <div v-else> | |||
<Header></Header> | |||
<div v-if="!configs"><img :src="loadingImgSrc" /></div> | |||
<div v-else class="jumbotron"> | |||
<totp-modal /> | |||
<table | |||
v-if=" | |||
this.user !== null && | |||
@@ -106,14 +103,14 @@ | |||
> | |||
</div> | |||
</div> | |||
</div> | |||
</div> --> | |||
</template> | |||
<style lang="scss"> | |||
@import '../_scss/base'; | |||
.page-container { | |||
height: 100vh; | |||
min-height: 100vh; | |||
} | |||
</style> | |||
@@ -121,13 +118,9 @@ | |||
import { mapState, mapActions, mapGetters } from 'vuex'; | |||
import { util } from '~/_helpers'; | |||
import { loadingImgSrc } from '~/_store'; | |||
import { Header } from '~/_components/layout'; | |||
export default { | |||
name: 'app', | |||
components: { | |||
Header, | |||
}, | |||
data() { | |||
return { | |||
showLocaleSelector: false, | |||
@@ -197,7 +190,7 @@ export default { | |||
'/support', | |||
'/legal', | |||
]; | |||
return ( | |||
newPages.includes(this.$route.path) || | |||
newPages.filter((p) => this.$route.path.startsWith(p)).length | |||