feat/ui-layout
naar master
4 jaren geleden
@@ -2,7 +2,9 @@ | |||
<template> | |||
<div 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" /> | |||
</router-link> | |||
<div class="flex-grow-1"></div> | |||
<div class="navbar d-none d-lg-flex"> | |||
<!--- If not logged in ---> | |||
@@ -31,11 +33,6 @@ | |||
</router-link> | |||
</div> | |||
<div v-else class="d-flex justify-content-center align-items-center"> | |||
<router-link to="/" class="d-flex align-items-center"> | |||
<Button headerLink> | |||
{{ messages.label_menu_dashboard }} | |||
</Button> | |||
</router-link> | |||
<router-link to="/bubbles" class="d-flex align-items-center"> | |||
<Button headerLink> | |||
{{ messages.label_menu_network }} | |||
@@ -107,9 +104,6 @@ | |||
</router-link> | |||
</div> | |||
<div v-else> | |||
<router-link class="dropdown-item" to="/"> | |||
{{ messages.label_menu_dashboard }} | |||
</router-link> | |||
<router-link class="dropdown-item" to="/bubbles"> | |||
{{ messages.label_menu_network }} | |||
</router-link> | |||
@@ -15,16 +15,76 @@ | |||
<h3 class="text-center mb-5"> | |||
{{ messages.field_label_show_advanced_plan_options }} | |||
</h3> | |||
<div v-if="showForkOption"> | |||
<!-- network type --> | |||
<div class="form-group"> | |||
<v-select | |||
:clearable="false" | |||
v-validate="'required'" | |||
:placeholder="messages.field_label_network_type" | |||
:options="networkTypeOptions" | |||
v-model="networkType" | |||
label="name" | |||
:reduce="(option) => option.value" | |||
> | |||
<template v-slot:selected-option="option"> | |||
<span> | |||
{{ messages.field_label_network_type }}: {{ option.name }} | |||
</span> | |||
</template> | |||
</v-select> | |||
</div> | |||
</div> | |||
<!-- fork host --> | |||
<div v-if="showForkOption && networkType === 'fork'" class="form-group"> | |||
<Input | |||
type="text" | |||
v-model="accountPlan.forkHost" | |||
name="forkHost" | |||
:placeholder="messages.field_label_network_fork_host" | |||
class="form-control" | |||
v-validate="'required'" | |||
:placeholder="messages.field_label_bubble_name" | |||
:class="{ 'is-invalid': submitted && errors.has('forkHost') }" | |||
/> | |||
<div | |||
v-if="submitted && errors.has('forkHost')" | |||
class="invalid-feedback d-block" | |||
> | |||
{{ errors.first('forkHost') }} | |||
</div> | |||
<div | |||
v-if="submitted && errors.has('fqdn')" | |||
class="invalid-feedback d-block" | |||
> | |||
{{ errors.first('fqdn') }} | |||
</div> | |||
<p | |||
class="text-center description" | |||
v-html="messages.field_description_network_fork_host" | |||
/> | |||
</div> | |||
<!-- OR, name --> | |||
<div v-else class="form-group"> | |||
<Input | |||
type="text" | |||
v-model="accountPlan.name" | |||
:placeholder="messages.field_label_network_name" | |||
v-validate="'required'" | |||
name="name" | |||
class="form-control" | |||
:class="{ 'is-invalid': submitted && errors.has('name') }" | |||
/> | |||
<div | |||
v-if="submitted && errors.has('name')" | |||
class="invalid-feedback d-block" | |||
> | |||
{{ errors.first('name') }} | |||
</div> | |||
</div> | |||
<div class="form-group" v-if="domains"> | |||
<v-select | |||
:clearable="false" | |||
v-validate="'required'" | |||
:placeholder="messages.field_label_network_domain" | |||
:options="domains" | |||
@@ -32,12 +92,16 @@ | |||
label="name" | |||
> | |||
<template v-slot:selected-option="option"> | |||
<span>{{messages.field_label_network_domain}}: {{ option.name }}</span> | |||
<span | |||
>{{ messages.field_label_network_domain }}: | |||
{{ option.name }}</span | |||
> | |||
</template> | |||
</v-select> | |||
</div> | |||
<div class="form-group" v-if="nearestRegions"> | |||
<v-select | |||
:clearable="false" | |||
v-validate="'required'" | |||
:placeholder="messages.field_label_region" | |||
:options="nearestRegions" | |||
@@ -56,8 +120,23 @@ | |||
</template> | |||
</v-select> | |||
</div> | |||
<div class="form-group"> | |||
<Checkbox | |||
:label="messages.field_label_flex_region" | |||
v-model="flexRegion" | |||
/> | |||
<p class="description"> | |||
{{ | |||
flexRegion | |||
? messages.field_label_flex_region_description | |||
: messages.field_label_exact_region_description | |||
}} | |||
</p> | |||
</div> | |||
<div class="form-group" v-if="localeTexts"> | |||
<v-select | |||
:clearable="false" | |||
v-validate="'required'" | |||
:placeholder="messages.field_label_locale" | |||
:options="localeTexts" | |||
@@ -72,6 +151,7 @@ | |||
</div> | |||
<div class="form-group" v-if="timezoneObjects"> | |||
<v-select | |||
:clearable="false" | |||
v-validate="'required'" | |||
:placeholder="messages.field_label_timezone" | |||
:options="timezoneObjects" | |||
@@ -82,12 +162,16 @@ | |||
name="timezone" | |||
> | |||
<template v-slot:selected-option="option"> | |||
<span>{{messages.field_label_timezone}}: {{ option.timezoneDescription }}</span> | |||
<span> | |||
{{ messages.field_label_timezone }}: | |||
{{ option.timezoneDescription }} | |||
</span> | |||
</template> | |||
</v-select> | |||
</div> | |||
<div class="form-group" v-if="footprintObjects"> | |||
<v-select | |||
:clearable="false" | |||
v-validate="'required'" | |||
:placeholder="messages.field_label_footprint" | |||
:options="footprintObjects" | |||
@@ -96,28 +180,39 @@ | |||
label="localName" | |||
> | |||
<template v-slot:selected-option="option"> | |||
<span>{{messages.field_label_footprint}}: {{ option.localName }}</span> | |||
<span> | |||
{{ messages.field_label_footprint }}: | |||
{{ option.localName }} | |||
</span> | |||
</template> | |||
</v-select> | |||
</div> | |||
<div class="form-group"> | |||
<v-select | |||
:clearable="false" | |||
:placeholder="messages.field_label_network_ssh_key" | |||
:options="sshKeys" | |||
> | |||
<template v-slot:selected-option="option"> | |||
<span>{{messages.field_label_network_ssh_key}}: {{ option.name }}</span> | |||
<span> | |||
{{ messages.field_label_network_ssh_key }}: | |||
{{ option.name }} | |||
</span> | |||
</template> | |||
<template v-slot:option="option"> | |||
{{ option.name }} | |||
( | |||
<span v-if="option.expiration">{{ | |||
<span v-if="option.expiration"> | |||
{{ | |||
messages.date_format_ssh_key_expiration.parseDateMessage( | |||
option.expiration, | |||
messages | |||
) | |||
}}</span> | |||
<span v-else>{{ messages.message_ssh_key_no_expiration }}</span> | |||
}} | |||
</span> | |||
<span v-else> | |||
{{ messages.message_ssh_key_no_expiration }} | |||
</span> | |||
) | |||
</template> | |||
</v-select> | |||
@@ -233,6 +328,9 @@ export default { | |||
paymentMethodType: null, | |||
paymentInfo: null, | |||
}, | |||
forkHost: '', | |||
syncAccount: true, | |||
launchLock: false, | |||
sendErrors: true, | |||
sendMetrics: true, | |||
}, | |||
@@ -245,11 +343,20 @@ export default { | |||
region: '', | |||
sshKey: '', | |||
}, | |||
flexRegion: true, | |||
cloudRegionUuid: '', | |||
networkType: 'bubble', | |||
submitted: false, | |||
}), | |||
computed: { | |||
...mapState('system', ['configs', 'messages', 'locales', 'timezones']), | |||
...mapState('system', [ | |||
'configs', | |||
'messages', | |||
'locales', | |||
'timezones', | |||
'detectedTimezone', | |||
]), | |||
...mapState('domains', ['domains']), | |||
...mapState('networks', ['nearestRegions', 'newNodeNotification']), | |||
...mapState('footprints', ['footprints']), | |||
@@ -267,6 +374,19 @@ export default { | |||
return tz_objects; | |||
}, | |||
networkTypeOptions: function() { | |||
return [ | |||
{ | |||
name: this.messages.field_label_network_type_regular, | |||
value: 'bubble', | |||
}, | |||
{ | |||
name: this.messages.field_label_network_type_fork, | |||
value: 'fork', | |||
}, | |||
]; | |||
}, | |||
isComplete() { | |||
return ( | |||
(this.accountPlan.name !== '' || this.accountPlan.forkHost !== '') && | |||
@@ -301,6 +421,16 @@ export default { | |||
})) | |||
: []; | |||
}, | |||
showForkOption() { | |||
return ( | |||
this.configs && | |||
this.configs.sageLauncher && | |||
this.configs.sageLauncher === true && | |||
this.user && | |||
this.user.admin === true | |||
); | |||
}, | |||
}, | |||
methods: { | |||
@@ -309,6 +439,7 @@ export default { | |||
...mapActions('footprints', ['getAllFootprints']), | |||
...mapActions('users', ['listSshKeysByUserId']), | |||
...mapGetters('networks', ['loading']), | |||
...mapActions('paymentMethods', ['getAllAccountPaymentMethods']), | |||
setAccountPaymentMethod(apm) { | |||
this.accountPlan.paymentMethodObject = { | |||
@@ -348,6 +479,11 @@ export default { | |||
messages: this.messages, | |||
errors: this.errors, | |||
}); | |||
this.getAllAccountPaymentMethods({ | |||
userId: currentUser.uuid, | |||
messages: this.messages, | |||
errors: this.errors, | |||
}); | |||
this.onUpdateSSH(); | |||
}, | |||
@@ -446,6 +582,7 @@ export default { | |||
this.defaults.domain = doms[0].name; | |||
} | |||
}, | |||
detectedTimezone(tz) { | |||
if (tz && tz.timeZoneId) { | |||
if ( | |||
@@ -457,6 +594,7 @@ export default { | |||
this.defaults.timezone = tz.timeZoneId; | |||
} | |||
}, | |||
detectedLocale(loc) { | |||
if (loc) { | |||
if (this.accountPlan.locale === null || this.accountPlan.locale === '') | |||
@@ -464,57 +602,16 @@ export default { | |||
this.defaults.locale = loc; | |||
} | |||
}, | |||
newNodeNotification(nn) { | |||
if (nn && nn.uuid) { | |||
this.$router.push({ path: '/new_pages/launching-bubble/' + nn.networkName }); | |||
this.$router.push({ | |||
path: '/launching-bubble/' + nn.networkName, | |||
}); | |||
this.submitted = false; | |||
} | |||
}, | |||
nearestRegions(regions) { | |||
if (regions) { | |||
this.regions = regions; | |||
if ( | |||
this.cloudRegionUuid === null || | |||
typeof regions.find((r) => r.uuid === this.cloudRegionUuid) === | |||
'undefined' | |||
) { | |||
this.cloudRegionUuid = regions[0].uuid; | |||
} | |||
if ( | |||
this.defaults.region === '' || | |||
typeof regions.find((r) => r.uuid === this.defaults.region.uuid) === | |||
'undefined' | |||
) { | |||
this.defaults.region = regions[0]; | |||
} | |||
} | |||
}, | |||
domains(doms) { | |||
if (doms && doms[0]) { | |||
if (this.accountPlan.domain == null || this.accountPlan.domain === '') | |||
this.accountPlan.domain = doms[0].name; | |||
this.defaults.domain = doms[0].name; | |||
} | |||
}, | |||
detectedTimezone(tz) { | |||
if (tz && tz.timeZoneId) { | |||
if ( | |||
this.accountPlan.timezone == null || | |||
this.accountPlan.timezone === '' | |||
) | |||
this.accountPlan.timezone = tz.timeZoneId; | |||
if (this.defaults.timezone == null || this.defaults.timezone === '') | |||
this.defaults.timezone = tz.timeZoneId; | |||
} | |||
}, | |||
detectedLocale(loc) { | |||
if (loc) { | |||
if (this.accountPlan.locale === null || this.accountPlan.locale === '') | |||
this.accountPlan.locale = loc; | |||
this.defaults.locale = loc; | |||
} | |||
}, | |||
nearestRegions(regions) { | |||
if (regions) { | |||
this.regions = regions; | |||
@@ -534,6 +631,7 @@ export default { | |||
} | |||
} | |||
}, | |||
accountPaymentMethods(pms) { | |||
if (pms) { | |||
const payMethods = []; |
@@ -2,5 +2,5 @@ | |||
* Copyright (c) 2020 Bubble, Inc. All rights reserved. | |||
* For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ | |||
*/ | |||
export { default as AdvancedSettingsModal } from './AdvancedSettings'; | |||
export { default as LaunchBubbleSettingsModal } from './LaunchBubbleSettingsModal'; | |||
export { default as AddSSHKeyModal } from './AddSshKey'; |
@@ -314,7 +314,7 @@ export const util = { | |||
}, | |||
validateAccount: function (vue) { | |||
vue.me = vue.$route.path.startsWith('/me/') || vue.$route.path.startsWith('/new_pages/me/'); | |||
vue.me = vue.$route.path.startsWith('/me/'); | |||
if (vue.me) { | |||
vue.linkPrefix = '/me'; | |||
if (vue.currentUser === null) { | |||
@@ -2,7 +2,10 @@ | |||
<template> | |||
<div class="wrapper"> | |||
<h3 class="text-center form-title"> | |||
{{ messages.label_welcome_message.parseExpression({ user }) }} | |||
{{ | |||
messages.label_welcome_message && | |||
messages.label_welcome_message.parseExpression({ user }) | |||
}} | |||
</h3> | |||
<h3 class="d-flex align-items-center justify-content-center form-sub-title"> | |||
<span class="text-center"> | |||
@@ -27,7 +30,7 @@ | |||
<a class="how-it-works-section-link text-center d-block mt-5" href="#"> | |||
{{ messages.how_it_works_title }} | |||
</a> | |||
<AdvancedSettingsModal ref="settingsModal" /> | |||
<LaunchBubbleSettingsModal ref="settingsModal" /> | |||
</div> | |||
</template> | |||
@@ -58,28 +61,150 @@ | |||
</style> | |||
<script> | |||
import { mapState, mapActions } from 'vuex'; | |||
import { mapState, mapActions, mapGetters } from 'vuex'; | |||
import Lottie from 'lottie-web'; | |||
import { util } from '~/_helpers'; | |||
import { Button } from '~/_components/shared'; | |||
import { AdvancedSettingsModal } from '~/_components/modals'; | |||
import { LaunchBubbleSettingsModal } from '~/_components/modals'; | |||
export default { | |||
components: { | |||
Button, | |||
AdvancedSettingsModal, | |||
LaunchBubbleSettingsModal, | |||
}, | |||
data() { | |||
return { | |||
data: () => ({ | |||
lottie: null, | |||
}; | |||
user: util.currentUser(), | |||
accountPlan: { | |||
name: '', | |||
domain: '', | |||
locale: util.currentUser().locale, | |||
timezone: '', | |||
plan: 'bubble', | |||
footprint: 'Worldwide', | |||
sshKey: '', | |||
paymentMethodObject: { | |||
uuid: null, | |||
paymentMethodType: null, | |||
paymentInfo: null, | |||
}, | |||
forkHost: '', | |||
syncAccount: true, | |||
launchLock: false, | |||
sendErrors: true, | |||
sendMetrics: true, | |||
}, | |||
defaults: { | |||
domain: '', | |||
locale: 'en_US', | |||
timezone: '', | |||
plan: 'bubble', | |||
footprint: 'Worldwide', | |||
region: '', | |||
sshKey: '', | |||
}, | |||
flexRegion: true, | |||
cloudRegionUuid: '', | |||
networkType: 'bubble', | |||
submitted: false, | |||
}), | |||
computed: { | |||
...mapState('system', ['messages']), | |||
...mapState('account', ['user']), | |||
...mapState('system', [ | |||
'configs', | |||
'messages', | |||
'locales', | |||
'timezones', | |||
'detectedTimezone', | |||
]), | |||
...mapState('domains', ['domains']), | |||
...mapState('networks', ['nearestRegions', 'newNodeNotification']), | |||
...mapState('footprints', ['footprints']), | |||
...mapState('users', ['sshKeys']), | |||
...mapState('paymentMethods', ['accountPaymentMethods']), | |||
}, | |||
methods: { | |||
...mapActions('domains', ['getAllDomains']), | |||
...mapActions('networks', ['getNearestRegions', 'addPlanAndStartNetwork']), | |||
...mapActions('footprints', ['getAllFootprints']), | |||
...mapActions('users', ['listSshKeysByUserId']), | |||
...mapGetters('networks', ['loading']), | |||
...mapActions('paymentMethods', ['getAllAccountPaymentMethods']), | |||
openSettingsModal(ev) { | |||
ev.preventDefault(); | |||
this.$refs.settingsModal.show(); | |||
}, | |||
setAccountPaymentMethod(apm) { | |||
this.accountPlan.paymentMethodObject = { | |||
uuid: apm.uuid, | |||
paymentMethodType: null, | |||
paymentInfo: null, | |||
}; | |||
return false; | |||
}, | |||
show() { | |||
this.$modal.show('advanced-settings'); | |||
}, | |||
hide() { | |||
this.$modal.hide('advanced-settings'); | |||
}, | |||
addSSHKey() { | |||
this.$refs.sshKeyModal.show(); | |||
}, | |||
initDefaults() { | |||
const currentUser = util.currentUser(); | |||
this.getAllAccountPaymentMethods({userId: currentUser.uuid, messages: this.messages, errors: this.errors}); | |||
}, | |||
findRegion(uuid) { | |||
if (this.nearestRegions) { | |||
for (let i = 0; i < this.nearestRegions.length; i++) { | |||
if (this.nearestRegions[i].uuid === uuid) | |||
return this.nearestRegions[i]; | |||
} | |||
} | |||
if (uuid !== null) console.log('findRegion: uuid not found: ' + uuid); | |||
return null; | |||
}, | |||
launchBubble() { | |||
if (this.paymentInfo || this.accountPlan.paymentMethodObject.uuid) { | |||
const cloudRegion = this.findRegion(this.cloudRegionUuid); | |||
if (cloudRegion === null) { | |||
this.errors.add({ | |||
field: 'region', | |||
msg: this.messages['err_region_notFound'], | |||
}); | |||
} else { | |||
if (this.configs.requireSendMetrics) { | |||
this.accountPlan.sendErrors = true; | |||
this.accountPlan.sendMetrics = true; | |||
} else { | |||
if (this.accountPlan.sendErrors === null) | |||
this.accountPlan.sendErrors = true; | |||
if (this.accountPlan.sendMetrics === null) | |||
this.accountPlan.sendMetrics = true; | |||
} | |||
this.addPlanAndStartNetwork({ | |||
userId: this.user.uuid, | |||
accountPlan: this.accountPlan, | |||
cloud: cloudRegion.cloud, | |||
region: cloudRegion.internalName, | |||
exactRegion: !this.flexRegion, | |||
messages: this.messages, | |||
errors: this.errors, | |||
}); | |||
} | |||
} | |||
}, | |||
}, | |||
mounted() { | |||
@@ -90,19 +215,90 @@ export default { | |||
autoplay: true, | |||
path: '/rocket-launch.json', | |||
}); | |||
this.initDefaults(); | |||
}, | |||
methods: { | |||
openSettingsModal(ev) { | |||
ev.preventDefault(); | |||
this.$refs.settingsModal.show(); | |||
watch: { | |||
domains(doms) { | |||
if (doms && doms[0]) { | |||
if (this.accountPlan.domain == null || this.accountPlan.domain === '') | |||
this.accountPlan.domain = doms[0].name; | |||
this.defaults.domain = doms[0].name; | |||
} | |||
}, | |||
launchBubble() { | |||
detectedTimezone(tz) { | |||
if (tz && tz.timeZoneId) { | |||
if ( | |||
this.accountPlan.timezone == null || | |||
this.accountPlan.timezone === '' | |||
) | |||
this.accountPlan.timezone = tz.timeZoneId; | |||
if (this.defaults.timezone == null || this.defaults.timezone === '') | |||
this.defaults.timezone = tz.timeZoneId; | |||
} | |||
}, | |||
detectedLocale(loc) { | |||
if (loc) { | |||
if (this.accountPlan.locale === null || this.accountPlan.locale === '') | |||
this.accountPlan.locale = loc; | |||
this.defaults.locale = loc; | |||
} | |||
}, | |||
newNodeNotification(nn) { | |||
if (nn && nn.uuid) { | |||
this.$router.push({ | |||
path: '/launching-bubble/' + nn.networkName, | |||
}); | |||
this.submitted = false; | |||
} | |||
}, | |||
nearestRegions(regions) { | |||
if (regions) { | |||
this.regions = regions; | |||
if ( | |||
this.cloudRegionUuid === null || | |||
typeof regions.find((r) => r.uuid === this.cloudRegionUuid) === | |||
'undefined' | |||
) { | |||
this.cloudRegionUuid = regions[0].uuid; | |||
} | |||
if ( | |||
this.defaults.region === '' || | |||
typeof regions.find((r) => r.uuid === this.defaults.region.uuid) === | |||
'undefined' | |||
) { | |||
this.defaults.region = regions[0]; | |||
} | |||
} | |||
}, | |||
watch: {}, | |||
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); | |||
} | |||
} | |||
this.accountPayMethods = payMethods; | |||
if ( | |||
this.accountPlan.paymentMethodObject.uuid === null && | |||
payMethods.length > 0 | |||
) { | |||
this.setAccountPaymentMethod(payMethods[0]); | |||
} | |||
} | |||
}, | |||
}, | |||
}; | |||
</script> |