summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-07-21 18:09:27 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-21 18:09:27 +0000
commitbd7e8cd64b1eb9b2d5aa2f83e547d2a5b519b26c (patch)
tree292f1690eec2707d091ede0188706ec92da16427 /app/assets
parenta8f5aaa7081cc2d400fbac1106d9e94d02d70ab4 (diff)
downloadgitlab-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.vue174
-rw-r--r--app/assets/javascripts/environments/mixins/environments_mixin.js14
-rw-r--r--app/assets/javascripts/environments/mount_show.js38
-rw-r--r--app/assets/javascripts/pages/admin/runners/index/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/environments/show/index.js4
-rw-r--r--app/assets/javascripts/registry/explorer/components/list_page/cleanup_status.vue6
-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.scss18
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
//