diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-10 09:09:29 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-10 09:09:29 +0300 |
commit | 76c4dd062c4eeb853866ef8b6451c59f9e24221c (patch) | |
tree | faf481c7b2f6da10c13234ad4e4a6ca1cb5a1030 /app/assets/javascripts/projects | |
parent | c2858333644a2bca10fd556a5a298b4a1aaedca2 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/projects')
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'); |