summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/environments/components
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/environments/components')
-rw-r--r--app/assets/javascripts/environments/components/environment.js192
-rw-r--r--app/assets/javascripts/environments/components/environment.vue236
-rw-r--r--app/assets/javascripts/environments/components/environment_actions.js80
-rw-r--r--app/assets/javascripts/environments/components/environment_actions.vue89
-rw-r--r--app/assets/javascripts/environments/components/environment_external_url.js30
-rw-r--r--app/assets/javascripts/environments/components/environment_external_url.vue33
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue (renamed from app/assets/javascripts/environments/components/environment_item.js)283
-rw-r--r--app/assets/javascripts/environments/components/environment_monitoring.js31
-rw-r--r--app/assets/javascripts/environments/components/environment_monitoring.vue32
-rw-r--r--app/assets/javascripts/environments/components/environment_rollback.js67
-rw-r--r--app/assets/javascripts/environments/components/environment_rollback.vue59
-rw-r--r--app/assets/javascripts/environments/components/environment_stop.js64
-rw-r--r--app/assets/javascripts/environments/components/environment_stop.vue61
-rw-r--r--app/assets/javascripts/environments/components/environment_terminal_button.vue (renamed from app/assets/javascripts/environments/components/environment_terminal_button.js)22
-rw-r--r--app/assets/javascripts/environments/components/environments_table.js60
-rw-r--r--app/assets/javascripts/environments/components/environments_table.vue112
16 files changed, 791 insertions, 660 deletions
diff --git a/app/assets/javascripts/environments/components/environment.js b/app/assets/javascripts/environments/components/environment.js
deleted file mode 100644
index 51aab8460f6..00000000000
--- a/app/assets/javascripts/environments/components/environment.js
+++ /dev/null
@@ -1,192 +0,0 @@
-/* eslint-disable no-new */
-/* global Flash */
-import Vue from 'vue';
-import EnvironmentsService from '../services/environments_service';
-import EnvironmentTable from './environments_table';
-import EnvironmentsStore from '../stores/environments_store';
-import TablePaginationComponent from '../../vue_shared/components/table_pagination';
-import '../../lib/utils/common_utils';
-import eventHub from '../event_hub';
-
-export default Vue.component('environment-component', {
-
- components: {
- 'environment-table': EnvironmentTable,
- 'table-pagination': TablePaginationComponent,
- },
-
- data() {
- const environmentsData = document.querySelector('#environments-list-view').dataset;
- const store = new EnvironmentsStore();
-
- return {
- store,
- state: store.state,
- visibility: 'available',
- isLoading: false,
- cssContainerClass: environmentsData.cssClass,
- endpoint: environmentsData.environmentsDataEndpoint,
- canCreateDeployment: environmentsData.canCreateDeployment,
- canReadEnvironment: environmentsData.canReadEnvironment,
- canCreateEnvironment: environmentsData.canCreateEnvironment,
- projectEnvironmentsPath: environmentsData.projectEnvironmentsPath,
- projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath,
- newEnvironmentPath: environmentsData.newEnvironmentPath,
- helpPagePath: environmentsData.helpPagePath,
-
- // Pagination Properties,
- paginationInformation: {},
- pageNumber: 1,
- };
- },
-
- computed: {
- scope() {
- return gl.utils.getParameterByName('scope');
- },
-
- canReadEnvironmentParsed() {
- return gl.utils.convertPermissionToBoolean(this.canReadEnvironment);
- },
-
- canCreateDeploymentParsed() {
- return gl.utils.convertPermissionToBoolean(this.canCreateDeployment);
- },
-
- canCreateEnvironmentParsed() {
- return gl.utils.convertPermissionToBoolean(this.canCreateEnvironment);
- },
- },
-
- /**
- * Fetches all the environments and stores them.
- * Toggles loading property.
- */
- created() {
- this.service = new EnvironmentsService(this.endpoint);
-
- this.fetchEnvironments();
-
- eventHub.$on('refreshEnvironments', this.fetchEnvironments);
- },
-
- beforeDestroyed() {
- eventHub.$off('refreshEnvironments');
- },
-
- methods: {
- toggleRow(model) {
- return this.store.toggleFolder(model.name);
- },
-
- /**
- * Will change the page number and update the URL.
- *
- * @param {Number} pageNumber desired page to go to.
- * @return {String}
- */
- changePage(pageNumber) {
- const param = gl.utils.setParamInURL('page', pageNumber);
-
- gl.utils.visitUrl(param);
- return param;
- },
-
- fetchEnvironments() {
- const scope = gl.utils.getParameterByName('scope') || this.visibility;
- const pageNumber = gl.utils.getParameterByName('page') || this.pageNumber;
-
- this.isLoading = true;
-
- return this.service.get(scope, pageNumber)
- .then(resp => ({
- headers: resp.headers,
- body: resp.json(),
- }))
- .then((response) => {
- this.store.storeAvailableCount(response.body.available_count);
- this.store.storeStoppedCount(response.body.stopped_count);
- this.store.storeEnvironments(response.body.environments);
- this.store.setPagination(response.headers);
- })
- .then(() => {
- this.isLoading = false;
- })
- .catch(() => {
- this.isLoading = false;
- new Flash('An error occurred while fetching the environments.');
- });
- },
- },
-
- template: `
- <div :class="cssContainerClass">
- <div class="top-area">
- <ul v-if="!isLoading" class="nav-links">
- <li v-bind:class="{ 'active': scope === null || scope === 'available' }">
- <a :href="projectEnvironmentsPath">
- Available
- <span class="badge js-available-environments-count">
- {{state.availableCounter}}
- </span>
- </a>
- </li>
- <li v-bind:class="{ 'active' : scope === 'stopped' }">
- <a :href="projectStoppedEnvironmentsPath">
- Stopped
- <span class="badge js-stopped-environments-count">
- {{state.stoppedCounter}}
- </span>
- </a>
- </li>
- </ul>
- <div v-if="canCreateEnvironmentParsed && !isLoading" class="nav-controls">
- <a :href="newEnvironmentPath" class="btn btn-create">
- New environment
- </a>
- </div>
- </div>
-
- <div class="content-list environments-container">
- <div class="environments-list-loading text-center" v-if="isLoading">
- <i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
- </div>
-
- <div class="blank-state blank-state-no-icon"
- v-if="!isLoading && state.environments.length === 0">
- <h2 class="blank-state-title js-blank-state-title">
- You don't have any environments right now.
- </h2>
- <p class="blank-state-text">
- Environments are places where code gets deployed, such as staging or production.
- <br />
- <a :href="helpPagePath">
- Read more about environments
- </a>
- </p>
-
- <a v-if="canCreateEnvironmentParsed"
- :href="newEnvironmentPath"
- class="btn btn-create js-new-environment-button">
- New Environment
- </a>
- </div>
-
- <div class="table-holder"
- v-if="!isLoading && state.environments.length > 0">
-
- <environment-table
- :environments="state.environments"
- :can-create-deployment="canCreateDeploymentParsed"
- :can-read-environment="canReadEnvironmentParsed"
- :service="service"/>
- </div>
-
- <table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
- :change="changePage"
- :pageInfo="state.paginationInformation">
- </table-pagination>
- </div>
- </div>
- `,
-});
diff --git a/app/assets/javascripts/environments/components/environment.vue b/app/assets/javascripts/environments/components/environment.vue
new file mode 100644
index 00000000000..d4e13f3c84a
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environment.vue
@@ -0,0 +1,236 @@
+<script>
+/* global Flash */
+import EnvironmentsService from '../services/environments_service';
+import environmentTable from './environments_table.vue';
+import EnvironmentsStore from '../stores/environments_store';
+import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+import tablePagination from '../../vue_shared/components/table_pagination.vue';
+import '../../lib/utils/common_utils';
+import eventHub from '../event_hub';
+
+export default {
+
+ components: {
+ environmentTable,
+ tablePagination,
+ loadingIcon,
+ },
+
+ data() {
+ const environmentsData = document.querySelector('#environments-list-view').dataset;
+ const store = new EnvironmentsStore();
+
+ return {
+ store,
+ state: store.state,
+ visibility: 'available',
+ isLoading: false,
+ isLoadingFolderContent: false,
+ cssContainerClass: environmentsData.cssClass,
+ endpoint: environmentsData.environmentsDataEndpoint,
+ canCreateDeployment: environmentsData.canCreateDeployment,
+ canReadEnvironment: environmentsData.canReadEnvironment,
+ canCreateEnvironment: environmentsData.canCreateEnvironment,
+ projectEnvironmentsPath: environmentsData.projectEnvironmentsPath,
+ projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath,
+ newEnvironmentPath: environmentsData.newEnvironmentPath,
+ helpPagePath: environmentsData.helpPagePath,
+
+ // Pagination Properties,
+ paginationInformation: {},
+ pageNumber: 1,
+ };
+ },
+
+ computed: {
+ scope() {
+ return gl.utils.getParameterByName('scope');
+ },
+
+ canReadEnvironmentParsed() {
+ return gl.utils.convertPermissionToBoolean(this.canReadEnvironment);
+ },
+
+ canCreateDeploymentParsed() {
+ return gl.utils.convertPermissionToBoolean(this.canCreateDeployment);
+ },
+
+ canCreateEnvironmentParsed() {
+ return gl.utils.convertPermissionToBoolean(this.canCreateEnvironment);
+ },
+ },
+
+ /**
+ * Fetches all the environments and stores them.
+ * Toggles loading property.
+ */
+ created() {
+ this.service = new EnvironmentsService(this.endpoint);
+
+ this.fetchEnvironments();
+
+ eventHub.$on('refreshEnvironments', this.fetchEnvironments);
+ eventHub.$on('toggleFolder', this.toggleFolder);
+ eventHub.$on('postAction', this.postAction);
+ },
+
+ beforeDestroyed() {
+ eventHub.$off('refreshEnvironments');
+ eventHub.$off('toggleFolder');
+ eventHub.$off('postAction');
+ },
+
+ methods: {
+ toggleFolder(folder, folderUrl) {
+ this.store.toggleFolder(folder);
+
+ if (!folder.isOpen) {
+ this.fetchChildEnvironments(folder, folderUrl);
+ }
+ },
+
+ /**
+ * Will change the page number and update the URL.
+ *
+ * @param {Number} pageNumber desired page to go to.
+ * @return {String}
+ */
+ changePage(pageNumber) {
+ const param = gl.utils.setParamInURL('page', pageNumber);
+
+ gl.utils.visitUrl(param);
+ return param;
+ },
+
+ fetchEnvironments() {
+ const scope = gl.utils.getParameterByName('scope') || this.visibility;
+ const pageNumber = gl.utils.getParameterByName('page') || this.pageNumber;
+
+ this.isLoading = true;
+
+ return this.service.get(scope, pageNumber)
+ .then(resp => ({
+ headers: resp.headers,
+ body: resp.json(),
+ }))
+ .then((response) => {
+ this.store.storeAvailableCount(response.body.available_count);
+ this.store.storeStoppedCount(response.body.stopped_count);
+ this.store.storeEnvironments(response.body.environments);
+ this.store.setPagination(response.headers);
+ })
+ .then(() => {
+ this.isLoading = false;
+ })
+ .catch(() => {
+ this.isLoading = false;
+ // eslint-disable-next-line no-new
+ new Flash('An error occurred while fetching the environments.');
+ });
+ },
+
+ fetchChildEnvironments(folder, folderUrl) {
+ this.isLoadingFolderContent = true;
+
+ this.service.getFolderContent(folderUrl)
+ .then(resp => resp.json())
+ .then((response) => {
+ this.store.setfolderContent(folder, response.environments);
+ this.isLoadingFolderContent = false;
+ })
+ .catch(() => {
+ this.isLoadingFolderContent = false;
+ // eslint-disable-next-line no-new
+ new Flash('An error occurred while fetching the environments.');
+ });
+ },
+
+ postAction(endpoint) {
+ this.service.postAction(endpoint)
+ .then(() => this.fetchEnvironments())
+ .catch(() => new Flash('An error occured while making the request.'));
+ },
+ },
+};
+</script>
+<template>
+ <div :class="cssContainerClass">
+ <div class="top-area">
+ <ul
+ v-if="!isLoading"
+ class="nav-links">
+ <li :class="{ active: scope === null || scope === 'available' }">
+ <a :href="projectEnvironmentsPath">
+ Available
+ <span class="badge js-available-environments-count">
+ {{state.availableCounter}}
+ </span>
+ </a>
+ </li>
+ <li :class="{ active : scope === 'stopped' }">
+ <a :href="projectStoppedEnvironmentsPath">
+ Stopped
+ <span class="badge js-stopped-environments-count">
+ {{state.stoppedCounter}}
+ </span>
+ </a>
+ </li>
+ </ul>
+ <div
+ v-if="canCreateEnvironmentParsed && !isLoading"
+ class="nav-controls">
+ <a
+ :href="newEnvironmentPath"
+ class="btn btn-create">
+ New environment
+ </a>
+ </div>
+ </div>
+
+ <div class="content-list environments-container">
+ <loading-icon
+ label="Loading environments"
+ size="3"
+ v-if="isLoading"
+ />
+
+ <div
+ class="blank-state blank-state-no-icon"
+ v-if="!isLoading && state.environments.length === 0">
+ <h2 class="blank-state-title js-blank-state-title">
+ You don't have any environments right now.
+ </h2>
+ <p class="blank-state-text">
+ Environments are places where code gets deployed, such as staging or production.
+ <br />
+ <a :href="helpPagePath">
+ Read more about environments
+ </a>
+ </p>
+
+ <a
+ v-if="canCreateEnvironmentParsed"
+ :href="newEnvironmentPath"
+ class="btn btn-create js-new-environment-button">
+ New Environment
+ </a>
+ </div>
+
+ <div
+ class="table-holder"
+ v-if="!isLoading && state.environments.length > 0">
+
+ <environment-table
+ :environments="state.environments"
+ :can-create-deployment="canCreateDeploymentParsed"
+ :can-read-environment="canReadEnvironmentParsed"
+ :is-loading-folder-content="isLoadingFolderContent" />
+ </div>
+
+ <table-pagination
+ v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
+ :change="changePage"
+ :pageInfo="state.paginationInformation" />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/environments/components/environment_actions.js b/app/assets/javascripts/environments/components/environment_actions.js
deleted file mode 100644
index 385085c03e2..00000000000
--- a/app/assets/javascripts/environments/components/environment_actions.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/* global Flash */
-/* eslint-disable no-new */
-
-import playIconSvg from 'icons/_icon_play.svg';
-import eventHub from '../event_hub';
-
-export default {
- props: {
- actions: {
- type: Array,
- required: false,
- default: () => [],
- },
-
- service: {
- type: Object,
- required: true,
- },
- },
-
- data() {
- return {
- playIconSvg,
- isLoading: false,
- };
- },
-
- computed: {
- title() {
- return 'Deploy to...';
- },
- },
-
- methods: {
- onClickAction(endpoint) {
- this.isLoading = true;
-
- this.service.postAction(endpoint)
- .then(() => {
- this.isLoading = false;
- eventHub.$emit('refreshEnvironments');
- })
- .catch(() => {
- this.isLoading = false;
- new Flash('An error occured while making the request.');
- });
- },
- },
-
- template: `
- <div class="btn-group" role="group">
- <button
- class="dropdown btn btn-default dropdown-new js-dropdown-play-icon-container has-tooltip"
- data-container="body"
- data-toggle="dropdown"
- :title="title"
- :aria-label="title"
- :disabled="isLoading">
- <span>
- <span v-html="playIconSvg"></span>
- <i class="fa fa-caret-down" aria-hidden="true"></i>
- <i v-if="isLoading" class="fa fa-spinner fa-spin" aria-hidden="true"></i>
- </span>
-
- <ul class="dropdown-menu dropdown-menu-align-right">
- <li v-for="action in actions">
- <button
- @click="onClickAction(action.play_path)"
- class="js-manual-action-link no-btn">
- ${playIconSvg}
- <span>
- {{action.name}}
- </span>
- </button>
- </li>
- </ul>
- </button>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue
new file mode 100644
index 00000000000..a2448520a5f
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environment_actions.vue
@@ -0,0 +1,89 @@
+<script>
+import playIconSvg from 'icons/_icon_play.svg';
+import eventHub from '../event_hub';
+import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+
+export default {
+ props: {
+ actions: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+
+ components: {
+ loadingIcon,
+ },
+
+ data() {
+ return {
+ playIconSvg,
+ isLoading: false,
+ };
+ },
+
+ computed: {
+ title() {
+ return 'Deploy to...';
+ },
+ },
+
+ methods: {
+ onClickAction(endpoint) {
+ this.isLoading = true;
+
+ $(this.$refs.tooltip).tooltip('destroy');
+
+ eventHub.$emit('postAction', endpoint);
+ },
+
+ isActionDisabled(action) {
+ if (action.playable === undefined) {
+ return false;
+ }
+
+ return !action.playable;
+ },
+ },
+};
+</script>
+<template>
+ <div
+ class="btn-group"
+ role="group">
+ <button
+ type="button"
+ class="dropdown btn btn-default dropdown-new js-dropdown-play-icon-container has-tooltip"
+ data-container="body"
+ data-toggle="dropdown"
+ ref="tooltip"
+ :title="title"
+ :aria-label="title"
+ :disabled="isLoading">
+ <span>
+ <span v-html="playIconSvg"></span>
+ <i
+ class="fa fa-caret-down"
+ aria-hidden="true"/>
+ <loading-icon v-if="isLoading" />
+ </span>
+ </button>
+
+ <ul class="dropdown-menu dropdown-menu-align-right">
+ <li v-for="action in actions">
+ <button
+ type="button"
+ class="js-manual-action-link no-btn btn"
+ @click="onClickAction(action.play_path)"
+ :class="{ disabled: isActionDisabled(action) }"
+ :disabled="isActionDisabled(action)">
+ <span v-html="playIconSvg"></span>
+ <span>
+ {{action.name}}
+ </span>
+ </button>
+ </li>
+ </ul>
+ </div>
+</template>
diff --git a/app/assets/javascripts/environments/components/environment_external_url.js b/app/assets/javascripts/environments/components/environment_external_url.js
deleted file mode 100644
index d79b916c360..00000000000
--- a/app/assets/javascripts/environments/components/environment_external_url.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * Renders the external url link in environments table.
- */
-export default {
- props: {
- externalUrl: {
- type: String,
- default: '',
- },
- },
-
- computed: {
- title() {
- return 'Open';
- },
- },
-
- template: `
- <a
- class="btn external-url has-tooltip"
- data-container="body"
- :href="externalUrl"
- target="_blank"
- rel="noopener noreferrer nofollow"
- :title="title"
- :aria-label="title">
- <i class="fa fa-external-link" aria-hidden="true"></i>
- </a>
- `,
-};
diff --git a/app/assets/javascripts/environments/components/environment_external_url.vue b/app/assets/javascripts/environments/components/environment_external_url.vue
new file mode 100644
index 00000000000..eaeec2bc53c
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environment_external_url.vue
@@ -0,0 +1,33 @@
+<script>
+/**
+ * Renders the external url link in environments table.
+ */
+export default {
+ props: {
+ externalUrl: {
+ type: String,
+ required: true,
+ },
+ },
+
+ computed: {
+ title() {
+ return 'Open';
+ },
+ },
+};
+</script>
+<template>
+ <a
+ class="btn external-url has-tooltip"
+ data-container="body"
+ target="_blank"
+ rel="noopener noreferrer nofollow"
+ :title="title"
+ :aria-label="title"
+ :href="externalUrl">
+ <i
+ class="fa fa-external-link"
+ aria-hidden="true" />
+ </a>
+</template>
diff --git a/app/assets/javascripts/environments/components/environment_item.js b/app/assets/javascripts/environments/components/environment_item.vue
index 9c196562c6c..012ff1f975b 100644
--- a/app/assets/javascripts/environments/components/environment_item.js
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -1,12 +1,16 @@
+<script>
import Timeago from 'timeago.js';
+import _ from 'underscore';
+import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import '../../lib/utils/text_utility';
-import ActionsComponent from './environment_actions';
-import ExternalUrlComponent from './environment_external_url';
-import StopComponent from './environment_stop';
-import RollbackComponent from './environment_rollback';
-import TerminalButtonComponent from './environment_terminal_button';
-import MonitoringButtonComponent from './environment_monitoring';
+import ActionsComponent from './environment_actions.vue';
+import ExternalUrlComponent from './environment_external_url.vue';
+import StopComponent from './environment_stop.vue';
+import RollbackComponent from './environment_rollback.vue';
+import TerminalButtonComponent from './environment_terminal_button.vue';
+import MonitoringButtonComponent from './environment_monitoring.vue';
import CommitComponent from '../../vue_shared/components/commit';
+import eventHub from '../event_hub';
/**
* Envrionment Item Component
@@ -17,6 +21,7 @@ const timeagoInstance = new Timeago();
export default {
components: {
+ userAvatarLink,
'commit-component': CommitComponent,
'actions-component': ActionsComponent,
'external-url-component': ExternalUrlComponent,
@@ -44,11 +49,6 @@ export default {
required: false,
default: false,
},
-
- service: {
- type: Object,
- required: true,
- },
},
computed: {
@@ -62,7 +62,7 @@ export default {
hasLastDeploymentKey() {
if (this.model &&
this.model.last_deployment &&
- !this.$options.isObjectEmpty(this.model.last_deployment)) {
+ !_.isEmpty(this.model.last_deployment)) {
return true;
}
return false;
@@ -141,6 +141,7 @@ export default {
const parsedAction = {
name: gl.text.humanize(action.name),
play_path: action.play_path,
+ playable: action.playable,
};
return parsedAction;
});
@@ -312,8 +313,8 @@ export default {
*/
deploymentHasUser() {
return this.model &&
- !this.$options.isObjectEmpty(this.model.last_deployment) &&
- !this.$options.isObjectEmpty(this.model.last_deployment.user);
+ !_.isEmpty(this.model.last_deployment) &&
+ !_.isEmpty(this.model.last_deployment.user);
},
/**
@@ -324,8 +325,8 @@ export default {
*/
deploymentUser() {
if (this.model &&
- !this.$options.isObjectEmpty(this.model.last_deployment) &&
- !this.$options.isObjectEmpty(this.model.last_deployment.user)) {
+ !_.isEmpty(this.model.last_deployment) &&
+ !_.isEmpty(this.model.last_deployment.user)) {
return this.model.last_deployment.user;
}
return {};
@@ -340,8 +341,8 @@ export default {
*/
shouldRenderBuildName() {
return !this.model.isFolder &&
- !this.$options.isObjectEmpty(this.model.last_deployment) &&
- !this.$options.isObjectEmpty(this.model.last_deployment.deployable);
+ !_.isEmpty(this.model.last_deployment) &&
+ !_.isEmpty(this.model.last_deployment.deployable);
},
/**
@@ -382,7 +383,7 @@ export default {
*/
shouldRenderDeploymentID() {
return !this.model.isFolder &&
- !this.$options.isObjectEmpty(this.model.last_deployment) &&
+ !_.isEmpty(this.model.last_deployment) &&
this.model.last_deployment.iid !== undefined;
},
@@ -410,118 +411,148 @@ export default {
folderUrl() {
return `${window.location.pathname}/folders/${this.model.folderName}`;
},
-
},
- /**
- * Helper to verify if certain given object are empty.
- * Should be replaced by lodash _.isEmpty - https://lodash.com/docs/4.17.2#isEmpty
- * @param {Object} object
- * @returns {Bollean}
- */
- isObjectEmpty(object) {
- for (const key in object) { // eslint-disable-line
- if (hasOwnProperty.call(object, key)) {
- return false;
- }
- }
- return true;
+ methods: {
+ onClickFolder() {
+ eventHub.$emit('toggleFolder', this.model, this.folderUrl);
+ },
},
+};
+</script>
+<template>
+ <tr :class="{ 'js-child-row': model.isChildren }">
+ <td>
+ <a
+ v-if="!model.isFolder"
+ class="environment-name"
+ :class="{ 'prepend-left-default': model.isChildren }"
+ :href="environmentPath">
+ {{model.name}}
+ </a>
+ <span
+ v-else
+ class="folder-name"
+ @click="onClickFolder"
+ role="button">
+
+ <span class="folder-icon">
+ <i
+ v-show="model.isOpen"
+ class="fa fa-caret-down"
+ aria-hidden="true" />
+ <i
+ v-show="!model.isOpen"
+ class="fa fa-caret-right"
+ aria-hidden="true"/>
+ </span>
- template: `
- <tr>
- <td>
- <a v-if="!model.isFolder"
- class="environment-name"
- :href="environmentPath">
- {{model.name}}
- </a>
- <a v-else class="folder-name" :href="folderUrl">
- <span class="folder-icon">
- <i class="fa fa-folder" aria-hidden="true"></i>
- </span>
-
- <span>
- {{model.folderName}}
- </span>
-
- <span class="badge">
- {{model.size}}
- </span>
- </a>
- </td>
-
- <td class="deployment-column">
- <span v-if="shouldRenderDeploymentID">
- {{deploymentInternalId}}
+ <span class="folder-icon">
+ <i
+ class="fa fa-folder"
+ aria-hidden="true" />
</span>
- <span v-if="!model.isFolder && deploymentHasUser">
- by
- <a :href="deploymentUser.web_url" class="js-deploy-user-container">
- <img class="avatar has-tooltip s20"
- :src="deploymentUser.avatar_url"
- :alt="userImageAltDescription"
- :title="deploymentUser.username" />
- </a>
+ <span>
+ {{model.folderName}}
</span>
- </td>
-
- <td class="environments-build-cell">
- <a v-if="shouldRenderBuildName"
- class="build-link"
- :href="buildPath">
- {{buildName}}
- </a>
- </td>
-
- <td>
- <div v-if="!model.isFolder && hasLastDeploymentKey" class="js-commit-component">
- <commit-component
- :tag="commitTag"
- :commit-ref="commitRef"
- :commit-url="commitUrl"
- :short-sha="commitShortSha"
- :title="commitTitle"
- :author="commitAuthor"/>
- </div>
- <p v-if="!model.isFolder && !hasLastDeploymentKey" class="commit-title">
- No deployments yet
- </p>
- </td>
-
- <td>
- <span v-if="!model.isFolder && canShowDate"
- class="environment-created-date-timeago">
- {{createdDate}}
+
+ <span class="badge">
+ {{model.size}}
</span>
- </td>
-
- <td class="environments-actions">
- <div v-if="!model.isFolder" class="btn-group pull-right" role="group">
- <actions-component v-if="hasManualActions && canCreateDeployment"
- :service="service"
- :actions="manualActions"/>
-
- <external-url-component v-if="externalURL && canReadEnvironment"
- :external-url="externalURL"/>
-
- <monitoring-button-component v-if="monitoringUrl && canReadEnvironment"
- :monitoring-url="monitoringUrl"/>
-
- <terminal-button-component v-if="model && model.terminal_path"
- :terminal-path="model.terminal_path"/>
-
- <stop-component v-if="hasStopAction && canCreateDeployment"
- :stop-url="model.stop_path"
- :service="service"/>
-
- <rollback-component v-if="canRetry && canCreateDeployment"
- :is-last-deployment="isLastDeployment"
- :retry-url="retryUrl"
- :service="service"/>
- </div>
- </td>
- </tr>
- `,
-};
+ </span>
+ </td>
+
+ <td class="deployment-column">
+ <span v-if="shouldRenderDeploymentID">
+ {{deploymentInternalId}}
+ </span>
+
+ <span v-if="!model.isFolder && deploymentHasUser">
+ by
+ <user-avatar-link
+ class="js-deploy-user-container"
+ :link-href="deploymentUser.web_url"
+ :img-src="deploymentUser.avatar_url"
+ :img-alt="userImageAltDescription"
+ :tooltip-text="deploymentUser.username"
+ />
+ </span>
+ </td>
+
+ <td class="environments-build-cell">
+ <a
+ v-if="shouldRenderBuildName"
+ class="build-link"
+ :href="buildPath">
+ {{buildName}}
+ </a>
+ </td>
+
+ <td>
+ <div
+ v-if="!model.isFolder && hasLastDeploymentKey"
+ class="js-commit-component">
+ <commit-component
+ :tag="commitTag"
+ :commit-ref="commitRef"
+ :commit-url="commitUrl"
+ :short-sha="commitShortSha"
+ :title="commitTitle"
+ :author="commitAuthor"/>
+ </div>
+ <p
+ v-if="!model.isFolder && !hasLastDeploymentKey"
+ class="commit-title">
+ No deployments yet
+ </p>
+ </td>
+
+ <td>
+ <span
+ v-if="!model.isFolder && canShowDate"
+ class="environment-created-date-timeago">
+ {{createdDate}}
+ </span>
+ </td>
+
+ <td class="environments-actions">
+ <div
+ v-if="!model.isFolder"
+ class="btn-group pull-right"
+ role="group">
+
+ <actions-component
+ v-if="hasManualActions && canCreateDeployment"
+ :actions="manualActions"
+ />
+
+ <external-url-component
+ v-if="externalURL && canReadEnvironment"
+ :external-url="externalURL"
+ />
+
+ <monitoring-button-component
+ v-if="monitoringUrl && canReadEnvironment"
+ :monitoring-url="monitoringUrl"
+ />
+
+ <terminal-button-component
+ v-if="model && model.terminal_path"
+ :terminal-path="model.terminal_path"
+ />
+
+ <stop-component
+ v-if="hasStopAction && canCreateDeployment"
+ :stop-url="model.stop_path"
+ />
+
+ <rollback-component
+ v-if="canRetry && canCreateDeployment"
+ :is-last-deployment="isLastDeployment"
+ :retry-url="retryUrl"
+ />
+ </div>
+ </td>
+ </tr>
+</template>
diff --git a/app/assets/javascripts/environments/components/environment_monitoring.js b/app/assets/javascripts/environments/components/environment_monitoring.js
deleted file mode 100644
index 064e2fc7434..00000000000
--- a/app/assets/javascripts/environments/components/environment_monitoring.js
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Renders the Monitoring (Metrics) link in environments table.
- */
-export default {
- props: {
- monitoringUrl: {
- type: String,
- default: '',
- required: true,
- },
- },
-
- computed: {
- title() {
- return 'Monitoring';
- },
- },
-
- template: `
- <a
- class="btn monitoring-url has-tooltip"
- data-container="body"
- :href="monitoringUrl"
- target="_blank"
- rel="noopener noreferrer nofollow"
- :title="title"
- :aria-label="title">
- <i class="fa fa-area-chart" aria-hidden="true"></i>
- </a>
- `,
-};
diff --git a/app/assets/javascripts/environments/components/environment_monitoring.vue b/app/assets/javascripts/environments/components/environment_monitoring.vue
new file mode 100644
index 00000000000..79c019b3491
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environment_monitoring.vue
@@ -0,0 +1,32 @@
+<script>
+/**
+ * Renders the Monitoring (Metrics) link in environments table.
+ */
+export default {
+ props: {
+ monitoringUrl: {
+ type: String,
+ required: true,
+ },
+ },
+
+ computed: {
+ title() {
+ return 'Monitoring';
+ },
+ },
+};
+</script>
+<template>
+ <a
+ class="btn monitoring-url has-tooltip"
+ data-container="body"
+ rel="noopener noreferrer nofollow"
+ :href="monitoringUrl"
+ :title="title"
+ :aria-label="title">
+ <i
+ class="fa fa-area-chart"
+ aria-hidden="true" />
+ </a>
+</template>
diff --git a/app/assets/javascripts/environments/components/environment_rollback.js b/app/assets/javascripts/environments/components/environment_rollback.js
deleted file mode 100644
index baa15d9e5b5..00000000000
--- a/app/assets/javascripts/environments/components/environment_rollback.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/* global Flash */
-/* eslint-disable no-new */
-/**
- * Renders Rollback or Re deploy button in environments table depending
- * of the provided property `isLastDeployment`.
- *
- * Makes a post request when the button is clicked.
- */
-import eventHub from '../event_hub';
-
-export default {
- props: {
- retryUrl: {
- type: String,
- default: '',
- },
-
- isLastDeployment: {
- type: Boolean,
- default: true,
- },
-
- service: {
- type: Object,
- required: true,
- },
- },
-
- data() {
- return {
- isLoading: false,
- };
- },
-
- methods: {
- onClick() {
- this.isLoading = true;
-
- this.service.postAction(this.retryUrl)
- .then(() => {
- this.isLoading = false;
- eventHub.$emit('refreshEnvironments');
- })
- .catch(() => {
- this.isLoading = false;
- new Flash('An error occured while making the request.');
- });
- },
- },
-
- template: `
- <button type="button"
- class="btn"
- @click="onClick"
- :disabled="isLoading">
-
- <span v-if="isLastDeployment">
- Re-deploy
- </span>
- <span v-else>
- Rollback
- </span>
-
- <i v-if="isLoading" class="fa fa-spinner fa-spin" aria-hidden="true"></i>
- </button>
- `,
-};
diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue
new file mode 100644
index 00000000000..2ba985bfe3e
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environment_rollback.vue
@@ -0,0 +1,59 @@
+<script>
+/**
+ * Renders Rollback or Re deploy button in environments table depending
+ * of the provided property `isLastDeployment`.
+ *
+ * Makes a post request when the button is clicked.
+ */
+import eventHub from '../event_hub';
+import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+
+export default {
+ props: {
+ retryUrl: {
+ type: String,
+ default: '',
+ },
+
+ isLastDeployment: {
+ type: Boolean,
+ default: true,
+ },
+ },
+
+ components: {
+ loadingIcon,
+ },
+
+ data() {
+ return {
+ isLoading: false,
+ };
+ },
+
+ methods: {
+ onClick() {
+ this.isLoading = true;
+
+ eventHub.$emit('postAction', this.retryUrl);
+ },
+ },
+};
+</script>
+<template>
+ <button
+ type="button"
+ class="btn"
+ @click="onClick"
+ :disabled="isLoading">
+
+ <span v-if="isLastDeployment">
+ Re-deploy
+ </span>
+ <span v-else>
+ Rollback
+ </span>
+
+ <loading-icon v-if="isLoading" />
+ </button>
+</template>
diff --git a/app/assets/javascripts/environments/components/environment_stop.js b/app/assets/javascripts/environments/components/environment_stop.js
deleted file mode 100644
index 47102692024..00000000000
--- a/app/assets/javascripts/environments/components/environment_stop.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/* global Flash */
-/* eslint-disable no-new, no-alert */
-/**
- * Renders the stop "button" that allows stop an environment.
- * Used in environments table.
- */
-import eventHub from '../event_hub';
-
-export default {
- props: {
- stopUrl: {
- type: String,
- default: '',
- },
-
- service: {
- type: Object,
- required: true,
- },
- },
-
- data() {
- return {
- isLoading: false,
- };
- },
-
- computed: {
- title() {
- return 'Stop';
- },
- },
-
- methods: {
- onClick() {
- if (confirm('Are you sure you want to stop this environment?')) {
- this.isLoading = true;
-
- this.service.postAction(this.retryUrl)
- .then(() => {
- this.isLoading = false;
- eventHub.$emit('refreshEnvironments');
- })
- .catch(() => {
- this.isLoading = false;
- new Flash('An error occured while making the request.', 'alert');
- });
- }
- },
- },
-
- template: `
- <button type="button"
- class="btn stop-env-link has-tooltip"
- data-container="body"
- @click="onClick"
- :disabled="isLoading"
- :title="title"
- :aria-label="title">
- <i class="fa fa-stop stop-env-icon" aria-hidden="true"></i>
- <i v-if="isLoading" class="fa fa-spinner fa-spin" aria-hidden="true"></i>
- </button>
- `,
-};
diff --git a/app/assets/javascripts/environments/components/environment_stop.vue b/app/assets/javascripts/environments/components/environment_stop.vue
new file mode 100644
index 00000000000..a904453ffa9
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environment_stop.vue
@@ -0,0 +1,61 @@
+<script>
+/**
+ * Renders the stop "button" that allows stop an environment.
+ * Used in environments table.
+ */
+import eventHub from '../event_hub';
+import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+
+export default {
+ props: {
+ stopUrl: {
+ type: String,
+ default: '',
+ },
+ },
+
+ data() {
+ return {
+ isLoading: false,
+ };
+ },
+
+ components: {
+ loadingIcon,
+ },
+
+ computed: {
+ title() {
+ return 'Stop';
+ },
+ },
+
+ methods: {
+ onClick() {
+ // eslint-disable-next-line no-alert
+ if (confirm('Are you sure you want to stop this environment?')) {
+ this.isLoading = true;
+
+ $(this.$el).tooltip('destroy');
+
+ eventHub.$emit('postAction', this.stopUrl);
+ }
+ },
+ },
+};
+</script>
+<template>
+ <button
+ type="button"
+ class="btn stop-env-link has-tooltip"
+ data-container="body"
+ @click="onClick"
+ :disabled="isLoading"
+ :title="title"
+ :aria-label="title">
+ <i
+ class="fa fa-stop stop-env-icon"
+ aria-hidden="true" />
+ <loading-icon v-if="isLoading" />
+ </button>
+</template>
diff --git a/app/assets/javascripts/environments/components/environment_terminal_button.js b/app/assets/javascripts/environments/components/environment_terminal_button.vue
index 092a50a0d6f..c8c1f17d4d8 100644
--- a/app/assets/javascripts/environments/components/environment_terminal_button.js
+++ b/app/assets/javascripts/environments/components/environment_terminal_button.vue
@@ -1,3 +1,4 @@
+<script>
/**
* Renders a terminal button to open a web terminal.
* Used in environments table.
@@ -24,14 +25,15 @@ export default {
return 'Terminal';
},
},
-
- template: `
- <a class="btn terminal-button has-tooltip"
- data-container="body"
- :title="title"
- :aria-label="title"
- :href="terminalPath">
- ${terminalIconSvg}
- </a>
- `,
};
+</script>
+<template>
+ <a
+ class="btn terminal-button has-tooltip"
+ data-container="body"
+ :title="title"
+ :aria-label="title"
+ :href="terminalPath"
+ v-html="terminalIconSvg">
+ </a>
+</template>
diff --git a/app/assets/javascripts/environments/components/environments_table.js b/app/assets/javascripts/environments/components/environments_table.js
deleted file mode 100644
index 338dff40bc9..00000000000
--- a/app/assets/javascripts/environments/components/environments_table.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Render environments table.
- */
-import EnvironmentTableRowComponent from './environment_item';
-
-export default {
- components: {
- 'environment-item': EnvironmentTableRowComponent,
- },
-
- props: {
- environments: {
- type: Array,
- required: true,
- default: () => ([]),
- },
-
- canReadEnvironment: {
- type: Boolean,
- required: false,
- default: false,
- },
-
- canCreateDeployment: {
- type: Boolean,
- required: false,
- default: false,
- },
-
- service: {
- type: Object,
- required: true,
- },
- },
-
- template: `
- <table class="table ci-table">
- <thead>
- <tr>
- <th class="environments-name">Environment</th>
- <th class="environments-deploy">Last deployment</th>
- <th class="environments-build">Job</th>
- <th class="environments-commit">Commit</th>
- <th class="environments-date">Updated</th>
- <th class="environments-actions"></th>
- </tr>
- </thead>
- <tbody>
- <template v-for="model in environments"
- v-bind:model="model">
- <tr is="environment-item"
- :model="model"
- :can-create-deployment="canCreateDeployment"
- :can-read-environment="canReadEnvironment"
- :service="service"></tr>
- </template>
- </tbody>
- </table>
- `,
-};
diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue
new file mode 100644
index 00000000000..5148a2ae79b
--- /dev/null
+++ b/app/assets/javascripts/environments/components/environments_table.vue
@@ -0,0 +1,112 @@
+<script>
+/**
+ * Render environments table.
+ */
+import EnvironmentTableRowComponent from './environment_item.vue';
+import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+
+export default {
+ components: {
+ 'environment-item': EnvironmentTableRowComponent,
+ loadingIcon,
+ },
+
+ props: {
+ environments: {
+ type: Array,
+ required: true,
+ default: () => ([]),
+ },
+
+ canReadEnvironment: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+
+ canCreateDeployment: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+
+ isLoadingFolderContent: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+
+ methods: {
+ folderUrl(model) {
+ return `${window.location.pathname}/folders/${model.folderName}`;
+ },
+ },
+};
+</script>
+<template>
+ <table class="table ci-table">
+ <thead>
+ <tr>
+ <th class="environments-name">
+ Environment
+ </th>
+ <th class="environments-deploy">
+ Last deployment
+ </th>
+ <th class="environments-build">
+ Job
+ </th>
+ <th class="environments-commit">
+ Commit
+ </th>
+ <th class="environments-date">
+ Updated
+ </th>
+ <th class="environments-actions"></th>
+ </tr>
+ </thead>
+ <tbody>
+ <template
+ v-for="model in environments"
+ v-bind:model="model">
+ <tr
+ is="environment-item"
+ :model="model"
+ :can-create-deployment="canCreateDeployment"
+ :can-read-environment="canReadEnvironment"
+ />
+
+ <template v-if="model.isFolder && model.isOpen && model.children && model.children.length > 0">
+ <tr v-if="isLoadingFolderContent">
+ <td colspan="6">
+ <loading-icon size="2" />
+ </td>
+ </tr>
+
+ <template v-else>
+ <tr
+ is="environment-item"
+ v-for="children in model.children"
+ :model="children"
+ :can-create-deployment="canCreateDeployment"
+ :can-read-environment="canReadEnvironment"
+ />
+
+ <tr>
+ <td
+ colspan="6"
+ class="text-center">
+ <a
+ :href="folderUrl(model)"
+ class="btn btn-default">
+ Show all
+ </a>
+ </td>
+ </tr>
+ </template>
+ </template>
+ </template>
+ </tbody>
+ </table>
+</template>