335 lines
8.6 KiB
Vue

<template>
<div>
<header
class="sticky top-0 z-10 flex items-center justify-between border-b bg-white px-5 py-2.5"
>
<Breadcrumbs
:items="[
{ label: 'Sites', route: { name: 'Sites' } },
{ label: 'New', route: { name: 'NewSite' } }
]"
/>
</header>
<WizardCard>
<div class="mb-2 text-center">
<h1 class="text-2xl font-bold">Create a new site</h1>
<p v-if="benchTitle" class="text-base text-gray-700">
Site will be created on bench
<span class="font-medium">{{ benchTitle }}</span>
</p>
</div>
<Steps :steps="steps">
<template
v-slot="{ active: activeStep, next, previous, hasPrevious, hasNext }"
>
<div class="mt-8"></div>
<Hostname
v-show="activeStep.name === 'Hostname'"
v-model="subdomain"
@error="error => (subdomainValid = !Boolean(error))"
/>
<Apps
v-show="activeStep.name === 'Apps'"
:privateBench="privateBench"
:bench="benchName"
v-model:selectedApps="selectedApps"
v-model:selectedGroup="selectedGroup"
v-model:selectedRegion="selectedRegion"
v-model:shareDetailsConsent="shareDetailsConsent"
/>
<div v-if="activeStep.name === 'Select App Plans'">
<ChangeAppPlanSelector
v-for="app in appsWithPlans"
:key="app.name"
:app="app.name"
:group="selectedGroup"
:editable="false"
class="mb-9"
@change="plan => (selectedAppPlans[app.name] = plan.name)"
/>
</div>
<Restore
v-model:selectedFiles="selectedFiles"
v-model:skipFailingPatches="skipFailingPatches"
v-show="activeStep.name == 'Restore'"
/>
<Plans
v-model:selectedPlan="selectedPlan"
:bench="bench"
:benchTeam="benchTeam"
v-show="activeStep.name === 'Plan'"
/>
<ErrorMessage :message="validationMessage" />
<div class="mt-4">
<!-- Region consent checkbox -->
<div class="my-6 w-full" v-if="!hasNext">
<FormControl
type="checkbox"
v-model="agreedToRegionConsent"
label="I agree that the laws of the region selected by me shall stand applicable to me and Jingrow."
/>
</div>
<ErrorMessage class="mb-4" :message="$resources.newSite.error" />
<div class="flex items-center justify-between">
<Button v-show="hasPrevious" @click="previous"> Back </Button>
<Button
v-show="
(activeStep.name !== 'Restore' || wantsToRestore) && hasNext
"
class="ml-auto"
variant="solid"
@click="nextStep(activeStep, next)"
:class="{ 'mt-2': hasPrevious }"
:loading="loadingPlans"
loadingText="Loading"
>
Next
</Button>
<Button
v-show="
!wantsToRestore && activeStep.name === 'Restore' && hasNext
"
class="ml-auto"
variant="solid"
@click="nextStep(activeStep, next)"
>
Skip
</Button>
<Button
v-show="!hasNext"
class="ml-auto"
variant="solid"
:class="{ 'mt-2': hasPrevious }"
@click="$resources.newSite.submit()"
:loading="$resources.newSite.loading"
>
Create Site
</Button>
</div>
</div>
</template>
</Steps>
</WizardCard>
</div>
</template>
<script>
import WizardCard from '@/components/WizardCard.vue';
import Steps from '@/components/Steps.vue';
import Hostname from './NewSiteHostname.vue';
import Apps from './NewSiteApps.vue';
import Restore from './NewSiteRestore.vue';
import Plans from './NewSitePlans.vue';
import ChangeAppPlanSelector from '@/components/ChangeAppPlanSelector.vue';
export default {
name: 'NewSite',
props: ['bench'],
components: {
WizardCard,
Steps,
Hostname,
Apps,
Restore,
Plans,
ChangeAppPlanSelector
},
data() {
return {
subdomain: null,
subdomainValid: false,
privateBench: false,
benchName: null,
benchTitle: null,
benchTeam: null,
selectedApps: [],
selectedGroup: null,
selectedRegion: null,
selectedFiles: {
database: null,
public: null,
private: null
},
skipFailingPatches: false,
selectedPlan: null,
shareDetailsConsent: false,
validationMessage: null,
steps: [
{
name: 'Hostname',
validate: () => {
return this.subdomainValid;
}
},
{
name: 'Apps',
validate: () => {
if (this.privateBench) return true;
if (!this.selectedRegion) {
this.validationMessage = 'Please select the region';
return false;
} else {
this.validationMessage = null;
}
return true;
}
},
{
name: 'Restore'
},
{
name: 'Plan'
}
],
agreedToRegionConsent: false,
selectedAppPlans: {},
loadingPlans: false
};
},
async mounted() {
if (this.$route.query.domain) {
let domain = this.$route.query.domain.split('.');
if (domain) {
this.subdomain = domain[0];
}
this.$router.replace({});
}
if (this.bench) {
this.privateBench = true;
this.selectedGroup = this.bench;
this.benchTitle = this.bench;
let { title, creation, team } = await this.$call(
'jcloud.api.bench.get_title_and_creation',
{
name: this.bench
}
);
this.benchName = this.bench;
this.benchTitle = title;
this.benchTeam = team;
if (team == this.$account.team.name) {
// Select a zero cost plan and remove the plan selection step
this.selectedPlan = { name: 'Unlimited' };
let plan_step_index = this.steps.findIndex(step => step.name == 'Plan');
this.steps.splice(plan_step_index, 1);
}
}
},
resources: {
newSite() {
return {
url: 'jcloud.api.site.new',
params: {
site: {
name: this.subdomain,
apps: this.selectedApps,
group: this.selectedGroup,
cluster: this.selectedRegion,
plan: this.selectedPlan ? this.selectedPlan.name : null,
files: this.selectedFiles,
share_details_consent: this.shareDetailsConsent,
skip_failing_patches: this.skipFailingPatches,
selected_app_plans: this.selectedAppPlans
}
},
onSuccess(data) {
let { site, job = '' } = data;
this.$router.push(`/sites/${site}/jobs/${job}`);
},
validate() {
let canCreate =
this.subdomainValid &&
this.selectedApps.length > 0 &&
this.selectedPlan &&
(!this.wantsToRestore || this.selectedFiles.database);
if (!this.agreedToRegionConsent) {
return 'Please agree to the above consent to create site';
}
if (!canCreate) {
return 'Cannot create site';
}
}
};
}
},
computed: {
wantsToRestore() {
if (this.selectedFiles.database) {
return true;
}
return false;
}
},
methods: {
async nextStep(activeStep, next) {
if (activeStep.name == 'Apps') {
this.loadingPlans = true;
// Fetch apps that have plans
this.appsWithPlans = await this.$call(
'jcloud.api.marketplace.get_apps_with_plans',
{
apps: JSON.stringify(this.selectedApps),
release_group: this.selectedGroup
}
);
if (this.appsWithPlans && this.appsWithPlans.length > 0) {
this.addPlanSelectionStep();
this.selectedAppPlans = {};
for (let app of this.appsWithPlans) {
this.selectedAppPlans[app.name] = null;
}
} else {
this.validationMessage = null;
this.removePlanSelectionStepIfExists();
}
this.loadingPlans = false;
}
next();
},
addPlanSelectionStep() {
const appsStepIndex = this.steps.findIndex(step => step.name == 'Apps');
const selectAppPlansStepIndex = this.steps.findIndex(
step => step.name == 'Select App Plans'
);
if (selectAppPlansStepIndex < 0) {
this.steps.splice(appsStepIndex + 1, 0, {
name: 'Select App Plans',
validate: () => {
for (let app of Object.keys(this.selectedAppPlans)) {
if (!this.selectedAppPlans[app]) {
this.validationMessage = `Please select a plan for ${app}`;
return false;
} else {
this.validationMessage = null;
}
}
return true;
}
});
}
},
removePlanSelectionStepIfExists() {
const selectAppPlansStepIndex = this.steps.findIndex(
step => step.name == 'Select App Plans'
);
if (selectAppPlansStepIndex >= 0) {
this.steps.splice(selectAppPlansStepIndex, 1);
}
}
}
};
</script>