diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-09-19 06:09:45 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-09-19 06:09:45 +0300 |
commit | bf53a3fd78390334ea7af084a64f74bf83c985e0 (patch) | |
tree | 3598cb074cb3be1d68aba19d572492cf2696ac1e /app/assets/javascripts/ci | |
parent | 3024d3ac9e2c7242cd3cfce70b6c103f357fa6a9 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/ci')
14 files changed, 668 insertions, 4 deletions
diff --git a/app/assets/javascripts/ci/admin/jobs_table/admin_jobs_table_app.vue b/app/assets/javascripts/ci/admin/jobs_table/admin_jobs_table_app.vue new file mode 100644 index 00000000000..89582e64f3a --- /dev/null +++ b/app/assets/javascripts/ci/admin/jobs_table/admin_jobs_table_app.vue @@ -0,0 +1,271 @@ +<script> +import { GlAlert, GlIntersectionObserver, GlLoadingIcon } from '@gitlab/ui'; +import { setUrlParams, updateHistory, queryToObject } from '~/lib/utils/url_utility'; +import { validateQueryString } from '~/ci/common/private/jobs_filtered_search/utils'; +import JobsTable from '~/ci/jobs_page/components/jobs_table.vue'; +import JobsTableTabs from '~/ci/jobs_page/components/jobs_table_tabs.vue'; +import JobsFilteredSearch from '~/ci/common/private/jobs_filtered_search/app.vue'; +import JobsTableEmptyState from '~/ci/jobs_page/components/jobs_table_empty_state.vue'; +import { createAlert } from '~/alert'; +import { + TOKEN_TYPE_STATUS, + TOKEN_TYPE_JOBS_RUNNER_TYPE, +} from '~/vue_shared/components/filtered_search_bar/constants'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import { + DEFAULT_FIELDS_ADMIN, + RAW_TEXT_WARNING_ADMIN, + JOBS_COUNT_ERROR_MESSAGE, + JOBS_FETCH_ERROR_MSG, + LOADING_ARIA_LABEL, + CANCELABLE_JOBS_ERROR_MSG, +} from './constants'; +import JobsSkeletonLoader from './components/jobs_skeleton_loader.vue'; +import GetAllJobs from './graphql/queries/get_all_jobs.query.graphql'; +import GetAllJobsCount from './graphql/queries/get_all_jobs_count.query.graphql'; +import getCancelableJobs from './graphql/queries/get_cancelable_jobs_count.query.graphql'; + +export default { + i18n: { + jobsCountErrorMsg: JOBS_COUNT_ERROR_MESSAGE, + jobsFetchErrorMsg: JOBS_FETCH_ERROR_MSG, + loadingAriaLabel: LOADING_ARIA_LABEL, + cancelableJobsErrorMsg: CANCELABLE_JOBS_ERROR_MSG, + }, + filterSearchBoxStyles: + 'gl-my-0 gl-p-5 gl-bg-gray-10 gl-text-gray-900 gl-border-b gl-border-gray-100', + components: { + JobsSkeletonLoader, + JobsTableEmptyState, + GlAlert, + JobsFilteredSearch, + JobsTable, + JobsTableTabs, + GlIntersectionObserver, + GlLoadingIcon, + }, + mixins: [glFeatureFlagsMixin()], + inject: { + jobStatuses: { + default: null, + required: false, + }, + url: { + default: '', + required: false, + }, + emptyStateSvgPath: { + default: '', + required: false, + }, + }, + apollo: { + jobs: { + query: GetAllJobs, + variables() { + return this.variables; + }, + update(data) { + const { jobs: { nodes: list = [], pageInfo = {} } = {} } = data || {}; + return { + list, + pageInfo, + }; + }, + error() { + this.error = this.$options.i18n.jobsFetchErrorMsg; + }, + }, + jobsCount: { + query: GetAllJobsCount, + variables() { + return this.variables; + }, + update(data) { + return data?.jobs?.count || 0; + }, + context: { + isSingleRequest: true, + }, + error() { + this.error = this.$options.i18n.jobsCountErrorMsg; + }, + }, + cancelable: { + query: getCancelableJobs, + update(data) { + this.isCancelable = data.cancelable.count !== 0; + }, + error() { + this.error = this.$options.i18n.cancelableJobsErrorMsg; + }, + }, + }, + data() { + return { + jobs: { + list: [], + }, + error: '', + count: 0, + scope: null, + infiniteScrollingTriggered: false, + filterSearchTriggered: false, + DEFAULT_FIELDS_ADMIN, + isCancelable: false, + jobsCount: null, + }; + }, + computed: { + loading() { + return this.$apollo.queries.jobs.loading; + }, + // Show when on All tab with no jobs + // Show only when not loading and filtered search has not been triggered + // So we don't show empty state when results are empty on a filtered search + showEmptyState() { + return ( + this.jobs.list.length === 0 && !this.scope && !this.loading && !this.filterSearchTriggered + ); + }, + hasNextPage() { + return this.jobs?.pageInfo?.hasNextPage; + }, + variables() { + return { ...this.validatedQueryString }; + }, + validatedQueryString() { + const queryStringObject = queryToObject(window.location.search); + + return validateQueryString(queryStringObject); + }, + showFilteredSearch() { + return !this.scope; + }, + showLoadingSpinner() { + return this.loading && this.infiniteScrollingTriggered; + }, + showSkeletonLoader() { + return this.loading && !this.showLoadingSpinner; + }, + }, + watch: { + // this watcher ensures that the count on the all tab + // is not updated when switching to the finished tab + jobsCount(newCount) { + if (this.scope) return; + + this.count = newCount; + }, + }, + methods: { + updateHistoryAndFetchCount(filterParams = {}) { + this.$apollo.queries.jobsCount.refetch(filterParams); + + updateHistory({ + url: setUrlParams(filterParams, window.location.href, true), + }); + }, + fetchJobsByStatus(scope) { + this.infiniteScrollingTriggered = false; + + if (this.scope === scope) return; + + this.scope = scope; + + if (!this.scope) this.updateHistoryAndFetchCount(); + + this.$apollo.queries.jobs.refetch({ statuses: scope }); + }, + fetchMoreJobs() { + if (!this.loading) { + this.infiniteScrollingTriggered = true; + + const parameters = this.variables; + parameters.after = this.jobs?.pageInfo?.endCursor; + + this.$apollo.queries.jobs.fetchMore({ + variables: parameters, + }); + } + }, + filterJobsBySearch(filters) { + this.infiniteScrollingTriggered = false; + this.filterSearchTriggered = true; + + if (filters.some((filter) => !filter.type)) { + // Raw text input in filtered search does not have a type + // when a user enters raw text we alert them that it is + // not supported and we do not make an additional API call + createAlert({ message: RAW_TEXT_WARNING_ADMIN, type: 'warning' }); + return; + } + + const defaultFilterParams = this.glFeatures.adminJobsFilterRunnerType + ? { statuses: null, runnerTypes: null } + : { statuses: null }; + + const filterParams = filters.reduce((acc, filter) => { + switch (filter.type) { + case TOKEN_TYPE_STATUS: + return { ...acc, statuses: filter.value.data }; + + case TOKEN_TYPE_JOBS_RUNNER_TYPE: + if (this.glFeatures.adminJobsFilterRunnerType) { + return { ...acc, runnerTypes: filter.value.data }; + } + return acc; + + default: + return acc; + } + }, defaultFilterParams); + + this.updateHistoryAndFetchCount(filterParams); + this.$apollo.queries.jobs.refetch(filterParams); + }, + }, +}; +</script> + +<template> + <div> + <gl-alert v-if="error" class="gl-mt-2" variant="danger" dismissible @dismiss="error = ''"> + {{ error }} + </gl-alert> + + <jobs-table-tabs + :all-jobs-count="count" + :loading="loading" + :show-cancel-all-jobs-button="isCancelable" + @fetchJobsByStatus="fetchJobsByStatus" + /> + + <div v-if="showFilteredSearch" :class="$options.filterSearchBoxStyles"> + <jobs-filtered-search + :query-string="validatedQueryString" + @filterJobsBySearch="filterJobsBySearch" + /> + </div> + + <jobs-skeleton-loader v-if="showSkeletonLoader" class="gl-mt-5" /> + + <jobs-table-empty-state v-else-if="showEmptyState" /> + + <jobs-table + v-else + :jobs="jobs.list" + :table-fields="DEFAULT_FIELDS_ADMIN" + admin + class="gl-table-no-top-border" + /> + + <gl-intersection-observer v-if="hasNextPage" @appear="fetchMoreJobs"> + <gl-loading-icon + v-if="showLoadingSpinner" + size="lg" + :aria-label="$options.i18n.loadingAriaLabel" + /> + </gl-intersection-observer> + </div> +</template> diff --git a/app/assets/javascripts/ci/admin/jobs_table/components/cancel_jobs.vue b/app/assets/javascripts/ci/admin/jobs_table/components/cancel_jobs.vue new file mode 100644 index 00000000000..fb13fd4b03e --- /dev/null +++ b/app/assets/javascripts/ci/admin/jobs_table/components/cancel_jobs.vue @@ -0,0 +1,37 @@ +<script> +import { GlButton, GlModalDirective, GlTooltipDirective } from '@gitlab/ui'; +import { CANCEL_JOBS_MODAL_ID, CANCEL_JOBS_BUTTON_TEXT, CANCEL_BUTTON_TOOLTIP } from '../constants'; +import CancelJobsModal from './cancel_jobs_modal.vue'; + +export default { + name: 'CancelJobs', + components: { + GlButton, + CancelJobsModal, + }, + directives: { + GlModal: GlModalDirective, + GlTooltip: GlTooltipDirective, + }, + props: { + url: { + type: String, + required: true, + }, + }, + modalId: CANCEL_JOBS_MODAL_ID, + buttonText: CANCEL_JOBS_BUTTON_TEXT, + buttonTooltip: CANCEL_BUTTON_TOOLTIP, +}; +</script> +<template> + <div> + <gl-button + v-gl-modal="$options.modalId" + v-gl-tooltip="$options.buttonTooltip" + variant="danger" + >{{ $options.buttonText }}</gl-button + > + <cancel-jobs-modal :modal-id="$options.modalId" :url="url" @confirm="$emit('confirm')" /> + </div> +</template> diff --git a/app/assets/javascripts/ci/admin/jobs_table/components/cancel_jobs_modal.vue b/app/assets/javascripts/ci/admin/jobs_table/components/cancel_jobs_modal.vue new file mode 100644 index 00000000000..7c1cd75609a --- /dev/null +++ b/app/assets/javascripts/ci/admin/jobs_table/components/cancel_jobs_modal.vue @@ -0,0 +1,66 @@ +<script> +import { GlModal } from '@gitlab/ui'; +import { createAlert } from '~/alert'; +import axios from '~/lib/utils/axios_utils'; +import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated +import { + CANCEL_TEXT, + CANCEL_JOBS_FAILED_TEXT, + CANCEL_JOBS_MODAL_TITLE, + CANCEL_JOBS_WARNING, + PRIMARY_ACTION_TEXT, +} from '../constants'; + +export default { + components: { + GlModal, + }, + props: { + url: { + type: String, + required: true, + }, + modalId: { + type: String, + required: true, + }, + }, + methods: { + onSubmit() { + return axios + .post(this.url) + .then((response) => { + // follow the rediect to refresh the page + redirectTo(response.request.responseURL); // eslint-disable-line import/no-deprecated + }) + .catch((error) => { + createAlert({ + message: CANCEL_JOBS_FAILED_TEXT, + }); + throw error; + }); + }, + }, + primaryAction: { + text: PRIMARY_ACTION_TEXT, + attributes: { variant: 'danger' }, + }, + cancelAction: { + text: CANCEL_TEXT, + }, + CANCEL_JOBS_WARNING, + CANCEL_JOBS_MODAL_TITLE, +}; +</script> + +<template> + <gl-modal + :modal-id="modalId" + :action-primary="$options.primaryAction" + :action-cancel="$options.cancelAction" + :title="$options.CANCEL_JOBS_MODAL_TITLE" + @primary="onSubmit" + > + {{ $options.CANCEL_JOBS_WARNING }} + </gl-modal> +</template> diff --git a/app/assets/javascripts/ci/admin/jobs_table/components/cells/project_cell.vue b/app/assets/javascripts/ci/admin/jobs_table/components/cells/project_cell.vue new file mode 100644 index 00000000000..cbb80a5175f --- /dev/null +++ b/app/assets/javascripts/ci/admin/jobs_table/components/cells/project_cell.vue @@ -0,0 +1,28 @@ +<script> +import { GlLink } from '@gitlab/ui'; + +export default { + components: { + GlLink, + }, + props: { + job: { + type: Object, + required: true, + }, + }, + computed: { + projectName() { + return this.job.pipeline?.project?.fullPath; + }, + projectUrl() { + return this.job.pipeline?.project?.webUrl; + }, + }, +}; +</script> +<template> + <div class="gl-text-truncate"> + <gl-link :href="projectUrl"> {{ projectName }}</gl-link> + </div> +</template> diff --git a/app/assets/javascripts/ci/admin/jobs_table/components/cells/runner_cell.vue b/app/assets/javascripts/ci/admin/jobs_table/components/cells/runner_cell.vue new file mode 100644 index 00000000000..a76829aa129 --- /dev/null +++ b/app/assets/javascripts/ci/admin/jobs_table/components/cells/runner_cell.vue @@ -0,0 +1,39 @@ +<script> +import { GlLink } from '@gitlab/ui'; +import { RUNNER_EMPTY_TEXT, RUNNER_NO_DESCRIPTION } from '../../constants'; + +export default { + i18n: { + emptyRunnerText: RUNNER_EMPTY_TEXT, + noRunnerDescription: RUNNER_NO_DESCRIPTION, + }, + components: { + GlLink, + }, + props: { + job: { + type: Object, + required: true, + }, + }, + computed: { + adminUrl() { + return this.job.runner?.adminUrl; + }, + description() { + return this.job.runner?.description + ? this.job.runner.description + : this.$options.i18n.noRunnerDescription; + }, + }, +}; +</script> + +<template> + <div class="gl-text-truncate"> + <gl-link v-if="adminUrl" :href="adminUrl"> + {{ description }} + </gl-link> + <span v-else data-testid="empty-runner-text"> {{ $options.i18n.emptyRunnerText }}</span> + </div> +</template> diff --git a/app/assets/javascripts/ci/admin/jobs_table/components/jobs_skeleton_loader.vue b/app/assets/javascripts/ci/admin/jobs_table/components/jobs_skeleton_loader.vue new file mode 100644 index 00000000000..c305e09af0d --- /dev/null +++ b/app/assets/javascripts/ci/admin/jobs_table/components/jobs_skeleton_loader.vue @@ -0,0 +1,26 @@ +<script> +import { GlSkeletonLoader } from '@gitlab/ui'; + +export default { + components: { + GlSkeletonLoader, + }, +}; +</script> + +<template> + <gl-skeleton-loader :width="1248" :height="73"> + <circle cx="748.031" cy="37.7193" r="15.0307" /> + <circle cx="787.241" cy="37.7193" r="15.0307" /> + <circle cx="827.759" cy="37.7193" r="15.0307" /> + <circle cx="866.969" cy="37.7193" r="15.0307" /> + <circle cx="380" cy="37" r="18" /> + <rect x="432" y="19" width="126.587" height="15" /> + <rect x="432" y="41" width="247" height="15" /> + <rect x="158" y="19" width="86.1" height="15" /> + <rect x="158" y="41" width="168" height="15" /> + <rect x="22" y="19" width="96" height="36" /> + <rect x="924" y="30" width="96" height="15" /> + <rect x="1057" y="20" width="166" height="35" /> + </gl-skeleton-loader> +</template> diff --git a/app/assets/javascripts/ci/admin/jobs_table/constants.js b/app/assets/javascripts/ci/admin/jobs_table/constants.js new file mode 100644 index 00000000000..ff0efdb1f5b --- /dev/null +++ b/app/assets/javascripts/ci/admin/jobs_table/constants.js @@ -0,0 +1,35 @@ +import { s__, __ } from '~/locale'; +import { RAW_TEXT_WARNING } from '~/ci/jobs_page/constants'; + +export const JOBS_COUNT_ERROR_MESSAGE = __('There was an error fetching the number of jobs.'); +export const JOBS_FETCH_ERROR_MSG = __('There was an error fetching the jobs.'); +export const LOADING_ARIA_LABEL = __('Loading'); +export const CANCELABLE_JOBS_ERROR_MSG = __('There was an error fetching the cancelable jobs.'); +export const CANCEL_JOBS_MODAL_ID = 'cancel-jobs-modal'; +export const CANCEL_JOBS_MODAL_TITLE = s__('AdminArea|Are you sure?'); +export const CANCEL_JOBS_BUTTON_TEXT = s__('AdminArea|Cancel all jobs'); +export const CANCEL_BUTTON_TOOLTIP = s__('AdminArea|Cancel all running and pending jobs'); +export const CANCEL_TEXT = __('Cancel'); +export const CANCEL_JOBS_FAILED_TEXT = s__('AdminArea|Canceling jobs failed'); +export const PRIMARY_ACTION_TEXT = s__('AdminArea|Yes, proceed'); +export const CANCEL_JOBS_WARNING = s__( + "AdminArea|You're about to cancel all running and pending jobs across this instance. Do you want to proceed?", +); +export const RUNNER_EMPTY_TEXT = __('None'); +export const RUNNER_NO_DESCRIPTION = s__('Runners|No description'); + +/* Admin Table constants */ +/* The field list is based on app/assets/javascripts/jobs/components/table/constants.js */ +export const DEFAULT_FIELDS_ADMIN = [ + { key: 'status', label: __('Status'), columnClass: 'gl-w-15p' }, + { key: 'job', label: __('Job'), columnClass: 'gl-w-20p' }, + { key: 'project', label: __('Project'), columnClass: 'gl-w-20p' }, + { key: 'runner', label: __('Runner'), columnClass: 'gl-w-15p' }, + { key: 'pipeline', label: __('Pipeline'), columnClass: 'gl-w-10p' }, + { key: 'stage', label: __('Stage'), columnClass: 'gl-w-10p' }, + { key: 'name', label: __('Name'), columnClass: 'gl-w-15p' }, + { key: 'duration', label: __('Duration'), columnClass: 'gl-w-15p' }, + { key: 'actions', label: '', columnClass: 'gl-w-10p' }, +]; + +export const RAW_TEXT_WARNING_ADMIN = RAW_TEXT_WARNING; diff --git a/app/assets/javascripts/ci/admin/jobs_table/graphql/cache_config.js b/app/assets/javascripts/ci/admin/jobs_table/graphql/cache_config.js new file mode 100644 index 00000000000..fd7ee2a6f8c --- /dev/null +++ b/app/assets/javascripts/ci/admin/jobs_table/graphql/cache_config.js @@ -0,0 +1,62 @@ +import { isEqual } from 'lodash'; + +export default { + typePolicies: { + Query: { + fields: { + jobs: { + keyArgs: ['statuses'], + }, + }, + }, + CiJobConnection: { + merge(existing = {}, incoming, { args = {} }) { + if (incoming.nodes) { + let nodes; + + const areNodesEqual = isEqual(existing.nodes, incoming.nodes); + const statuses = Array.isArray(args.statuses) ? [...args.statuses] : args.statuses; + const { pageInfo } = incoming; + + if (Object.keys(existing).length !== 0 && isEqual(existing?.statuses, args?.statuses)) { + if (areNodesEqual) { + if (incoming.pageInfo.hasNextPage) { + nodes = [...existing.nodes, ...incoming.nodes]; + } else { + nodes = [...incoming.nodes]; + } + } else { + if (!existing.pageInfo?.hasNextPage) { + nodes = [...incoming.nodes]; + + return { + nodes, + statuses, + pageInfo, + count: incoming.count, + }; + } + + nodes = [...existing.nodes, ...incoming.nodes]; + } + } else { + nodes = [...incoming.nodes]; + } + + return { + nodes, + statuses, + pageInfo, + count: incoming.count, + }; + } + + return { + nodes: existing.nodes, + pageInfo: existing.pageInfo, + statuses: args.statuses, + }; + }, + }, + }, +}; diff --git a/app/assets/javascripts/ci/admin/jobs_table/graphql/queries/get_all_jobs.query.graphql b/app/assets/javascripts/ci/admin/jobs_table/graphql/queries/get_all_jobs.query.graphql new file mode 100644 index 00000000000..89fb1782e46 --- /dev/null +++ b/app/assets/javascripts/ci/admin/jobs_table/graphql/queries/get_all_jobs.query.graphql @@ -0,0 +1,90 @@ +query getAllJobs( + $after: String + $first: Int = 50 + $statuses: [CiJobStatus!] + $runnerTypes: [CiRunnerType!] +) { + jobs(after: $after, first: $first, statuses: $statuses, runnerTypes: $runnerTypes) { + pageInfo { + endCursor + hasNextPage + hasPreviousPage + startCursor + } + nodes { + runner { + id + description + adminUrl + } + artifacts { + nodes { + id + downloadPath + fileType + } + } + allowFailure + status + scheduledAt + manualJob + triggered + createdByTag + detailedStatus { + id + detailsPath + group + icon + label + text + tooltip + action { + id + buttonTitle + icon + method + path + title + } + } + id + refName + refPath + tags + shortSha + commitPath + pipeline { + id + project { + id + fullPath + webUrl + } + path + user { + id + webPath + avatarUrl + } + } + stage { + id + name + } + name + duration + finishedAt + coverage + retryable + playable + cancelable + active + stuck + userPermissions { + readBuild + readJobArtifacts + updateBuild + } + } + } +} diff --git a/app/assets/javascripts/ci/admin/jobs_table/graphql/queries/get_all_jobs_count.query.graphql b/app/assets/javascripts/ci/admin/jobs_table/graphql/queries/get_all_jobs_count.query.graphql new file mode 100644 index 00000000000..bcb0123e9e3 --- /dev/null +++ b/app/assets/javascripts/ci/admin/jobs_table/graphql/queries/get_all_jobs_count.query.graphql @@ -0,0 +1,5 @@ +query getAllJobsCount($statuses: [CiJobStatus!], $runnerTypes: [CiRunnerType!]) { + jobs(statuses: $statuses, runnerTypes: $runnerTypes) { + count + } +} diff --git a/app/assets/javascripts/ci/admin/jobs_table/graphql/queries/get_cancelable_jobs_count.query.graphql b/app/assets/javascripts/ci/admin/jobs_table/graphql/queries/get_cancelable_jobs_count.query.graphql new file mode 100644 index 00000000000..9bf5e1449b7 --- /dev/null +++ b/app/assets/javascripts/ci/admin/jobs_table/graphql/queries/get_cancelable_jobs_count.query.graphql @@ -0,0 +1,5 @@ +query getCancelableJobsCount { + cancelable: jobs(statuses: [PENDING, RUNNING]) { + count + } +} diff --git a/app/assets/javascripts/ci/jobs_page/components/jobs_table.vue b/app/assets/javascripts/ci/jobs_page/components/jobs_table.vue index 9a66e4698df..23100a3f3db 100644 --- a/app/assets/javascripts/ci/jobs_page/components/jobs_table.vue +++ b/app/assets/javascripts/ci/jobs_page/components/jobs_table.vue @@ -2,8 +2,8 @@ import { GlTable } from '@gitlab/ui'; import { s__ } from '~/locale'; import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue'; -import ProjectCell from '~/pages/admin/jobs/components/table/cell/project_cell.vue'; -import RunnerCell from '~/pages/admin/jobs/components/table/cells/runner_cell.vue'; +import ProjectCell from '~/ci/admin/jobs_table/components/cells/project_cell.vue'; +import RunnerCell from '~/ci/admin/jobs_table/components/cells/runner_cell.vue'; import { DEFAULT_FIELDS } from '../constants'; import ActionsCell from './job_cells/actions_cell.vue'; import DurationCell from './job_cells/duration_cell.vue'; diff --git a/app/assets/javascripts/ci/jobs_page/components/jobs_table_tabs.vue b/app/assets/javascripts/ci/jobs_page/components/jobs_table_tabs.vue index 464b500c66e..b753195da9a 100644 --- a/app/assets/javascripts/ci/jobs_page/components/jobs_table_tabs.vue +++ b/app/assets/javascripts/ci/jobs_page/components/jobs_table_tabs.vue @@ -1,7 +1,7 @@ <script> import { GlBadge, GlTab, GlTabs, GlLoadingIcon } from '@gitlab/ui'; import { s__ } from '~/locale'; -import CancelJobs from '~/pages/admin/jobs/components/cancel_jobs.vue'; +import CancelJobs from '~/ci/admin/jobs_table/components/cancel_jobs.vue'; import { limitedCounterWithDelimiter } from '~/lib/utils/text_utility'; export default { diff --git a/app/assets/javascripts/ci/jobs_page/jobs_page_app.vue b/app/assets/javascripts/ci/jobs_page/jobs_page_app.vue index c801b35e868..03e0f2dadc8 100644 --- a/app/assets/javascripts/ci/jobs_page/jobs_page_app.vue +++ b/app/assets/javascripts/ci/jobs_page/jobs_page_app.vue @@ -3,7 +3,7 @@ import { GlAlert, GlIntersectionObserver, GlLoadingIcon } from '@gitlab/ui'; import { __ } from '~/locale'; import { createAlert } from '~/alert'; import { setUrlParams, updateHistory, queryToObject } from '~/lib/utils/url_utility'; -import JobsSkeletonLoader from '~/pages/admin/jobs/components/jobs_skeleton_loader.vue'; +import JobsSkeletonLoader from '~/ci/admin/jobs_table/components/jobs_skeleton_loader.vue'; import JobsFilteredSearch from '~/ci/common/private/jobs_filtered_search/app.vue'; import { validateQueryString } from '~/ci/common/private/jobs_filtered_search/utils'; import GetJobs from './graphql/queries/get_jobs.query.graphql'; |