Files
ploi-core/resources/js/Pages/Servers/Index.vue
2022-08-12 21:11:23 +02:00

306 lines
11 KiB
Vue

<template>
<Page>
<Head><title>{{ __('Servers') }}</title></Head>
<Portal to="modals" v-if="can('servers', 'create')">
<ModalContainer>
<Modal @close="modalIsOpen = false" v-if="modalIsOpen" @submit="submit">
<template #title>{{ __('Create a server') }}</template>
<template #form>
<FormInput :loading="loading" :label="__('Name')" placeholder="webserver-01"
:errors="$page.props.errors.name" v-model="form.name" />
<FormSelect :loading="loading" :errors="$page.props.errors.provider_id" :label="__('Select provider')"
v-model="form.provider_id">
<option :value="`${null}`">{{ __('Select random provider') }}</option>
<option v-for="(name, id) in providers" :value="id">{{ name }}</option>
</FormSelect>
<FormSelect :loading="loading" :errors="$page.props.errors.provider_region_id" :label="__('Select region')"
v-model="form.provider_region_id">
<option :value="`${null}`">{{ __('Select random region') }}</option>
<option v-for="(name, id) in regions" :value="id">{{ name }}</option>
</FormSelect>
<FormSelect :loading="loading" :errors="$page.props.errors.provider_plan_id" :label="__('Select plan')"
v-model="form.provider_plan_id">
<option :value="`${null}`">{{ __('Select random plan') }}</option>
<option v-for="(name, id) in plans" :value="id">{{ name }}</option>
</FormSelect>
<FormSelect :loading="loading" :errors="$page.props.errors.database_type" :label="__('Select database type')"
v-model="form.database_type">
<option value="mysql">{{ __('MySQL 5.7') }}</option>
<option value="mariadb">{{ __('MariaDB') }}</option>
<option value="postgresql">{{ __('PostgreSQL') }}</option>
<option value="postgresql13">{{ __('PostgreSQL 13') }}</option>
</FormSelect>
</template>
<template #form-actions>
<Button v-text="__('Create')"></Button>
</template>
</Modal>
</ModalContainer>
</Portal>
<TopBar :breadcrumbs="breadcrumbs" />
<Content>
<Container>
<PageHeader>
<template #start>
<PageHeaderTitle>{{ __('Servers') }}</PageHeaderTitle>
</template>
<template #end v-if="can('servers', 'create')">
<Button @click="modalIsOpen = true">{{ __('Create server') }}</Button>
</template>
</PageHeader>
<PageBody>
<EmptyImage v-if="!servers.meta.total" />
<List>
<ListItem v-for="server in servers.data" :key="server.id">
<template #prefix>
<StatusBubble :variant="server.status === 'busy' ? 'gray' : 'success'" />
</template>
<template #title>
<inertia-link class="text-primary font-medium" :href="route('servers.show', server.id)">
{{ server.name }}
</inertia-link>
</template>
<template #subtitle>{{ server.ip }} <span v-if="server.ip">&centerdot;</span>
{{ server.sites_count }} {{ __choice('site|sites', server.sites_count) }}
</template>
<template #suffix>
<Dropdown v-slot="{ isOpen, toggle, position }">
<IconButton @click="toggle">
<IconMore class="w-5 h-5" />
</IconButton>
<DropdownList :position="position" v-if="isOpen">
<DropdownListItem :to="route('servers.show', server.id)">View</DropdownListItem>
<DropdownListItemButton v-if="can('servers', 'delete')" class="!text-danger"
@click="confirmDelete(server)">
Delete
</DropdownListItemButton>
</DropdownList>
</Dropdown>
</template>
</ListItem>
</List>
</PageBody>
</Container>
</Content>
</Page>
</template>
<script>
import TopBar from './components/TopBar.vue'
import Container from '@/components/Container.vue'
import Content from '@/components/Content.vue'
import Page from '@/components/Page.vue'
import PageHeader from '@/components/PageHeader.vue'
import PageHeaderTitle from '@/components/PageHeaderTitle.vue'
import PageBody from '@/components/PageBody.vue'
import Button from '@/components/Button.vue'
import List from '@/components/List.vue'
import ListItem from '@/components/ListItem.vue'
import StatusBubble from '@/components/StatusBubble.vue'
import NotificationBadge from '@/components/NotificationBadge.vue'
import MainLayout from '@/Layouts/MainLayout.vue'
import IconBox from '@/components/icons/IconBox.vue'
import IconGlobe from '@/components/icons/IconGlobe.vue'
import IconStorage from '@/components/icons/IconStorage.vue'
import IconButton from '@/components/IconButton.vue'
import IconMore from '@/components/icons/IconMore.vue'
import EmptyImage from '@/components/EmptyImage.vue'
import Modal from '@/components/Modal.vue'
import ModalContainer from '@/components/ModalContainer.vue'
import FormInput from '@/components/forms/FormInput.vue'
import FormSelect from '@/components/forms/FormSelect.vue'
import FormActions from '@/components/FormActions.vue'
import Dropdown from '@/components/Dropdown.vue'
import DropdownList from '@/components/DropdownList.vue'
import DropdownListItem from '@/components/DropdownListItem.vue'
import DropdownListItemButton from '@/components/DropdownListItemButton.vue'
import {useConfirm} from '@/hooks/confirm'
export default {
layout: MainLayout,
components: {
TopBar,
Container,
Content,
Page,
PageHeader,
PageHeaderTitle,
PageBody,
Button,
List,
IconButton,
IconMore,
ListItem,
StatusBubble,
NotificationBadge,
IconBox,
IconGlobe,
IconStorage,
EmptyImage,
Modal,
ModalContainer,
FormInput,
FormActions,
FormSelect,
Dropdown,
DropdownList,
DropdownListItem,
DropdownListItemButton
},
props: {
servers: Object,
dataProviders: [Array, Object],
},
computed: {
shouldBePolling() {
return !!this.servers.data.filter((server) => {
return server.status === 'busy';
}).length;
}
},
mounted() {
if (this.shouldBePolling) {
this.startPollingInterval();
}
},
watch: {
shouldBePolling: function (value) {
if (!value) {
this.clearPollingInterval();
return;
}
if (!this.pollingInterval) {
this.startPollingInterval();
}
},
'form.provider': function (value) {
// Reset values if null
if (!value) {
this.regions = [];
this.plans = [];
return;
}
this.loading = true;
window.axios.get(this.route('servers.plans-and-regions', value))
.then(response => {
this.loading = false;
this.regions = response.data.regions;
this.plans = response.data.plans;
})
.catch(error => {
this.loading = false;
})
}
},
data() {
return {
loading: false,
form: {
name: null,
provider_id: null,
provider_region_id: null,
provider_plan_id: null,
database_type: 'mysql'
},
providers: this.dataProviders,
regions: [],
plans: [],
pollingInterval: null,
modalIsOpen: false,
breadcrumbs: [
{
title: this.$page.props.settings.name,
to: '/',
},
{
title: this.__('Servers'),
to: this.route('servers.index'),
},
],
}
},
methods: {
startPollingInterval() {
this.pollingInterval = setInterval(function () {
this.poll();
}.bind(this), 120000);
},
clearPollingInterval() {
clearTimeout(this.pollingInterval);
this.pollingInterval = null;
},
poll() {
this.$inertia.get(this.route('servers.index'), {}, {
only: ['servers'],
preserveScroll: true,
})
},
submit() {
this.$inertia.post(this.route('servers.store'), this.form, {
only: ['errors', 'flash', 'servers'],
onStart: () => this.loading = true,
onSuccess: () => {
if (!Object.keys(this.$page.props.errors).length) {
this.form.domain = null;
this.modalIsOpen = false;
this.form = {
name: null,
provider_id: null,
provider_region_id: null,
provider_plan_id: null,
}
}
},
onFinish: () => this.loading = false
});
},
confirmDelete(server) {
useConfirm({
title: this.__('Are you sure?'),
message: `Your server will be deleted completely, this action is irreversible.`,
onConfirm: () => this.delete(server),
})
},
delete(server) {
this.$inertia.delete(this.route('servers.delete', server.id))
}
},
beforeUnmount() {
this.clearPollingInterval();
}
}
</script>