diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-20 14:22:11 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-20 14:22:11 +0000 |
commit | 0c872e02b2c822e3397515ec324051ff540f0cd5 (patch) | |
tree | ce2fb6ce7030e4dad0f4118d21ab6453e5938cdd /app/assets/javascripts/projects | |
parent | f7e05a6853b12f02911494c4b3fe53d9540d74fc (diff) | |
download | gitlab-ce-15.7.0-rc42.tar.gz |
Add latest changes from gitlab-org/gitlab@15-7-stable-eev15.7.0-rc42
Diffstat (limited to 'app/assets/javascripts/projects')
23 files changed, 253 insertions, 48 deletions
diff --git a/app/assets/javascripts/projects/commit/components/branches_dropdown.vue b/app/assets/javascripts/projects/commit/components/branches_dropdown.vue index 52da8aaba4d..a037e721677 100644 --- a/app/assets/javascripts/projects/commit/components/branches_dropdown.vue +++ b/app/assets/javascripts/projects/commit/components/branches_dropdown.vue @@ -28,6 +28,11 @@ export default { required: false, default: '', }, + blanked: { + type: Boolean, + required: false, + default: false, + }, }, i18n: { noResultsMessage: I18N_NO_RESULTS_MESSAGE, @@ -36,7 +41,7 @@ export default { }, data() { return { - searchTerm: this.value, + searchTerm: this.blanked ? '' : this.value, }; }, computed: { diff --git a/app/assets/javascripts/projects/commit/components/form_modal.vue b/app/assets/javascripts/projects/commit/components/form_modal.vue index d9aaa574fec..1febe8ceaab 100644 --- a/app/assets/javascripts/projects/commit/components/form_modal.vue +++ b/app/assets/javascripts/projects/commit/components/form_modal.vue @@ -41,6 +41,11 @@ export default { required: false, default: false, }, + isRevert: { + type: Boolean, + required: false, + default: false, + }, primaryActionEventName: { type: String, required: false, @@ -150,7 +155,12 @@ export default { > <input id="start_branch" type="hidden" name="start_branch" :value="branch" /> - <branches-dropdown class="gl-w-half" :value="branch" @selectBranch="setBranch" /> + <branches-dropdown + class="gl-w-half" + :value="branch" + :blanked="isRevert" + @selectBranch="setBranch" + /> </gl-form-group> <gl-form-checkbox diff --git a/app/assets/javascripts/projects/commit/init_revert_commit_modal.js b/app/assets/javascripts/projects/commit/init_revert_commit_modal.js index 849b2f4858c..41be71932e5 100644 --- a/app/assets/javascripts/projects/commit/init_revert_commit_modal.js +++ b/app/assets/javascripts/projects/commit/init_revert_commit_modal.js @@ -49,6 +49,7 @@ export default function initInviteMembersModal(primaryActionEventName) { i18n: { ...I18N_REVERT_MODAL, ...I18N_MODAL }, openModal: OPEN_REVERT_MODAL, modalId: REVERT_MODAL_ID, + isRevert: true, primaryActionEventName, }, }), diff --git a/app/assets/javascripts/projects/commits/index.js b/app/assets/javascripts/projects/commits/index.js index 03b94fde0f3..53169f689c9 100644 --- a/app/assets/javascripts/projects/commits/index.js +++ b/app/assets/javascripts/projects/commits/index.js @@ -1,11 +1,13 @@ import Vue from 'vue'; import Vuex from 'vuex'; +import { visitUrl } from '~/lib/utils/url_utility'; +import RefSelector from '~/ref/components/ref_selector.vue'; import AuthorSelectApp from './components/author_select.vue'; import store from './store'; Vue.use(Vuex); -export default (el) => { +export const mountCommits = (el) => { if (!el) { return null; } @@ -24,3 +26,30 @@ export default (el) => { }, }); }; + +export const initCommitsRefSwitcher = () => { + const el = document.getElementById('js-project-commits-ref-switcher'); + const COMMITS_PATH_REGEX = /^(.*?)\/-\/commits/g; + + if (!el) return false; + + const { projectId, ref, commitsPath } = el.dataset; + const commitsPathPrefix = commitsPath.match(COMMITS_PATH_REGEX)?.[0]; + + return new Vue({ + el, + render(createElement) { + return createElement(RefSelector, { + props: { + projectId, + value: ref, + }, + on: { + input(selected) { + visitUrl(`${commitsPathPrefix}/${selected}`); + }, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/projects/compare/components/repo_dropdown.vue b/app/assets/javascripts/projects/compare/components/repo_dropdown.vue index ba1e00a2b36..c00e75db722 100644 --- a/app/assets/javascripts/projects/compare/components/repo_dropdown.vue +++ b/app/assets/javascripts/projects/compare/components/repo_dropdown.vue @@ -57,7 +57,7 @@ export default { <gl-dropdown :text="selectedProject.name" :header-text="s__(`CompareRevisions|Select target project`)" - class="gl-w-full gl-font-monospace gl-sm-pr-3" + class="gl-w-full gl-font-monospace" toggle-class="gl-min-w-0" :disabled="disableRepoDropdown" > diff --git a/app/assets/javascripts/projects/compare/components/revision_card.vue b/app/assets/javascripts/projects/compare/components/revision_card.vue index d6ada24604d..162aca44f9d 100644 --- a/app/assets/javascripts/projects/compare/components/revision_card.vue +++ b/app/assets/javascripts/projects/compare/components/revision_card.vue @@ -43,7 +43,7 @@ export default { <h2 class="gl-font-size-h2"> {{ s__(`CompareRevisions|${revisionText}`) }} </h2> - <div class="gl-sm-display-flex gl-align-items-center"> + <div class="gl-sm-display-flex gl-align-items-center gl-gap-3"> <repo-dropdown class="gl-sm-w-half" :params-name="paramsName" diff --git a/app/assets/javascripts/projects/default_project_templates.js b/app/assets/javascripts/projects/default_project_templates.js index 3671b24b502..a44855c14d5 100644 --- a/app/assets/javascripts/projects/default_project_templates.js +++ b/app/assets/javascripts/projects/default_project_templates.js @@ -113,4 +113,12 @@ export default { text: s__('ProjectTemplates|Jsonnet for Dynamic Child Pipelines'), icon: '.template-option .icon-gitlab_logo', }, + bridgetown: { + text: s__('ProjectTemplates|Pages/Bridgetown'), + icon: '.template-option .icon-gitlab_logo', + }, + typo3_distribution: { + text: s__('ProjectTemplates|TYPO3 Distribution'), + icon: '.template-option .icon-typo3', + }, }; diff --git a/app/assets/javascripts/projects/new/components/app.vue b/app/assets/javascripts/projects/new/components/app.vue index 59ca393fe92..3100029eb31 100644 --- a/app/assets/javascripts/projects/new/components/app.vue +++ b/app/assets/javascripts/projects/new/components/app.vue @@ -3,7 +3,7 @@ import createFromTemplateIllustration from '@gitlab/svgs/dist/illustrations/proj import blankProjectIllustration from '@gitlab/svgs/dist/illustrations/project-create-new-sm.svg'; import importProjectIllustration from '@gitlab/svgs/dist/illustrations/project-import-sm.svg'; import ciCdProjectIllustration from '@gitlab/svgs/dist/illustrations/project-run-CICD-pipelines-sm.svg'; -import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; +import SafeHtml from '~/vue_shared/directives/safe_html'; import { s__ } from '~/locale'; import NewNamespacePage from '~/vue_shared/new_namespace/new_namespace_page.vue'; import NewProjectPushTipPopover from './new_project_push_tip_popover.vue'; 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 eccfb3d844c..d6d88b5b297 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 @@ -46,7 +46,15 @@ export default { debounce: DEBOUNCE_DELAY, }, }, - inject: ['namespaceFullPath', 'namespaceId', 'rootUrl', 'trackLabel', 'userNamespaceId'], + inject: [ + 'namespaceFullPath', + 'namespaceId', + 'rootUrl', + 'trackLabel', + 'userNamespaceId', + 'inputName', + 'inputId', + ], data() { return { currentUser: {}, @@ -124,6 +132,11 @@ export default { } : this.$options.emptyNameSpace; }, + trackDropdownShow() { + if (this.trackLabel) { + this.track('activate_form_input', { label: this.trackLabel, property: 'project_path' }); + } + }, }, emptyNameSpace: { id: undefined, @@ -145,7 +158,7 @@ export default { class="js-group-namespace-dropdown gl-flex-grow-1" :toggle-class="`gl-rounded-top-right-base! gl-rounded-bottom-right-base! gl-w-20 ${dropdownPlaceholderClass}`" data-qa-selector="select_namespace_dropdown" - @show="track('activate_form_input', { label: trackLabel, property: 'project_path' })" + @show="trackDropdownShow" @shown="handleDropdownShown" > <template #button-text> @@ -173,7 +186,7 @@ export default { {{ group.fullPath }} </gl-dropdown-item> </template> - <template v-if="hasNamespaceMatches"> + <template v-if="hasNamespaceMatches && userNamespaceId"> <gl-dropdown-section-header>{{ __('Users') }}</gl-dropdown-section-header> <gl-dropdown-item @click="handleDropdownItemClick(userNamespace)"> {{ userNamespace.fullPath }} @@ -186,9 +199,9 @@ export default { <input type="hidden" name="project[selected_namespace_id]" :value="selectedNamespace.id" /> <input - id="project_namespace_id" + :id="inputId" type="hidden" - name="project[namespace_id]" + :name="inputName" :value="selectedNamespace.id || userNamespaceId" /> </gl-button-group> diff --git a/app/assets/javascripts/projects/new/constants.js b/app/assets/javascripts/projects/new/constants.js index e52a84dc07e..7b6b2cfc7ca 100644 --- a/app/assets/javascripts/projects/new/constants.js +++ b/app/assets/javascripts/projects/new/constants.js @@ -12,6 +12,8 @@ export const DEPLOYMENT_TARGET_SELECTIONS = [ s__('DeploymentTarget|Registry (package or container)'), s__('DeploymentTarget|Infrastructure provider (Terraform, Cloudformation, and so on)'), s__('DeploymentTarget|Serverless backend (Lambda, Cloud functions)'), + s__('DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)'), + s__('DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)'), s__('DeploymentTarget|GitLab Pages'), s__('DeploymentTarget|Other hosting service'), s__('DeploymentTarget|No deployment planned'), diff --git a/app/assets/javascripts/projects/new/index.js b/app/assets/javascripts/projects/new/index.js index a72172a4f5e..910244c657b 100644 --- a/app/assets/javascripts/projects/new/index.js +++ b/app/assets/javascripts/projects/new/index.js @@ -59,6 +59,8 @@ export function initNewProjectUrlSelect() { rootUrl: el.dataset.rootUrl, trackLabel: el.dataset.trackLabel, userNamespaceId: el.dataset.userNamespaceId, + inputId: el.dataset.inputId, + inputName: el.dataset.inputName, }, render: (createElement) => createElement(NewProjectUrlSelect), }), diff --git a/app/assets/javascripts/projects/project_name_rules.js b/app/assets/javascripts/projects/project_name_rules.js new file mode 100644 index 00000000000..eeef1fb5afc --- /dev/null +++ b/app/assets/javascripts/projects/project_name_rules.js @@ -0,0 +1,28 @@ +import { __ } from '~/locale'; + +const rulesReg = [ + { + reg: /^[a-zA-Z0-9\u{00A9}-\u{1f9ff}_]/u, + msg: __("Name must start with a letter, digit, emoji, or '_'"), + }, + { + reg: /^[a-zA-Z0-9\p{Pd}\u{002B}\u{00A9}-\u{1f9ff}_. ]+$/u, + msg: __("Name can contain only letters, digits, emojis, '_', '.', '+', dashes, or spaces"), + }, +]; + +/** + * + * @param {string} text + * @returns {string} msg + */ +function checkRules(text) { + for (const item of rulesReg) { + if (!item.reg.test(text)) { + return item.msg; + } + } + return ''; +} + +export { checkRules }; diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js index 424ea3b61c5..d71e80dffcf 100644 --- a/app/assets/javascripts/projects/project_new.js +++ b/app/assets/javascripts/projects/project_new.js @@ -12,6 +12,7 @@ import { slugify, convertUnicodeToAscii, } from '../lib/utils/text_utility'; +import { checkRules } from './project_name_rules'; let hasUserDefinedProjectPath = false; let hasUserDefinedProjectName = false; @@ -87,10 +88,23 @@ const validateGroupNamespaceDropdown = (e) => { } }; +const checkProjectName = (projectNameInput) => { + const msg = checkRules(projectNameInput.value); + const projectNameError = document.querySelector('#project_name_error'); + if (!projectNameError) return; + if (msg) { + projectNameError.innerText = msg; + projectNameError.classList.remove('hidden'); + } else { + projectNameError.classList.add('hidden'); + } +}; + const setProjectNamePathHandlers = ($projectNameInput, $projectPathInput) => { const specialRepo = document.querySelector('.js-user-readme-repo'); const projectNameInputListener = () => { onProjectNameChange($projectNameInput, $projectPathInput); + checkProjectName($projectNameInput); hasUserDefinedProjectName = $projectNameInput.value.trim().length > 0; hasUserDefinedProjectPath = $projectPathInput.value.trim().length > 0; }; diff --git a/app/assets/javascripts/projects/settings/access_dropdown.js b/app/assets/javascripts/projects/settings/access_dropdown.js index 335545c802a..dcf7415a444 100644 --- a/app/assets/javascripts/projects/settings/access_dropdown.js +++ b/app/assets/javascripts/projects/settings/access_dropdown.js @@ -580,7 +580,7 @@ export default class AccessDropdown { return ` <li> <a href="#" class="${isActiveClass} item-${role.type}" data-role-id="${role.id}"> - ${role.text} + ${escape(role.text)} </a> </li> `; diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js b/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js index 6da058ebc9c..61c37a2348a 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js +++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js @@ -6,6 +6,7 @@ export const I18N = { branchNameOrPattern: s__('BranchRules|Branch name or pattern'), branch: s__('BranchRules|Target Branch'), allBranches: s__('BranchRules|All branches'), + matchingBranchesLinkTitle: s__('BranchRules|%{total} matching %{subject}'), protectBranchTitle: s__('BranchRules|Protect branch'), protectBranchDescription: s__( 'BranchRules|Keep stable branches secure and force developers to use merge requests. %{linkStart}What are protected branches?%{linkEnd}', diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue b/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue index eb11e17dd1b..626ed67c466 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue +++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue @@ -1,9 +1,10 @@ <script> import { GlSprintf, GlLink, GlLoadingIcon } from '@gitlab/ui'; -import { sprintf } from '~/locale'; -import { getParameterByName } from '~/lib/utils/url_utility'; +import { sprintf, n__ } from '~/locale'; +import { getParameterByName, mergeUrlParams } from '~/lib/utils/url_utility'; import { helpPagePath } from '~/helpers/help_page_helper'; import branchRulesQuery from '../../queries/branch_rules_details.query.graphql'; +import { getAccessLevels } from '../../../utils'; import Protection from './protection.vue'; import { I18N, @@ -41,6 +42,9 @@ export default { statusChecksPath: { default: '', }, + branchesPath: { + default: '', + }, }, apollo: { project: { @@ -55,6 +59,7 @@ export default { this.branchProtection = branchRule?.branchProtection; this.approvalRules = branchRule?.approvalRules; this.statusChecks = branchRule?.externalStatusChecks?.nodes || []; + this.matchingBranchesCount = branchRule?.matchingBranchesCount; }, }, }, @@ -64,6 +69,7 @@ export default { branchProtection: {}, approvalRules: {}, statusChecks: [], + matchingBranchesCount: null, }; }, computed: { @@ -115,28 +121,20 @@ export default { ? this.$options.i18n.targetBranch : this.$options.i18n.branchNameOrPattern; }, + matchingBranchesLinkHref() { + return mergeUrlParams({ state: 'all', search: this.branch }, this.branchesPath); + }, + matchingBranchesLinkTitle() { + const total = this.matchingBranchesCount; + const subject = n__('branch', 'branches', total); + return sprintf(this.$options.i18n.matchingBranchesLinkTitle, { total, subject }); + }, approvals() { return this.approvalRules?.nodes || []; }, }, methods: { - getAccessLevels(accessLevels = {}) { - const total = accessLevels.edges?.length; - const accessLevelTypes = { total, users: [], groups: [], roles: [] }; - - accessLevels.edges?.forEach(({ node }) => { - if (node.user) { - const src = node.user.avatarUrl; - accessLevelTypes.users.push({ src, ...node.user }); - } else if (node.group) { - accessLevelTypes.groups.push(node); - } else { - accessLevelTypes.roles.push(node); - } - }); - - return accessLevelTypes; - }, + getAccessLevels, }, }; </script> @@ -161,6 +159,10 @@ export default { </div> <code v-else class="gl-mt-2" data-testid="branch">{{ branch }}</code> + <p v-if="matchingBranchesCount" class="gl-mt-3"> + <gl-link :href="matchingBranchesLinkHref">{{ matchingBranchesLinkTitle }}</gl-link> + </p> + <h4 class="gl-mb-1 gl-mt-5">{{ $options.i18n.protectBranchTitle }}</h4> <gl-sprintf :message="$options.i18n.protectBranchDescription"> <template #link="{ content }"> diff --git a/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js b/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js index 89cfb1e1c8e..7639acc1181 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js +++ b/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js @@ -14,7 +14,13 @@ export default function mountBranchRules(el) { defaultClient: createDefaultClient(), }); - const { projectPath, protectedBranchesPath, approvalRulesPath, statusChecksPath } = el.dataset; + const { + projectPath, + protectedBranchesPath, + approvalRulesPath, + statusChecksPath, + branchesPath, + } = el.dataset; return new Vue({ el, @@ -24,6 +30,7 @@ export default function mountBranchRules(el) { protectedBranchesPath, approvalRulesPath, statusChecksPath, + branchesPath, }, render(h) { return h(View); diff --git a/app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql b/app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql index aa1e4923aa8..a832e59aa67 100644 --- a/app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql +++ b/app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql @@ -68,6 +68,7 @@ query getBranchRulesDetails($projectPath: ID!) { externalUrl } } + matchingBranchesCount } } } diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue index a9eb2a53fbf..9b669024a8b 100644 --- a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue +++ b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue @@ -1,7 +1,7 @@ <script> import { s__ } from '~/locale'; import { createAlert } from '~/flash'; -import branchRulesQuery from './graphql/queries/branch_rules.query.graphql'; +import branchRulesQuery from 'ee_else_ce/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql'; import BranchRule from './components/branch_rule.vue'; export const i18n = { @@ -51,13 +51,14 @@ export default { <template> <div class="settings-content"> <branch-rule - v-for="rule in branchRules" - :key="rule.name" + v-for="(rule, index) in branchRules" + :key="`${rule.name}-${index}`" :name="rule.name" :is-default="rule.isDefault" :branch-protection="rule.branchProtection" - :status-checks-total="rule.externalStatusChecks.nodes.length" - :approval-rules-total="rule.approvalRules.nodes.length" + :status-checks-total="rule.externalStatusChecks ? rule.externalStatusChecks.nodes.length : 0" + :approval-rules-total="rule.approvalRules ? rule.approvalRules.nodes.length : 0" + :matching-branches-count="rule.matchingBranchesCount" /> <span v-if="!branchRules.length" data-testid="empty">{{ $options.i18n.emptyState }}</span> diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue b/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue index 78c824c66d1..41947834bdb 100644 --- a/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue +++ b/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue @@ -1,6 +1,7 @@ <script> import { GlBadge, GlButton } from '@gitlab/ui'; import { s__, sprintf, n__ } from '~/locale'; +import { getAccessLevels } from '../../../utils'; export const i18n = { defaultLabel: s__('BranchRules|default'), @@ -9,6 +10,9 @@ export const i18n = { codeOwnerApprovalRequired: s__('BranchRules|Requires CODEOWNERS approval'), statusChecks: s__('BranchRules|%{total} status %{subject}'), approvalRules: s__('BranchRules|%{total} approval %{subject}'), + matchingBranches: s__('BranchRules|%{total} matching %{subject}'), + pushAccessLevels: s__('BranchRules|Allowed to merge'), + mergeAccessLevels: s__('BranchRules|Allowed to push'), }; export default { @@ -48,8 +52,16 @@ export default { required: false, default: 0, }, + matchingBranchesCount: { + type: Number, + required: false, + default: 0, + }, }, computed: { + isWildcard() { + return this.name.includes('*'); + }, hasApprovalDetails() { return this.approvalDetails.length; }, @@ -68,8 +80,31 @@ export default { subject: n__('rule', 'rules', this.approvalRulesTotal), }); }, + matchingBranchesText() { + return sprintf(this.$options.i18n.matchingBranches, { + total: this.matchingBranchesCount, + subject: n__('branch', 'branches', this.matchingBranchesCount), + }); + }, + mergeAccessLevels() { + const { mergeAccessLevels } = this.branchProtection || {}; + return this.getAccessLevels(mergeAccessLevels); + }, + pushAccessLevels() { + const { pushAccessLevels } = this.branchProtection || {}; + return this.getAccessLevels(pushAccessLevels); + }, + pushAccessLevelsText() { + return this.getAccessLevelsText(this.$options.i18n.pushAccessLevels, this.pushAccessLevels); + }, + mergeAccessLevelsText() { + return this.getAccessLevelsText(this.$options.i18n.mergeAccessLevels, this.mergeAccessLevels); + }, approvalDetails() { const approvalDetails = []; + if (this.isWildcard) { + approvalDetails.push(this.matchingBranchesText); + } if (this.branchProtection.allowForcePush) { approvalDetails.push(this.$options.i18n.allowForcePush); } @@ -82,9 +117,31 @@ export default { if (this.approvalRulesTotal) { approvalDetails.push(this.approvalRulesText); } + if (this.mergeAccessLevels.total > 0) { + approvalDetails.push(this.mergeAccessLevelsText); + } + if (this.pushAccessLevels.total > 0) { + approvalDetails.push(this.pushAccessLevelsText); + } return approvalDetails; }, }, + methods: { + getAccessLevels, + getAccessLevelsText(beginString = '', accessLevels) { + const textParts = []; + if (accessLevels.roles.length) { + textParts.push(n__('1 role', '%d roles', accessLevels.roles.length)); + } + if (accessLevels.groups.length) { + textParts.push(n__('1 group', '%d groups', accessLevels.groups.length)); + } + if (accessLevels.users.length) { + textParts.push(n__('1 user', '%d users', accessLevels.users.length)); + } + return `${beginString}: ${textParts.join(', ')}`; + }, + }, }; </script> diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql b/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql index 49e089e7805..a8cdda5505f 100644 --- a/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql +++ b/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql @@ -5,18 +5,24 @@ query getBranchRules($projectPath: ID!) { nodes { name isDefault + matchingBranchesCount branchProtection { allowForcePush - codeOwnerApprovalRequired - } - externalStatusChecks { - nodes { - id + mergeAccessLevels { + edges { + node { + accessLevel + accessLevelDescription + } + } } - } - approvalRules { - nodes { - id + pushAccessLevels { + edges { + node { + accessLevel + accessLevelDescription + } + } } } } diff --git a/app/assets/javascripts/projects/settings/utils.js b/app/assets/javascripts/projects/settings/utils.js new file mode 100644 index 00000000000..7bcfde39178 --- /dev/null +++ b/app/assets/javascripts/projects/settings/utils.js @@ -0,0 +1,17 @@ +export const getAccessLevels = (accessLevels = {}) => { + const total = accessLevels.edges?.length; + const accessLevelTypes = { total, users: [], groups: [], roles: [] }; + + accessLevels.edges?.forEach(({ node }) => { + if (node.user) { + const src = node.user.avatarUrl; + accessLevelTypes.users.push({ src, ...node.user }); + } else if (node.group) { + accessLevelTypes.groups.push(node); + } else { + accessLevelTypes.roles.push(node); + } + }); + + return accessLevelTypes; +}; diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue index 71ff3e892b1..b79b3fa4573 100644 --- a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue +++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue @@ -1,5 +1,6 @@ <script> -import { GlAlert, GlSprintf, GlLink, GlSafeHtmlDirective } from '@gitlab/ui'; +import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui'; +import SafeHtml from '~/vue_shared/directives/safe_html'; import axios from '~/lib/utils/axios_utils'; import { helpPagePath } from '~/helpers/help_page_helper'; import { __, sprintf } from '~/locale'; @@ -16,7 +17,7 @@ export default { ServiceDeskSetting, }, directives: { - SafeHtml: GlSafeHtmlDirective, + SafeHtml, }, inject: { initialIsEnabled: { |