diff options
Diffstat (limited to 'app/assets/javascripts/environments/components')
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> |