Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 16:37:47 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-12-20 16:37:47 +0300
commitaee0a117a889461ce8ced6fcf73207fe017f1d99 (patch)
tree891d9ef189227a8445d83f35c1b0fc99573f4380 /app/assets/javascripts/projects
parent8d46af3258650d305f53b819eabf7ab18d22f59e (diff)
Add latest changes from gitlab-org/gitlab@14-6-stable-eev14.6.0-rc42
Diffstat (limited to 'app/assets/javascripts/projects')
-rw-r--r--app/assets/javascripts/projects/commit/constants.js2
-rw-r--r--app/assets/javascripts/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql6
-rw-r--r--app/assets/javascripts/projects/new/components/new_project_url_select.vue34
-rw-r--r--app/assets/javascripts/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql1
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/components/app.vue25
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue17
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql1
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql1
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/index.js5
-rw-r--r--app/assets/javascripts/projects/settings/components/transfer_project_form.vue63
-rw-r--r--app/assets/javascripts/projects/settings/init_transfer_project_form.js53
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue201
-rw-r--r--app/assets/javascripts/projects/storage_counter/components/app.vue106
-rw-r--r--app/assets/javascripts/projects/storage_counter/components/storage_table.vue88
-rw-r--r--app/assets/javascripts/projects/storage_counter/components/storage_type_icon.vue35
-rw-r--r--app/assets/javascripts/projects/storage_counter/constants.js61
-rw-r--r--app/assets/javascripts/projects/storage_counter/index.js51
-rw-r--r--app/assets/javascripts/projects/storage_counter/queries/project_storage.query.graphql16
-rw-r--r--app/assets/javascripts/projects/storage_counter/utils.js36
19 files changed, 306 insertions, 496 deletions
diff --git a/app/assets/javascripts/projects/commit/constants.js b/app/assets/javascripts/projects/commit/constants.js
index d553bca360e..eb3673461bd 100644
--- a/app/assets/javascripts/projects/commit/constants.js
+++ b/app/assets/javascripts/projects/commit/constants.js
@@ -11,7 +11,7 @@ export const I18N_MODAL = {
'ChangeTypeAction|Your changes will be committed to %{branchName} because a merge request is open.',
),
branchInFork: s__(
- 'ChangeTypeAction|A new branch will be created in your fork and a new merge request will be started.',
+ 'ChangeTypeAction|GitLab will create a branch in your fork and start a merge request.',
),
newMergeRequest: __('new merge request'),
actionCancelText: __('Cancel'),
diff --git a/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql b/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql
index ee18c70b6fd..c6a0d48626a 100644
--- a/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql
+++ b/app/assets/javascripts/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql
@@ -1,15 +1,19 @@
query getLinkedPipelines($fullPath: ID!, $iid: ID!) {
project(fullPath: $fullPath) {
+ id
pipeline(iid: $iid) {
+ id
path
downstream {
nodes {
id
path
project {
+ id
name
}
detailedStatus {
+ id
group
icon
label
@@ -20,9 +24,11 @@ query getLinkedPipelines($fullPath: ID!, $iid: ID!) {
id
path
project {
+ id
name
}
detailedStatus {
+ id
group
icon
label
diff --git a/app/assets/javascripts/projects/new/components/new_project_url_select.vue b/app/assets/javascripts/projects/new/components/new_project_url_select.vue
index e0ba60074af..f4a21c6057c 100644
--- a/app/assets/javascripts/projects/new/components/new_project_url_select.vue
+++ b/app/assets/javascripts/projects/new/components/new_project_url_select.vue
@@ -8,7 +8,7 @@ import {
GlDropdownSectionHeader,
GlSearchBoxByType,
} from '@gitlab/ui';
-import { joinPaths } from '~/lib/utils/url_utility';
+import { joinPaths, PATH_SEPARATOR } from '~/lib/utils/url_utility';
import { MINIMUM_SEARCH_LENGTH } from '~/graphql_shared/constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import Tracking from '~/tracking';
@@ -36,7 +36,9 @@ export default {
};
},
skip() {
- return this.search.length > 0 && this.search.length < MINIMUM_SEARCH_LENGTH;
+ const hasNotEnoughSearchCharacters =
+ this.search.length > 0 && this.search.length < MINIMUM_SEARCH_LENGTH;
+ return this.shouldSkipQuery || hasNotEnoughSearchCharacters;
},
debounce: DEBOUNCE_DELAY,
},
@@ -52,7 +54,7 @@ export default {
data() {
return {
currentUser: {},
- groupToFilterBy: undefined,
+ groupPathToFilterBy: undefined,
search: '',
selectedNamespace: this.namespaceId
? {
@@ -63,6 +65,7 @@ export default {
id: this.userNamespaceId,
fullPath: this.userNamespaceFullPath,
},
+ shouldSkipQuery: true,
};
},
computed: {
@@ -73,10 +76,8 @@ export default {
return this.currentUser.namespace || {};
},
filteredGroups() {
- return this.groupToFilterBy
- ? this.userGroups.filter((group) =>
- group.fullPath.startsWith(this.groupToFilterBy.fullPath),
- )
+ return this.groupPathToFilterBy
+ ? this.userGroups.filter((group) => group.fullPath.startsWith(this.groupPathToFilterBy))
: this.userGroups;
},
hasGroupMatches() {
@@ -85,7 +86,7 @@ export default {
hasNamespaceMatches() {
return (
this.userNamespace.fullPath?.toLowerCase().includes(this.search.toLowerCase()) &&
- !this.groupToFilterBy
+ !this.groupPathToFilterBy
);
},
hasNoMatches() {
@@ -99,7 +100,10 @@ export default {
eventHub.$off('select-template', this.handleSelectTemplate);
},
methods: {
- focusInput() {
+ handleDropdownShown() {
+ if (this.shouldSkipQuery) {
+ this.shouldSkipQuery = false;
+ }
this.$refs.search.focusInput();
},
handleDropdownItemClick(namespace) {
@@ -111,13 +115,9 @@ export default {
});
this.setNamespace(namespace);
},
- handleSelectTemplate(groupId) {
- this.groupToFilterBy = this.userGroups.find(
- (group) => getIdFromGraphQLId(group.id) === groupId,
- );
- if (this.groupToFilterBy) {
- this.setNamespace(this.groupToFilterBy);
- }
+ handleSelectTemplate(id, fullPath) {
+ this.groupPathToFilterBy = fullPath.split(PATH_SEPARATOR).shift();
+ this.setNamespace({ id, fullPath });
},
setNamespace({ id, fullPath }) {
this.selectedNamespace = {
@@ -137,7 +137,7 @@ export default {
toggle-class="gl-rounded-top-right-base! gl-rounded-bottom-right-base! gl-w-20"
data-qa-selector="select_namespace_dropdown"
@show="track('activate_form_input', { label: trackLabel, property: 'project_path' })"
- @shown="focusInput"
+ @shown="handleDropdownShown"
>
<gl-search-box-by-type
ref="search"
diff --git a/app/assets/javascripts/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql b/app/assets/javascripts/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql
index 74febec5a51..568e05d1966 100644
--- a/app/assets/javascripts/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql
+++ b/app/assets/javascripts/projects/new/queries/search_namespaces_where_user_can_create_projects.query.graphql
@@ -1,5 +1,6 @@
query searchNamespacesWhereUserCanCreateProjects($search: String) {
currentUser {
+ id
groups(permissionScope: CREATE_PROJECTS, search: $search) {
nodes {
id
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/app.vue b/app/assets/javascripts/projects/pipelines/charts/components/app.vue
index 7379d5caed7..d4b1f7e57d8 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/app.vue
+++ b/app/assets/javascripts/projects/pipelines/charts/components/app.vue
@@ -1,5 +1,6 @@
<script>
import { GlTabs, GlTab } from '@gitlab/ui';
+import API from '~/api';
import { mergeUrlParams, updateHistory, getParameterValues } from '~/lib/utils/url_utility';
import PipelineCharts from './pipeline_charts.vue';
@@ -13,6 +14,9 @@ export default {
LeadTimeCharts: () => import('ee_component/dora/components/lead_time_charts.vue'),
ProjectQualitySummary: () => import('ee_component/project_quality_summary/app.vue'),
},
+ piplelinesTabEvent: 'p_analytics_ci_cd_pipelines',
+ deploymentFrequencyTabEvent: 'p_analytics_ci_cd_deployment_frequency',
+ leadTimeTabEvent: 'p_analytics_ci_cd_lead_time',
inject: {
shouldRenderDoraCharts: {
type: Boolean,
@@ -60,20 +64,35 @@ export default {
updateHistory({ url: path, title: window.title });
}
},
+ trackTabClick(tab) {
+ API.trackRedisHllUserEvent(tab);
+ },
},
};
</script>
<template>
<div>
<gl-tabs v-if="charts.length > 1" :value="selectedTab" @input="onTabChange">
- <gl-tab :title="__('Pipelines')">
+ <gl-tab
+ :title="__('Pipelines')"
+ data-testid="pipelines-tab"
+ @click="trackTabClick($options.piplelinesTabEvent)"
+ >
<pipeline-charts />
</gl-tab>
<template v-if="shouldRenderDoraCharts">
- <gl-tab :title="__('Deployment frequency')">
+ <gl-tab
+ :title="__('Deployment frequency')"
+ data-testid="deployment-frequency-tab"
+ @click="trackTabClick($options.deploymentFrequencyTabEvent)"
+ >
<deployment-frequency-charts />
</gl-tab>
- <gl-tab :title="__('Lead time')">
+ <gl-tab
+ :title="__('Lead time')"
+ data-testid="lead-time-tab"
+ @click="trackTabClick($options.leadTimeTabEvent)"
+ >
<lead-time-charts />
</gl-tab>
</template>
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue b/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue
index 7bc3b787f75..5383a6cdddf 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue
+++ b/app/assets/javascripts/projects/pipelines/charts/components/statistics_list.vue
@@ -1,10 +1,19 @@
<script>
+import { GlLink } from '@gitlab/ui';
import { SUPPORTED_FORMATS, getFormatter } from '~/lib/utils/unit_format';
import { s__, n__ } from '~/locale';
const defaultPrecision = 2;
export default {
+ components: {
+ GlLink,
+ },
+ inject: {
+ failedPipelinesLink: {
+ default: '',
+ },
+ },
props: {
counts: {
type: Object,
@@ -27,6 +36,7 @@ export default {
{
title: s__('PipelineCharts|Failed:'),
value: n__('1 pipeline', '%d pipelines', this.counts.failed),
+ link: this.failedPipelinesLink,
},
{
title: s__('PipelineCharts|Success ratio:'),
@@ -39,10 +49,13 @@ export default {
</script>
<template>
<ul>
- <template v-for="({ title, value }, index) in statistics">
+ <template v-for="({ title, value, link }, index) in statistics">
<li :key="index">
<span>{{ title }}</span>
- <strong>{{ value }}</strong>
+ <gl-link v-if="link" :href="link">
+ {{ value }}
+ </gl-link>
+ <strong v-else>{{ value }}</strong>
</li>
</template>
</ul>
diff --git a/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql b/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql
index d68df689f5f..ac7fe51384c 100644
--- a/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql
+++ b/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_pipeline_count_by_status.query.graphql
@@ -1,5 +1,6 @@
query getPipelineCountByStatus($projectPath: ID!) {
project(fullPath: $projectPath) {
+ id
totalPipelines: pipelines {
count
}
diff --git a/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql b/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql
index 18b645f8831..46e8a6dc87d 100644
--- a/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql
+++ b/app/assets/javascripts/projects/pipelines/charts/graphql/queries/get_project_pipeline_statistics.query.graphql
@@ -1,5 +1,6 @@
query getProjectPipelineStatistics($projectPath: ID!) {
project(fullPath: $projectPath) {
+ id
pipelineAnalytics {
weekPipelinesTotals
weekPipelinesLabels
diff --git a/app/assets/javascripts/projects/pipelines/charts/index.js b/app/assets/javascripts/projects/pipelines/charts/index.js
index 003b61d94b1..94d32609e5d 100644
--- a/app/assets/javascripts/projects/pipelines/charts/index.js
+++ b/app/assets/javascripts/projects/pipelines/charts/index.js
@@ -11,7 +11,7 @@ const apolloProvider = new VueApollo({
});
const mountPipelineChartsApp = (el) => {
- const { projectPath } = el.dataset;
+ const { projectPath, failedPipelinesLink, coverageChartPath, defaultBranch } = el.dataset;
const shouldRenderDoraCharts = parseBoolean(el.dataset.shouldRenderDoraCharts);
const shouldRenderQualitySummary = parseBoolean(el.dataset.shouldRenderQualitySummary);
@@ -25,8 +25,11 @@ const mountPipelineChartsApp = (el) => {
apolloProvider,
provide: {
projectPath,
+ failedPipelinesLink,
shouldRenderDoraCharts,
shouldRenderQualitySummary,
+ coverageChartPath,
+ defaultBranch,
},
render: (createElement) => createElement(ProjectPipelinesCharts, {}),
});
diff --git a/app/assets/javascripts/projects/settings/components/transfer_project_form.vue b/app/assets/javascripts/projects/settings/components/transfer_project_form.vue
new file mode 100644
index 00000000000..b98e1101884
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/components/transfer_project_form.vue
@@ -0,0 +1,63 @@
+<script>
+import { GlFormGroup } from '@gitlab/ui';
+import NamespaceSelect from '~/vue_shared/components/namespace_select/namespace_select.vue';
+import ConfirmDanger from '~/vue_shared/components/confirm_danger/confirm_danger.vue';
+
+export default {
+ name: 'TransferProjectForm',
+ components: {
+ GlFormGroup,
+ NamespaceSelect,
+ ConfirmDanger,
+ },
+ props: {
+ namespaces: {
+ type: Object,
+ required: true,
+ },
+ confirmationPhrase: {
+ type: String,
+ required: true,
+ },
+ confirmButtonText: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return { selectedNamespace: null };
+ },
+ computed: {
+ hasSelectedNamespace() {
+ return Boolean(this.selectedNamespace?.id);
+ },
+ },
+ methods: {
+ handleSelect(selectedNamespace) {
+ this.selectedNamespace = selectedNamespace;
+ this.$emit('selectNamespace', selectedNamespace.id);
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <gl-form-group>
+ <namespace-select
+ class="qa-namespaces-list"
+ data-testid="transfer-project-namespace"
+ :full-width="true"
+ :data="namespaces"
+ :selected-namespace="selectedNamespace"
+ @select="handleSelect"
+ />
+ </gl-form-group>
+ <confirm-danger
+ button-class="qa-transfer-button"
+ :disabled="!hasSelectedNamespace"
+ :phrase="confirmationPhrase"
+ :button-text="confirmButtonText"
+ @confirm="$emit('confirm')"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/projects/settings/init_transfer_project_form.js b/app/assets/javascripts/projects/settings/init_transfer_project_form.js
new file mode 100644
index 00000000000..47b49031dc9
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/init_transfer_project_form.js
@@ -0,0 +1,53 @@
+import Vue from 'vue';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import TransferProjectForm from './components/transfer_project_form.vue';
+
+const prepareNamespaces = (rawNamespaces = '') => {
+ const data = JSON.parse(rawNamespaces);
+ return {
+ group: data?.group.map(convertObjectPropsToCamelCase),
+ user: data?.user.map(convertObjectPropsToCamelCase),
+ };
+};
+
+export default () => {
+ const el = document.querySelector('.js-transfer-project-form');
+ if (!el) {
+ return false;
+ }
+
+ const {
+ targetFormId = null,
+ targetHiddenInputId = null,
+ buttonText: confirmButtonText = '',
+ phrase: confirmationPhrase = '',
+ confirmDangerMessage = '',
+ namespaces = '',
+ } = el.dataset;
+
+ return new Vue({
+ el,
+ provide: {
+ confirmDangerMessage,
+ },
+ render(createElement) {
+ return createElement(TransferProjectForm, {
+ props: {
+ confirmButtonText,
+ confirmationPhrase,
+ namespaces: prepareNamespaces(namespaces),
+ },
+ on: {
+ selectNamespace: (id) => {
+ if (targetHiddenInputId && document.getElementById(targetHiddenInputId)) {
+ document.getElementById(targetHiddenInputId).value = id;
+ }
+ },
+ confirm: () => {
+ if (targetFormId) document.getElementById(targetFormId)?.submit();
+ },
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue
index b8053bf9ab5..e5ddfe82e3b 100644
--- a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue
+++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue
@@ -1,5 +1,15 @@
<script>
-import { GlButton, GlToggle, GlLoadingIcon, GlSprintf, GlFormInput, GlLink } from '@gitlab/ui';
+import {
+ GlButton,
+ GlToggle,
+ GlLoadingIcon,
+ GlSprintf,
+ GlFormInputGroup,
+ GlFormGroup,
+ GlFormInput,
+ GlLink,
+} from '@gitlab/ui';
+import { helpPagePath } from '~/helpers/help_page_helper';
import { __ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import ServiceDeskTemplateDropdown from './service_desk_template_dropdown.vue';
@@ -15,6 +25,8 @@ export default {
GlLoadingIcon,
GlSprintf,
GlFormInput,
+ GlFormGroup,
+ GlFormInputGroup,
GlLink,
ServiceDeskTemplateDropdown,
},
@@ -88,6 +100,16 @@ export default {
hasCustomEmail() {
return this.customEmail && this.customEmail !== this.incomingEmail;
},
+ emailSuffixHelpUrl() {
+ return helpPagePath('user/project/service_desk.html', {
+ anchor: 'configuring-a-custom-email-address-suffix',
+ });
+ },
+ customEmailAddressHelpUrl() {
+ return helpPagePath('user/project/service_desk.html', {
+ anchor: 'using-a-custom-email-address',
+ });
+ },
},
methods: {
onCheckboxToggle(isChecked) {
@@ -132,101 +154,122 @@ export default {
</label>
<div v-if="isEnabled" class="row mt-3">
<div class="col-md-9 mb-0">
- <strong
- id="incoming-email-describer"
- class="gl-display-block gl-mb-1"
- data-testid="incoming-email-describer"
+ <gl-form-group
+ :label="__('Email address to use for Support Desk')"
+ label-for="incoming-email"
+ data-testid="incoming-email-label"
>
- {{ __('Email address to use for Support Desk') }}
- </strong>
- <template v-if="email">
- <div class="input-group">
- <input
+ <gl-form-input-group v-if="email">
+ <gl-form-input
+ id="incoming-email"
ref="service-desk-incoming-email"
type="text"
- class="form-control"
data-testid="incoming-email"
:placeholder="__('Incoming email')"
:aria-label="__('Incoming email')"
aria-describedby="incoming-email-describer"
:value="email"
- disabled="true"
+ :disabled="true"
/>
- <div class="input-group-append">
+ <template #append>
<clipboard-button :title="__('Copy')" :text="email" css-class="input-group-text" />
- </div>
- </div>
- <span v-if="hasCustomEmail" class="form-text text-muted">
- <gl-sprintf :message="__('Emails sent to %{email} are also supported.')">
- <template #email>
- <code>{{ incomingEmail }}</code>
+ </template>
+ </gl-form-input-group>
+ <template v-if="email && hasCustomEmail" #description>
+ <span class="gl-mt-2 d-inline-block">
+ <gl-sprintf :message="__('Emails sent to %{email} are also supported.')">
+ <template #email>
+ <code>{{ incomingEmail }}</code>
+ </template>
+ </gl-sprintf>
+ </span>
+ </template>
+ <template v-if="!email">
+ <gl-loading-icon size="sm" :inline="true" />
+ <span class="sr-only">{{ __('Fetching incoming email') }}</span>
+ </template>
+ </gl-form-group>
+
+ <gl-form-group :label="__('Email address suffix')" :state="!projectKeyError">
+ <gl-form-input
+ v-if="hasProjectKeySupport"
+ id="service-desk-project-suffix"
+ v-model.trim="projectKey"
+ data-testid="project-suffix"
+ @blur="validateProjectKey"
+ />
+
+ <template v-if="hasProjectKeySupport" #description>
+ <gl-sprintf
+ :message="
+ __('Add a suffix to Service Desk email address. %{linkStart}Learn more.%{linkEnd}')
+ "
+ >
+ <template #link="{ content }">
+ <gl-link
+ :href="emailSuffixHelpUrl"
+ target="_blank"
+ class="gl-text-blue-600 font-size-inherit"
+ >{{ content }}
+ </gl-link>
</template>
</gl-sprintf>
- </span>
- </template>
- <template v-else>
- <gl-loading-icon size="sm" :inline="true" />
- <span class="sr-only">{{ __('Fetching incoming email') }}</span>
- </template>
+ </template>
+ <template v-else #description>
+ <gl-sprintf
+ :message="
+ __(
+ 'To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}',
+ )
+ "
+ >
+ <template #link="{ content }">
+ <gl-link
+ :href="customEmailAddressHelpUrl"
+ target="_blank"
+ class="gl-text-blue-600 font-size-inherit"
+ >{{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </template>
+
+ <template v-if="hasProjectKeySupport && projectKeyError" #invalid-feedback>
+ {{ projectKeyError }}
+ </template>
+ </gl-form-group>
- <label for="service-desk-project-suffix" class="mt-3">
- {{ __('Project name suffix') }}
- </label>
- <gl-form-input
- v-if="hasProjectKeySupport"
- id="service-desk-project-suffix"
- v-model.trim="projectKey"
- data-testid="project-suffix"
- class="form-control"
+ <gl-form-group
+ :label="__('Template to append to all Service Desk issues')"
:state="!projectKeyError"
- @blur="validateProjectKey"
- />
- <span v-if="hasProjectKeySupport && projectKeyError" class="form-text text-danger">
- {{ projectKeyError }}
- </span>
- <span
- v-if="hasProjectKeySupport"
- class="form-text text-muted"
- :class="{ 'gl-mt-2!': hasProjectKeySupport && projectKeyError }"
+ class="mt-3"
>
- {{ __('A string appended to the project path to form the Service Desk email address.') }}
- </span>
- <span v-else class="form-text text-muted">
- <gl-sprintf
- :message="
- __(
- 'To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}',
- )
- "
- >
- <template #link="{ content }">
- <gl-link
- href="https://docs.gitlab.com/ee/user/project/service_desk.html#using-a-custom-email-address"
- target="_blank"
- class="gl-text-blue-600 font-size-inherit"
- >{{ content }}
- </gl-link>
- </template>
- </gl-sprintf>
- </span>
+ <service-desk-template-dropdown
+ :selected-template="selectedTemplate"
+ :selected-file-template-project-id="selectedFileTemplateProjectId"
+ :templates="templates"
+ @change="templateChange"
+ />
+ </gl-form-group>
+
+ <gl-form-group
+ :label="__('Email display name')"
+ label-for="service-desk-email-from-name"
+ :state="!projectKeyError"
+ class="mt-3"
+ >
+ <gl-form-input
+ v-if="hasProjectKeySupport"
+ id="service-desk-email-from-name"
+ v-model.trim="outgoingName"
+ data-testid="email-from-name"
+ />
- <label for="service-desk-template-select" class="mt-3">
- {{ __('Template to append to all Service Desk issues') }}
- </label>
- <service-desk-template-dropdown
- :selected-template="selectedTemplate"
- :selected-file-template-project-id="selectedFileTemplateProjectId"
- :templates="templates"
- @change="templateChange"
- />
+ <template v-if="hasProjectKeySupport" #description>
+ {{ __('Emails sent from Service Desk have this name.') }}
+ </template>
+ </gl-form-group>
- <label for="service-desk-email-from-name" class="mt-3">
- {{ __('Email display name') }}
- </label>
- <input id="service-desk-email-from-name" v-model.trim="outgoingName" class="form-control" />
- <span class="form-text text-muted">
- {{ __('Emails sent from Service Desk have this name.') }}
- </span>
<div class="gl-display-flex gl-justify-content-end">
<gl-button
variant="success"
diff --git a/app/assets/javascripts/projects/storage_counter/components/app.vue b/app/assets/javascripts/projects/storage_counter/components/app.vue
deleted file mode 100644
index 1a911ea3d9b..00000000000
--- a/app/assets/javascripts/projects/storage_counter/components/app.vue
+++ /dev/null
@@ -1,106 +0,0 @@
-<script>
-import { GlAlert, GlLink, GlLoadingIcon } from '@gitlab/ui';
-import { sprintf } from '~/locale';
-import UsageGraph from '~/vue_shared/components/storage_counter/usage_graph.vue';
-import {
- ERROR_MESSAGE,
- LEARN_MORE_LABEL,
- USAGE_QUOTAS_LABEL,
- TOTAL_USAGE_TITLE,
- TOTAL_USAGE_SUBTITLE,
- TOTAL_USAGE_DEFAULT_TEXT,
- HELP_LINK_ARIA_LABEL,
-} from '../constants';
-import getProjectStorageCount from '../queries/project_storage.query.graphql';
-import { parseGetProjectStorageResults } from '../utils';
-import StorageTable from './storage_table.vue';
-
-export default {
- name: 'StorageCounterApp',
- components: {
- GlAlert,
- GlLink,
- GlLoadingIcon,
- StorageTable,
- UsageGraph,
- },
- inject: ['projectPath', 'helpLinks'],
- apollo: {
- project: {
- query: getProjectStorageCount,
- variables() {
- return {
- fullPath: this.projectPath,
- };
- },
- update(data) {
- return parseGetProjectStorageResults(data, this.helpLinks);
- },
- error() {
- this.error = ERROR_MESSAGE;
- },
- },
- },
- data() {
- return {
- project: {},
- error: '',
- };
- },
- computed: {
- totalUsage() {
- return this.project?.storage?.totalUsage || TOTAL_USAGE_DEFAULT_TEXT;
- },
- storageTypes() {
- return this.project?.storage?.storageTypes || [];
- },
- },
- methods: {
- clearError() {
- this.error = '';
- },
- helpLinkAriaLabel(linkTitle) {
- return sprintf(HELP_LINK_ARIA_LABEL, {
- linkTitle,
- });
- },
- },
- LEARN_MORE_LABEL,
- USAGE_QUOTAS_LABEL,
- TOTAL_USAGE_TITLE,
- TOTAL_USAGE_SUBTITLE,
-};
-</script>
-<template>
- <gl-loading-icon v-if="$apollo.queries.project.loading" class="gl-mt-5" size="md" />
- <gl-alert v-else-if="error" variant="danger" @dismiss="clearError">
- {{ error }}
- </gl-alert>
- <div v-else>
- <div class="gl-pt-5 gl-px-3">
- <div class="gl-display-flex gl-justify-content-space-between gl-align-items-center">
- <div>
- <p class="gl-m-0 gl-font-lg gl-font-weight-bold">{{ $options.TOTAL_USAGE_TITLE }}</p>
- <p class="gl-m-0 gl-text-gray-400">
- {{ $options.TOTAL_USAGE_SUBTITLE }}
- <gl-link
- :href="helpLinks.usageQuotasHelpPagePath"
- target="_blank"
- :aria-label="helpLinkAriaLabel($options.USAGE_QUOTAS_LABEL)"
- data-testid="usage-quotas-help-link"
- >
- {{ $options.LEARN_MORE_LABEL }}
- </gl-link>
- </p>
- </div>
- <p class="gl-m-0 gl-font-size-h-display gl-font-weight-bold" data-testid="total-usage">
- {{ totalUsage }}
- </p>
- </div>
- </div>
- <div v-if="project.statistics" class="gl-w-full">
- <usage-graph :root-storage-statistics="project.statistics" :limit="0" />
- </div>
- <storage-table :storage-types="storageTypes" />
- </div>
-</template>
diff --git a/app/assets/javascripts/projects/storage_counter/components/storage_table.vue b/app/assets/javascripts/projects/storage_counter/components/storage_table.vue
deleted file mode 100644
index a42a9711572..00000000000
--- a/app/assets/javascripts/projects/storage_counter/components/storage_table.vue
+++ /dev/null
@@ -1,88 +0,0 @@
-<script>
-import { GlLink, GlIcon, GlTableLite as GlTable, GlSprintf } from '@gitlab/ui';
-import { numberToHumanSize } from '~/lib/utils/number_utils';
-import { thWidthClass } from '~/lib/utils/table_utility';
-import { sprintf } from '~/locale';
-import { PROJECT_TABLE_LABELS, HELP_LINK_ARIA_LABEL } from '../constants';
-import StorageTypeIcon from './storage_type_icon.vue';
-
-export default {
- name: 'StorageTable',
- components: {
- GlLink,
- GlIcon,
- GlTable,
- GlSprintf,
- StorageTypeIcon,
- },
- props: {
- storageTypes: {
- type: Array,
- required: true,
- },
- },
- methods: {
- helpLinkAriaLabel(linkTitle) {
- return sprintf(HELP_LINK_ARIA_LABEL, {
- linkTitle,
- });
- },
- },
- projectTableFields: [
- {
- key: 'storageType',
- label: PROJECT_TABLE_LABELS.STORAGE_TYPE,
- thClass: thWidthClass(90),
- sortable: true,
- },
- {
- key: 'value',
- label: PROJECT_TABLE_LABELS.VALUE,
- thClass: thWidthClass(10),
- sortable: true,
- formatter: (value) => {
- return numberToHumanSize(value, 1);
- },
- },
- ],
-};
-</script>
-<template>
- <gl-table :items="storageTypes" :fields="$options.projectTableFields">
- <template #cell(storageType)="{ item }">
- <div class="gl-display-flex gl-flex-direction-row">
- <storage-type-icon
- :name="item.storageType.id"
- :data-testid="`${item.storageType.id}-icon`"
- />
- <div>
- <p class="gl-font-weight-bold gl-mb-0" :data-testid="`${item.storageType.id}-name`">
- {{ item.storageType.name }}
- <gl-link
- v-if="item.storageType.helpPath"
- :href="item.storageType.helpPath"
- target="_blank"
- :aria-label="helpLinkAriaLabel(item.storageType.name)"
- :data-testid="`${item.storageType.id}-help-link`"
- >
- <gl-icon name="question" :size="12" />
- </gl-link>
- </p>
- <p class="gl-mb-0" :data-testid="`${item.storageType.id}-description`">
- {{ item.storageType.description }}
- </p>
- <p v-if="item.storageType.warningMessage" class="gl-mb-0 gl-font-sm">
- <gl-icon name="warning" :size="12" />
- <gl-sprintf :message="item.storageType.warningMessage">
- <template #warningLink="{ content }">
- <gl-link :href="item.storageType.warningLink" target="_blank" class="gl-font-sm">{{
- content
- }}</gl-link>
- </template>
- </gl-sprintf>
- </p>
- </div>
- </div>
- </template>
- </gl-table>
-</template>
diff --git a/app/assets/javascripts/projects/storage_counter/components/storage_type_icon.vue b/app/assets/javascripts/projects/storage_counter/components/storage_type_icon.vue
deleted file mode 100644
index bc7cd42df1e..00000000000
--- a/app/assets/javascripts/projects/storage_counter/components/storage_type_icon.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-<script>
-import { GlIcon } from '@gitlab/ui';
-
-export default {
- components: { GlIcon },
- props: {
- name: {
- type: String,
- required: false,
- default: '',
- },
- },
- methods: {
- iconName(storageTypeName) {
- const defaultStorageTypeIcon = 'disk';
- const storageTypeIconMap = {
- lfsObjectsSize: 'doc-image',
- snippetsSize: 'snippet',
- uploadsSize: 'upload',
- repositorySize: 'infrastructure-registry',
- packagesSize: 'package',
- };
-
- return storageTypeIconMap[`${storageTypeName}`] ?? defaultStorageTypeIcon;
- },
- },
-};
-</script>
-<template>
- <span
- class="gl-display-inline-flex gl-align-items-flex-start gl-justify-content-center gl-min-w-8 gl-pr-2 gl-pt-1"
- >
- <gl-icon :name="iconName(name)" :size="16" class="gl-mt-1" />
- </span>
-</template>
diff --git a/app/assets/javascripts/projects/storage_counter/constants.js b/app/assets/javascripts/projects/storage_counter/constants.js
deleted file mode 100644
index df4b1800dff..00000000000
--- a/app/assets/javascripts/projects/storage_counter/constants.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { s__, __ } from '~/locale';
-
-export const PROJECT_STORAGE_TYPES = [
- {
- id: 'buildArtifactsSize',
- name: s__('UsageQuota|Artifacts'),
- description: s__('UsageQuota|Pipeline artifacts and job artifacts, created with CI/CD.'),
- warningMessage: s__(
- 'UsageQuota|Because of a known issue, the artifact total for some projects may be incorrect. For more details, read %{warningLinkStart}the epic%{warningLinkEnd}.',
- ),
- warningLink: 'https://gitlab.com/groups/gitlab-org/-/epics/5380',
- },
- {
- id: 'lfsObjectsSize',
- name: s__('UsageQuota|LFS storage'),
- description: s__('UsageQuota|Audio samples, videos, datasets, and graphics.'),
- },
- {
- id: 'packagesSize',
- name: s__('UsageQuota|Packages'),
- description: s__('UsageQuota|Code packages and container images.'),
- },
- {
- id: 'repositorySize',
- name: s__('UsageQuota|Repository'),
- description: s__('UsageQuota|Git repository.'),
- },
- {
- id: 'snippetsSize',
- name: s__('UsageQuota|Snippets'),
- description: s__('UsageQuota|Shared bits of code and text.'),
- },
- {
- id: 'uploadsSize',
- name: s__('UsageQuota|Uploads'),
- description: s__('UsageQuota|File attachments and smaller design graphics.'),
- },
- {
- id: 'wikiSize',
- name: s__('UsageQuota|Wiki'),
- description: s__('UsageQuota|Wiki content.'),
- },
-];
-
-export const PROJECT_TABLE_LABELS = {
- STORAGE_TYPE: s__('UsageQuota|Storage type'),
- VALUE: s__('UsageQuota|Usage'),
-};
-
-export const ERROR_MESSAGE = s__(
- 'UsageQuota|Something went wrong while fetching project storage statistics',
-);
-
-export const LEARN_MORE_LABEL = __('Learn more.');
-export const USAGE_QUOTAS_LABEL = s__('UsageQuota|Usage Quotas');
-export const HELP_LINK_ARIA_LABEL = s__('UsageQuota|%{linkTitle} help link');
-export const TOTAL_USAGE_DEFAULT_TEXT = __('N/A');
-export const TOTAL_USAGE_TITLE = s__('UsageQuota|Usage breakdown');
-export const TOTAL_USAGE_SUBTITLE = s__(
- 'UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items.',
-);
diff --git a/app/assets/javascripts/projects/storage_counter/index.js b/app/assets/javascripts/projects/storage_counter/index.js
deleted file mode 100644
index 15796bc1870..00000000000
--- a/app/assets/javascripts/projects/storage_counter/index.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createDefaultClient from '~/lib/graphql';
-import StorageCounterApp from './components/app.vue';
-
-Vue.use(VueApollo);
-
-export default (containerId = 'js-project-storage-count-app') => {
- const el = document.getElementById(containerId);
-
- if (!el) {
- return false;
- }
-
- const {
- projectPath,
- usageQuotasHelpPagePath,
- buildArtifactsHelpPagePath,
- lfsObjectsHelpPagePath,
- packagesHelpPagePath,
- repositoryHelpPagePath,
- snippetsHelpPagePath,
- uploadsHelpPagePath,
- wikiHelpPagePath,
- } = el.dataset;
-
- const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(),
- });
-
- return new Vue({
- el,
- apolloProvider,
- provide: {
- projectPath,
- helpLinks: {
- usageQuotasHelpPagePath,
- buildArtifactsHelpPagePath,
- lfsObjectsHelpPagePath,
- packagesHelpPagePath,
- repositoryHelpPagePath,
- snippetsHelpPagePath,
- uploadsHelpPagePath,
- wikiHelpPagePath,
- },
- },
- render(createElement) {
- return createElement(StorageCounterApp);
- },
- });
-};
diff --git a/app/assets/javascripts/projects/storage_counter/queries/project_storage.query.graphql b/app/assets/javascripts/projects/storage_counter/queries/project_storage.query.graphql
deleted file mode 100644
index a4f2c529522..00000000000
--- a/app/assets/javascripts/projects/storage_counter/queries/project_storage.query.graphql
+++ /dev/null
@@ -1,16 +0,0 @@
-query getProjectStorageCount($fullPath: ID!) {
- project(fullPath: $fullPath) {
- id
- statistics {
- buildArtifactsSize
- pipelineArtifactsSize
- lfsObjectsSize
- packagesSize
- repositorySize
- snippetsSize
- storageSize
- uploadsSize
- wikiSize
- }
- }
-}
diff --git a/app/assets/javascripts/projects/storage_counter/utils.js b/app/assets/javascripts/projects/storage_counter/utils.js
deleted file mode 100644
index 9fca9d88f46..00000000000
--- a/app/assets/javascripts/projects/storage_counter/utils.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { numberToHumanSize } from '~/lib/utils/number_utils';
-import { PROJECT_STORAGE_TYPES } from './constants';
-
-/**
- * This method parses the results from `getProjectStorageCount` call.
- *
- * @param {Object} data graphql result
- * @returns {Object}
- */
-export const parseGetProjectStorageResults = (data, helpLinks) => {
- const projectStatistics = data?.project?.statistics;
- if (!projectStatistics) {
- return {};
- }
- const { storageSize, ...storageStatistics } = projectStatistics;
- const storageTypes = PROJECT_STORAGE_TYPES.reduce((types, currentType) => {
- const helpPathKey = currentType.id.replace(`Size`, `HelpPagePath`);
- const helpPath = helpLinks[helpPathKey];
-
- return types.concat({
- storageType: {
- ...currentType,
- helpPath,
- },
- value: storageStatistics[currentType.id],
- });
- }, []);
-
- return {
- storage: {
- totalUsage: numberToHumanSize(storageSize, 1),
- storageTypes,
- },
- statistics: projectStatistics,
- };
-};