summaryrefslogtreecommitdiff
path: root/app/assets/javascripts
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts')
-rw-r--r--app/assets/javascripts/google_cloud/components/deployments_service_table.vue61
-rw-r--r--app/assets/javascripts/google_cloud/components/home.vue17
-rw-r--r--app/assets/javascripts/group.js7
-rw-r--r--app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js37
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue6
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue20
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/constants.js1
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/index.js58
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/pages/details.js27
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue (renamed from app/assets/javascripts/packages_and_registries/package_registry/components/details/app.vue)12
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/router.js20
-rw-r--r--app/assets/javascripts/packages_and_registries/shared/components/registry_breadcrumb.vue11
-rw-r--r--app/assets/javascripts/packages_and_registries/shared/utils.js35
-rw-r--r--app/assets/javascripts/pages/groups/packages/index.js8
-rw-r--r--app/assets/javascripts/pages/groups/packages/index/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/packages/packages/index.js8
-rw-r--r--app/assets/javascripts/pages/projects/packages/packages/index/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/packages/packages/show/index.js3
-rw-r--r--app/assets/javascripts/runner/admin_runners/admin_runners_app.vue41
-rw-r--r--app/assets/javascripts/runner/admin_runners/index.js8
-rw-r--r--app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue30
-rw-r--r--app/assets/javascripts/runner/components/stat/runner_online_stat.vue17
-rw-r--r--app/assets/javascripts/runner/components/stat/runner_stats.vue49
-rw-r--r--app/assets/javascripts/runner/components/stat/runner_status_stat.vue65
-rw-r--r--app/assets/javascripts/runner/graphql/get_group_runners.query.graphql2
-rw-r--r--app/assets/javascripts/runner/graphql/get_group_runners_count.query.graphql20
-rw-r--r--app/assets/javascripts/runner/group_runners/group_runners_app.vue52
27 files changed, 471 insertions, 150 deletions
diff --git a/app/assets/javascripts/google_cloud/components/deployments_service_table.vue b/app/assets/javascripts/google_cloud/components/deployments_service_table.vue
new file mode 100644
index 00000000000..7d27d7cf6b2
--- /dev/null
+++ b/app/assets/javascripts/google_cloud/components/deployments_service_table.vue
@@ -0,0 +1,61 @@
+<script>
+import { GlButton, GlTable } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+const i18n = {
+ cloudRun: __('Cloud Run'),
+ cloudRunDescription: __('Deploy container based web apps on Google managed clusters'),
+ cloudStorage: __('Cloud Storage'),
+ cloudStorageDescription: __('Deploy static assets and resources to Google managed CDN'),
+ deployments: __('Deployments'),
+ deploymentsDescription: __(
+ 'Configure pipelines to deploy web apps, backend services, APIs and static resources to Google Cloud',
+ ),
+ configureViaMergeRequest: __('Configure via Merge Request'),
+ service: __('Service'),
+ description: __('Description'),
+};
+
+export default {
+ components: { GlButton, GlTable },
+ props: {
+ cloudRunUrl: {
+ type: String,
+ required: true,
+ },
+ cloudStorageUrl: {
+ type: String,
+ required: true,
+ },
+ },
+ fields: [
+ { key: 'title', label: i18n.service },
+ { key: 'description', label: i18n.description },
+ { key: 'action', label: '' },
+ ],
+ items: [
+ {
+ title: i18n.cloudRun,
+ description: i18n.cloudRunDescription,
+ action: { title: i18n.configureViaMergeRequest, disabled: true },
+ },
+ {
+ title: i18n.cloudStorage,
+ description: i18n.cloudStorageDescription,
+ action: { title: i18n.configureViaMergeRequest, disabled: true },
+ },
+ ],
+ i18n,
+};
+</script>
+<template>
+ <div class="gl-mx-3">
+ <h2 class="gl-font-size-h2">{{ $options.i18n.deployments }}</h2>
+ <p>{{ $options.i18n.deploymentsDescription }}</p>
+ <gl-table :fields="$options.fields" :items="$options.items">
+ <template #cell(action)="{ value }">
+ <gl-button :disabled="value.disabled">{{ value.title }}</gl-button>
+ </template>
+ </gl-table>
+ </div>
+</template>
diff --git a/app/assets/javascripts/google_cloud/components/home.vue b/app/assets/javascripts/google_cloud/components/home.vue
index 05f39de66ee..8ef110dcf22 100644
--- a/app/assets/javascripts/google_cloud/components/home.vue
+++ b/app/assets/javascripts/google_cloud/components/home.vue
@@ -1,11 +1,13 @@
<script>
import { GlTabs, GlTab } from '@gitlab/ui';
+import DeploymentsServiceTable from './deployments_service_table.vue';
import ServiceAccountsList from './service_accounts_list.vue';
export default {
components: {
GlTabs,
GlTab,
+ DeploymentsServiceTable,
ServiceAccountsList,
},
props: {
@@ -21,6 +23,14 @@ export default {
type: String,
required: true,
},
+ deploymentsCloudRunUrl: {
+ type: String,
+ required: true,
+ },
+ deploymentsCloudStorageUrl: {
+ type: String,
+ required: true,
+ },
},
};
</script>
@@ -35,7 +45,12 @@ export default {
:empty-illustration-url="emptyIllustrationUrl"
/>
</gl-tab>
- <gl-tab :title="__('Deployments')" disabled />
+ <gl-tab :title="__('Deployments')">
+ <deployments-service-table
+ :cloud-run-url="deploymentsCloudRunUrl"
+ :cloud-storage-url="deploymentsCloudStorageUrl"
+ />
+ </gl-tab>
<gl-tab :title="__('Services')" disabled />
</gl-tabs>
</template>
diff --git a/app/assets/javascripts/group.js b/app/assets/javascripts/group.js
index f255f8a084c..b6a6720e7a1 100644
--- a/app/assets/javascripts/group.js
+++ b/app/assets/javascripts/group.js
@@ -13,11 +13,8 @@ export default class Group {
this.updateGroupPathSlugHandler = this.updateGroupPathSlug.bind(this);
this.groupNames.forEach((groupName) => {
- if (groupName.value === '') {
- groupName.addEventListener('keyup', this.updateHandler);
-
- groupName.addEventListener('keyup', this.updateGroupPathSlugHandler);
- }
+ groupName.addEventListener('keyup', this.updateHandler);
+ groupName.addEventListener('keyup', this.updateGroupPathSlugHandler);
});
this.groupPaths.forEach((groupPath) => {
diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js b/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js
index e6c197a30dd..ca5bd8d6964 100644
--- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js
+++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/index.js
@@ -4,6 +4,7 @@ import { parseBoolean } from '~/lib/utils/common_utils';
import PerformancePlugin from '~/performance/vue_performance_plugin';
import Translate from '~/vue_shared/translate';
import RegistryBreadcrumb from '~/packages_and_registries/shared/components/registry_breadcrumb.vue';
+import { renderBreadcrumb } from '~/packages_and_registries/shared/utils';
import { apolloProvider } from './graphql/index';
import RegistryExplorer from './pages/index.vue';
import createRouter from './router';
@@ -84,38 +85,8 @@ export default () => {
},
});
- const attachBreadcrumb = () => {
- const breadCrumbEls = document.querySelectorAll('nav .js-breadcrumbs-list li');
- const breadCrumbEl = breadCrumbEls[breadCrumbEls.length - 1];
- const crumbs = [breadCrumbEl.querySelector('h2')];
- const nestedBreadcrumbEl = document.createElement('div');
- breadCrumbEl.replaceChild(nestedBreadcrumbEl, breadCrumbEl.querySelector('h2'));
- return new Vue({
- el: nestedBreadcrumbEl,
- router,
- apolloProvider,
- components: {
- RegistryBreadcrumb,
- },
- render(createElement) {
- // FIXME(@tnir): this is a workaround until the MR gets merged:
- // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48115
- const parentEl = breadCrumbEl.parentElement.parentElement;
- if (parentEl) {
- parentEl.classList.remove('breadcrumbs-container');
- parentEl.classList.add('gl-display-flex');
- parentEl.classList.add('w-100');
- }
- // End of FIXME(@tnir)
- return createElement('registry-breadcrumb', {
- class: breadCrumbEl.className,
- props: {
- crumbs,
- },
- });
- },
- });
+ return {
+ attachBreadcrumb: renderBreadcrumb(router, apolloProvider, RegistryBreadcrumb),
+ attachMainComponent,
};
-
- return { attachBreadcrumb, attachMainComponent };
};
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue
index 95b09b25678..7479f748a56 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/npm_installation.vue
@@ -26,7 +26,7 @@ export default {
GlSprintf,
GlFormRadioGroup,
},
- inject: ['npmPath'],
+ inject: ['npmInstanceUrl'],
props: {
packageEntity: {
type: Object,
@@ -66,7 +66,9 @@ export default {
npmSetupCommand(type, endpointType) {
const scope = this.packageEntity.name.substring(0, this.packageEntity.name.indexOf('/'));
const npmPathForEndpoint =
- endpointType === INSTANCE_PACKAGE_ENDPOINT_TYPE ? this.npmPath : this.packageEntity.npmUrl;
+ endpointType === INSTANCE_PACKAGE_ENDPOINT_TYPE
+ ? this.npmInstanceUrl
+ : this.packageEntity.npmUrl;
if (type === NPM_PACKAGE_MANAGER) {
return `echo ${scope}:registry=${npmPathForEndpoint}/ >> .npmrc`;
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue
index 6fd96c0654f..6222c2e73d7 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue
@@ -1,5 +1,5 @@
<script>
-import { GlButton, GlLink, GlSprintf, GlTooltipDirective, GlTruncate } from '@gitlab/ui';
+import { GlButton, GlSprintf, GlTooltipDirective, GlTruncate } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
import {
@@ -18,7 +18,6 @@ export default {
name: 'PackageListRow',
components: {
GlButton,
- GlLink,
GlSprintf,
GlTruncate,
PackageTags,
@@ -42,9 +41,8 @@ export default {
packageType() {
return getPackageTypeLabel(this.packageEntity.packageType);
},
- packageLink() {
- const { project, id } = this.packageEntity;
- return `${project?.webUrl}/-/packages/${getIdFromGraphQLId(id)}`;
+ packageId() {
+ return getIdFromGraphQLId(this.packageEntity.id);
},
pipeline() {
return this.packageEntity?.pipelines?.nodes[0];
@@ -61,6 +59,9 @@ export default {
disabledRow() {
return this.packageEntity.status && this.packageEntity.status !== PACKAGE_DEFAULT_STATUS;
},
+ routerLinkEvent() {
+ return this.disabledRow ? '' : 'click';
+ },
},
i18n: {
erroredPackageText: s__('PackageRegistry|Invalid Package: failed metadata extraction'),
@@ -73,14 +74,15 @@ export default {
<list-item data-qa-selector="package_row" :disabled="disabledRow">
<template #left-primary>
<div class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0">
- <gl-link
- :href="packageLink"
+ <router-link
class="gl-text-body gl-min-w-0"
+ data-testid="details-link"
data-qa-selector="package_link"
- :disabled="disabledRow"
+ :event="routerLinkEvent"
+ :to="{ name: 'details', params: { id: packageId } }"
>
<gl-truncate :text="packageEntity.name" />
- </gl-link>
+ </router-link>
<gl-button
v-if="showWarningIcon"
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/constants.js b/app/assets/javascripts/packages_and_registries/package_registry/constants.js
index af4e586231c..c4d331fa384 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/constants.js
+++ b/app/assets/javascripts/packages_and_registries/package_registry/constants.js
@@ -74,6 +74,7 @@ export const FETCH_PACKAGE_DETAILS_ERROR_MESSAGE = s__(
);
export const DELETE_PACKAGE_SUCCESS_MESSAGE = s__('PackageRegistry|Package deleted successfully');
+export const PACKAGE_REGISTRY_TITLE = __('Package Registry');
export const PACKAGE_ERROR_STATUS = 'ERROR';
export const PACKAGE_DEFAULT_STATUS = 'DEFAULT';
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/index.js b/app/assets/javascripts/packages_and_registries/package_registry/index.js
index 7ec931ff9a0..6680e612985 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/index.js
+++ b/app/assets/javascripts/packages_and_registries/package_registry/index.js
@@ -2,29 +2,59 @@ import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import { apolloProvider } from '~/packages_and_registries/package_registry/graphql/index';
import PackageRegistry from '~/packages_and_registries/package_registry/pages/index.vue';
+import RegistryBreadcrumb from '~/packages_and_registries/shared/components/registry_breadcrumb.vue';
+import { renderBreadcrumb } from '~/packages_and_registries/shared/utils';
import createRouter from './router';
Vue.use(Translate);
export default () => {
const el = document.getElementById('js-vue-packages-list');
- const { endpoint, resourceId, fullPath, pageType, emptyListIllustration } = el.dataset;
- const router = createRouter(endpoint);
+ const {
+ endpoint,
+ resourceId,
+ fullPath,
+ pageType,
+ emptyListIllustration,
+ npmInstanceUrl,
+ projectListUrl,
+ groupListUrl,
+ } = el.dataset;
const isGroupPage = pageType === 'groups';
- return new Vue({
- el,
- router,
- apolloProvider,
- provide: {
- resourceId,
- fullPath,
- emptyListIllustration,
- isGroupPage,
- },
- render(createElement) {
- return createElement(PackageRegistry);
+ // This is a mini state to help the breadcrumb have the correct name in the details page
+ const breadCrumbState = Vue.observable({
+ name: '',
+ updateName(value) {
+ this.name = value;
},
});
+
+ const router = createRouter(endpoint, breadCrumbState);
+
+ const attachMainComponent = () =>
+ new Vue({
+ el,
+ router,
+ apolloProvider,
+ provide: {
+ resourceId,
+ fullPath,
+ emptyListIllustration,
+ isGroupPage,
+ npmInstanceUrl,
+ projectListUrl,
+ groupListUrl,
+ breadCrumbState,
+ },
+ render(createElement) {
+ return createElement(PackageRegistry);
+ },
+ });
+
+ return {
+ attachBreadcrumb: renderBreadcrumb(router, apolloProvider, RegistryBreadcrumb),
+ attachMainComponent,
+ };
};
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/pages/details.js b/app/assets/javascripts/packages_and_registries/package_registry/pages/details.js
deleted file mode 100644
index d94bbd21035..00000000000
--- a/app/assets/javascripts/packages_and_registries/package_registry/pages/details.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import Vue from 'vue';
-import { parseBoolean } from '~/lib/utils/common_utils';
-import PackagesApp from '~/packages_and_registries/package_registry/components/details/app.vue';
-import { apolloProvider } from '~/packages_and_registries/package_registry/graphql/index';
-import Translate from '~/vue_shared/translate';
-
-Vue.use(Translate);
-
-export default () => {
- const el = document.getElementById('js-vue-packages-detail-new');
- if (!el) {
- return null;
- }
-
- const { canDelete, ...datasetOptions } = el.dataset;
- return new Vue({
- el,
- apolloProvider,
- provide: {
- canDelete: parseBoolean(canDelete),
- ...datasetOptions,
- },
- render(createElement) {
- return createElement(PackagesApp);
- },
- });
-};
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/app.vue b/app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue
index 52fea4bebbe..162b420a784 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/app.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue
@@ -68,7 +68,7 @@ export default {
GlModal: GlModalDirective,
},
mixins: [Tracking.mixin()],
- inject: ['packageId', 'svgPath', 'projectListUrl', 'groupListUrl'],
+ inject: ['emptyListIllustration', 'projectListUrl', 'groupListUrl', 'breadCrumbState'],
trackingActions: {
DELETE_PACKAGE_TRACKING_ACTION,
REQUEST_DELETE_PACKAGE_TRACKING_ACTION,
@@ -100,12 +100,20 @@ export default {
error,
});
},
+ result() {
+ this.breadCrumbState.updateName(
+ `${this.packageEntity?.name} v ${this.packageEntity?.version}`,
+ );
+ },
},
},
computed: {
projectName() {
return this.packageEntity.project?.name;
},
+ packageId() {
+ return this.$route.params.id;
+ },
queryVariables() {
return {
id: convertToGraphQLId('Packages::Package', this.packageId),
@@ -229,7 +237,7 @@ export default {
v-if="!isValidPackage"
:title="s__('PackageRegistry|Unable to load package')"
:description="s__('PackageRegistry|There was a problem fetching the details for this package.')"
- :svg-path="svgPath"
+ :svg-path="emptyListIllustration"
/>
<div v-else-if="!isLoading" class="packages-app">
<package-title :package-entity="packageEntity">
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/router.js b/app/assets/javascripts/packages_and_registries/package_registry/router.js
index ea5b740e879..c5ef4f70dd9 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/router.js
+++ b/app/assets/javascripts/packages_and_registries/package_registry/router.js
@@ -1,10 +1,12 @@
import Vue from 'vue';
import VueRouter from 'vue-router';
import List from '~/packages_and_registries/package_registry/pages/list.vue';
+import Details from '~/packages_and_registries/package_registry/pages/details.vue';
+import { PACKAGE_REGISTRY_TITLE } from '~/packages_and_registries/package_registry/constants';
Vue.use(VueRouter);
-export default function createRouter(base) {
+export default function createRouter(base, breadCrumbState) {
const router = new VueRouter({
base,
mode: 'history',
@@ -13,9 +15,25 @@ export default function createRouter(base) {
name: 'list',
path: '/',
component: List,
+ meta: {
+ nameGenerator: () => PACKAGE_REGISTRY_TITLE,
+ root: true,
+ },
+ },
+ {
+ name: 'details',
+ path: '/:id',
+ component: Details,
+ meta: {
+ nameGenerator: () => breadCrumbState.name,
+ },
},
],
});
+ router.afterEach(() => {
+ breadCrumbState.updateName('');
+ });
+
return router;
}
diff --git a/app/assets/javascripts/packages_and_registries/shared/components/registry_breadcrumb.vue b/app/assets/javascripts/packages_and_registries/shared/components/registry_breadcrumb.vue
index e77eda31596..a1e3c06812c 100644
--- a/app/assets/javascripts/packages_and_registries/shared/components/registry_breadcrumb.vue
+++ b/app/assets/javascripts/packages_and_registries/shared/components/registry_breadcrumb.vue
@@ -20,8 +20,11 @@ export default {
isRootRoute() {
return this.$route.name === this.rootRoute.name;
},
+ detailsRouteName() {
+ return this.detailsRoute.meta.nameGenerator();
+ },
isLoaded() {
- return this.isRootRoute || this.$store?.state.imageDetails?.name;
+ return this.isRootRoute || this.detailsRouteName;
},
allCrumbs() {
const crumbs = [
@@ -32,7 +35,7 @@ export default {
];
if (!this.isRootRoute) {
crumbs.push({
- text: this.detailsRoute.meta.nameGenerator(),
+ text: this.detailsRouteName,
href: this.detailsRoute.meta.path,
});
}
@@ -45,7 +48,9 @@ export default {
<template>
<gl-breadcrumb :key="isLoaded" :items="allCrumbs">
<template #separator>
- <gl-icon name="angle-right" :size="8" />
+ <span class="gl-mx-n5">
+ <gl-icon name="angle-right" :size="8" />
+ </span>
</template>
</gl-breadcrumb>
</template>
diff --git a/app/assets/javascripts/packages_and_registries/shared/utils.js b/app/assets/javascripts/packages_and_registries/shared/utils.js
index cf18f655e79..7e963cd0b08 100644
--- a/app/assets/javascripts/packages_and_registries/shared/utils.js
+++ b/app/assets/javascripts/packages_and_registries/shared/utils.js
@@ -1,3 +1,4 @@
+import Vue from 'vue';
import { queryToObject } from '~/lib/utils/url_utility';
import { FILTERED_SEARCH_TERM } from './constants';
@@ -38,3 +39,37 @@ export const getCommitLink = ({ project_path: projectPath, pipeline = {} }, isGr
return `../commit/${pipeline.sha}`;
};
+
+export const renderBreadcrumb = (router, apolloProvider, RegistryBreadcrumb) => () => {
+ const breadCrumbEls = document.querySelectorAll('nav .js-breadcrumbs-list li');
+ const breadCrumbEl = breadCrumbEls[breadCrumbEls.length - 1];
+ const lastCrumb = breadCrumbEl.children[0];
+ const crumbs = [lastCrumb];
+ const nestedBreadcrumbEl = document.createElement('div');
+ breadCrumbEl.replaceChild(nestedBreadcrumbEl, lastCrumb);
+ return new Vue({
+ el: nestedBreadcrumbEl,
+ router,
+ apolloProvider,
+ components: {
+ RegistryBreadcrumb,
+ },
+ render(createElement) {
+ // FIXME(@tnir): this is a workaround until the MR gets merged:
+ // https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48115
+ const parentEl = breadCrumbEl.parentElement.parentElement;
+ if (parentEl) {
+ parentEl.classList.remove('breadcrumbs-container');
+ parentEl.classList.add('gl-display-flex');
+ parentEl.classList.add('w-100');
+ }
+ // End of FIXME(@tnir)
+ return createElement('registry-breadcrumb', {
+ class: breadCrumbEl.className,
+ props: {
+ crumbs,
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/pages/groups/packages/index.js b/app/assets/javascripts/pages/groups/packages/index.js
new file mode 100644
index 00000000000..cbe08565cfa
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/packages/index.js
@@ -0,0 +1,8 @@
+import packageApp from '~/packages_and_registries/package_registry/index';
+
+const app = packageApp();
+
+if (app) {
+ app.attachBreadcrumb();
+ app.attachMainComponent();
+}
diff --git a/app/assets/javascripts/pages/groups/packages/index/index.js b/app/assets/javascripts/pages/groups/packages/index/index.js
deleted file mode 100644
index 174973a9fad..00000000000
--- a/app/assets/javascripts/pages/groups/packages/index/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import packageApp from '~/packages_and_registries/package_registry/index';
-
-packageApp();
diff --git a/app/assets/javascripts/pages/projects/packages/packages/index.js b/app/assets/javascripts/pages/projects/packages/packages/index.js
new file mode 100644
index 00000000000..cbe08565cfa
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/packages/packages/index.js
@@ -0,0 +1,8 @@
+import packageApp from '~/packages_and_registries/package_registry/index';
+
+const app = packageApp();
+
+if (app) {
+ app.attachBreadcrumb();
+ app.attachMainComponent();
+}
diff --git a/app/assets/javascripts/pages/projects/packages/packages/index/index.js b/app/assets/javascripts/pages/projects/packages/packages/index/index.js
deleted file mode 100644
index 174973a9fad..00000000000
--- a/app/assets/javascripts/pages/projects/packages/packages/index/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import packageApp from '~/packages_and_registries/package_registry/index';
-
-packageApp();
diff --git a/app/assets/javascripts/pages/projects/packages/packages/show/index.js b/app/assets/javascripts/pages/projects/packages/packages/show/index.js
deleted file mode 100644
index 2dee87985cb..00000000000
--- a/app/assets/javascripts/pages/projects/packages/packages/show/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import initPackageDetails from '~/packages_and_registries/package_registry/pages/details';
-
-initPackageDetails();
diff --git a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue
index 3366942f6ad..bb2bac531a7 100644
--- a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue
+++ b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue
@@ -9,7 +9,7 @@ import RegistrationDropdown from '../components/registration/registration_dropdo
import RunnerFilteredSearchBar from '../components/runner_filtered_search_bar.vue';
import RunnerList from '../components/runner_list.vue';
import RunnerName from '../components/runner_name.vue';
-import RunnerOnlineStat from '../components/stat/runner_online_stat.vue';
+import RunnerStats from '../components/stat/runner_stats.vue';
import RunnerPagination from '../components/runner_pagination.vue';
import RunnerTypeTabs from '../components/runner_type_tabs.vue';
@@ -20,6 +20,9 @@ import {
INSTANCE_TYPE,
GROUP_TYPE,
PROJECT_TYPE,
+ STATUS_ONLINE,
+ STATUS_OFFLINE,
+ STATUS_STALE,
I18N_FETCH_ERROR,
} from '../constants';
import getRunnersQuery from '../graphql/get_runners.query.graphql';
@@ -51,7 +54,7 @@ export default {
RunnerFilteredSearchBar,
RunnerList,
RunnerName,
- RunnerOnlineStat,
+ RunnerStats,
RunnerPagination,
RunnerTypeTabs,
},
@@ -60,10 +63,6 @@ export default {
type: String,
required: true,
},
- activeRunnersCount: {
- type: String,
- required: true,
- },
},
data() {
return {
@@ -130,6 +129,30 @@ export default {
};
},
},
+ onlineRunnersTotal: {
+ ...runnersCountSmartQuery,
+ variables() {
+ return {
+ status: STATUS_ONLINE,
+ };
+ },
+ },
+ offlineRunnersTotal: {
+ ...runnersCountSmartQuery,
+ variables() {
+ return {
+ status: STATUS_OFFLINE,
+ };
+ },
+ },
+ staleRunnersTotal: {
+ ...runnersCountSmartQuery,
+ variables() {
+ return {
+ status: STATUS_STALE,
+ };
+ },
+ },
},
computed: {
variables() {
@@ -205,7 +228,11 @@ export default {
</script>
<template>
<div>
- <runner-online-stat class="gl-py-6 gl-px-5" :value="activeRunnersCount" />
+ <runner-stats
+ :online-runners-count="onlineRunnersTotal"
+ :offline-runners-count="offlineRunnersTotal"
+ :stale-runners-count="staleRunnersTotal"
+ />
<div
class="gl-display-flex gl-align-items-center gl-flex-direction-column-reverse gl-md-flex-direction-row gl-mt-3 gl-md-mt-0"
diff --git a/app/assets/javascripts/runner/admin_runners/index.js b/app/assets/javascripts/runner/admin_runners/index.js
index 5ca87aa7a68..3b8a8fe9cd1 100644
--- a/app/assets/javascripts/runner/admin_runners/index.js
+++ b/app/assets/javascripts/runner/admin_runners/index.js
@@ -25,9 +25,7 @@ export const initAdminRunners = (selector = '#js-admin-runners') => {
return null;
}
- // TODO `activeRunnersCount` should be implemented using a GraphQL API
- // https://gitlab.com/gitlab-org/gitlab/-/issues/333806
- const { runnerInstallHelpPage, registrationToken, activeRunnersCount } = el.dataset;
+ const { runnerInstallHelpPage, registrationToken } = el.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
@@ -43,10 +41,6 @@ export const initAdminRunners = (selector = '#js-admin-runners') => {
return h(AdminRunnersApp, {
props: {
registrationToken,
-
- // Runner counts are returned as formatted
- // strings, we do not use `parseInt`.
- activeRunnersCount,
},
});
},
diff --git a/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue b/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue
index edcbcb2bf69..0e259807f98 100644
--- a/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue
+++ b/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue
@@ -1,5 +1,5 @@
<script>
-import { GlDropdownItem, GlLoadingIcon } from '@gitlab/ui';
+import { GlDropdownItem, GlLoadingIcon, GlModal, GlModalDirective } from '@gitlab/ui';
import { createAlert } from '~/flash';
import { TYPE_GROUP, TYPE_PROJECT } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
@@ -10,9 +10,17 @@ import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '../../constants';
export default {
name: 'RunnerRegistrationTokenReset',
+ i18n: {
+ modalTitle: __('Reset registration token'),
+ modalCopy: __('Are you sure you want to reset the registration token?'),
+ },
components: {
GlDropdownItem,
GlLoadingIcon,
+ GlModal,
+ },
+ directives: {
+ GlModal: GlModalDirective,
},
inject: {
groupId: {
@@ -22,6 +30,7 @@ export default {
default: null,
},
},
+ modalID: 'token-reset-modal',
props: {
type: {
type: String,
@@ -59,14 +68,10 @@ export default {
},
},
methods: {
+ handleModalPrimary() {
+ this.resetToken();
+ },
async resetToken() {
- // TODO Replace confirmation with gl-modal
- // See: https://gitlab.com/gitlab-org/gitlab/-/issues/333810
- // eslint-disable-next-line no-alert
- if (!window.confirm(__('Are you sure you want to reset the registration token?'))) {
- return;
- }
-
this.loading = true;
try {
const {
@@ -106,8 +111,15 @@ export default {
};
</script>
<template>
- <gl-dropdown-item @click.capture.native.stop="resetToken">
+ <gl-dropdown-item v-gl-modal="$options.modalID">
{{ __('Reset registration token') }}
+ <gl-modal
+ :modal-id="$options.modalID"
+ :title="$options.i18n.modalTitle"
+ @primary="handleModalPrimary"
+ >
+ <p>{{ $options.i18n.modalCopy }}</p>
+ </gl-modal>
<gl-loading-icon v-if="loading" inline />
</gl-dropdown-item>
</template>
diff --git a/app/assets/javascripts/runner/components/stat/runner_online_stat.vue b/app/assets/javascripts/runner/components/stat/runner_online_stat.vue
deleted file mode 100644
index b92b9badef0..00000000000
--- a/app/assets/javascripts/runner/components/stat/runner_online_stat.vue
+++ /dev/null
@@ -1,17 +0,0 @@
-<script>
-import { GlSingleStat } from '@gitlab/ui/dist/charts';
-
-export default {
- components: {
- GlSingleStat,
- },
-};
-</script>
-<template>
- <gl-single-stat
- v-bind="$attrs"
- variant="success"
- :title="s__('Runners|Online Runners')"
- :meta-text="s__('Runners|online')"
- />
-</template>
diff --git a/app/assets/javascripts/runner/components/stat/runner_stats.vue b/app/assets/javascripts/runner/components/stat/runner_stats.vue
new file mode 100644
index 00000000000..d3693ee593e
--- /dev/null
+++ b/app/assets/javascripts/runner/components/stat/runner_stats.vue
@@ -0,0 +1,49 @@
+<script>
+import { STATUS_ONLINE, STATUS_OFFLINE, STATUS_STALE } from '../../constants';
+import RunnerStatusStat from './runner_status_stat.vue';
+
+export default {
+ components: {
+ RunnerStatusStat,
+ },
+ props: {
+ onlineRunnersCount: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ offlineRunnersCount: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ staleRunnersCount: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ },
+ STATUS_ONLINE,
+ STATUS_OFFLINE,
+ STATUS_STALE,
+};
+</script>
+<template>
+ <div class="gl-display-flex gl-py-6">
+ <runner-status-stat
+ class="gl-px-5"
+ :status="$options.STATUS_ONLINE"
+ :value="onlineRunnersCount"
+ />
+ <runner-status-stat
+ class="gl-px-5"
+ :status="$options.STATUS_OFFLINE"
+ :value="offlineRunnersCount"
+ />
+ <runner-status-stat
+ class="gl-px-5"
+ :status="$options.STATUS_STALE"
+ :value="staleRunnersCount"
+ />
+ </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
new file mode 100644
index 00000000000..b77bbe15541
--- /dev/null
+++ b/app/assets/javascripts/runner/components/stat/runner_status_stat.vue
@@ -0,0 +1,65 @@
+<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>
diff --git a/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql b/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql
index 6da9e276f74..f7bcd683718 100644
--- a/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql
+++ b/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql
@@ -13,7 +13,7 @@ query getGroupRunners(
$sort: CiRunnerSort
) {
group(fullPath: $groupFullPath) {
- id
+ id # Apollo required
runners(
membership: DESCENDANTS
before: $before
diff --git a/app/assets/javascripts/runner/graphql/get_group_runners_count.query.graphql b/app/assets/javascripts/runner/graphql/get_group_runners_count.query.graphql
new file mode 100644
index 00000000000..554eb09e372
--- /dev/null
+++ b/app/assets/javascripts/runner/graphql/get_group_runners_count.query.graphql
@@ -0,0 +1,20 @@
+query getGroupRunnersCount(
+ $groupFullPath: ID!
+ $status: CiRunnerStatus
+ $type: CiRunnerType
+ $tagList: [String!]
+ $search: String
+) {
+ group(fullPath: $groupFullPath) {
+ id # Apollo required
+ runners(
+ membership: DESCENDANTS
+ status: $status
+ type: $type
+ tagList: $tagList
+ search: $search
+ ) {
+ count
+ }
+ }
+}
diff --git a/app/assets/javascripts/runner/group_runners/group_runners_app.vue b/app/assets/javascripts/runner/group_runners/group_runners_app.vue
index f33f28c11e3..3a7b58e3dc9 100644
--- a/app/assets/javascripts/runner/group_runners/group_runners_app.vue
+++ b/app/assets/javascripts/runner/group_runners/group_runners_app.vue
@@ -9,7 +9,7 @@ import RegistrationDropdown from '../components/registration/registration_dropdo
import RunnerFilteredSearchBar from '../components/runner_filtered_search_bar.vue';
import RunnerList from '../components/runner_list.vue';
import RunnerName from '../components/runner_name.vue';
-import RunnerOnlineStat from '../components/stat/runner_online_stat.vue';
+import RunnerStats from '../components/stat/runner_stats.vue';
import RunnerPagination from '../components/runner_pagination.vue';
import RunnerTypeTabs from '../components/runner_type_tabs.vue';
@@ -19,8 +19,12 @@ import {
GROUP_FILTERED_SEARCH_NAMESPACE,
GROUP_TYPE,
GROUP_RUNNER_COUNT_LIMIT,
+ STATUS_ONLINE,
+ STATUS_OFFLINE,
+ STATUS_STALE,
} from '../constants';
import getGroupRunnersQuery from '../graphql/get_group_runners.query.graphql';
+import getGroupRunnersCountQuery from '../graphql/get_group_runners_count.query.graphql';
import {
fromUrlQueryToSearch,
fromSearchToUrl,
@@ -28,6 +32,17 @@ import {
} from '../runner_search_utils';
import { captureException } from '../sentry_utils';
+const runnersCountSmartQuery = {
+ query: getGroupRunnersCountQuery,
+ fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
+ update(data) {
+ return data?.group?.runners?.count;
+ },
+ error(error) {
+ this.reportToSentry(error);
+ },
+};
+
export default {
name: 'GroupRunnersApp',
components: {
@@ -36,7 +51,7 @@ export default {
RunnerFilteredSearchBar,
RunnerList,
RunnerName,
- RunnerOnlineStat,
+ RunnerStats,
RunnerPagination,
RunnerTypeTabs,
},
@@ -89,6 +104,33 @@ export default {
this.reportToSentry(error);
},
},
+ onlineRunnersTotal: {
+ ...runnersCountSmartQuery,
+ variables() {
+ return {
+ groupFullPath: this.groupFullPath,
+ status: STATUS_ONLINE,
+ };
+ },
+ },
+ offlineRunnersTotal: {
+ ...runnersCountSmartQuery,
+ variables() {
+ return {
+ groupFullPath: this.groupFullPath,
+ status: STATUS_OFFLINE,
+ };
+ },
+ },
+ staleRunnersTotal: {
+ ...runnersCountSmartQuery,
+ variables() {
+ return {
+ groupFullPath: this.groupFullPath,
+ status: STATUS_STALE,
+ };
+ },
+ },
},
computed: {
variables() {
@@ -147,7 +189,11 @@ export default {
<template>
<div>
- <runner-online-stat class="gl-py-6 gl-px-5" :value="groupRunnersCount" />
+ <runner-stats
+ :online-runners-count="onlineRunnersTotal"
+ :offline-runners-count="offlineRunnersTotal"
+ :stale-runners-count="staleRunnersTotal"
+ />
<div class="gl-display-flex gl-align-items-center">
<runner-type-tabs