diff options
Diffstat (limited to 'app/assets/javascripts/projects/settings_service_desk')
5 files changed, 400 insertions, 0 deletions
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 new file mode 100644 index 00000000000..d61569fcd6e --- /dev/null +++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue @@ -0,0 +1,160 @@ +<script> +import { GlAlert } from '@gitlab/ui'; +import { __ } from '~/locale'; +import ServiceDeskSetting from './service_desk_setting.vue'; +import ServiceDeskService from '../services/service_desk_service'; +import eventHub from '../event_hub'; + +export default { + name: 'ServiceDeskRoot', + components: { + GlAlert, + ServiceDeskSetting, + }, + props: { + initialIsEnabled: { + type: Boolean, + required: true, + }, + endpoint: { + type: String, + required: true, + }, + initialIncomingEmail: { + type: String, + required: false, + default: '', + }, + selectedTemplate: { + type: String, + required: false, + default: '', + }, + outgoingName: { + type: String, + required: false, + default: '', + }, + projectKey: { + type: String, + required: false, + default: '', + }, + templates: { + type: Array, + required: false, + default: () => [], + }, + }, + + data() { + return { + isEnabled: this.initialIsEnabled, + incomingEmail: this.initialIncomingEmail, + isTemplateSaving: false, + isAlertShowing: false, + alertVariant: 'danger', + alertMessage: '', + }; + }, + + created() { + eventHub.$on('serviceDeskEnabledCheckboxToggled', this.onEnableToggled); + eventHub.$on('serviceDeskTemplateSave', this.onSaveTemplate); + + this.service = new ServiceDeskService(this.endpoint); + + if (this.isEnabled && !this.incomingEmail) { + this.fetchIncomingEmail(); + } + }, + + beforeDestroy() { + eventHub.$off('serviceDeskEnabledCheckboxToggled', this.onEnableToggled); + eventHub.$off('serviceDeskTemplateSave', this.onSaveTemplate); + }, + + methods: { + fetchIncomingEmail() { + this.service + .fetchIncomingEmail() + .then(({ data }) => { + const email = data.service_desk_address; + if (!email) { + throw new Error(__("Response didn't include `service_desk_address`")); + } + + this.incomingEmail = email; + }) + .catch(() => + this.showAlert(__('An error occurred while fetching the Service Desk address.')), + ); + }, + + onEnableToggled(isChecked) { + this.isEnabled = isChecked; + this.incomingEmail = ''; + + this.service + .toggleServiceDesk(isChecked) + .then(({ data }) => { + const email = data.service_desk_address; + if (isChecked && !email) { + throw new Error(__("Response didn't include `service_desk_address`")); + } + + this.incomingEmail = email; + }) + .catch(() => { + const message = isChecked + ? __('An error occurred while enabling Service Desk.') + : __('An error occurred while disabling Service Desk.'); + + this.showAlert(message); + }); + }, + + onSaveTemplate({ selectedTemplate, outgoingName, projectKey }) { + this.isTemplateSaving = true; + this.service + .updateTemplate({ selectedTemplate, outgoingName, projectKey }, this.isEnabled) + .then(() => this.showAlert(__('Template was successfully saved.'), 'success')) + .catch(() => + this.showAlert( + __('An error occurred while saving the template. Please check if the template exists.'), + ), + ) + .finally(() => { + this.isTemplateSaving = false; + }); + }, + + showAlert(message, variant = 'danger') { + this.isAlertShowing = true; + this.alertMessage = message; + this.alertVariant = variant; + }, + + onDismiss() { + this.isAlertShowing = false; + }, + }, +}; +</script> + +<template> + <div> + <gl-alert v-if="isAlertShowing" class="mb-3" :variant="alertVariant" @dismiss="onDismiss"> + {{ alertMessage }} + </gl-alert> + <service-desk-setting + :is-enabled="isEnabled" + :incoming-email="incomingEmail" + :initial-selected-template="selectedTemplate" + :initial-outgoing-name="outgoingName" + :initial-project-key="projectKey" + :templates="templates" + :is-template-saving="isTemplateSaving" + /> + </div> +</template> diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue new file mode 100644 index 00000000000..43c20fea43e --- /dev/null +++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue @@ -0,0 +1,169 @@ +<script> +import { GlDeprecatedButton, GlFormSelect, GlToggle, GlLoadingIcon } from '@gitlab/ui'; +import { __ } from '~/locale'; +import tooltip from '~/vue_shared/directives/tooltip'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import eventHub from '../event_hub'; + +export default { + name: 'ServiceDeskSetting', + directives: { + tooltip, + }, + components: { + ClipboardButton, + GlDeprecatedButton, + GlFormSelect, + GlToggle, + GlLoadingIcon, + }, + mixins: [glFeatureFlagsMixin()], + props: { + isEnabled: { + type: Boolean, + required: true, + }, + incomingEmail: { + type: String, + required: false, + default: '', + }, + initialSelectedTemplate: { + type: String, + required: false, + default: '', + }, + initialOutgoingName: { + type: String, + required: false, + default: '', + }, + initialProjectKey: { + type: String, + required: false, + default: '', + }, + templates: { + type: Array, + required: false, + default: () => [], + }, + isTemplateSaving: { + type: Boolean, + required: false, + default: false, + }, + }, + data() { + return { + selectedTemplate: this.initialSelectedTemplate, + outgoingName: this.initialOutgoingName || __('GitLab Support Bot'), + projectKey: this.initialProjectKey, + }; + }, + computed: { + templateOptions() { + return [''].concat(this.templates); + }, + hasProjectKeySupport() { + return Boolean(this.glFeatures.serviceDeskCustomAddress); + }, + }, + methods: { + onCheckboxToggle(isChecked) { + eventHub.$emit('serviceDeskEnabledCheckboxToggled', isChecked); + }, + onSaveTemplate() { + eventHub.$emit('serviceDeskTemplateSave', { + selectedTemplate: this.selectedTemplate, + outgoingName: this.outgoingName, + projectKey: this.projectKey, + }); + }, + }, +}; +</script> + +<template> + <div> + <gl-toggle + id="service-desk-checkbox" + :value="isEnabled" + class="d-inline-block align-middle mr-1" + label="Service desk" + label-position="left" + @change="onCheckboxToggle" + /> + <label class="align-middle" for="service-desk-checkbox"> + {{ __('Activate Service Desk') }} + </label> + <div v-if="isEnabled" class="row mt-3"> + <div class="col-md-9 mb-0"> + <strong id="incoming-email-describer" class="d-block mb-1"> + {{ __('Forward external support email address to') }} + </strong> + <template v-if="incomingEmail"> + <div class="input-group"> + <input + ref="service-desk-incoming-email" + type="text" + class="form-control incoming-email h-auto" + :placeholder="__('Incoming email')" + :aria-label="__('Incoming email')" + aria-describedby="incoming-email-describer" + :value="incomingEmail" + disabled="true" + /> + <div class="input-group-append"> + <clipboard-button + :title="__('Copy')" + :text="incomingEmail" + css-class="btn qa-clipboard-button" + /> + </div> + </div> + </template> + <template v-else> + <gl-loading-icon :inline="true" /> + <span class="sr-only">{{ __('Fetching incoming email') }}</span> + </template> + + <label for="service-desk-template-select" class="mt-3"> + {{ __('Template to append to all Service Desk issues') }} + </label> + <gl-form-select + id="service-desk-template-select" + v-model="selectedTemplate" + :options="templateOptions" + /> + <label for="service-desk-email-from-name" class="mt-3"> + {{ __('Email display name') }} + </label> + <input id="service-desk-email-from-name" v-model.trim="outgoingName" class="form-control" /> + <span class="form-text text-muted"> + {{ __('Emails sent from Service Desk will have this name') }} + </span> + <template v-if="hasProjectKeySupport"> + <label for="service-desk-project-suffix" class="mt-3"> + {{ __('Project name suffix') }} + </label> + <input id="service-desk-project-suffix" v-model.trim="projectKey" class="form-control" /> + <span class="form-text text-muted mb-3"> + {{ + __( + 'Project name suffix is a user-defined string which will be appended to the project path, and will form the Service Desk email address.', + ) + }} + </span> + </template> + <gl-deprecated-button + variant="success" + :disabled="isTemplateSaving" + @click="onSaveTemplate" + >{{ __('Save template') }}</gl-deprecated-button + > + </div> + </div> + </div> +</template> diff --git a/app/assets/javascripts/projects/settings_service_desk/event_hub.js b/app/assets/javascripts/projects/settings_service_desk/event_hub.js new file mode 100644 index 00000000000..e31806ad199 --- /dev/null +++ b/app/assets/javascripts/projects/settings_service_desk/event_hub.js @@ -0,0 +1,3 @@ +import createEventHub from '~/helpers/event_hub_factory'; + +export default createEventHub(); diff --git a/app/assets/javascripts/projects/settings_service_desk/index.js b/app/assets/javascripts/projects/settings_service_desk/index.js new file mode 100644 index 00000000000..15c077de72e --- /dev/null +++ b/app/assets/javascripts/projects/settings_service_desk/index.js @@ -0,0 +1,41 @@ +import Vue from 'vue'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import ServiceDeskRoot from './components/service_desk_root.vue'; + +export default () => { + const serviceDeskRootElement = document.querySelector('.js-service-desk-setting-root'); + if (serviceDeskRootElement) { + // eslint-disable-next-line no-new + new Vue({ + el: serviceDeskRootElement, + components: { + ServiceDeskRoot, + }, + data() { + const { dataset } = serviceDeskRootElement; + return { + initialIsEnabled: parseBoolean(dataset.enabled), + endpoint: dataset.endpoint, + incomingEmail: dataset.incomingEmail, + selectedTemplate: dataset.selectedTemplate, + outgoingName: dataset.outgoingName, + projectKey: dataset.projectKey, + templates: JSON.parse(dataset.templates), + }; + }, + render(createElement) { + return createElement('service-desk-root', { + props: { + initialIsEnabled: this.initialIsEnabled, + endpoint: this.endpoint, + initialIncomingEmail: this.incomingEmail, + selectedTemplate: this.selectedTemplate, + outgoingName: this.outgoingName, + projectKey: this.projectKey, + templates: this.templates, + }, + }); + }, + }); + } +}; diff --git a/app/assets/javascripts/projects/settings_service_desk/services/service_desk_service.js b/app/assets/javascripts/projects/settings_service_desk/services/service_desk_service.js new file mode 100644 index 00000000000..d707763c64e --- /dev/null +++ b/app/assets/javascripts/projects/settings_service_desk/services/service_desk_service.js @@ -0,0 +1,27 @@ +import axios from '~/lib/utils/axios_utils'; + +class ServiceDeskService { + constructor(endpoint) { + this.endpoint = endpoint; + } + + fetchIncomingEmail() { + return axios.get(this.endpoint); + } + + toggleServiceDesk(enable) { + return axios.put(this.endpoint, { service_desk_enabled: enable }); + } + + updateTemplate({ selectedTemplate, outgoingName, projectKey = '' }, isEnabled) { + const body = { + issue_template_key: selectedTemplate, + outgoing_name: outgoingName, + project_key: projectKey, + service_desk_enabled: isEnabled, + }; + return axios.put(this.endpoint, body); + } +} + +export default ServiceDeskService; |