diff options
Diffstat (limited to 'app/assets/javascripts/jobs/components/table')
19 files changed, 0 insertions, 1328 deletions
diff --git a/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue b/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue deleted file mode 100644 index d97f6f6ff8c..00000000000 --- a/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue +++ /dev/null @@ -1,265 +0,0 @@ -<script> -import { - GlButton, - GlButtonGroup, - GlModal, - GlModalDirective, - GlSprintf, - GlTooltipDirective, -} from '@gitlab/ui'; -import GlCountdown from '~/vue_shared/components/gl_countdown.vue'; -import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated -import { - ACTIONS_DOWNLOAD_ARTIFACTS, - ACTIONS_START_NOW, - ACTIONS_UNSCHEDULE, - ACTIONS_PLAY, - ACTIONS_RETRY, - ACTIONS_RUN_AGAIN, - CANCEL, - GENERIC_ERROR, - JOB_SCHEDULED, - JOB_SUCCESS, - PLAY_JOB_CONFIRMATION_MESSAGE, - RUN_JOB_NOW_HEADER_TITLE, - FILE_TYPE_ARCHIVE, -} from '../constants'; -import eventHub from '../event_hub'; -import cancelJobMutation from '../graphql/mutations/job_cancel.mutation.graphql'; -import playJobMutation from '../graphql/mutations/job_play.mutation.graphql'; -import retryJobMutation from '../graphql/mutations/job_retry.mutation.graphql'; -import unscheduleJobMutation from '../graphql/mutations/job_unschedule.mutation.graphql'; -import { reportMessageToSentry } from '../../../utils'; - -export default { - ACTIONS_DOWNLOAD_ARTIFACTS, - ACTIONS_START_NOW, - ACTIONS_UNSCHEDULE, - ACTIONS_PLAY, - ACTIONS_RETRY, - CANCEL, - GENERIC_ERROR, - PLAY_JOB_CONFIRMATION_MESSAGE, - RUN_JOB_NOW_HEADER_TITLE, - jobRetry: 'jobRetry', - jobCancel: 'jobCancel', - jobPlay: 'jobPlay', - jobUnschedule: 'jobUnschedule', - playJobModalId: 'play-job-modal', - name: 'JobActionsCell', - components: { - GlButton, - GlButtonGroup, - GlCountdown, - GlModal, - GlSprintf, - }, - directives: { - GlModalDirective, - GlTooltip: GlTooltipDirective, - }, - inject: { - admin: { - default: false, - }, - }, - props: { - job: { - type: Object, - required: true, - }, - }, - data() { - return { - retryBtnDisabled: false, - cancelBtnDisabled: false, - playManualBtnDisabled: false, - unscheduleBtnDisabled: false, - }; - }, - computed: { - hasArtifacts() { - return this.job.artifacts.nodes.find((artifact) => artifact.fileType === FILE_TYPE_ARCHIVE); - }, - artifactDownloadPath() { - return this.hasArtifacts.downloadPath; - }, - canReadJob() { - return this.job.userPermissions?.readBuild; - }, - canUpdateJob() { - return this.job.userPermissions?.updateBuild; - }, - canReadArtifacts() { - return this.job.userPermissions?.readJobArtifacts; - }, - isActive() { - return this.job.active; - }, - manualJobPlayable() { - return this.job.playable && !this.admin && this.job.manualJob; - }, - isRetryable() { - return this.job.retryable; - }, - isScheduled() { - return this.job.status === JOB_SCHEDULED; - }, - scheduledAt() { - return this.job.scheduledAt; - }, - currentJobActionPath() { - return this.job.detailedStatus?.action?.path; - }, - currentJobMethod() { - return this.job.detailedStatus?.action?.method; - }, - shouldDisplayArtifacts() { - return this.canReadArtifacts && this.hasArtifacts; - }, - retryButtonTitle() { - return this.job.status === JOB_SUCCESS ? ACTIONS_RUN_AGAIN : ACTIONS_RETRY; - }, - }, - methods: { - async postJobAction(name, mutation, redirect = false) { - try { - const { - data: { - [name]: { errors, job }, - }, - } = await this.$apollo.mutate({ - mutation, - variables: { id: this.job.id }, - }); - if (errors.length > 0) { - reportMessageToSentry(this.$options.name, errors.join(', '), {}); - this.showToastMessage(); - } else if (redirect) { - // Retry and Play actions redirect to job detail view - // we don't need to refetch with jobActionPerformed event - redirectTo(job.detailedStatus.detailsPath); // eslint-disable-line import/no-deprecated - } else { - eventHub.$emit('jobActionPerformed'); - } - } catch (failure) { - reportMessageToSentry(this.$options.name, failure, {}); - this.showToastMessage(); - } - }, - showToastMessage() { - const toastProps = { - text: this.$options.GENERIC_ERROR, - variant: 'danger', - }; - - this.$toast.show(toastProps.text, { - variant: toastProps.variant, - }); - }, - cancelJob() { - this.cancelBtnDisabled = true; - - this.postJobAction(this.$options.jobCancel, cancelJobMutation); - }, - retryJob() { - this.retryBtnDisabled = true; - - this.postJobAction(this.$options.jobRetry, retryJobMutation, true); - }, - playJob() { - this.playManualBtnDisabled = true; - - this.postJobAction(this.$options.jobPlay, playJobMutation, true); - }, - unscheduleJob() { - this.unscheduleBtnDisabled = true; - - this.postJobAction(this.$options.jobUnschedule, unscheduleJobMutation); - }, - }, -}; -</script> - -<template> - <gl-button-group> - <template v-if="canReadJob && canUpdateJob"> - <gl-button - v-if="isActive" - v-gl-tooltip - icon="cancel" - :title="$options.CANCEL" - :aria-label="$options.CANCEL" - :disabled="cancelBtnDisabled" - data-testid="cancel-button" - @click="cancelJob()" - /> - <template v-else-if="isScheduled"> - <gl-button icon="planning" disabled data-testid="countdown"> - <gl-countdown :end-date-string="scheduledAt" /> - </gl-button> - <gl-button - v-gl-modal-directive="$options.playJobModalId" - v-gl-tooltip - icon="play" - :title="$options.ACTIONS_START_NOW" - :aria-label="$options.ACTIONS_START_NOW" - data-testid="play-scheduled" - /> - <gl-modal - :modal-id="$options.playJobModalId" - :title="$options.RUN_JOB_NOW_HEADER_TITLE" - @primary="playJob()" - > - <gl-sprintf :message="$options.PLAY_JOB_CONFIRMATION_MESSAGE"> - <template #job_name>{{ job.name }}</template> - </gl-sprintf> - </gl-modal> - <gl-button - v-gl-tooltip - icon="time-out" - :title="$options.ACTIONS_UNSCHEDULE" - :aria-label="$options.ACTIONS_UNSCHEDULE" - :disabled="unscheduleBtnDisabled" - data-testid="unschedule" - @click="unscheduleJob()" - /> - </template> - <template v-else> - <!--Note: This is the manual job play button --> - <gl-button - v-if="manualJobPlayable" - v-gl-tooltip - icon="play" - :title="$options.ACTIONS_PLAY" - :aria-label="$options.ACTIONS_PLAY" - :disabled="playManualBtnDisabled" - data-testid="play" - @click="playJob()" - /> - <gl-button - v-else-if="isRetryable" - v-gl-tooltip - icon="retry" - :title="retryButtonTitle" - :aria-label="retryButtonTitle" - :method="currentJobMethod" - :disabled="retryBtnDisabled" - data-testid="retry" - @click="retryJob()" - /> - </template> - </template> - <gl-button - v-if="shouldDisplayArtifacts" - v-gl-tooltip - icon="download" - :title="$options.ACTIONS_DOWNLOAD_ARTIFACTS" - :aria-label="$options.ACTIONS_DOWNLOAD_ARTIFACTS" - :href="artifactDownloadPath" - rel="nofollow" - download - data-testid="download-artifacts" - /> - </gl-button-group> -</template> diff --git a/app/assets/javascripts/jobs/components/table/cells/duration_cell.vue b/app/assets/javascripts/jobs/components/table/cells/duration_cell.vue deleted file mode 100644 index 11593fa355a..00000000000 --- a/app/assets/javascripts/jobs/components/table/cells/duration_cell.vue +++ /dev/null @@ -1,45 +0,0 @@ -<script> -import { GlIcon } from '@gitlab/ui'; -import { formatTime } from '~/lib/utils/datetime_utility'; -import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; -import timeagoMixin from '~/vue_shared/mixins/timeago'; - -export default { - iconSize: 12, - components: { - GlIcon, - TimeAgoTooltip, - }, - mixins: [timeagoMixin], - props: { - job: { - type: Object, - required: true, - }, - }, - computed: { - finishedTime() { - return this.job?.finishedAt; - }, - duration() { - return this.job?.duration; - }, - durationFormatted() { - return formatTime(this.duration * 1000); - }, - }, -}; -</script> - -<template> - <div> - <div v-if="duration" data-testid="job-duration"> - <gl-icon name="timer" :size="$options.iconSize" data-testid="duration-icon" /> - {{ durationFormatted }} - </div> - <div v-if="finishedTime" data-testid="job-finished-time"> - <gl-icon name="calendar" :size="$options.iconSize" data-testid="finished-time-icon" /> - <time-ago-tooltip :time="finishedTime" /> - </div> - </div> -</template> diff --git a/app/assets/javascripts/jobs/components/table/cells/job_cell.vue b/app/assets/javascripts/jobs/components/table/cells/job_cell.vue deleted file mode 100644 index 27d286fc766..00000000000 --- a/app/assets/javascripts/jobs/components/table/cells/job_cell.vue +++ /dev/null @@ -1,171 +0,0 @@ -<script> -import { GlBadge, GlIcon, GlLink, GlTooltipDirective } from '@gitlab/ui'; -import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import { s__ } from '~/locale'; -import { SUCCESS_STATUS } from '../../../constants'; - -export default { - iconSize: 12, - badgeSize: 'sm', - i18n: { - stuckText: s__('Jobs|Job is stuck. Check runners.'), - }, - directives: { - GlTooltip: GlTooltipDirective, - }, - components: { - GlBadge, - GlIcon, - GlLink, - }, - props: { - job: { - type: Object, - required: true, - }, - }, - computed: { - jobId() { - const id = getIdFromGraphQLId(this.job.id); - return `#${id}`; - }, - jobPath() { - return this.job.detailedStatus?.detailsPath; - }, - jobRef() { - return this.job?.refName; - }, - jobRefPath() { - return this.job?.refPath; - }, - jobTags() { - return this.job.tags; - }, - createdByTag() { - return this.job.createdByTag; - }, - triggered() { - return this.job.triggered; - }, - isManualJob() { - return this.job.manualJob; - }, - successfulJob() { - return this.job.status === SUCCESS_STATUS; - }, - showAllowedToFailBadge() { - return this.job.allowFailure && !this.successfulJob; - }, - isScheduledJob() { - return Boolean(this.job.scheduledAt); - }, - canReadJob() { - return this.job?.userPermissions?.readBuild; - }, - jobStuck() { - return this.job?.stuck; - }, - }, -}; -</script> - -<template> - <div> - <div class="gl-text-truncate gl-mb-2"> - <gl-link - v-if="canReadJob" - class="gl-text-blue-600!" - :href="jobPath" - data-testid="job-id-link" - > - {{ jobId }} - </gl-link> - - <span v-else data-testid="job-id-limited-access">{{ jobId }}</span> - - <gl-icon - v-if="jobStuck" - v-gl-tooltip="$options.i18n.stuckText" - name="warning" - :size="$options.iconSize" - data-testid="stuck-icon" - /> - - <div - class="gl-display-flex gl-text-gray-700 gl-align-items-center gl-lg-justify-content-start gl-justify-content-end gl-mt-2" - > - <div - v-if="jobRef" - class="gl-px-2 gl-rounded-base gl-bg-gray-50 gl-max-w-15 gl-text-truncate" - > - <gl-icon - v-if="createdByTag" - name="label" - :size="$options.iconSize" - data-testid="label-icon" - /> - <gl-icon v-else name="fork" :size="$options.iconSize" data-testid="fork-icon" /> - <gl-link - class="gl-font-sm gl-font-monospace gl-text-gray-700 gl-hover-text-gray-900" - :href="job.refPath" - data-testid="job-ref" - >{{ job.refName }}</gl-link - > - </div> - - <span v-else>{{ __('none') }}</span> - <div class="gl-ml-2 gl-rounded-base gl-px-2 gl-bg-gray-50"> - <gl-icon class="gl-mx-2" name="commit" :size="$options.iconSize" /> - <gl-link - class="gl-font-sm gl-font-monospace gl-text-gray-700 gl-hover-text-gray-900" - :href="job.commitPath" - data-testid="job-sha" - >{{ job.shortSha }}</gl-link - > - </div> - </div> - </div> - - <div> - <gl-badge - v-for="tag in jobTags" - :key="tag" - variant="info" - :size="$options.badgeSize" - data-testid="job-tag-badge" - > - {{ tag }} - </gl-badge> - - <gl-badge - v-if="triggered" - variant="info" - :size="$options.badgeSize" - data-testid="triggered-job-badge" - >{{ s__('Job|triggered') }} - </gl-badge> - <gl-badge - v-if="showAllowedToFailBadge" - variant="warning" - :size="$options.badgeSize" - data-testid="fail-job-badge" - >{{ s__('Job|allowed to fail') }} - </gl-badge> - <gl-badge - v-if="isScheduledJob" - variant="info" - :size="$options.badgeSize" - data-testid="delayed-job-badge" - >{{ s__('Job|delayed') }} - </gl-badge> - <gl-badge - v-if="isManualJob" - variant="info" - :size="$options.badgeSize" - data-testid="manual-job-badge" - > - {{ s__('Job|manual') }} - </gl-badge> - </div> - </div> -</template> diff --git a/app/assets/javascripts/jobs/components/table/cells/pipeline_cell.vue b/app/assets/javascripts/jobs/components/table/cells/pipeline_cell.vue deleted file mode 100644 index 1a6d1a341b0..00000000000 --- a/app/assets/javascripts/jobs/components/table/cells/pipeline_cell.vue +++ /dev/null @@ -1,52 +0,0 @@ -<script> -import { GlAvatar, GlLink } from '@gitlab/ui'; -import { getIdFromGraphQLId } from '~/graphql_shared/utils'; - -export default { - components: { - GlAvatar, - GlLink, - }, - props: { - job: { - type: Object, - required: true, - }, - }, - computed: { - pipelineId() { - const id = getIdFromGraphQLId(this.job.pipeline.id); - return `#${id}`; - }, - pipelinePath() { - return this.job.pipeline?.path; - }, - pipelineUserAvatar() { - return this.job.pipeline?.user?.avatarUrl; - }, - userPath() { - return this.job.pipeline?.user?.webPath; - }, - showAvatar() { - return this.job.pipeline?.user; - }, - }, -}; -</script> - -<template> - <div> - <div class="gl-text-truncate"> - <gl-link class="gl-text-gray-500!" :href="pipelinePath" data-testid="pipeline-id"> - {{ pipelineId }} - </gl-link> - </div> - <div> - <span>{{ __('created by') }}</span> - <gl-link v-if="showAvatar" :href="userPath" data-testid="pipeline-user-link"> - <gl-avatar :src="pipelineUserAvatar" :size="16" /> - </gl-link> - <span v-else>{{ __('API') }}</span> - </div> - </div> -</template> diff --git a/app/assets/javascripts/jobs/components/table/constants.js b/app/assets/javascripts/jobs/components/table/constants.js deleted file mode 100644 index 1b572e60c58..00000000000 --- a/app/assets/javascripts/jobs/components/table/constants.js +++ /dev/null @@ -1,76 +0,0 @@ -import { s__, __ } from '~/locale'; - -/* Error constants */ -export const DEFAULT = 'default'; -export const RAW_TEXT_WARNING = s__( - 'Jobs|Raw text search is not currently supported for the jobs filtered search feature. Please use the available search tokens.', -); - -/* Job Status Constants */ -export const JOB_SCHEDULED = 'SCHEDULED'; -export const JOB_SUCCESS = 'SUCCESS'; - -/* Artifact file types */ -export const FILE_TYPE_ARCHIVE = 'ARCHIVE'; - -/* i18n */ -export const ACTIONS_DOWNLOAD_ARTIFACTS = __('Download artifacts'); -export const ACTIONS_START_NOW = s__('DelayedJobs|Start now'); -export const ACTIONS_UNSCHEDULE = s__('DelayedJobs|Unschedule'); -export const ACTIONS_PLAY = __('Play'); -export const ACTIONS_RETRY = __('Retry'); -export const ACTIONS_RUN_AGAIN = __('Run again'); - -export const CANCEL = __('Cancel'); -export const GENERIC_ERROR = __('An error occurred while making the request.'); -export const PLAY_JOB_CONFIRMATION_MESSAGE = s__( - `DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after its timer finishes.`, -); -export const RUN_JOB_NOW_HEADER_TITLE = s__('DelayedJobs|Run the delayed job now?'); - -/* Table constants */ -export const DEFAULT_FIELDS = [ - { - key: 'status', - label: __('Status'), - columnClass: 'gl-w-10p', - }, - { - key: 'job', - label: __('Job'), - columnClass: 'gl-w-20p', - }, - { - 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: 'coverage', - label: __('Coverage'), - tdClass: 'gl-display-none! gl-lg-display-table-cell!', - columnClass: 'gl-w-10p', - }, - { - key: 'actions', - label: '', - columnClass: 'gl-w-10p', - }, -]; - -export const JOBS_TAB_FIELDS = DEFAULT_FIELDS.filter((field) => field.key !== 'pipeline'); diff --git a/app/assets/javascripts/jobs/components/table/event_hub.js b/app/assets/javascripts/jobs/components/table/event_hub.js deleted file mode 100644 index e31806ad199..00000000000 --- a/app/assets/javascripts/jobs/components/table/event_hub.js +++ /dev/null @@ -1,3 +0,0 @@ -import createEventHub from '~/helpers/event_hub_factory'; - -export default createEventHub(); diff --git a/app/assets/javascripts/jobs/components/table/graphql/cache_config.js b/app/assets/javascripts/jobs/components/table/graphql/cache_config.js deleted file mode 100644 index 5390c023da4..00000000000 --- a/app/assets/javascripts/jobs/components/table/graphql/cache_config.js +++ /dev/null @@ -1,60 +0,0 @@ -import { isEqual } from 'lodash'; - -export default { - typePolicies: { - Project: { - fields: { - jobs: { - keyArgs: false, - }, - }, - }, - 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, - }; - } - - nodes = [...existing.nodes, ...incoming.nodes]; - } - } else { - nodes = [...incoming.nodes]; - } - - return { - nodes, - statuses, - pageInfo, - }; - } - - return { - nodes: existing.nodes, - pageInfo: existing.pageInfo, - statuses: args.statuses, - }; - }, - }, - }, -}; diff --git a/app/assets/javascripts/jobs/components/table/graphql/fragments/job.fragment.graphql b/app/assets/javascripts/jobs/components/table/graphql/fragments/job.fragment.graphql deleted file mode 100644 index 3038216fdfc..00000000000 --- a/app/assets/javascripts/jobs/components/table/graphql/fragments/job.fragment.graphql +++ /dev/null @@ -1,7 +0,0 @@ -fragment Job on CiJob { - id - detailedStatus { - id - detailsPath - } -} diff --git a/app/assets/javascripts/jobs/components/table/graphql/mutations/job_cancel.mutation.graphql b/app/assets/javascripts/jobs/components/table/graphql/mutations/job_cancel.mutation.graphql deleted file mode 100644 index 20935514d51..00000000000 --- a/app/assets/javascripts/jobs/components/table/graphql/mutations/job_cancel.mutation.graphql +++ /dev/null @@ -1,10 +0,0 @@ -#import "../fragments/job.fragment.graphql" - -mutation cancelJob($id: CiBuildID!) { - jobCancel(input: { id: $id }) { - job { - ...Job - } - errors - } -} diff --git a/app/assets/javascripts/jobs/components/table/graphql/mutations/job_play.mutation.graphql b/app/assets/javascripts/jobs/components/table/graphql/mutations/job_play.mutation.graphql deleted file mode 100644 index c94b045ac40..00000000000 --- a/app/assets/javascripts/jobs/components/table/graphql/mutations/job_play.mutation.graphql +++ /dev/null @@ -1,10 +0,0 @@ -#import "../fragments/job.fragment.graphql" - -mutation playJob($id: CiBuildID!) { - jobPlay(input: { id: $id }) { - job { - ...Job - } - errors - } -} diff --git a/app/assets/javascripts/jobs/components/table/graphql/mutations/job_retry.mutation.graphql b/app/assets/javascripts/jobs/components/table/graphql/mutations/job_retry.mutation.graphql deleted file mode 100644 index 6e51f9a20fa..00000000000 --- a/app/assets/javascripts/jobs/components/table/graphql/mutations/job_retry.mutation.graphql +++ /dev/null @@ -1,10 +0,0 @@ -#import "../fragments/job.fragment.graphql" - -mutation retryJob($id: CiBuildID!) { - jobRetry(input: { id: $id }) { - job { - ...Job - } - errors - } -} diff --git a/app/assets/javascripts/jobs/components/table/graphql/mutations/job_unschedule.mutation.graphql b/app/assets/javascripts/jobs/components/table/graphql/mutations/job_unschedule.mutation.graphql deleted file mode 100644 index 8be8c42f3c3..00000000000 --- a/app/assets/javascripts/jobs/components/table/graphql/mutations/job_unschedule.mutation.graphql +++ /dev/null @@ -1,10 +0,0 @@ -#import "../fragments/job.fragment.graphql" - -mutation unscheduleJob($id: CiBuildID!) { - jobUnschedule(input: { id: $id }) { - job { - ...Job - } - errors - } -} diff --git a/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql b/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql deleted file mode 100644 index 69719011079..00000000000 --- a/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql +++ /dev/null @@ -1,78 +0,0 @@ -query getJobs($fullPath: ID!, $after: String, $first: Int = 30, $statuses: [CiJobStatus!]) { - project(fullPath: $fullPath) { - id - jobs(after: $after, first: $first, statuses: $statuses) { - pageInfo { - endCursor - hasNextPage - hasPreviousPage - startCursor - } - nodes { - artifacts { - # eslint-disable-next-line @graphql-eslint/require-id-when-available - nodes { - 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 - 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/jobs/components/table/graphql/queries/get_jobs_count.query.graphql b/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs_count.query.graphql deleted file mode 100644 index a4e02ae721a..00000000000 --- a/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs_count.query.graphql +++ /dev/null @@ -1,8 +0,0 @@ -query getJobsCount($fullPath: ID!, $statuses: [CiJobStatus!]) { - project(fullPath: $fullPath) { - id - jobs(statuses: $statuses) { - count - } - } -} diff --git a/app/assets/javascripts/jobs/components/table/index.js b/app/assets/javascripts/jobs/components/table/index.js deleted file mode 100644 index 88da1169e01..00000000000 --- a/app/assets/javascripts/jobs/components/table/index.js +++ /dev/null @@ -1,50 +0,0 @@ -import { GlToast } from '@gitlab/ui'; -import Vue from 'vue'; -import VueApollo from 'vue-apollo'; -import JobsTableApp from '~/jobs/components/table/jobs_table_app.vue'; -import createDefaultClient from '~/lib/graphql'; -import { parseBoolean } from '~/lib/utils/common_utils'; -import cacheConfig from './graphql/cache_config'; - -Vue.use(VueApollo); -Vue.use(GlToast); - -const apolloProvider = new VueApollo({ - defaultClient: createDefaultClient( - {}, - { - cacheConfig, - }, - ), -}); - -export default (containerId = 'js-jobs-table') => { - const containerEl = document.getElementById(containerId); - - if (!containerEl) { - return false; - } - - const { - fullPath, - jobStatuses, - pipelineEditorPath, - emptyStateSvgPath, - admin, - } = containerEl.dataset; - - return new Vue({ - el: containerEl, - apolloProvider, - provide: { - emptyStateSvgPath, - fullPath, - pipelineEditorPath, - jobStatuses: JSON.parse(jobStatuses), - admin: parseBoolean(admin), - }, - render(createElement) { - return createElement(JobsTableApp); - }, - }); -}; diff --git a/app/assets/javascripts/jobs/components/table/jobs_table.vue b/app/assets/javascripts/jobs/components/table/jobs_table.vue deleted file mode 100644 index 84479ec421e..00000000000 --- a/app/assets/javascripts/jobs/components/table/jobs_table.vue +++ /dev/null @@ -1,112 +0,0 @@ -<script> -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 ActionsCell from './cells/actions_cell.vue'; -import DurationCell from './cells/duration_cell.vue'; -import JobCell from './cells/job_cell.vue'; -import PipelineCell from './cells/pipeline_cell.vue'; -import { DEFAULT_FIELDS } from './constants'; - -export default { - i18n: { - emptyText: s__('Jobs|No jobs to show'), - }, - components: { - ActionsCell, - CiBadgeLink, - DurationCell, - GlTable, - JobCell, - PipelineCell, - ProjectCell, - RunnerCell, - }, - props: { - jobs: { - type: Array, - required: true, - }, - tableFields: { - type: Array, - required: false, - default: () => DEFAULT_FIELDS, - }, - admin: { - type: Boolean, - required: false, - default: false, - }, - }, - methods: { - formatCoverage(coverage) { - return coverage ? `${coverage}%` : ''; - }, - }, -}; -</script> - -<template> - <gl-table - :items="jobs" - :fields="tableFields" - :tbody-tr-attr="{ 'data-testid': 'jobs-table-row' }" - :empty-text="$options.i18n.emptyText" - data-testid="jobs-table" - show-empty - stacked="lg" - fixed - > - <template #table-colgroup="{ fields }"> - <col v-for="field in fields" :key="field.key" :class="field.columnClass" /> - </template> - - <template #cell(status)="{ item }"> - <ci-badge-link :status="item.detailedStatus" /> - </template> - - <template #cell(job)="{ item }"> - <job-cell :job="item" /> - </template> - - <template #cell(pipeline)="{ item }"> - <pipeline-cell :job="item" /> - </template> - - <template v-if="admin" #cell(project)="{ item }"> - <project-cell :job="item" /> - </template> - - <template v-if="admin" #cell(runner)="{ item }"> - <runner-cell :job="item" /> - </template> - - <template #cell(stage)="{ item }"> - <div class="gl-text-truncate"> - <span data-testid="job-stage-name">{{ item.stage.name }}</span> - </div> - </template> - - <template #cell(name)="{ item }"> - <div class="gl-text-truncate"> - <span data-testid="job-name">{{ item.name }}</span> - </div> - </template> - - <template #cell(duration)="{ item }"> - <duration-cell :job="item" /> - </template> - - <template #cell(coverage)="{ item }"> - <span v-if="item.coverage" data-testid="job-coverage">{{ - formatCoverage(item.coverage) - }}</span> - </template> - - <template #cell(actions)="{ item }"> - <actions-cell class="gl-float-right" :job="item" /> - </template> - </gl-table> -</template> diff --git a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue deleted file mode 100644 index 09fa006cb88..00000000000 --- a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue +++ /dev/null @@ -1,238 +0,0 @@ -<script> -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 JobsFilteredSearch from '../filtered_search/jobs_filtered_search.vue'; -import { validateQueryString } from '../filtered_search/utils'; -import GetJobs from './graphql/queries/get_jobs.query.graphql'; -import GetJobsCount from './graphql/queries/get_jobs_count.query.graphql'; -import JobsTable from './jobs_table.vue'; -import JobsTableEmptyState from './jobs_table_empty_state.vue'; -import JobsTableTabs from './jobs_table_tabs.vue'; -import { RAW_TEXT_WARNING } from './constants'; - -export default { - i18n: { - jobsFetchErrorMsg: __('There was an error fetching the jobs for your project.'), - jobsCountErrorMsg: __('There was an error fetching the number of jobs for your project.'), - loadingAriaLabel: __('Loading'), - }, - filterSearchBoxStyles: - 'gl-my-0 gl-p-5 gl-bg-gray-10 gl-text-gray-900 gl-border-b gl-border-gray-100', - components: { - GlAlert, - JobsFilteredSearch, - JobsTable, - JobsTableEmptyState, - JobsTableTabs, - GlIntersectionObserver, - GlLoadingIcon, - JobsSkeletonLoader, - }, - inject: { - fullPath: { - default: '', - }, - }, - apollo: { - jobs: { - query: GetJobs, - variables() { - return { - fullPath: this.fullPath, - ...this.validatedQueryString, - }; - }, - update(data) { - const { jobs: { nodes: list = [], pageInfo = {} } = {} } = data.project || {}; - return { - list, - pageInfo, - }; - }, - error() { - this.error = this.$options.i18n.jobsFetchErrorMsg; - }, - }, - jobsCount: { - query: GetJobsCount, - context: { - isSingleRequest: true, - }, - variables() { - return { - fullPath: this.fullPath, - ...this.validatedQueryString, - }; - }, - update({ project }) { - return project?.jobs?.count || 0; - }, - error() { - this.error = this.$options.i18n.jobsCountErrorMsg; - }, - }, - }, - data() { - return { - jobs: { - list: [], - }, - error: '', - scope: null, - infiniteScrollingTriggered: false, - filterSearchTriggered: false, - jobsCount: null, - count: 0, - }; - }, - 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; - }, - showLoadingSpinner() { - return this.loading && this.infiniteScrollingTriggered; - }, - showSkeletonLoader() { - return this.loading && !this.showLoadingSpinner; - }, - showFilteredSearch() { - return !this.scope; - }, - validatedQueryString() { - const queryStringObject = queryToObject(window.location.search); - - return validateQueryString(queryStringObject); - }, - }, - watch: { - // this watcher ensures that the count on the all tab - // is not updated when switching to the finished tab - jobsCount(newCount, oldCount) { - if (this.scope) { - this.count = oldCount; - } else { - this.count = newCount; - } - }, - }, - methods: { - updateHistoryAndFetchCount(status = null) { - this.$apollo.queries.jobsCount.refetch({ statuses: status }); - - updateHistory({ - url: setUrlParams({ statuses: status }, 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 }); - }, - filterJobsBySearch(filters) { - this.infiniteScrollingTriggered = false; - this.filterSearchTriggered = true; - - // all filters have been cleared reset query param - // and refetch jobs/count with defaults - if (!filters.length) { - this.updateHistoryAndFetchCount(); - this.$apollo.queries.jobs.refetch({ statuses: null }); - - return; - } - - // Eventually there will be more tokens available - // this code is written to scale for those tokens - filters.forEach((filter) => { - // 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 - if (!filter.type) { - createAlert({ - message: RAW_TEXT_WARNING, - type: 'warning', - }); - } - - if (filter.type === 'status') { - this.updateHistoryAndFetchCount(filter.value.data); - this.$apollo.queries.jobs.refetch({ statuses: filter.value.data }); - } - }); - }, - fetchMoreJobs() { - if (!this.loading) { - this.infiniteScrollingTriggered = true; - - this.$apollo.queries.jobs.fetchMore({ - variables: { - fullPath: this.fullPath, - after: this.jobs?.pageInfo?.endCursor, - }, - }); - } - }, - }, -}; -</script> - -<template> - <div> - <gl-alert - v-if="error" - class="gl-mt-2" - variant="danger" - data-testid="jobs-table-error-alert" - dismissible - @dismiss="error = ''" - > - {{ error }} - </gl-alert> - - <jobs-table-tabs - :all-jobs-count="count" - :loading="loading" - @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" 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/jobs/components/table/jobs_table_empty_state.vue b/app/assets/javascripts/jobs/components/table/jobs_table_empty_state.vue deleted file mode 100644 index fcdd52b719c..00000000000 --- a/app/assets/javascripts/jobs/components/table/jobs_table_empty_state.vue +++ /dev/null @@ -1,35 +0,0 @@ -<script> -import { GlEmptyState } from '@gitlab/ui'; -import { s__ } from '~/locale'; - -export default { - i18n: { - title: s__('Jobs|Use jobs to automate your tasks'), - description: s__( - 'Jobs|Jobs are the building blocks of a GitLab CI/CD pipeline. Each job has a specific task, like testing code. To set up jobs in a CI/CD pipeline, add a CI/CD configuration file to your project.', - ), - buttonText: s__('Jobs|Create CI/CD configuration file'), - }, - components: { - GlEmptyState, - }, - inject: { - pipelineEditorPath: { - default: '', - }, - emptyStateSvgPath: { - default: '', - }, - }, -}; -</script> - -<template> - <gl-empty-state - :title="$options.i18n.title" - :description="$options.i18n.description" - :svg-path="emptyStateSvgPath" - :primary-button-link="pipelineEditorPath" - :primary-button-text="$options.i18n.buttonText" - /> -</template> diff --git a/app/assets/javascripts/jobs/components/table/jobs_table_tabs.vue b/app/assets/javascripts/jobs/components/table/jobs_table_tabs.vue deleted file mode 100644 index 797facb1eb8..00000000000 --- a/app/assets/javascripts/jobs/components/table/jobs_table_tabs.vue +++ /dev/null @@ -1,88 +0,0 @@ -<script> -import { GlBadge, GlTab, GlTabs, GlLoadingIcon } from '@gitlab/ui'; -import { s__ } from '~/locale'; -import CancelJobs from '~/pages/admin/jobs/components/cancel_jobs.vue'; -import { limitedCounterWithDelimiter } from '~/lib/utils/text_utility'; - -export default { - components: { - GlBadge, - GlTab, - GlTabs, - GlLoadingIcon, - CancelJobs, - }, - inject: { - jobStatuses: { - default: {}, - }, - url: { - type: String, - default: '', - }, - }, - props: { - allJobsCount: { - type: Number, - required: true, - }, - loading: { - type: Boolean, - required: true, - }, - showCancelAllJobsButton: { - type: Boolean, - required: false, - default: false, - }, - }, - computed: { - tabs() { - return [ - { - text: s__('Jobs|All'), - count: limitedCounterWithDelimiter(this.allJobsCount), - scope: null, - testId: 'jobs-all-tab', - showBadge: true, - }, - { - text: s__('Jobs|Finished'), - scope: [this.jobStatuses.success, this.jobStatuses.failed, this.jobStatuses.canceled], - testId: 'jobs-finished-tab', - showBadge: false, - }, - ]; - }, - showLoadingIcon() { - return this.loading && !this.allJobsCount; - }, - }, -}; -</script> - -<template> - <div class="gl-display-flex align-items-lg-center"> - <gl-tabs content-class="gl-py-0"> - <gl-tab - v-for="tab in tabs" - :key="tab.text" - :title-link-attributes="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ { - 'data-testid': tab.testId, - } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */" - @click="$emit('fetchJobsByStatus', tab.scope)" - > - <template #title> - <span>{{ tab.text }}</span> - <gl-loading-icon v-if="showLoadingIcon && tab.showBadge" class="gl-ml-2" /> - - <gl-badge v-else-if="tab.showBadge" size="sm" class="gl-tab-counter-badge"> - {{ tab.count }} - </gl-badge> - </template> - </gl-tab> - </gl-tabs> - <div class="gl-flex-grow-1"></div> - <cancel-jobs v-if="showCancelAllJobsButton" :url="url" /> - </div> -</template> |