#54 Launch Bubble page

Fusionado
jonathan fusionados 104 commits de feat/ui-layout en master hace 4 años
  1. +3
    -9
      src/_components/layout/Header.vue
  2. +160
    -62
      src/_components/modals/LaunchBubbleSettingsModal.vue
  3. +1
    -1
      src/_components/modals/index.js
  4. +1
    -1
      src/_helpers/util.js
  5. +216
    -20
      src/_pages/main/bubble/LaunchBubble.vue

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

@@ -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 ">
<img src="/small-BubbleLogo-Horizontal-BlackText.png" height="40" />
<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>


src/_components/modals/AdvancedSettings.vue → src/_components/modals/LaunchBubbleSettingsModal.vue Ver fichero

@@ -15,16 +15,76 @@
<h3 class="text-center mb-5">
{{ messages.field_label_show_advanced_plan_options }}
</h3>
<div class="form-group">

<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"
@@ -47,7 +111,7 @@
>
<template v-slot:selected-option="option">
<span>
{{messages.field_label_region}}: {{ option.name }}
{{ messages.field_label_region }}: {{ option.name }}
{{ regionDistance(option.uuid) }}
</span>
</template>
@@ -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"
@@ -66,12 +145,13 @@
label="label"
>
<template v-slot:selected-option="option">
<span>{{messages.field_label_locale}}: {{ option.label }}</span>
<span>{{ messages.field_label_locale }}: {{ option.label }}</span>
</template>
</v-select>
</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">{{
messages.date_format_ssh_key_expiration.parseDateMessage(
option.expiration,
messages
)
}}</span>
<span v-else>{{ messages.message_ssh_key_no_expiration }}</span>
<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>
)
</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 = [];

+ 1
- 1
src/_components/modals/index.js Ver fichero

@@ -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';

+ 1
- 1
src/_helpers/util.js Ver fichero

@@ -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) {


+ 216
- 20
src/_pages/main/bubble/LaunchBubble.vue Ver fichero

@@ -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 {
lottie: null,
};
},
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;
}
},
},

watch: {},
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];
}
}
},

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>

Cargando…
Cancelar
Guardar