diff options
Diffstat (limited to 'app/assets/javascripts/runner/components')
15 files changed, 376 insertions, 204 deletions
diff --git a/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue b/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue index 1eb383a1904..1cd098d6713 100644 --- a/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue +++ b/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue @@ -59,7 +59,11 @@ export default { <tooltip-on-truncate class="gl-display-block gl-text-truncate" :title="description"> {{ description }} </tooltip-on-truncate> - <tooltip-on-truncate class="gl-display-block gl-text-truncate" :title="ipAddress"> + <tooltip-on-truncate + v-if="ipAddress" + class="gl-display-block gl-text-truncate" + :title="ipAddress" + > <span class="gl-md-display-none gl-lg-display-inline">{{ __('IP Address') }}</span> <strong>{{ ipAddress }}</strong> </tooltip-on-truncate> diff --git a/app/assets/javascripts/runner/components/runner_assigned_item.vue b/app/assets/javascripts/runner/components/runner_assigned_item.vue index 38bdfecb7df..2fa87bdd776 100644 --- a/app/assets/javascripts/runner/components/runner_assigned_item.vue +++ b/app/assets/javascripts/runner/components/runner_assigned_item.vue @@ -1,10 +1,11 @@ <script> -import { GlAvatar, GlLink } from '@gitlab/ui'; +import { GlAvatar, GlBadge, GlLink } from '@gitlab/ui'; import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants'; export default { components: { GlAvatar, + GlBadge, GlLink, }, props: { @@ -25,6 +26,16 @@ export default { required: false, default: null, }, + description: { + type: String, + required: false, + default: null, + }, + isOwner: { + type: Boolean, + required: false, + default: false, + }, }, AVATAR_SHAPE_OPTION_RECT, }; @@ -41,7 +52,12 @@ export default { :size="48" /> </gl-link> - - <gl-link :href="href" class="gl-font-weight-bold gl-text-gray-900!">{{ fullName }}</gl-link> + <div> + <div class="gl-mb-1"> + <gl-link :href="href" class="gl-font-weight-bold gl-text-gray-900!">{{ fullName }}</gl-link> + <gl-badge v-if="isOwner" variant="info">{{ s__('Runner|Owner') }}</gl-badge> + </div> + <div v-if="description">{{ description }}</div> + </div> </div> </template> diff --git a/app/assets/javascripts/runner/components/runner_bulk_delete.vue b/app/assets/javascripts/runner/components/runner_bulk_delete.vue index 50791de0bda..703da01d9c8 100644 --- a/app/assets/javascripts/runner/components/runner_bulk_delete.vue +++ b/app/assets/javascripts/runner/components/runner_bulk_delete.vue @@ -1,21 +1,31 @@ <script> -import { GlButton, GlModalDirective, GlSprintf } from '@gitlab/ui'; -import { n__, sprintf } from '~/locale'; -import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending'; -import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; +import { GlButton, GlModalDirective, GlModal, GlSprintf } from '@gitlab/ui'; +import { createAlert } from '~/flash'; +import { __, s__, n__, sprintf } from '~/locale'; import checkedRunnerIdsQuery from '../graphql/list/checked_runner_ids.query.graphql'; +import BulkRunnerDelete from '../graphql/list/bulk_runner_delete.mutation.graphql'; +import { RUNNER_TYPENAME } from '../constants'; export default { components: { GlButton, + GlModal, GlSprintf, }, directives: { GlModal: GlModalDirective, }, inject: ['localMutations'], + props: { + runners: { + type: Array, + default: () => [], + required: false, + }, + }, data() { return { + isDeleting: false, checkedRunnerIds: [], }; }, @@ -25,8 +35,13 @@ export default { }, }, computed: { + currentCheckedRunnerIds() { + return this.runners + .map(({ id }) => id) + .filter((id) => this.checkedRunnerIds.indexOf(id) >= 0); + }, checkedCount() { - return this.checkedRunnerIds.length || 0; + return this.currentCheckedRunnerIds.length || 0; }, bannerMessage() { return sprintf( @@ -43,48 +58,103 @@ export default { modalTitle() { return n__('Runners|Delete %d runner', 'Runners|Delete %d runners', this.checkedCount); }, - modalHtmlMessage() { + modalActionPrimary() { + return { + text: n__( + 'Runners|Permanently delete %d runner', + 'Runners|Permanently delete %d runners', + this.checkedCount, + ), + attributes: { + loading: this.isDeleting, + variant: 'danger', + }, + }; + }, + modalActionCancel() { + return { + text: __('Cancel'), + attributes: { + loading: this.isDeleting, + }, + }; + }, + modalMessage() { return sprintf( n__( 'Runners|%{strongStart}%{count}%{strongEnd} runner will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?', 'Runners|%{strongStart}%{count}%{strongEnd} runners will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?', this.checkedCount, ), - { - strongStart: '<strong>', - strongEnd: '</strong>', - count: this.checkedCount, - }, - false, + { count: this.checkedCount }, ); }, - primaryBtnText() { + }, + methods: { + toastConfirmationMessage(deletedCount) { return n__( - 'Runners|Permanently delete %d runner', - 'Runners|Permanently delete %d runners', - this.checkedCount, + 'Runners|%d selected runner deleted', + 'Runners|%d selected runners deleted', + deletedCount, ); }, - }, - methods: { onClearChecked() { this.localMutations.clearChecked(); }, - onClickDelete: ignoreWhilePending(async function onClickDelete() { - const confirmed = await confirmAction(null, { - title: this.modalTitle, - modalHtmlMessage: this.modalHtmlMessage, - primaryBtnVariant: 'danger', - primaryBtnText: this.primaryBtnText, - }); + async onConfirmDelete(e) { + this.isDeleting = true; + e.preventDefault(); // don't close modal until deletion is complete + + try { + await this.$apollo.mutate({ + mutation: BulkRunnerDelete, + variables: { + input: { + ids: this.currentCheckedRunnerIds, + }, + }, + update: (cache, { data }) => { + const { errors, deletedIds } = data.bulkRunnerDelete; + + if (errors?.length) { + this.onError(new Error(errors.join(' '))); + this.$refs.modal.hide(); + return; + } + + this.$emit('deleted', { + message: this.toastConfirmationMessage(deletedIds.length), + }); - if (confirmed) { - // TODO Call $apollo.mutate with list of runner - // ids in `this.checkedRunnerIds`. - // See https://gitlab.com/gitlab-org/gitlab/-/issues/339525/ + // Clean up + + // Remove deleted runners from the cache + deletedIds.forEach((id) => { + const cacheId = cache.identify({ __typename: RUNNER_TYPENAME, id }); + cache.evict({ id: cacheId }); + }); + cache.gc(); + + this.$refs.modal.hide(); + }, + }); + } catch (error) { + this.onError(error); + } finally { + this.isDeleting = false; } - }), + }, + onError(error) { + createAlert({ + message: s__( + 'Runners|Something went wrong while deleting. Please refresh the page to try again.', + ), + captureError: true, + error, + }); + }, }, + BULK_DELETE_MODAL_ID: 'bulk-delete-modal', }; </script> @@ -99,13 +169,28 @@ export default { </gl-sprintf> </div> <div class="gl-ml-auto"> - <gl-button data-testid="clear-btn" variant="default" @click="onClearChecked">{{ + <gl-button variant="default" @click="onClearChecked">{{ s__('Runners|Clear selection') }}</gl-button> - <gl-button data-testid="delete-btn" variant="danger" @click="onClickDelete">{{ + <gl-button v-gl-modal="$options.BULK_DELETE_MODAL_ID" variant="danger">{{ s__('Runners|Delete selected') }}</gl-button> </div> </div> + <gl-modal + ref="modal" + size="sm" + :modal-id="$options.BULK_DELETE_MODAL_ID" + :title="modalTitle" + :action-primary="modalActionPrimary" + :action-cancel="modalActionCancel" + @primary="onConfirmDelete" + > + <gl-sprintf :message="modalMessage"> + <template #strong="{ content }"> + <strong>{{ content }}</strong> + </template> + </gl-sprintf> + </gl-modal> </div> </template> diff --git a/app/assets/javascripts/runner/components/runner_bulk_delete_checkbox.vue b/app/assets/javascripts/runner/components/runner_bulk_delete_checkbox.vue new file mode 100644 index 00000000000..dde5a5a4a05 --- /dev/null +++ b/app/assets/javascripts/runner/components/runner_bulk_delete_checkbox.vue @@ -0,0 +1,59 @@ +<script> +import { GlFormCheckbox } from '@gitlab/ui'; +import checkedRunnerIdsQuery from '../graphql/list/checked_runner_ids.query.graphql'; + +export default { + components: { + GlFormCheckbox, + }, + inject: ['localMutations'], + props: { + runners: { + type: Array, + default: () => [], + required: false, + }, + }, + data() { + return { + checkedRunnerIds: [], + }; + }, + apollo: { + checkedRunnerIds: { + query: checkedRunnerIdsQuery, + }, + }, + computed: { + disabled() { + return !this.runners.length; + }, + checked() { + return Boolean(this.runners.length) && this.runners.every(this.isChecked); + }, + indeterminate() { + return !this.checked && this.runners.some(this.isChecked); + }, + }, + methods: { + isChecked({ id }) { + return this.checkedRunnerIds.indexOf(id) >= 0; + }, + onChange($event) { + this.localMutations.setRunnersChecked({ + runners: this.runners, + isChecked: $event, + }); + }, + }, +}; +</script> + +<template> + <gl-form-checkbox + :indeterminate="indeterminate" + :checked="checked" + :disabled="disabled" + @change="onChange" + /> +</template> diff --git a/app/assets/javascripts/runner/components/runner_detail.vue b/app/assets/javascripts/runner/components/runner_detail.vue index db67acef3db..584f77b7648 100644 --- a/app/assets/javascripts/runner/components/runner_detail.vue +++ b/app/assets/javascripts/runner/components/runner_detail.vue @@ -38,11 +38,10 @@ export default { </script> <template> - <div class="gl-display-flex gl-pb-4"> - <dt class="gl-mr-2">{{ label }}</dt> - <dd class="gl-mb-0"> - <!-- eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots --> - <template v-if="value || $slots.value"> + <div class="gl-display-contents"> + <dt class="gl-mb-5 gl-mr-6 gl-max-w-26">{{ label }}</dt> + <dd class="gl-mb-5"> + <template v-if="value || $scopedSlots.value"> <slot name="value">{{ value }}</slot> </template> <span v-else class="gl-text-gray-500">{{ emptyValue }}</span> diff --git a/app/assets/javascripts/runner/components/runner_details.vue b/app/assets/javascripts/runner/components/runner_details.vue index 60469d26dd5..d5222f39b81 100644 --- a/app/assets/javascripts/runner/components/runner_details.vue +++ b/app/assets/javascripts/runner/components/runner_details.vue @@ -51,6 +51,9 @@ export default { } return null; }, + tagList() { + return this.runner.tagList || []; + }, isGroupRunner() { return this.runner?.runnerType === GROUP_TYPE; }, @@ -66,14 +69,17 @@ export default { <div> <runner-upgrade-status-alert class="gl-my-4" :runner="runner" /> <div class="gl-pt-4"> - <dl class="gl-mb-0" data-testid="runner-details-list"> + <dl + class="gl-mb-0 gl-display-grid runner-details-grid-template" + data-testid="runner-details-list" + > <runner-detail :label="s__('Runners|Description')" :value="runner.description" /> <runner-detail :label="s__('Runners|Last contact')" :empty-value="s__('Runners|Never contacted')" > - <template #value> - <time-ago v-if="runner.contactedAt" :time="runner.contactedAt" /> + <template v-if="runner.contactedAt" #value> + <time-ago :time="runner.contactedAt" /> </template> </runner-detail> <runner-detail :label="s__('Runners|Version')"> @@ -87,8 +93,8 @@ export default { <runner-detail :label="s__('Runners|Architecture')" :value="runner.architectureName" /> <runner-detail :label="s__('Runners|Platform')" :value="runner.platformName" /> <runner-detail :label="s__('Runners|Configuration')"> - <template #value> - <gl-intersperse v-if="configTextProtected || configTextUntagged"> + <template v-if="configTextProtected || configTextUntagged" #value> + <gl-intersperse> <span v-if="configTextProtected">{{ configTextProtected }}</span> <span v-if="configTextUntagged">{{ configTextUntagged }}</span> </gl-intersperse> @@ -96,13 +102,8 @@ export default { </runner-detail> <runner-detail :label="s__('Runners|Maximum job timeout')" :value="maximumTimeout" /> <runner-detail :label="s__('Runners|Tags')"> - <template #value> - <runner-tags - v-if="runner.tagList && runner.tagList.length" - class="gl-vertical-align-middle" - :tag-list="runner.tagList" - size="sm" - /> + <template v-if="tagList.length" #value> + <runner-tags class="gl-vertical-align-middle" :tag-list="tagList" size="sm" /> </template> </runner-detail> diff --git a/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue b/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue index bff5ec9b238..5a9ab21a457 100644 --- a/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue +++ b/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue @@ -64,19 +64,19 @@ export default { }, methods: { onFilter(filters) { - // Apply new filters, from page 1 + // Apply new filters, resetting pagination this.$emit('input', { ...this.value, filters, - pagination: { page: 1 }, + pagination: {}, }); }, onSort(sort) { - // Apply new sort, from page 1 + // Apply new sort, resetting pagination this.$emit('input', { ...this.value, sort, - pagination: { page: 1 }, + pagination: {}, }); }, }, diff --git a/app/assets/javascripts/runner/components/runner_jobs.vue b/app/assets/javascripts/runner/components/runner_jobs.vue index 57afdc4b9be..9003eba3636 100644 --- a/app/assets/javascripts/runner/components/runner_jobs.vue +++ b/app/assets/javascripts/runner/components/runner_jobs.vue @@ -27,9 +27,7 @@ export default { items: [], pageInfo: {}, }, - pagination: { - page: 1, - }, + pagination: {}, }; }, apollo: { @@ -62,6 +60,11 @@ export default { return this.$apollo.queries.jobs.loading; }, }, + methods: { + onPaginationInput(value) { + this.pagination = value; + }, + }, I18N_NO_JOBS_FOUND, }; </script> @@ -74,6 +77,6 @@ export default { <runner-jobs-table v-else-if="jobs.items.length" :jobs="jobs.items" /> <p v-else>{{ $options.I18N_NO_JOBS_FOUND }}</p> - <runner-pagination v-model="pagination" :disabled="loading" :page-info="jobs.pageInfo" /> + <runner-pagination :disabled="loading" :page-info="jobs.pageInfo" @input="onPaginationInput" /> </div> </template> diff --git a/app/assets/javascripts/runner/components/runner_list.vue b/app/assets/javascripts/runner/components/runner_list.vue index f1f99c728c5..2e406f71792 100644 --- a/app/assets/javascripts/runner/components/runner_list.vue +++ b/app/assets/javascripts/runner/components/runner_list.vue @@ -1,5 +1,5 @@ <script> -import { GlTableLite, GlTooltipDirective, GlSkeletonLoader } from '@gitlab/ui'; +import { GlFormCheckbox, GlTableLite, GlTooltipDirective, GlSkeletonLoader } from '@gitlab/ui'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { __, s__ } from '~/locale'; @@ -23,6 +23,7 @@ const defaultFields = [ export default { components: { + GlFormCheckbox, GlTableLite, GlSkeletonLoader, TooltipOnTruncate, @@ -123,19 +124,11 @@ export default { fixed > <template #head(checkbox)> - <!-- - Checkbox to select all to be added here - See https://gitlab.com/gitlab-org/gitlab/-/issues/339525/ - --> - <span></span> + <slot name="head-checkbox"></slot> </template> <template #cell(checkbox)="{ item }"> - <input - type="checkbox" - :checked="isChecked(item)" - @change="onCheckboxChange(item, $event.target.checked)" - /> + <gl-form-checkbox :checked="isChecked(item)" @change="onCheckboxChange(item, $event)" /> </template> <template #head(status)="{ label }"> diff --git a/app/assets/javascripts/runner/components/runner_pagination.vue b/app/assets/javascripts/runner/components/runner_pagination.vue index cfc21d1407b..a5bf3074dd1 100644 --- a/app/assets/javascripts/runner/components/runner_pagination.vue +++ b/app/assets/javascripts/runner/components/runner_pagination.vue @@ -1,18 +1,12 @@ <script> -import { GlPagination } from '@gitlab/ui'; +import { GlKeysetPagination } from '@gitlab/ui'; export default { components: { - GlPagination, + GlKeysetPagination, }, + inheritAttrs: false, props: { - value: { - required: false, - type: Object, - default: () => ({ - page: 1, - }), - }, pageInfo: { required: false, type: Object, @@ -20,46 +14,37 @@ export default { }, }, computed: { - prevPage() { - return this.pageInfo?.hasPreviousPage ? this.value.page - 1 : null; + paginationProps() { + return { ...this.pageInfo, ...this.$attrs }; }, - nextPage() { - return this.pageInfo?.hasNextPage ? this.value.page + 1 : null; + isShown() { + const { hasPreviousPage, hasNextPage } = this.pageInfo; + return hasPreviousPage || hasNextPage; }, }, methods: { - handlePageChange(page) { - if (page === 1) { - // Small optimization for first page - // If we have loaded using "first", - // page is already cached. - this.$emit('input', { - page, - }); - } else if (page > this.value.page) { - this.$emit('input', { - page, - after: this.pageInfo.endCursor, - }); - } else { - this.$emit('input', { - page, - before: this.pageInfo.startCursor, - }); - } + prevPage() { + this.$emit('input', { + before: this.pageInfo.startCursor, + }); + }, + nextPage() { + this.$emit('input', { + after: this.pageInfo.endCursor, + }); }, }, }; </script> <template> - <gl-pagination - v-bind="$attrs" - :value="value.page" - :prev-page="prevPage" - :next-page="nextPage" - align="center" - class="gl-pagination" - @input="handlePageChange" - /> + <div v-if="isShown" class="gl-text-center"> + <gl-keyset-pagination + v-bind="paginationProps" + :prev-text="s__('Pagination|Prev')" + :next-text="s__('Pagination|Next')" + @prev="prevPage" + @next="nextPage" + /> + </div> </template> diff --git a/app/assets/javascripts/runner/components/runner_projects.vue b/app/assets/javascripts/runner/components/runner_projects.vue index c0c0c14e91e..2c1d2fc2b10 100644 --- a/app/assets/javascripts/runner/components/runner_projects.vue +++ b/app/assets/javascripts/runner/components/runner_projects.vue @@ -30,13 +30,12 @@ export default { data() { return { projects: { + ownerProjectId: null, items: [], pageInfo: {}, count: 0, }, - pagination: { - page: 1, - }, + pagination: {}, }; }, apollo: { @@ -48,6 +47,7 @@ export default { update(data) { const { runner } = data; return { + ownerProjectId: runner?.ownerProject?.id, count: runner?.projectCount || 0, items: runner?.projects?.nodes || [], pageInfo: runner?.projects?.pageInfo || {}, @@ -76,6 +76,14 @@ export default { }); }, }, + methods: { + isOwner(projectId) { + return projectId === this.projects.ownerProjectId; + }, + onPaginationInput(value) { + this.pagination = value; + }, + }, I18N_NONE, }; </script> @@ -98,10 +106,16 @@ export default { :name="project.name" :full-name="project.nameWithNamespace" :avatar-url="project.avatarUrl" + :description="project.description" + :is-owner="isOwner(project.id)" /> </template> <span v-else class="gl-text-gray-500">{{ $options.I18N_NONE }}</span> - <runner-pagination v-model="pagination" :disabled="loading" :page-info="projects.pageInfo" /> + <runner-pagination + :disabled="loading" + :page-info="projects.pageInfo" + @input="onPaginationInput" + /> </div> </template> diff --git a/app/assets/javascripts/runner/components/stat/runner_count.vue b/app/assets/javascripts/runner/components/stat/runner_count.vue index af18b203f90..37c6f922f9a 100644 --- a/app/assets/javascripts/runner/components/stat/runner_count.vue +++ b/app/assets/javascripts/runner/components/stat/runner_count.vue @@ -1,8 +1,9 @@ <script> import { fetchPolicies } from '~/lib/graphql'; +import allRunnersCountQuery from 'ee_else_ce/runner/graphql/list/all_runners_count.query.graphql'; +import groupRunnersCountQuery from 'ee_else_ce/runner/graphql/list/group_runners_count.query.graphql'; + import { captureException } from '../../sentry_utils'; -import allRunnersCountQuery from '../../graphql/list/all_runners_count.query.graphql'; -import groupRunnersCountQuery from '../../graphql/list/group_runners_count.query.graphql'; import { INSTANCE_TYPE, GROUP_TYPE } from '../../constants'; /** @@ -38,7 +39,7 @@ export default { variables: { type: Object, required: false, - default: () => {}, + default: () => ({}), }, skip: { type: Boolean, diff --git a/app/assets/javascripts/runner/components/stat/runner_single_stat.vue b/app/assets/javascripts/runner/components/stat/runner_single_stat.vue new file mode 100644 index 00000000000..ae732b052ac --- /dev/null +++ b/app/assets/javascripts/runner/components/stat/runner_single_stat.vue @@ -0,0 +1,41 @@ +<script> +import { GlSingleStat } from '@gitlab/ui/dist/charts'; +import { formatNumber } from '~/locale'; +import RunnerCount from './runner_count.vue'; + +export default { + components: { + GlSingleStat, + RunnerCount, + }, + props: { + scope: { + type: String, + required: true, + }, + variables: { + type: Object, + required: false, + default: () => ({}), + }, + skip: { + type: Boolean, + required: false, + default: false, + }, + }, + methods: { + formattedValue(value) { + if (typeof value === 'number') { + return formatNumber(value); + } + return '-'; + }, + }, +}; +</script> +<template> + <runner-count #default="{ count }" :scope="scope" :variables="variables" :skip="skip"> + <gl-single-stat v-bind="$attrs" :value="formattedValue(count)" /> + </runner-count> +</template> diff --git a/app/assets/javascripts/runner/components/stat/runner_stats.vue b/app/assets/javascripts/runner/components/stat/runner_stats.vue index 9e1ca9ba4ee..93e54ebe7f4 100644 --- a/app/assets/javascripts/runner/components/stat/runner_stats.vue +++ b/app/assets/javascripts/runner/components/stat/runner_stats.vue @@ -1,12 +1,13 @@ <script> +import { s__ } from '~/locale'; +import RunnerSingleStat from '~/runner/components/stat/runner_single_stat.vue'; import { STATUS_ONLINE, STATUS_OFFLINE, STATUS_STALE } from '../../constants'; -import RunnerCount from './runner_count.vue'; -import RunnerStatusStat from './runner_status_stat.vue'; export default { components: { - RunnerCount, - RunnerStatusStat, + RunnerSingleStat, + RunnerUpgradeStatusStats: () => + import('ee_component/runner/components/stat/runner_upgrade_status_stats.vue'), }, props: { scope: { @@ -16,32 +17,67 @@ export default { variables: { type: Object, required: false, - default: () => {}, + default: () => ({}), }, }, - methods: { - countVariables(vars) { - return { ...this.variables, ...vars }; + computed: { + stats() { + return [ + { + key: STATUS_ONLINE, + props: { + skip: this.statusCountSkip(STATUS_ONLINE), + variables: { ...this.variables, status: STATUS_ONLINE }, + variant: 'success', + title: s__('Runners|Online runners'), + metaText: s__('Runners|online'), + }, + }, + { + key: STATUS_OFFLINE, + props: { + skip: this.statusCountSkip(STATUS_OFFLINE), + variables: { ...this.variables, status: STATUS_OFFLINE }, + variant: 'muted', + title: s__('Runners|Offline runners'), + metaText: s__('Runners|offline'), + }, + }, + { + key: STATUS_STALE, + props: { + skip: this.statusCountSkip(STATUS_STALE), + variables: { ...this.variables, status: STATUS_STALE }, + variant: 'warning', + title: s__('Runners|Stale runners'), + metaText: s__('Runners|stale'), + }, + }, + ]; }, + }, + methods: { statusCountSkip(status) { // Show an empty result when we already filter by another status return this.variables.status && this.variables.status !== status; }, }, - STATUS_LIST: [STATUS_ONLINE, STATUS_OFFLINE, STATUS_STALE], }; </script> <template> - <div class="gl-display-flex gl-py-6"> - <runner-count - v-for="status in $options.STATUS_LIST" - #default="{ count }" - :key="status" + <div class="gl-display-flex gl-flex-wrap gl-py-6"> + <runner-single-stat + v-for="stat in stats" + :key="stat.key" + :scope="scope" + v-bind="stat.props" + class="gl-px-5" + /> + + <runner-upgrade-status-stats + class="gl-display-contents" :scope="scope" - :variables="countVariables({ status })" - :skip="statusCountSkip(status)" - > - <runner-status-stat class="gl-px-5" :status="status" :value="count" /> - </runner-count> + :variables="variables" + /> </div> </template> diff --git a/app/assets/javascripts/runner/components/stat/runner_status_stat.vue b/app/assets/javascripts/runner/components/stat/runner_status_stat.vue deleted file mode 100644 index b77bbe15541..00000000000 --- a/app/assets/javascripts/runner/components/stat/runner_status_stat.vue +++ /dev/null @@ -1,65 +0,0 @@ -<script> -import { GlSingleStat } from '@gitlab/ui/dist/charts'; -import { s__, formatNumber } from '~/locale'; -import { STATUS_ONLINE, STATUS_OFFLINE, STATUS_STALE } from '../../constants'; - -export default { - components: { - GlSingleStat, - }, - props: { - value: { - type: Number, - required: false, - default: null, - }, - status: { - type: String, - required: true, - }, - }, - computed: { - formattedValue() { - if (typeof this.value === 'number') { - return formatNumber(this.value); - } - return '-'; - }, - stat() { - switch (this.status) { - case STATUS_ONLINE: - return { - variant: 'success', - title: s__('Runners|Online runners'), - metaText: s__('Runners|online'), - }; - case STATUS_OFFLINE: - return { - variant: 'muted', - title: s__('Runners|Offline runners'), - metaText: s__('Runners|offline'), - }; - case STATUS_STALE: - return { - variant: 'warning', - title: s__('Runners|Stale runners'), - metaText: s__('Runners|stale'), - }; - default: - return { - title: s__('Runners|Runners'), - }; - } - }, - }, -}; -</script> -<template> - <gl-single-stat - v-if="stat" - :value="formattedValue" - :variant="stat.variant" - :title="stat.title" - :meta-text="stat.metaText" - /> -</template> |