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>2023-08-10 09:09:29 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-08-10 09:09:29 +0300
commit76c4dd062c4eeb853866ef8b6451c59f9e24221c (patch)
treefaf481c7b2f6da10c13234ad4e4a6ca1cb5a1030 /app/assets/javascripts/projects
parentc2858333644a2bca10fd556a5a298b4a1aaedca2 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/projects')
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/custom_email.vue36
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/custom_email_form.vue291
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/custom_email_constants.js43
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/index.js3
4 files changed, 372 insertions, 1 deletions
diff --git a/app/assets/javascripts/projects/settings_service_desk/components/custom_email.vue b/app/assets/javascripts/projects/settings_service_desk/components/custom_email.vue
index f90633c6e03..a1c1b1141a7 100644
--- a/app/assets/javascripts/projects/settings_service_desk/components/custom_email.vue
+++ b/app/assets/javascripts/projects/settings_service_desk/components/custom_email.vue
@@ -8,7 +8,9 @@ import {
I18N_CARD_TITLE,
I18N_GENERIC_ERROR,
I18N_FEEDBACK_PARAGRAPH,
+ I18N_TOAST_SAVED,
} from '../custom_email_constants';
+import CustomEmailForm from './custom_email_form.vue';
export default {
components: {
@@ -18,11 +20,13 @@ export default {
GlSprintf,
GlLink,
GlCard,
+ CustomEmailForm,
},
FEEDBACK_ISSUE_URL,
I18N_LOADING_LABEL,
I18N_CARD_TITLE,
I18N_FEEDBACK_PARAGRAPH,
+ I18N_TOAST_SAVED,
props: {
incomingEmail: {
type: String,
@@ -38,6 +42,7 @@ export default {
data() {
return {
loading: true,
+ submitting: false,
customEmail: null,
enabled: false,
verificationState: null,
@@ -47,6 +52,11 @@ export default {
alertMessage: null,
};
},
+ computed: {
+ customEmailNotSetUp() {
+ return !this.enabled && this.verificationState === null && this.customEmail === null;
+ },
+ },
mounted() {
this.getCustomEmailDetails();
},
@@ -76,6 +86,21 @@ export default {
this.smtpAddress = data.custom_email_smtp_address;
this.errorMessage = data.error_message;
},
+ onSaveCustomEmail(requestData) {
+ this.alertMessage = null;
+ this.submitting = true;
+
+ axios
+ .post(this.customEmailEndpoint, requestData)
+ .then(({ data }) => {
+ this.updateData(data);
+ this.$toast.show(this.$options.I18N_TOAST_SAVED);
+ })
+ .catch(this.handleRequestError)
+ .finally(() => {
+ this.submitting = false;
+ });
+ },
},
};
</script>
@@ -108,11 +133,20 @@ export default {
<gl-alert
v-if="alertMessage"
variant="warning"
- class="gl-mt-n5 gl-mx-n5"
+ class="gl-mt-n5 gl-mb-4 gl-mx-n5"
@dismiss="dismissAlert"
>
{{ alertMessage }}
</gl-alert>
+
+ <!-- Use v-show to preserve form data after verification failure
+ without the need to maintain a state in this component. -->
+ <custom-email-form
+ v-show="customEmailNotSetUp && !loading"
+ :incoming-email="incomingEmail"
+ :submitting="submitting"
+ @submit="onSaveCustomEmail"
+ />
</template>
<template #footer>
diff --git a/app/assets/javascripts/projects/settings_service_desk/components/custom_email_form.vue b/app/assets/javascripts/projects/settings_service_desk/components/custom_email_form.vue
new file mode 100644
index 00000000000..7088627a487
--- /dev/null
+++ b/app/assets/javascripts/projects/settings_service_desk/components/custom_email_form.vue
@@ -0,0 +1,291 @@
+<script>
+import { GlButton, GlForm, GlFormGroup, GlFormInputGroup, GlFormInput } from '@gitlab/ui';
+import { isEmptyValue, hasMinimumLength, isIntegerGreaterThan, isEmail } from '~/lib/utils/forms';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import {
+ I18N_FORM_INTRODUCTION_PARAGRAPH,
+ I18N_FORM_CUSTOM_EMAIL_LABEL,
+ I18N_FORM_CUSTOM_EMAIL_DESCRIPTION,
+ I18N_FORM_FORWARDING_LABEL,
+ I18N_FORM_FORWARDING_CLIPBOARD_BUTTON_TITLE,
+ I18N_FORM_SMTP_ADDRESS_LABEL,
+ I18N_FORM_SMTP_PORT_LABEL,
+ I18N_FORM_SMTP_PORT_DESCRIPTION,
+ I18N_FORM_SMTP_USERNAME_LABEL,
+ I18N_FORM_SMTP_PASSWORD_LABEL,
+ I18N_FORM_SMTP_PASSWORD_DESCRIPTION,
+ I18N_FORM_SUBMIT_LABEL,
+ I18N_FORM_INVALID_FEEDBACK_CUSTOM_EMAIL,
+ I18N_FORM_INVALID_FEEDBACK_SMTP_ADDRESS,
+ I18N_FORM_INVALID_FEEDBACK_SMTP_PORT,
+ I18N_FORM_INVALID_FEEDBACK_SMTP_USERNAME,
+ I18N_FORM_INVALID_FEEDBACK_SMTP_PASSWORD,
+} from '../custom_email_constants';
+
+export default {
+ components: {
+ ClipboardButton,
+ GlButton,
+ GlForm,
+ GlFormGroup,
+ GlFormInputGroup,
+ GlFormInput,
+ },
+ I18N_FORM_INTRODUCTION_PARAGRAPH,
+ I18N_FORM_CUSTOM_EMAIL_LABEL,
+ I18N_FORM_CUSTOM_EMAIL_DESCRIPTION,
+ I18N_FORM_FORWARDING_LABEL,
+ I18N_FORM_FORWARDING_CLIPBOARD_BUTTON_TITLE,
+ I18N_FORM_SMTP_ADDRESS_LABEL,
+ I18N_FORM_SMTP_PORT_LABEL,
+ I18N_FORM_SMTP_PORT_DESCRIPTION,
+ I18N_FORM_SMTP_USERNAME_LABEL,
+ I18N_FORM_SMTP_PASSWORD_LABEL,
+ I18N_FORM_SMTP_PASSWORD_DESCRIPTION,
+ I18N_FORM_SUBMIT_LABEL,
+ I18N_FORM_INVALID_FEEDBACK_CUSTOM_EMAIL,
+ I18N_FORM_INVALID_FEEDBACK_SMTP_ADDRESS,
+ I18N_FORM_INVALID_FEEDBACK_SMTP_PORT,
+ I18N_FORM_INVALID_FEEDBACK_SMTP_USERNAME,
+ I18N_FORM_INVALID_FEEDBACK_SMTP_PASSWORD,
+ props: {
+ incomingEmail: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ submitting: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ customEmail: '',
+ forwardingConfigured: false,
+ smtpAddress: '',
+ smtpPort: '587',
+ smtpUsername: '',
+ smtpPassword: '',
+ validationState: {
+ customEmail: null,
+ smtpAddress: null,
+ smtpPort: true,
+ smtpUsername: null,
+ smtpPassword: null,
+ },
+ };
+ },
+ computed: {
+ isFormValid() {
+ return Object.values(this.validationState).every(Boolean);
+ },
+ },
+ methods: {
+ onSubmit() {
+ this.triggerVerification();
+
+ if (!this.isFormValid) {
+ return;
+ }
+
+ this.$emit('submit', this.getRequestFormData());
+ },
+ getRequestFormData() {
+ return {
+ custom_email: this.customEmail,
+ smtp_address: this.smtpAddress,
+ smtp_port: this.smtpPort,
+ smtp_username: this.smtpUsername,
+ smtp_password: this.smtpPassword,
+ };
+ },
+ onCustomEmailChange() {
+ this.validateCustomEmail();
+
+ if (this.validationState.customEmail && isEmptyValue(this.smtpUsername)) {
+ this.smtpUsername = this.customEmail;
+ this.validateSmtpUsername();
+ }
+ },
+ validateCustomEmail() {
+ this.validationState.customEmail = isEmail(this.customEmail);
+ },
+ validateSmtpAddress() {
+ this.validationState.smtpAddress = !isEmptyValue(this.smtpAddress);
+ },
+ validateSmtpPort() {
+ this.validationState.smtpPort = isIntegerGreaterThan(this.smtpPort, 0);
+ },
+ validateSmtpUsername() {
+ this.validationState.smtpUsername = !isEmptyValue(this.smtpUsername);
+ },
+ validateSmtpPassword() {
+ this.validationState.smtpPassword = hasMinimumLength(this.smtpPassword, 8);
+ },
+ triggerVerification() {
+ this.validateCustomEmail();
+ this.validateSmtpAddress();
+ this.validateSmtpPort();
+ this.validateSmtpUsername();
+ this.validateSmtpPassword();
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <p>{{ $options.I18N_FORM_INTRODUCTION_PARAGRAPH }}</p>
+ <gl-form class="js-quick-submit" @submit.prevent="onSubmit">
+ <gl-form-group
+ :label="$options.I18N_FORM_FORWARDING_LABEL"
+ label-for="custom-email-form-forwarding"
+ class="gl-mt-3"
+ >
+ <gl-form-input-group>
+ <gl-form-input
+ id="custom-email-form-forwarding"
+ ref="service-desk-incoming-email"
+ type="text"
+ data-testid="custom-email-form-forwarding"
+ :aria-label="$options.I18N_FORM_FORWARDING_LABEL"
+ :value="incomingEmail"
+ :disabled="true"
+ />
+ <template #append>
+ <clipboard-button
+ :title="$options.I18N_FORM_FORWARDING_CLIPBOARD_BUTTON_TITLE"
+ :text="incomingEmail"
+ css-class="input-group-text"
+ />
+ </template>
+ </gl-form-input-group>
+ </gl-form-group>
+
+ <gl-form-group
+ :label="$options.I18N_FORM_CUSTOM_EMAIL_LABEL"
+ label-for="custom-email-form-custom-email"
+ data-testid="form-group-custom-email"
+ :invalid-feedback="$options.I18N_FORM_INVALID_FEEDBACK_CUSTOM_EMAIL"
+ class="gl-mt-3"
+ :description="$options.I18N_FORM_CUSTOM_EMAIL_DESCRIPTION"
+ >
+ <!-- eslint-disable @gitlab/vue-require-i18n-attribute-strings -->
+ <gl-form-input
+ id="custom-email-form-custom-email"
+ v-model.trim="customEmail"
+ data-testid="form-custom-email"
+ :aria-label="$options.I18N_FORM_CUSTOM_EMAIL_LABEL"
+ placeholder="contact@example.com"
+ type="email"
+ :state="validationState.customEmail"
+ :required="true"
+ :disabled="submitting"
+ @change="onCustomEmailChange"
+ />
+ <!-- eslint-enable @gitlab/vue-require-i18n-attribute-strings -->
+ </gl-form-group>
+
+ <gl-form-group
+ :label="$options.I18N_FORM_SMTP_ADDRESS_LABEL"
+ label-for="custom-email-form-smtp-address"
+ data-testid="form-group-smtp-address"
+ :invalid-feedback="$options.I18N_FORM_INVALID_FEEDBACK_SMTP_ADDRESS"
+ class="gl-mt-3"
+ >
+ <!-- eslint-disable @gitlab/vue-require-i18n-attribute-strings -->
+ <gl-form-input
+ id="custom-email-form-smtp-address"
+ v-model.trim="smtpAddress"
+ data-testid="form-smtp-address"
+ :aria-label="$options.I18N_FORM_SMTP_ADDRESS_LABEL"
+ placeholder="smtp.example.com"
+ type="email"
+ :state="validationState.smtpAddress"
+ :required="true"
+ :disabled="submitting"
+ @change="validateSmtpAddress"
+ />
+ <!-- eslint-enable @gitlab/vue-require-i18n-attribute-strings -->
+ </gl-form-group>
+
+ <gl-form-group
+ :label="$options.I18N_FORM_SMTP_PORT_LABEL"
+ label-for="custom-email-form-smtp-port"
+ :invalid-feedback="$options.I18N_FORM_INVALID_FEEDBACK_SMTP_PORT"
+ class="gl-mt-3"
+ :description="$options.I18N_FORM_SMTP_PORT_DESCRIPTION"
+ >
+ <!-- eslint-disable @gitlab/vue-require-i18n-attribute-strings -->
+ <gl-form-input
+ id="custom-email-form-smtp-port"
+ v-model.trim="smtpPort"
+ data-testid="form-smtp-port"
+ :aria-label="$options.I18N_FORM_SMTP_PORT_LABEL"
+ placeholder="587"
+ type="number"
+ :state="validationState.smtpPort"
+ :required="true"
+ :disabled="submitting"
+ @change="validateSmtpPort"
+ />
+ <!-- eslint-enable @gitlab/vue-require-i18n-attribute-strings -->
+ </gl-form-group>
+
+ <gl-form-group
+ :label="$options.I18N_FORM_SMTP_USERNAME_LABEL"
+ label-for="custom-email-form-smtp-username"
+ :invalid-feedback="$options.I18N_FORM_INVALID_FEEDBACK_SMTP_USERNAME"
+ class="gl-mt-3"
+ >
+ <!-- eslint-disable @gitlab/vue-require-i18n-attribute-strings -->
+ <gl-form-input
+ id="custom-email-form-smtp-username"
+ v-model.trim="smtpUsername"
+ data-testid="form-smtp-username"
+ :aria-label="$options.I18N_FORM_SMTP_USERNAME_LABEL"
+ placeholder="contact@example.com"
+ :state="validationState.smtpUsername"
+ :required="true"
+ :disabled="submitting"
+ @change="validateSmtpUsername"
+ />
+ <!-- eslint-enable @gitlab/vue-require-i18n-attribute-strings -->
+ </gl-form-group>
+
+ <gl-form-group
+ :label="$options.I18N_FORM_SMTP_PASSWORD_LABEL"
+ label-for="custom-email-form-smtp-password"
+ :invalid-feedback="$options.I18N_FORM_INVALID_FEEDBACK_SMTP_PASSWORD"
+ class="gl-mt-3"
+ :description="$options.I18N_FORM_SMTP_PASSWORD_DESCRIPTION"
+ >
+ <gl-form-input
+ id="custom-email-form-smtp-password"
+ v-model.trim="smtpPassword"
+ data-testid="form-smtp-password"
+ :aria-label="$options.I18N_FORM_SMTP_PASSWORD_LABEL"
+ type="password"
+ :state="validationState.smtpPassword"
+ :required="true"
+ :disabled="submitting"
+ @change="validateSmtpPassword"
+ />
+ </gl-form-group>
+
+ <gl-button
+ type="submit"
+ variant="confirm"
+ class="gl-mt-5"
+ data-testid="form-submit"
+ :disabled="!isFormValid"
+ :loading="submitting"
+ @click="onSubmit"
+ >
+ {{ $options.I18N_FORM_SUBMIT_LABEL }}
+ </gl-button>
+ </gl-form>
+ </div>
+</template>
diff --git a/app/assets/javascripts/projects/settings_service_desk/custom_email_constants.js b/app/assets/javascripts/projects/settings_service_desk/custom_email_constants.js
index 9770a1f4df9..cc5dc8af2e4 100644
--- a/app/assets/javascripts/projects/settings_service_desk/custom_email_constants.js
+++ b/app/assets/javascripts/projects/settings_service_desk/custom_email_constants.js
@@ -8,3 +8,46 @@ export const I18N_FEEDBACK_PARAGRAPH = s__(
'ServiceDesk|Please share your feedback on this feature in the %{linkStart}feedback issue%{linkEnd}',
);
export const I18N_GENERIC_ERROR = __('An error occurred. Please try again.');
+
+export const I18N_TOAST_SAVED = s__(
+ 'ServiceDesk|Saved custom email address and started verification.',
+);
+
+export const I18N_FORM_INTRODUCTION_PARAGRAPH = s__(
+ 'ServiceDesk|Connect a custom email address your customers can use to create Service Desk issues. Forward all emails from your custom email address to the Service Desk email address of this project. GitLab will send Service Desk emails from the custom address on your behalf using your SMTP credentials.',
+);
+export const I18N_FORM_FORWARDING_LABEL = s__(
+ 'ServiceDesk|Service Desk email address to forward emails to',
+);
+export const I18N_FORM_FORWARDING_CLIPBOARD_BUTTON_TITLE = s__(
+ 'ServiceDesk|Copy Service Desk email address',
+);
+export const I18N_FORM_CUSTOM_EMAIL_LABEL = s__('ServiceDesk|Custom email address');
+export const I18N_FORM_CUSTOM_EMAIL_DESCRIPTION = s__(
+ 'ServiceDesk|Email address your customers can use to send support requests. It must support sub-addressing.',
+);
+export const I18N_FORM_SMTP_ADDRESS_LABEL = s__('ServiceDesk|SMTP host');
+export const I18N_FORM_SMTP_PORT_LABEL = s__('ServiceDesk|SMTP port');
+export const I18N_FORM_SMTP_PORT_DESCRIPTION = s__(
+ 'ServiceDesk|Common ports are 587 when using TLS, and 25 when not.',
+);
+export const I18N_FORM_SMTP_USERNAME_LABEL = s__('ServiceDesk|SMTP username');
+export const I18N_FORM_SMTP_PASSWORD_LABEL = s__('ServiceDesk|SMTP password');
+export const I18N_FORM_SMTP_PASSWORD_DESCRIPTION = s__('ServiceDesk|Minimum 8 characters long.');
+export const I18N_FORM_SUBMIT_LABEL = s__('ServiceDesk|Save and test connection');
+
+export const I18N_FORM_INVALID_FEEDBACK_CUSTOM_EMAIL = s__(
+ 'ServiceDesk|Custom email is required and must be a valid email address.',
+);
+export const I18N_FORM_INVALID_FEEDBACK_SMTP_ADDRESS = s__(
+ 'ServiceDesk|SMTP address is required and must be resolvable.',
+);
+export const I18N_FORM_INVALID_FEEDBACK_SMTP_PORT = s__(
+ 'ServiceDesk|SMTP port is required and must be a port number larger than 0.',
+);
+export const I18N_FORM_INVALID_FEEDBACK_SMTP_USERNAME = s__(
+ 'ServiceDesk|SMTP username is required.',
+);
+export const I18N_FORM_INVALID_FEEDBACK_SMTP_PASSWORD = s__(
+ 'ServiceDesk|SMTP password is required and must be at least 8 characters long.',
+);
diff --git a/app/assets/javascripts/projects/settings_service_desk/index.js b/app/assets/javascripts/projects/settings_service_desk/index.js
index dd9585734db..c4d4f42576f 100644
--- a/app/assets/javascripts/projects/settings_service_desk/index.js
+++ b/app/assets/javascripts/projects/settings_service_desk/index.js
@@ -1,7 +1,10 @@
+import { GlToast } from '@gitlab/ui';
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import ServiceDeskRoot from './components/service_desk_root.vue';
+Vue.use(GlToast);
+
export default () => {
const el = document.querySelector('.js-service-desk-setting-root');