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-07-14 21:08:31 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-07-14 21:08:31 +0300
commit06ac12d53c3f0b7cee2755a1254bf1af05d55044 (patch)
tree95d0be0bd751a22d6135f496c425c44d774fbe54 /app/assets/javascripts/admin
parentb689f371350fbf1b71f266764ee018befc9b91f7 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/admin')
-rw-r--r--app/assets/javascripts/admin/users/components/modals/delete_user_modal.vue151
-rw-r--r--app/assets/javascripts/admin/users/components/modals/user_modal_manager.vue77
-rw-r--r--app/assets/javascripts/admin/users/constants.js6
-rw-r--r--app/assets/javascripts/admin/users/index.js49
4 files changed, 283 insertions, 0 deletions
diff --git a/app/assets/javascripts/admin/users/components/modals/delete_user_modal.vue b/app/assets/javascripts/admin/users/components/modals/delete_user_modal.vue
new file mode 100644
index 00000000000..413163c8536
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/modals/delete_user_modal.vue
@@ -0,0 +1,151 @@
+<script>
+import { GlModal, GlButton, GlFormInput, GlSprintf } from '@gitlab/ui';
+import * as Sentry from '@sentry/browser';
+import { s__, sprintf } from '~/locale';
+import OncallSchedulesList from '~/vue_shared/components/oncall_schedules_list.vue';
+
+export default {
+ components: {
+ GlModal,
+ GlButton,
+ GlFormInput,
+ GlSprintf,
+ OncallSchedulesList,
+ },
+ props: {
+ title: {
+ type: String,
+ required: true,
+ },
+ content: {
+ type: String,
+ required: true,
+ },
+ action: {
+ type: String,
+ required: true,
+ },
+ secondaryAction: {
+ type: String,
+ required: true,
+ },
+ deleteUserUrl: {
+ type: String,
+ required: true,
+ },
+ blockUserUrl: {
+ type: String,
+ required: true,
+ },
+ username: {
+ type: String,
+ required: true,
+ },
+ csrfToken: {
+ type: String,
+ required: true,
+ },
+ oncallSchedules: {
+ type: String,
+ required: false,
+ default: '[]',
+ },
+ },
+ data() {
+ return {
+ enteredUsername: '',
+ };
+ },
+ computed: {
+ modalTitle() {
+ return sprintf(this.title, { username: this.username }, false);
+ },
+ secondaryButtonLabel() {
+ return s__('AdminUsers|Block user');
+ },
+ canSubmit() {
+ return this.enteredUsername === this.username;
+ },
+ schedules() {
+ try {
+ return JSON.parse(this.oncallSchedules);
+ } catch (e) {
+ Sentry.captureException(e);
+ }
+ return [];
+ },
+ },
+ methods: {
+ show() {
+ this.$refs.modal.show();
+ },
+ onCancel() {
+ this.enteredUsername = '';
+ this.$refs.modal.hide();
+ },
+ onSecondaryAction() {
+ const { form } = this.$refs;
+
+ form.action = this.blockUserUrl;
+ this.$refs.method.value = 'put';
+
+ form.submit();
+ },
+ onSubmit() {
+ this.$refs.form.submit();
+ this.enteredUsername = '';
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-modal ref="modal" modal-id="delete-user-modal" :title="modalTitle" kind="danger">
+ <p>
+ <gl-sprintf :message="content">
+ <template #username>
+ <strong>{{ username }}</strong>
+ </template>
+ <template #strong="props">
+ <strong>{{ props.content }}</strong>
+ </template>
+ </gl-sprintf>
+ </p>
+
+ <oncall-schedules-list v-if="schedules.length" :schedules="schedules" :user-name="username" />
+
+ <p>
+ <gl-sprintf :message="s__('AdminUsers|To confirm, type %{username}')">
+ <template #username>
+ <code>{{ username }}</code>
+ </template>
+ </gl-sprintf>
+ </p>
+
+ <form ref="form" :action="deleteUserUrl" method="post" @submit.prevent>
+ <input ref="method" type="hidden" name="_method" value="delete" />
+ <input :value="csrfToken" type="hidden" name="authenticity_token" />
+ <gl-form-input
+ v-model="enteredUsername"
+ autofocus
+ type="text"
+ name="username"
+ autocomplete="off"
+ />
+ </form>
+ <template #modal-footer>
+ <gl-button @click="onCancel">{{ s__('Cancel') }}</gl-button>
+ <gl-button
+ :disabled="!canSubmit"
+ category="secondary"
+ variant="danger"
+ @click="onSecondaryAction"
+ >
+ {{ secondaryAction }}
+ </gl-button>
+ <gl-button :disabled="!canSubmit" category="primary" variant="danger" @click="onSubmit">{{
+ action
+ }}</gl-button>
+ </template>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/admin/users/components/modals/user_modal_manager.vue b/app/assets/javascripts/admin/users/components/modals/user_modal_manager.vue
new file mode 100644
index 00000000000..1dfea3f1e7b
--- /dev/null
+++ b/app/assets/javascripts/admin/users/components/modals/user_modal_manager.vue
@@ -0,0 +1,77 @@
+<script>
+import DeleteUserModal from './delete_user_modal.vue';
+
+export default {
+ components: { DeleteUserModal },
+ props: {
+ modalConfiguration: {
+ required: true,
+ type: Object,
+ },
+ csrfToken: {
+ required: true,
+ type: String,
+ },
+ selector: {
+ required: true,
+ type: String,
+ },
+ },
+ data() {
+ return {
+ currentModalData: null,
+ };
+ },
+ computed: {
+ activeModal() {
+ return Boolean(this.currentModalData);
+ },
+
+ modalProps() {
+ const { glModalAction: requestedAction } = this.currentModalData;
+ return {
+ ...this.modalConfiguration[requestedAction],
+ ...this.currentModalData,
+ csrfToken: this.csrfToken,
+ };
+ },
+ },
+
+ mounted() {
+ /*
+ * Here we're looking for every button that needs to launch a modal
+ * on click, and then attaching a click event handler to show the modal
+ * if it's correctly configured.
+ *
+ * TODO: Replace this with integrated modal components https://gitlab.com/gitlab-org/gitlab/-/issues/320922
+ */
+ document.querySelectorAll(this.selector).forEach((button) => {
+ button.addEventListener('click', (e) => {
+ if (!button.dataset.glModalAction) return;
+
+ e.preventDefault();
+ this.show(button.dataset);
+ });
+ });
+ },
+
+ methods: {
+ show(modalData) {
+ const { glModalAction: requestedAction } = modalData;
+
+ if (!this.modalConfiguration[requestedAction]) {
+ throw new Error(`Modal action ${requestedAction} has no configuration in HTML`);
+ }
+
+ this.currentModalData = modalData;
+
+ return this.$nextTick().then(() => {
+ this.$refs.modal.show();
+ });
+ },
+ },
+};
+</script>
+<template>
+ <delete-user-modal v-if="activeModal" ref="modal" v-bind="modalProps" />
+</template>
diff --git a/app/assets/javascripts/admin/users/constants.js b/app/assets/javascripts/admin/users/constants.js
index d0421413132..33ee7e1cb0d 100644
--- a/app/assets/javascripts/admin/users/constants.js
+++ b/app/assets/javascripts/admin/users/constants.js
@@ -20,3 +20,9 @@ export const I18N_USER_ACTIONS = {
ban: s__('AdminUsers|Ban user'),
unban: s__('AdminUsers|Unban user'),
};
+
+export const CONFIRM_DELETE_BUTTON_SELECTOR = '.js-delete-user-modal-button';
+
+export const MODAL_TEXTS_CONTAINER_SELECTOR = '#js-modal-texts';
+
+export const MODAL_MANAGER_SELECTOR = '#js-delete-user-modal';
diff --git a/app/assets/javascripts/admin/users/index.js b/app/assets/javascripts/admin/users/index.js
index 54c8edc080b..05f8469e61a 100644
--- a/app/assets/javascripts/admin/users/index.js
+++ b/app/assets/javascripts/admin/users/index.js
@@ -2,7 +2,14 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import csrf from '~/lib/utils/csrf';
import AdminUsersApp from './components/app.vue';
+import ModalManager from './components/modals/user_modal_manager.vue';
+import {
+ CONFIRM_DELETE_BUTTON_SELECTOR,
+ MODAL_TEXTS_CONTAINER_SELECTOR,
+ MODAL_MANAGER_SELECTOR,
+} from './constants';
Vue.use(VueApollo);
@@ -29,3 +36,45 @@ export const initAdminUsersApp = (el = document.querySelector('#js-admin-users-a
}),
});
};
+
+export const initDeleteUserModals = () => {
+ const modalsMountElement = document.querySelector(MODAL_TEXTS_CONTAINER_SELECTOR);
+
+ if (!modalsMountElement) {
+ return;
+ }
+
+ const modalConfiguration = Array.from(modalsMountElement.children).reduce((accumulator, node) => {
+ const { modal, ...config } = node.dataset;
+
+ return {
+ ...accumulator,
+ [modal]: {
+ title: node.dataset.title,
+ ...config,
+ content: node.innerHTML,
+ },
+ };
+ }, {});
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: MODAL_MANAGER_SELECTOR,
+ functional: true,
+ methods: {
+ show(...args) {
+ this.$refs.manager.show(...args);
+ },
+ },
+ render(h) {
+ return h(ModalManager, {
+ ref: 'manager',
+ props: {
+ selector: CONFIRM_DELETE_BUTTON_SELECTOR,
+ modalConfiguration,
+ csrfToken: csrf.token,
+ },
+ });
+ },
+ });
+};