dashboard/pkg/epinio/components/application/AppProgress.vue

263 lines
6.8 KiB
Vue

<script lang="ts">
import Vue, { PropType } from 'vue';
import ApplicationAction, { APPLICATION_ACTION_TYPE } from '../../models/application-action';
import SortableTable from '@shell/components/SortableTable/index.vue';
import Checkbox from '@components/Form/Checkbox/Checkbox.vue';
import BadgeState from '@components/BadgeState/BadgeState.vue';
import { STATE, DESCRIPTION } from '@shell/config/table-headers';
import { EPINIO_TYPES, APPLICATION_ACTION_STATE, APPLICATION_SOURCE_TYPE, EpinioApplication } from '../../types';
import { EpinioAppSource } from '../../components/application/AppSource.vue';
import { EpinioAppBindings } from '../../components/application/AppConfiguration.vue';
import EpinioNamespace from '~/pkg/epinio/models/namespaces';
interface Data {
running: boolean;
actionHeaders: any[];
actions: ApplicationAction[]
}
export default Vue.extend<Data, any, any, any>({
components: {
SortableTable,
BadgeState,
Checkbox,
},
props: {
application: {
type: Object as PropType<EpinioApplication>,
required: true
},
source: {
type: Object as PropType<EpinioAppSource>,
required: true
},
bindings: {
type: Object as PropType<EpinioAppBindings>,
default: () => null
},
mode: {
type: String,
required: true
},
step: {
type: Object as PropType<any>,
required: true
}
},
async fetch() {
const coreArgs: Partial<ApplicationAction & {
application: EpinioApplication,
bindings: EpinioAppBindings,
type: string,
}> = {
application: this.application,
bindings: this.bindings,
type: EPINIO_TYPES.APP_ACTION,
};
if (!this.namespaces.find((ns: EpinioNamespace) => ns.name === coreArgs.application?.meta.namespace)) {
this.actions.push(await this.$store.dispatch('epinio/create', {
action: APPLICATION_ACTION_TYPE.CREATE_NS,
index: 0, // index used for sorting
...coreArgs,
}));
}
this.actions.push(await this.$store.dispatch('epinio/create', {
action: APPLICATION_ACTION_TYPE.CREATE,
index: 1, // index used for sorting
...coreArgs,
}));
if (this.bindings?.configurations?.length) {
this.actions.push(await this.$store.dispatch('epinio/create', {
action: APPLICATION_ACTION_TYPE.BIND_CONFIGURATIONS,
index: 2,
...coreArgs,
}));
}
if (this.bindings?.services?.length) {
this.actions.push(await this.$store.dispatch('epinio/create', {
action: APPLICATION_ACTION_TYPE.BIND_SERVICES,
index: 3,
...coreArgs,
}));
}
if (this.source.type === APPLICATION_SOURCE_TYPE.ARCHIVE ||
this.source.type === APPLICATION_SOURCE_TYPE.FOLDER) {
this.actions.push(await this.$store.dispatch('epinio/create', {
action: APPLICATION_ACTION_TYPE.UPLOAD,
index: 4,
...coreArgs,
}));
}
if (this.source.type === APPLICATION_SOURCE_TYPE.GIT_URL) {
this.actions.push(await this.$store.dispatch('epinio/create', {
action: APPLICATION_ACTION_TYPE.GIT_FETCH,
index: 4,
...coreArgs,
}));
}
if (this.source.type === APPLICATION_SOURCE_TYPE.ARCHIVE ||
this.source.type === APPLICATION_SOURCE_TYPE.FOLDER ||
this.source.type === APPLICATION_SOURCE_TYPE.GIT_URL) {
this.actions.push(await this.$store.dispatch('epinio/create', {
action: APPLICATION_ACTION_TYPE.BUILD,
index: 5,
...coreArgs,
}));
}
this.actions.push(await this.$store.dispatch('epinio/create', {
action: APPLICATION_ACTION_TYPE.DEPLOY,
index: 6,
...coreArgs,
}));
this.create();
},
data() {
return {
running: false,
actionHeaders: [
{
name: 'epinio-name',
labelKey: 'epinio.applications.steps.progress.table.stage.label',
value: 'name',
sort: ['index'],
width: 150,
},
{
...DESCRIPTION,
sort: undefined,
value: 'description',
width: 450,
},
{
...STATE,
sort: undefined,
labelKey: 'epinio.applications.steps.progress.table.status',
width: 150
},
],
actions: [],
APPLICATION_ACTION_STATE
};
},
computed: {
actionsToRun() {
return this.actions.filter((action: ApplicationAction) => action.run);
},
namespaces() {
return this.$store.getters['epinio/all'](EPINIO_TYPES.NAMESPACE);
},
},
watch: {
running(neu, prev) {
if (prev && !neu) {
Vue.set(this.step, 'ready', true);
}
}
},
methods: {
async fetchApp() {
try {
await this.application.forceFetch();
} catch (err) {
}
},
async create() {
Vue.set(this, 'running', true);
const enabledActions = [...this.actionsToRun];
for (const action of enabledActions) {
try {
// TODO: RC bind to SI action
await action.execute({ source: this.source });
} catch (err) {
Vue.set(this, 'running', false);
console.error(err);// eslint-disable-line no-console
await this.fetchApp();
return;
}
}
await this.fetchApp();
Vue.set(this, 'running', false);
this.$emit('finished', true);
}
}
});
</script>
<template>
<div v-if="!$fetchState.pending" class="progress-container">
<div class="progress">
<SortableTable
:rows="actions"
:headers="actionHeaders"
:table-actions="false"
:row-actions="false"
default-sort-by="epinio-name"
:search="false"
key-field="key"
>
<template #cell:index="{row}">
<Checkbox v-model="row.run" :disabled="true" />
</template>
<template #cell:state="{row}">
<div class="status">
<i
v-if="row.state === APPLICATION_ACTION_STATE.RUNNING"
v-tooltip="row.stateDisplay"
class="icon icon-lg icon-spinner icon-spin"
/>
<BadgeState v-else :color="row.stateBackground" :label="row.stateDisplay" class="badge" />
</div>
</template>
</SortableTable>
</div>
</div>
</template>
<style lang="scss" scoped>
.progress-container {
display: flex;
justify-content: center;
.progress {
padding: 10px 0;
$statusHeight: 20px;
.status {
min-height: $statusHeight; // Ensure switching from spinner to badge doesn't wibble
display: flex;
align-items: center;
.badge {
min-height: $statusHeight;
}
}
}
}
</style>