diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-21 18:09:27 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-07-21 18:09:27 +0000 |
commit | bd7e8cd64b1eb9b2d5aa2f83e547d2a5b519b26c (patch) | |
tree | 292f1690eec2707d091ede0188706ec92da16427 /app/assets | |
parent | a8f5aaa7081cc2d400fbac1106d9e94d02d70ab4 (diff) | |
download | gitlab-ce-bd7e8cd64b1eb9b2d5aa2f83e547d2a5b519b26c.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
-rw-r--r-- | app/assets/javascripts/environments/components/environments_detail_header.vue | 174 | ||||
-rw-r--r-- | app/assets/javascripts/environments/mixins/environments_mixin.js | 14 | ||||
-rw-r--r-- | app/assets/javascripts/environments/mount_show.js | 38 | ||||
-rw-r--r-- | app/assets/javascripts/pages/admin/runners/index/index.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/pages/projects/environments/show/index.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/registry/explorer/components/list_page/cleanup_status.vue | 6 | ||||
-rw-r--r-- | app/assets/javascripts/runner/admin_runners/admin_runners_app.vue (renamed from app/assets/javascripts/runner/runner_list/runner_list_app.vue) | 6 | ||||
-rw-r--r-- | app/assets/javascripts/runner/admin_runners/index.js (renamed from app/assets/javascripts/runner/runner_list/index.js) | 6 | ||||
-rw-r--r-- | app/assets/javascripts/runner/runner_search_utils.js (renamed from app/assets/javascripts/runner/runner_list/runner_search_utils.js) | 2 | ||||
-rw-r--r-- | app/assets/stylesheets/framework/files.scss | 18 |
10 files changed, 247 insertions, 25 deletions
diff --git a/app/assets/javascripts/environments/components/environments_detail_header.vue b/app/assets/javascripts/environments/components/environments_detail_header.vue new file mode 100644 index 00000000000..467c89fd8b8 --- /dev/null +++ b/app/assets/javascripts/environments/components/environments_detail_header.vue @@ -0,0 +1,174 @@ +<script> +import { GlButton, GlModalDirective, GlTooltipDirective as GlTooltip, GlSprintf } from '@gitlab/ui'; +import csrf from '~/lib/utils/csrf'; +import { __, s__ } from '~/locale'; +import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; +import timeagoMixin from '~/vue_shared/mixins/timeago'; +import DeleteEnvironmentModal from './delete_environment_modal.vue'; +import StopEnvironmentModal from './stop_environment_modal.vue'; + +export default { + name: 'EnvironmentsDetailHeader', + csrf, + components: { + GlButton, + GlSprintf, + TimeAgo, + DeleteEnvironmentModal, + StopEnvironmentModal, + }, + directives: { + GlModalDirective, + GlTooltip, + }, + mixins: [timeagoMixin], + props: { + environment: { + type: Object, + required: true, + }, + canReadEnvironment: { + type: Boolean, + required: true, + }, + canAdminEnvironment: { + type: Boolean, + required: true, + }, + canUpdateEnvironment: { + type: Boolean, + required: true, + }, + canDestroyEnvironment: { + type: Boolean, + required: true, + }, + canStopEnvironment: { + type: Boolean, + required: true, + }, + cancelAutoStopPath: { + type: String, + required: false, + default: '', + }, + metricsPath: { + type: String, + required: false, + default: '', + }, + updatePath: { + type: String, + required: false, + default: '', + }, + terminalPath: { + type: String, + required: false, + default: '', + }, + }, + i18n: { + autoStopAtText: s__('Environments|Auto stops %{autoStopAt}'), + metricsButtonTitle: __('See metrics'), + metricsButtonText: __('Monitoring'), + editButtonText: __('Edit'), + stopButtonText: s__('Environments|Stop'), + deleteButtonText: s__('Environments|Delete'), + externalButtonTitle: s__('Environments|Open live environment'), + externalButtonText: __('View deployment'), + cancelAutoStopButtonTitle: __('Prevent environment from auto-stopping'), + }, + computed: { + shouldShowCancelAutoStopButton() { + return this.environment.isAvailable && Boolean(this.environment.autoStopAt); + }, + shouldShowExternalUrlButton() { + return this.canReadEnvironment && Boolean(this.environment.externalUrl); + }, + shouldShowStopButton() { + return this.canStopEnvironment && this.environment.isAvailable; + }, + shouldShowTerminalButton() { + return this.canAdminEnvironment && this.environment.hasTerminals; + }, + }, +}; +</script> +<template> + <header class="top-area gl-justify-content-between"> + <div class="gl-display-flex gl-flex-grow-1 gl-align-items-center"> + <h3 class="page-title"> + {{ environment.name }} + </h3> + <p v-if="shouldShowCancelAutoStopButton" class="gl-mb-0 gl-ml-3" data-testid="auto-stops-at"> + <gl-sprintf :message="$options.i18n.autoStopAtText"> + <template #autoStopAt> + <time-ago :time="environment.autoStopAt" /> + </template> + </gl-sprintf> + </p> + </div> + <div class="nav-controls gl-my-1"> + <form method="POST" :action="cancelAutoStopPath" data-testid="cancel-auto-stop-form"> + <input :value="$options.csrf.token" type="hidden" name="authenticity_token" /> + <gl-button + v-if="shouldShowCancelAutoStopButton" + v-gl-tooltip.hover + data-testid="cancel-auto-stop-button" + :title="$options.i18n.cancelAutoStopButtonTitle" + type="submit" + icon="thumbtack" + /> + </form> + <gl-button + v-if="shouldShowTerminalButton" + data-testid="terminal-button" + :href="terminalPath" + icon="terminal" + /> + <gl-button + v-if="shouldShowExternalUrlButton" + v-gl-tooltip.hover + data-testid="external-url-button" + :title="$options.i18n.externalButtonTitle" + :href="environment.externalUrl" + icon="external-link" + target="_blank" + >{{ $options.i18n.externalButtonText }}</gl-button + > + <gl-button + v-if="canReadEnvironment" + data-testid="metrics-button" + :href="metricsPath" + :title="$options.i18n.metricsButtonTitle" + icon="chart" + class="gl-mr-2" + > + {{ $options.i18n.metricsButtonText }} + </gl-button> + <gl-button v-if="canUpdateEnvironment" data-testid="edit-button" :href="updatePath"> + {{ $options.i18n.editButtonText }} + </gl-button> + <gl-button + v-if="shouldShowStopButton" + v-gl-modal-directive="'stop-environment-modal'" + data-testid="stop-button" + icon="stop" + variant="danger" + > + {{ $options.i18n.stopButtonText }} + </gl-button> + <gl-button + v-if="canDestroyEnvironment" + v-gl-modal-directive="'delete-environment-modal'" + data-testid="destroy-button" + variant="danger" + > + {{ $options.i18n.deleteButtonText }} + </gl-button> + </div> + <delete-environment-modal v-if="canDestroyEnvironment" :environment="environment" /> + <stop-environment-modal v-if="shouldShowStopButton" :environment="environment" /> + </header> +</template> diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js index 6f701f87261..85cff73cc3e 100644 --- a/app/assets/javascripts/environments/mixins/environments_mixin.js +++ b/app/assets/javascripts/environments/mixins/environments_mixin.js @@ -108,7 +108,19 @@ export default { this.service .postAction(endpoint) - .then(() => this.fetchEnvironments()) + .then(() => { + // Originally, the detail page buttons were implemented as <form>s that POSTed + // to the server, which would naturally result in a page refresh. + // When environment details page was converted to Vue, the buttons were updated to trigger + // HTTP requests using `axios`, which did not cause a refresh on completion. + // To preserve the original behavior, we manually reload the page when + // network requests complete successfully. + if (!this.isDetailView) { + this.fetchEnvironments(); + } else { + window.location.reload(); + } + }) .catch((err) => { this.isLoading = false; createFlash({ diff --git a/app/assets/javascripts/environments/mount_show.js b/app/assets/javascripts/environments/mount_show.js index d0b68b0c14f..f1c2dfec94b 100644 --- a/app/assets/javascripts/environments/mount_show.js +++ b/app/assets/javascripts/environments/mount_show.js @@ -1,30 +1,48 @@ import Vue from 'vue'; -import DeleteEnvironmentModal from './components/delete_environment_modal.vue'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import EnvironmentsDetailHeader from './components/environments_detail_header.vue'; import environmentsMixin from './mixins/environments_mixin'; -export default () => { - const el = document.getElementById('delete-environment-modal'); +export const initHeader = () => { + const el = document.getElementById('environments-detail-view-header'); const container = document.getElementById('environments-detail-view'); + const dataset = convertObjectPropsToCamelCase(JSON.parse(container.dataset.details)); return new Vue({ el, - components: { - DeleteEnvironmentModal, - }, mixins: [environmentsMixin], data() { - const environment = JSON.parse(JSON.stringify(container.dataset)); - environment.delete_path = environment.deletePath; - environment.onSingleEnvironmentPage = true; + const environment = { + name: dataset.name, + id: Number(dataset.id), + externalUrl: dataset.externalUrl, + isAvailable: dataset.isEnvironmentAvailable, + hasTerminals: dataset.hasTerminals, + autoStopAt: dataset.autoStopAt, + onSingleEnvironmentPage: true, + // TODO: These two props are snake_case because the environments_mixin file uses + // them and the mixin is imported in several files. It would be nice to conver them to camelCase. + stop_path: dataset.environmentStopPath, + delete_path: dataset.environmentDeletePath, + }; return { environment, }; }, render(createElement) { - return createElement('delete-environment-modal', { + return createElement(EnvironmentsDetailHeader, { props: { environment: this.environment, + canDestroyEnvironment: dataset.canDestroyEnvironment, + canUpdateEnvironment: dataset.canUpdateEnvironment, + canReadEnvironment: dataset.canReadEnvironment, + canStopEnvironment: dataset.canStopEnvironment, + canAdminEnvironment: dataset.canAdminEnvironment, + cancelAutoStopPath: dataset.environmentCancelAutoStopPath, + terminalPath: dataset.environmentTerminalPath, + metricsPath: dataset.environmentMetricsPath, + updatePath: dataset.environmentEditPath, }, }); }, diff --git a/app/assets/javascripts/pages/admin/runners/index/index.js b/app/assets/javascripts/pages/admin/runners/index/index.js index d5563470394..8e7861c300a 100644 --- a/app/assets/javascripts/pages/admin/runners/index/index.js +++ b/app/assets/javascripts/pages/admin/runners/index/index.js @@ -2,7 +2,7 @@ import AdminRunnersFilteredSearchTokenKeys from '~/filtered_search/admin_runners import { FILTERED_SEARCH } from '~/pages/constants'; import initFilteredSearch from '~/pages/search/init_filtered_search'; import { initInstallRunner } from '~/pages/shared/mount_runner_instructions'; -import { initRunnerList } from '~/runner/runner_list'; +import { initAdminRunners } from '~/runner/admin_runners'; initFilteredSearch({ page: FILTERED_SEARCH.ADMIN_RUNNERS, @@ -13,5 +13,5 @@ initFilteredSearch({ initInstallRunner(); if (gon.features?.runnerListViewVueUi) { - initRunnerList(); + initAdminRunners(); } diff --git a/app/assets/javascripts/pages/projects/environments/show/index.js b/app/assets/javascripts/pages/projects/environments/show/index.js index a4960037eaa..cd3a0d62bb6 100644 --- a/app/assets/javascripts/pages/projects/environments/show/index.js +++ b/app/assets/javascripts/pages/projects/environments/show/index.js @@ -1,3 +1,3 @@ -import initShowEnvironment from '~/environments/mount_show'; +import { initHeader } from '~/environments/mount_show'; -initShowEnvironment(); +initHeader(); diff --git a/app/assets/javascripts/registry/explorer/components/list_page/cleanup_status.vue b/app/assets/javascripts/registry/explorer/components/list_page/cleanup_status.vue index 8d9e221af4c..1f52e319ad0 100644 --- a/app/assets/javascripts/registry/explorer/components/list_page/cleanup_status.vue +++ b/app/assets/javascripts/registry/explorer/components/list_page/cleanup_status.vue @@ -1,7 +1,7 @@ <script> import { GlTooltipDirective, GlIcon } from '@gitlab/ui'; import { - ASYNC_DELETE_IMAGE_ERROR_MESSAGE, + CLEANUP_TIMED_OUT_ERROR_MESSAGE, CLEANUP_STATUS_SCHEDULED, CLEANUP_STATUS_ONGOING, CLEANUP_STATUS_UNFINISHED, @@ -34,7 +34,7 @@ export default { CLEANUP_STATUS_SCHEDULED, CLEANUP_STATUS_ONGOING, CLEANUP_STATUS_UNFINISHED, - ASYNC_DELETE_IMAGE_ERROR_MESSAGE, + CLEANUP_TIMED_OUT_ERROR_MESSAGE, }, computed: { showStatus() { @@ -61,7 +61,7 @@ export default { </span> <gl-icon v-if="failedDelete" - v-gl-tooltip="{ title: $options.i18n.ASYNC_DELETE_IMAGE_ERROR_MESSAGE }" + v-gl-tooltip="{ title: $options.i18n.CLEANUP_TIMED_OUT_ERROR_MESSAGE }" :size="14" class="gl-text-black-normal" data-testid="extra-info" diff --git a/app/assets/javascripts/runner/runner_list/runner_list_app.vue b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue index 8d39243d609..23ecee449a4 100644 --- a/app/assets/javascripts/runner/runner_list/runner_list_app.vue +++ b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue @@ -9,15 +9,15 @@ import RunnerPagination from '../components/runner_pagination.vue'; import RunnerTypeHelp from '../components/runner_type_help.vue'; import { INSTANCE_TYPE, I18N_FETCH_ERROR } from '../constants'; import getRunnersQuery from '../graphql/get_runners.query.graphql'; -import { captureException } from '../sentry_utils'; import { fromUrlQueryToSearch, fromSearchToUrl, fromSearchToVariables, -} from './runner_search_utils'; +} from '../runner_search_utils'; +import { captureException } from '../sentry_utils'; export default { - name: 'RunnerListApp', + name: 'AdminRunnersApp', components: { RunnerFilteredSearchBar, RunnerList, diff --git a/app/assets/javascripts/runner/runner_list/index.js b/app/assets/javascripts/runner/admin_runners/index.js index 16616f00d1e..1eec1019b73 100644 --- a/app/assets/javascripts/runner/runner_list/index.js +++ b/app/assets/javascripts/runner/admin_runners/index.js @@ -1,11 +1,11 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; -import RunnerDetailsApp from './runner_list_app.vue'; +import AdminRunnersApp from './admin_runners_app.vue'; Vue.use(VueApollo); -export const initRunnerList = (selector = '#js-runner-list') => { +export const initAdminRunners = (selector = '#js-admin-runners') => { const el = document.querySelector(selector); if (!el) { @@ -32,7 +32,7 @@ export const initRunnerList = (selector = '#js-runner-list') => { runnerInstallHelpPage, }, render(h) { - return h(RunnerDetailsApp, { + return h(AdminRunnersApp, { props: { activeRunnersCount: parseInt(activeRunnersCount, 10), registrationToken, diff --git a/app/assets/javascripts/runner/runner_list/runner_search_utils.js b/app/assets/javascripts/runner/runner_search_utils.js index 9a0dc9c3a32..65f75eb11ac 100644 --- a/app/assets/javascripts/runner/runner_list/runner_search_utils.js +++ b/app/assets/javascripts/runner/runner_search_utils.js @@ -16,7 +16,7 @@ import { PARAM_KEY_BEFORE, DEFAULT_SORT, RUNNER_PAGE_SIZE, -} from '../constants'; +} from './constants'; const getPaginationFromParams = (params) => { const page = parseInt(params[PARAM_KEY_PAGE], 10); diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 5ad7ceecb2b..465b1a80fa4 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -509,6 +509,24 @@ span.idiff { } } +.version-link { + @include gl-display-inline-block; + @include gl-align-self-center; + @include gl-mt-2; + @include gl-w-5; + @include gl-h-5; + @include gl-float-left; + background-color: $gray-400; + mask-image: asset_url('icons-stacked.svg#doc-versions'); + mask-repeat: no-repeat; + mask-size: cover; + mask-position: center; + + &:hover { + background-color: $black; + } +} + // // IMPORTANT PERFORMANCE OPTIMIZATION BELOW // |