Commit 925655ed authored by Robert Hunt's avatar Robert Hunt Committed by Savas Vedova

Add loading icon and form hiding on error to group-level merge request settings

parent c83cf1f8
<script> <script>
import { GlButton, GlForm, GlFormGroup } from '@gitlab/ui'; import { GlAlert, GlButton, GlForm, GlFormGroup, GlLoadingIcon } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { mapActions, mapState } from 'vuex'; import { mapActions, mapState } from 'vuex';
import { mapComputed } from '~/vuex_shared/bindings'; import { mapComputed } from '~/vuex_shared/bindings';
import { APPROVAL_SETTINGS_I18N } from '../constants'; import { APPROVAL_SETTINGS_I18N } from '../constants';
...@@ -8,9 +9,11 @@ import ApprovalSettingsCheckbox from './approval_settings_checkbox.vue'; ...@@ -8,9 +9,11 @@ import ApprovalSettingsCheckbox from './approval_settings_checkbox.vue';
export default { export default {
components: { components: {
ApprovalSettingsCheckbox, ApprovalSettingsCheckbox,
GlAlert,
GlButton, GlButton,
GlForm, GlForm,
GlFormGroup, GlFormGroup,
GlLoadingIcon,
}, },
props: { props: {
approvalSettingsPath: { approvalSettingsPath: {
...@@ -18,14 +21,12 @@ export default { ...@@ -18,14 +21,12 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
hasFormLoaded: false,
};
},
computed: { computed: {
...mapState({ ...mapState({
isLoading: (state) => state.approvals.isLoading, isLoading: (state) => state.approvals.isLoading,
isUpdated: (state) => state.approvals.isUpdated,
settings: (state) => state.approvals.settings,
errorMessage: (state) => state.approvals.errorMessage,
}), }),
...mapComputed( ...mapComputed(
[ [
...@@ -38,15 +39,25 @@ export default { ...@@ -38,15 +39,25 @@ export default {
undefined, undefined,
(state) => state.approvals.settings, (state) => state.approvals.settings,
), ),
hasSettings() {
return !isEmpty(this.settings);
},
isLoaded() {
return this.hasSettings || this.errorMessage;
},
}, },
async created() { created() {
await this.fetchSettings(this.approvalSettingsPath); this.fetchSettings(this.approvalSettingsPath);
this.hasFormLoaded = true;
}, },
methods: { methods: {
...mapActions(['fetchSettings', 'updateSettings']), ...mapActions([
onSubmit() { 'fetchSettings',
this.updateSettings(this.approvalSettingsPath); 'updateSettings',
'dismissErrorMessage',
'dismissSuccessMessage',
]),
async onSubmit() {
await this.updateSettings(this.approvalSettingsPath);
}, },
}, },
links: { links: {
...@@ -62,41 +73,66 @@ export default { ...@@ -62,41 +73,66 @@ export default {
</script> </script>
<template> <template>
<gl-form v-if="hasFormLoaded" @submit.prevent="onSubmit"> <div>
<gl-form-group> <gl-loading-icon v-if="!isLoaded" size="lg" />
<approval-settings-checkbox <gl-alert
v-model="preventAuthorApproval" v-if="errorMessage"
:label="$options.i18n.authorApprovalLabel" variant="danger"
:anchor="$options.links.preventAuthorApprovalDocsAnchor" :dismissible="hasSettings"
data-testid="prevent-author-approval" :contained="true"
/> :class="{ 'gl-mb-6': hasSettings }"
<approval-settings-checkbox data-testid="error-alert"
v-model="preventMrApprovalRuleEdit" @dismiss="dismissErrorMessage"
:label="$options.i18n.preventMrApprovalRuleEditLabel" >
:anchor="$options.links.preventMrApprovalRuleEditDocsAnchor" {{ errorMessage }}
data-testid="prevent-mr-approval-rule-edit" </gl-alert>
/> <gl-alert
<approval-settings-checkbox v-if="isUpdated"
v-model="requireUserPassword" variant="success"
:label="$options.i18n.requireUserPasswordLabel" :dismissible="true"
:anchor="$options.links.requireUserPasswordDocsAnchor" :contained="true"
data-testid="require-user-password" class="gl-mb-6"
/> data-testid="success-alert"
<approval-settings-checkbox @dismiss="dismissSuccessMessage"
v-model="removeApprovalsOnPush" >
:label="$options.i18n.removeApprovalsOnPushLabel" {{ $options.i18n.savingSuccessMessage }}
:anchor="$options.links.removeApprovalsOnPushDocsAnchor" </gl-alert>
data-testid="remove-approvals-on-push" <gl-form v-if="hasSettings" @submit.prevent="onSubmit">
/> <gl-form-group>
<approval-settings-checkbox <approval-settings-checkbox
v-model="preventCommittersApproval" v-model="preventAuthorApproval"
:label="$options.i18n.preventCommittersApprovalLabel" :label="$options.i18n.authorApprovalLabel"
:anchor="$options.links.preventCommittersApprovalAnchor" :anchor="$options.links.preventAuthorApprovalDocsAnchor"
data-testid="prevent-committers-approval" data-testid="prevent-author-approval"
/> />
</gl-form-group> <approval-settings-checkbox
<gl-button type="submit" variant="success" category="primary" :disabled="isLoading"> v-model="preventMrApprovalRuleEdit"
{{ $options.i18n.saveChanges }} :label="$options.i18n.preventMrApprovalRuleEditLabel"
</gl-button> :anchor="$options.links.preventMrApprovalRuleEditDocsAnchor"
</gl-form> data-testid="prevent-mr-approval-rule-edit"
/>
<approval-settings-checkbox
v-model="requireUserPassword"
:label="$options.i18n.requireUserPasswordLabel"
:anchor="$options.links.requireUserPasswordDocsAnchor"
data-testid="require-user-password"
/>
<approval-settings-checkbox
v-model="removeApprovalsOnPush"
:label="$options.i18n.removeApprovalsOnPushLabel"
:anchor="$options.links.removeApprovalsOnPushDocsAnchor"
data-testid="remove-approvals-on-push"
/>
<approval-settings-checkbox
v-model="preventCommittersApproval"
:label="$options.i18n.preventCommittersApprovalLabel"
:anchor="$options.links.preventCommittersApprovalAnchor"
data-testid="prevent-committers-approval"
/>
</gl-form-group>
<gl-button type="submit" variant="confirm" category="primary" :loading="isLoading">
{{ $options.i18n.saveChanges }}
</gl-button>
</gl-form>
</div>
</template> </template>
...@@ -50,14 +50,23 @@ export const APPROVAL_RULE_CONFIGS = { ...@@ -50,14 +50,23 @@ export const APPROVAL_RULE_CONFIGS = {
export const APPROVALS_HELP_PATH = 'user/project/merge_requests/merge_request_approvals'; export const APPROVALS_HELP_PATH = 'user/project/merge_requests/merge_request_approvals';
export const APPROVAL_SETTINGS_I18N = { export const APPROVAL_SETTINGS_I18N = {
authorApprovalLabel: __('Prevent MR approvals by the author.'), authorApprovalLabel: s__('ApprovalSettings|Prevent MR approvals by the author.'),
preventMrApprovalRuleEditLabel: __('Prevent users from modifying MR approval rules.'), preventMrApprovalRuleEditLabel: s__(
preventCommittersApprovalLabel: __( 'ApprovalSettings|Prevent users from modifying MR approval rules.',
'Prevent approval of merge requests by merge request committers.',
), ),
requireUserPasswordLabel: __('Require user password for approvals.'), preventCommittersApprovalLabel: s__(
removeApprovalsOnPushLabel: __( 'ApprovalSettings|Prevent approval of merge requests by merge request committers.',
'Remove all approvals in a merge request when new commits are pushed to its source branch.', ),
requireUserPasswordLabel: s__('ApprovalSettings|Require user password for approvals.'),
removeApprovalsOnPushLabel: s__(
'ApprovalSettings|Remove all approvals in a merge request when new commits are pushed to its source branch.',
), ),
saveChanges: __('Save changes'), saveChanges: __('Save changes'),
loadingErrorMessage: s__(
'ApprovalSettings|There was an error loading merge request approval settings.',
),
savingErrorMessage: s__(
'ApprovalSettings|There was an error updating merge request approval settings.',
),
savingSuccessMessage: s__('ApprovalSettings|Merge request approval settings have been updated.'),
}; };
import createFlash from '~/flash'; import * as Sentry from '@sentry/browser';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import * as types from './mutation_types'; import * as types from './mutation_types';
const mapStateToPayload = (state) => ({ const mapStateToPayload = (state) => ({
...@@ -19,15 +18,11 @@ export const fetchSettings = ({ commit }, endpoint) => { ...@@ -19,15 +18,11 @@ export const fetchSettings = ({ commit }, endpoint) => {
.then(({ data }) => { .then(({ data }) => {
commit(types.RECEIVE_SETTINGS_SUCCESS, data); commit(types.RECEIVE_SETTINGS_SUCCESS, data);
}) })
.catch(({ response }) => { .catch((e) => {
const error = response?.data?.message; const error = e?.response?.data?.message || e;
commit(types.RECEIVE_SETTINGS_ERROR, error); Sentry.captureException(error);
createFlash({ commit(types.RECEIVE_SETTINGS_ERROR);
message: __('There was an error loading merge request approval settings.'),
captureError: true,
error,
});
}); });
}; };
...@@ -38,23 +33,23 @@ export const updateSettings = ({ commit, state }, endpoint) => { ...@@ -38,23 +33,23 @@ export const updateSettings = ({ commit, state }, endpoint) => {
.put(endpoint, { ...mapStateToPayload(state) }) .put(endpoint, { ...mapStateToPayload(state) })
.then(({ data }) => { .then(({ data }) => {
commit(types.UPDATE_SETTINGS_SUCCESS, data); commit(types.UPDATE_SETTINGS_SUCCESS, data);
createFlash({
message: __('Merge request approval settings have been updated.'),
type: 'notice',
});
}) })
.catch(({ response }) => { .catch((e) => {
const error = response?.data?.message; const error = e?.response?.data?.message || e;
commit(types.UPDATE_SETTINGS_ERROR, error); Sentry.captureException(error);
createFlash({ commit(types.UPDATE_SETTINGS_ERROR);
message: __('There was an error updating merge request approval settings.'),
captureError: true,
error,
});
}); });
}; };
export const dismissSuccessMessage = ({ commit }) => {
commit(types.DISMISS_SUCCESS_MESSAGE);
};
export const dismissErrorMessage = ({ commit }) => {
commit(types.DISMISS_ERROR_MESSAGE);
};
export const setPreventAuthorApproval = ({ commit }, { preventAuthorApproval }) => { export const setPreventAuthorApproval = ({ commit }, { preventAuthorApproval }) => {
commit(types.SET_PREVENT_AUTHOR_APPROVAL, preventAuthorApproval); commit(types.SET_PREVENT_AUTHOR_APPROVAL, preventAuthorApproval);
}; };
......
...@@ -6,6 +6,8 @@ export const RECEIVE_SETTINGS_ERROR = 'RECEIVE_SETTINGS_ERROR'; ...@@ -6,6 +6,8 @@ export const RECEIVE_SETTINGS_ERROR = 'RECEIVE_SETTINGS_ERROR';
export const REQUEST_UPDATE_SETTINGS = 'REQUEST_UPDATE_SETTINGS'; export const REQUEST_UPDATE_SETTINGS = 'REQUEST_UPDATE_SETTINGS';
export const UPDATE_SETTINGS_SUCCESS = 'UPDATE_SETTINGS_SUCCESS'; export const UPDATE_SETTINGS_SUCCESS = 'UPDATE_SETTINGS_SUCCESS';
export const UPDATE_SETTINGS_ERROR = 'UPDATE_SETTINGS_ERROR'; export const UPDATE_SETTINGS_ERROR = 'UPDATE_SETTINGS_ERROR';
export const DISMISS_SUCCESS_MESSAGE = 'DISMISS_SUCCESS_MESSAGE';
export const DISMISS_ERROR_MESSAGE = 'DISMISS_ERROR_MESSAGE';
export const SET_PREVENT_AUTHOR_APPROVAL = 'SET_PREVENT_AUTHOR_APPROVAL'; export const SET_PREVENT_AUTHOR_APPROVAL = 'SET_PREVENT_AUTHOR_APPROVAL';
export const SET_PREVENT_COMMITTERS_APPROVAL = 'SET_PREVENT_COMMITTERS_APPROVAL'; export const SET_PREVENT_COMMITTERS_APPROVAL = 'SET_PREVENT_COMMITTERS_APPROVAL';
......
import { APPROVAL_SETTINGS_I18N } from '../../../constants';
import * as types from './mutation_types'; import * as types from './mutation_types';
const mapDataToState = (data) => ({ const mapDataToState = (data) => ({
...@@ -11,6 +12,7 @@ const mapDataToState = (data) => ({ ...@@ -11,6 +12,7 @@ const mapDataToState = (data) => ({
export default { export default {
[types.REQUEST_SETTINGS](state) { [types.REQUEST_SETTINGS](state) {
state.isLoading = true; state.isLoading = true;
state.errorMessage = '';
}, },
[types.RECEIVE_SETTINGS_SUCCESS](state, data) { [types.RECEIVE_SETTINGS_SUCCESS](state, data) {
state.settings = { ...mapDataToState(data) }; state.settings = { ...mapDataToState(data) };
...@@ -18,16 +20,27 @@ export default { ...@@ -18,16 +20,27 @@ export default {
}, },
[types.RECEIVE_SETTINGS_ERROR](state) { [types.RECEIVE_SETTINGS_ERROR](state) {
state.isLoading = false; state.isLoading = false;
state.errorMessage = APPROVAL_SETTINGS_I18N.loadingErrorMessage;
}, },
[types.REQUEST_UPDATE_SETTINGS](state) { [types.REQUEST_UPDATE_SETTINGS](state) {
state.isLoading = true; state.isLoading = true;
state.isUpdated = false;
state.errorMessage = '';
}, },
[types.UPDATE_SETTINGS_SUCCESS](state, data) { [types.UPDATE_SETTINGS_SUCCESS](state, data) {
state.settings = { ...mapDataToState(data) }; state.settings = { ...mapDataToState(data) };
state.isLoading = false; state.isLoading = false;
state.isUpdated = true;
}, },
[types.UPDATE_SETTINGS_ERROR](state) { [types.UPDATE_SETTINGS_ERROR](state) {
state.isLoading = false; state.isLoading = false;
state.errorMessage = APPROVAL_SETTINGS_I18N.savingErrorMessage;
},
[types.DISMISS_SUCCESS_MESSAGE](state) {
state.isUpdated = false;
},
[types.DISMISS_ERROR_MESSAGE](state) {
state.errorMessage = '';
}, },
[types.SET_PREVENT_AUTHOR_APPROVAL](state, preventAuthorApproval) { [types.SET_PREVENT_AUTHOR_APPROVAL](state, preventAuthorApproval) {
state.settings.preventAuthorApproval = preventAuthorApproval; state.settings.preventAuthorApproval = preventAuthorApproval;
......
export default () => ({ export default () => ({
settings: {}, settings: {},
isLoading: false, isLoading: false,
isUpdated: false,
errorMessage: '',
}); });
import * as Sentry from '@sentry/browser';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import * as actions from 'ee/approvals/stores/modules/group_settings/actions'; import * as actions from 'ee/approvals/stores/modules/group_settings/actions';
import * as types from 'ee/approvals/stores/modules/group_settings/mutation_types'; import * as types from 'ee/approvals/stores/modules/group_settings/mutation_types';
import getInitialState from 'ee/approvals/stores/modules/group_settings/state'; import getInitialState from 'ee/approvals/stores/modules/group_settings/state';
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status'; import httpStatus from '~/lib/utils/http_status';
jest.mock('~/flash');
describe('EE approvals group settings module actions', () => { describe('EE approvals group settings module actions', () => {
let state; let state;
let mock; let mock;
...@@ -18,10 +16,10 @@ describe('EE approvals group settings module actions', () => { ...@@ -18,10 +16,10 @@ describe('EE approvals group settings module actions', () => {
beforeEach(() => { beforeEach(() => {
state = getInitialState(); state = getInitialState();
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
jest.spyOn(Sentry, 'captureException');
}); });
afterEach(() => { afterEach(() => {
createFlash.mockClear();
mock.restore(); mock.restore();
}); });
...@@ -53,17 +51,10 @@ describe('EE approvals group settings module actions', () => { ...@@ -53,17 +51,10 @@ describe('EE approvals group settings module actions', () => {
actions.fetchSettings, actions.fetchSettings,
approvalSettingsPath, approvalSettingsPath,
state, state,
[ [{ type: types.REQUEST_SETTINGS }, { type: types.RECEIVE_SETTINGS_ERROR }],
{ type: types.REQUEST_SETTINGS },
{ type: types.RECEIVE_SETTINGS_ERROR, payload: data.message },
],
[], [],
).then(() => { ).then(() => {
expect(createFlash).toHaveBeenCalledWith({ expect(Sentry.captureException.mock.calls[0][0]).toBe(data.message);
message: 'There was an error loading merge request approval settings.',
captureError: true,
error: 'Internal Server Error',
});
}); });
}); });
}); });
...@@ -102,12 +93,7 @@ describe('EE approvals group settings module actions', () => { ...@@ -102,12 +93,7 @@ describe('EE approvals group settings module actions', () => {
{ type: types.UPDATE_SETTINGS_SUCCESS, payload: data }, { type: types.UPDATE_SETTINGS_SUCCESS, payload: data },
], ],
[], [],
).then(() => { );
expect(createFlash).toHaveBeenCalledWith({
message: 'Merge request approval settings have been updated.',
type: 'notice',
});
});
}); });
}); });
...@@ -120,22 +106,39 @@ describe('EE approvals group settings module actions', () => { ...@@ -120,22 +106,39 @@ describe('EE approvals group settings module actions', () => {
actions.updateSettings, actions.updateSettings,
approvalSettingsPath, approvalSettingsPath,
state, state,
[ [{ type: types.REQUEST_UPDATE_SETTINGS }, { type: types.UPDATE_SETTINGS_ERROR }],
{ type: types.REQUEST_UPDATE_SETTINGS },
{ type: types.UPDATE_SETTINGS_ERROR, payload: data.message },
],
[], [],
).then(() => { ).then(() => {
expect(createFlash).toHaveBeenCalledWith({ expect(Sentry.captureException.mock.calls[0][0]).toBe(data.message);
message: 'There was an error updating merge request approval settings.',
captureError: true,
error: 'Internal Server Error',
});
}); });
}); });
}); });
}); });
describe('dismissSuccessMessage', () => {
it('commits DISMISS_SUCCESS_MESSAGE', () => {
return testAction(
actions.dismissSuccessMessage,
{},
state,
[{ type: types.DISMISS_SUCCESS_MESSAGE }],
[],
);
});
});
describe('dismissErrorMessage', () => {
it('commits DISMISS_ERROR_MESSAGE', () => {
return testAction(
actions.dismissErrorMessage,
{},
state,
[{ type: types.DISMISS_ERROR_MESSAGE }],
[],
);
});
});
describe.each` describe.each`
action | type | prop action | type | prop
${'setPreventAuthorApproval'} | ${types.SET_PREVENT_AUTHOR_APPROVAL} | ${'preventAuthorApproval'} ${'setPreventAuthorApproval'} | ${types.SET_PREVENT_AUTHOR_APPROVAL} | ${'preventAuthorApproval'}
......
import { APPROVAL_SETTINGS_I18N } from 'ee/approvals/constants';
import mutations from 'ee/approvals/stores/modules/group_settings/mutations'; import mutations from 'ee/approvals/stores/modules/group_settings/mutations';
import getInitialState from 'ee/approvals/stores/modules/group_settings/state'; import getInitialState from 'ee/approvals/stores/modules/group_settings/state';
...@@ -13,6 +14,7 @@ describe('Group settings store mutations', () => { ...@@ -13,6 +14,7 @@ describe('Group settings store mutations', () => {
mutations.REQUEST_SETTINGS(state); mutations.REQUEST_SETTINGS(state);
expect(state.isLoading).toBe(true); expect(state.isLoading).toBe(true);
expect(state.errorMessage).toBe('');
}); });
}); });
...@@ -40,6 +42,7 @@ describe('Group settings store mutations', () => { ...@@ -40,6 +42,7 @@ describe('Group settings store mutations', () => {
mutations.RECEIVE_SETTINGS_ERROR(state); mutations.RECEIVE_SETTINGS_ERROR(state);
expect(state.isLoading).toBe(false); expect(state.isLoading).toBe(false);
expect(state.errorMessage).toBe(APPROVAL_SETTINGS_I18N.loadingErrorMessage);
}); });
}); });
...@@ -48,6 +51,8 @@ describe('Group settings store mutations', () => { ...@@ -48,6 +51,8 @@ describe('Group settings store mutations', () => {
mutations.REQUEST_UPDATE_SETTINGS(state); mutations.REQUEST_UPDATE_SETTINGS(state);
expect(state.isLoading).toBe(true); expect(state.isLoading).toBe(true);
expect(state.isUpdated).toBe(false);
expect(state.errorMessage).toBe('');
}); });
}); });
...@@ -65,6 +70,7 @@ describe('Group settings store mutations', () => { ...@@ -65,6 +70,7 @@ describe('Group settings store mutations', () => {
expect(state.settings.requireUserPassword).toBe(true); expect(state.settings.requireUserPassword).toBe(true);
expect(state.settings.removeApprovalsOnPush).toBe(false); expect(state.settings.removeApprovalsOnPush).toBe(false);
expect(state.isLoading).toBe(false); expect(state.isLoading).toBe(false);
expect(state.isUpdated).toBe(true);
}); });
}); });
...@@ -73,6 +79,23 @@ describe('Group settings store mutations', () => { ...@@ -73,6 +79,23 @@ describe('Group settings store mutations', () => {
mutations.UPDATE_SETTINGS_ERROR(state); mutations.UPDATE_SETTINGS_ERROR(state);
expect(state.isLoading).toBe(false); expect(state.isLoading).toBe(false);
expect(state.errorMessage).toBe(APPROVAL_SETTINGS_I18N.savingErrorMessage);
});
});
describe('DISMISS_SUCCESS_MESSAGE', () => {
it('resets isUpdated', () => {
mutations.DISMISS_SUCCESS_MESSAGE(state);
expect(state.isUpdated).toBe(false);
});
});
describe('DISMISS_ERROR_MESSAGE', () => {
it('resets errorMessage', () => {
mutations.DISMISS_ERROR_MESSAGE(state);
expect(state.errorMessage).toBe('');
}); });
}); });
......
...@@ -4160,6 +4160,30 @@ msgstr "" ...@@ -4160,6 +4160,30 @@ msgstr ""
msgid "ApprovalRule|Target branch" msgid "ApprovalRule|Target branch"
msgstr "" msgstr ""
msgid "ApprovalSettings|Merge request approval settings have been updated."
msgstr ""
msgid "ApprovalSettings|Prevent MR approvals by the author."
msgstr ""
msgid "ApprovalSettings|Prevent approval of merge requests by merge request committers."
msgstr ""
msgid "ApprovalSettings|Prevent users from modifying MR approval rules."
msgstr ""
msgid "ApprovalSettings|Remove all approvals in a merge request when new commits are pushed to its source branch."
msgstr ""
msgid "ApprovalSettings|Require user password for approvals."
msgstr ""
msgid "ApprovalSettings|There was an error loading merge request approval settings."
msgstr ""
msgid "ApprovalSettings|There was an error updating merge request approval settings."
msgstr ""
msgid "ApprovalStatusTooltip|Adheres to separation of duties" msgid "ApprovalStatusTooltip|Adheres to separation of duties"
msgstr "" msgstr ""
...@@ -20486,9 +20510,6 @@ msgstr "" ...@@ -20486,9 +20510,6 @@ msgstr ""
msgid "Merge request analytics" msgid "Merge request analytics"
msgstr "" msgstr ""
msgid "Merge request approval settings have been updated."
msgstr ""
msgid "Merge request approvals" msgid "Merge request approvals"
msgstr "" msgstr ""
...@@ -24764,9 +24785,6 @@ msgstr "" ...@@ -24764,9 +24785,6 @@ msgstr ""
msgid "Prevent adding new members to project membership within this group" msgid "Prevent adding new members to project membership within this group"
msgstr "" msgstr ""
msgid "Prevent approval of merge requests by merge request committers."
msgstr ""
msgid "Prevent environment from auto-stopping" msgid "Prevent environment from auto-stopping"
msgstr "" msgstr ""
...@@ -24782,9 +24800,6 @@ msgstr "" ...@@ -24782,9 +24800,6 @@ msgstr ""
msgid "Prevent users from modifying MR approval rules in projects and merge requests." msgid "Prevent users from modifying MR approval rules in projects and merge requests."
msgstr "" msgstr ""
msgid "Prevent users from modifying MR approval rules."
msgstr ""
msgid "Prevent users from performing write operations on GitLab while performing maintenance." msgid "Prevent users from performing write operations on GitLab while performing maintenance."
msgstr "" msgstr ""
...@@ -27123,9 +27138,6 @@ msgstr "" ...@@ -27123,9 +27138,6 @@ msgstr ""
msgid "Remove access" msgid "Remove access"
msgstr "" msgstr ""
msgid "Remove all approvals in a merge request when new commits are pushed to its source branch."
msgstr ""
msgid "Remove all or specific assignee(s)" msgid "Remove all or specific assignee(s)"
msgstr "" msgstr ""
...@@ -33123,9 +33135,6 @@ msgstr "" ...@@ -33123,9 +33135,6 @@ msgstr ""
msgid "There was an error importing the Jira project." msgid "There was an error importing the Jira project."
msgstr "" msgstr ""
msgid "There was an error loading merge request approval settings."
msgstr ""
msgid "There was an error loading related feature flags" msgid "There was an error loading related feature flags"
msgstr "" msgstr ""
...@@ -33165,9 +33174,6 @@ msgstr "" ...@@ -33165,9 +33174,6 @@ msgstr ""
msgid "There was an error trying to validate your query" msgid "There was an error trying to validate your query"
msgstr "" msgstr ""
msgid "There was an error updating merge request approval settings."
msgstr ""
msgid "There was an error updating the Geo Settings" msgid "There was an error updating the Geo Settings"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment