diff options
Diffstat (limited to 'app/assets/javascripts/projects/settings_service_desk')
4 files changed, 164 insertions, 20 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 index 4c083ed5496..14c8c53dd19 100644 --- 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 @@ -31,6 +31,9 @@ export default { selectedTemplate: { default: '', }, + selectedFileTemplateProjectId: { + default: null, + }, outgoingName: { default: '', }, @@ -80,7 +83,7 @@ export default { }); }, - onSaveTemplate({ selectedTemplate, outgoingName, projectKey }) { + onSaveTemplate({ selectedTemplate, fileTemplateProjectId, outgoingName, projectKey }) { this.isTemplateSaving = true; const body = { @@ -88,6 +91,7 @@ export default { outgoing_name: outgoingName, project_key: projectKey, service_desk_enabled: this.isEnabled, + file_template_project_id: fileTemplateProjectId, }; return axios @@ -132,6 +136,7 @@ export default { :custom-email="updatedCustomEmail" :custom-email-enabled="customEmailEnabled" :initial-selected-template="selectedTemplate" + :initial-selected-file-template-project-id="selectedFileTemplateProjectId" :initial-outgoing-name="outgoingName" :initial-project-key="projectKey" :templates="templates" 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 index fe2d376f1da..b8053bf9ab5 100644 --- 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 @@ -1,15 +1,8 @@ <script> -import { - GlButton, - GlFormSelect, - GlToggle, - GlLoadingIcon, - GlSprintf, - GlFormInput, - GlLink, -} from '@gitlab/ui'; +import { GlButton, GlToggle, GlLoadingIcon, GlSprintf, GlFormInput, GlLink } from '@gitlab/ui'; import { __ } from '~/locale'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import ServiceDeskTemplateDropdown from './service_desk_template_dropdown.vue'; export default { i18n: { @@ -18,12 +11,12 @@ export default { components: { ClipboardButton, GlButton, - GlFormSelect, GlToggle, GlLoadingIcon, GlSprintf, GlFormInput, GlLink, + ServiceDeskTemplateDropdown, }, props: { isEnabled: { @@ -49,6 +42,11 @@ export default { required: false, default: '', }, + initialSelectedFileTemplateProjectId: { + type: Number, + required: false, + default: null, + }, initialOutgoingName: { type: String, required: false, @@ -73,14 +71,14 @@ export default { data() { return { selectedTemplate: this.initialSelectedTemplate, + selectedFileTemplateProjectId: this.initialSelectedFileTemplateProjectId, outgoingName: this.initialOutgoingName || __('GitLab Support Bot'), projectKey: this.initialProjectKey, + searchTerm: '', + projectKeyError: null, }; }, computed: { - templateOptions() { - return [''].concat(this.templates); - }, hasProjectKeySupport() { return Boolean(this.customEmailEnabled); }, @@ -100,8 +98,21 @@ export default { selectedTemplate: this.selectedTemplate, outgoingName: this.outgoingName, projectKey: this.projectKey, + fileTemplateProjectId: this.selectedFileTemplateProjectId, }); }, + templateChange({ selectedFileTemplateProjectId, selectedTemplate }) { + this.selectedFileTemplateProjectId = selectedFileTemplateProjectId; + this.selectedTemplate = selectedTemplate; + }, + validateProjectKey() { + if (this.projectKey && !new RegExp(/^[a-z0-9_]+$/).test(this.projectKey)) { + this.projectKeyError = __('Only use lowercase letters, numbers, and underscores.'); + return; + } + + this.projectKeyError = null; + }, }, }; </script> @@ -167,8 +178,17 @@ export default { v-model.trim="projectKey" data-testid="project-suffix" class="form-control" + :state="!projectKeyError" + @blur="validateProjectKey" /> - <span v-if="hasProjectKeySupport" class="form-text text-muted"> + <span v-if="hasProjectKeySupport && projectKeyError" class="form-text text-danger"> + {{ projectKeyError }} + </span> + <span + v-if="hasProjectKeySupport" + class="form-text text-muted" + :class="{ 'gl-mt-2!': hasProjectKeySupport && projectKeyError }" + > {{ __('A string appended to the project path to form the Service Desk email address.') }} </span> <span v-else class="form-text text-muted"> @@ -193,12 +213,13 @@ export default { <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" - data-qa-selector="service_desk_template_dropdown" - :options="templateOptions" + <service-desk-template-dropdown + :selected-template="selectedTemplate" + :selected-file-template-project-id="selectedFileTemplateProjectId" + :templates="templates" + @change="templateChange" /> + <label for="service-desk-email-from-name" class="mt-3"> {{ __('Email display name') }} </label> @@ -210,6 +231,7 @@ export default { <gl-button variant="success" class="gl-mt-5" + data-testid="save_service_desk_settings_button" data-qa-selector="save_service_desk_settings_button" :disabled="isTemplateSaving" @click="onSaveTemplate" diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_template_dropdown.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_template_dropdown.vue new file mode 100644 index 00000000000..bdd9f940d79 --- /dev/null +++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_template_dropdown.vue @@ -0,0 +1,115 @@ +<script> +import fuzzaldrinPlus from 'fuzzaldrin-plus'; +import { GlDropdown, GlDropdownSectionHeader, GlDropdownItem, GlSearchBoxByType } from '@gitlab/ui'; +import { __ } from '~/locale'; + +export default { + components: { + GlDropdown, + GlDropdownSectionHeader, + GlDropdownItem, + GlSearchBoxByType, + }, + props: { + selectedTemplate: { + type: String, + required: false, + default: '', + }, + templates: { + type: Array, + required: true, + }, + selectedFileTemplateProjectId: { + type: Number, + required: false, + default: null, + }, + }, + data() { + return { + searchTerm: '', + }; + }, + computed: { + templateOptions() { + if (this.searchTerm) { + const filteredTemplates = []; + for (let i = 0; i < this.templates.length; i += 2) { + const sectionName = this.templates[i]; + const availableTemplates = this.templates[i + 1]; + + const matchedTemplates = fuzzaldrinPlus.filter(availableTemplates, this.searchTerm, { + key: 'name', + }); + + if (matchedTemplates.length > 0) { + filteredTemplates.push(sectionName, matchedTemplates); + } + } + + return filteredTemplates; + } + + return this.templates; + }, + }, + methods: { + templateClick(template) { + // Clicking on the same template should unselect it + if ( + template.name === this.selectedTemplate && + template.project_id === this.selectedFileTemplateProjectId + ) { + this.$emit('change', { + selectedFileTemplateProjectId: null, + selectedTemplate: null, + }); + return; + } + + this.$emit('change', { + selectedFileTemplateProjectId: template.project_id, + selectedTemplate: template.key, + }); + }, + }, + i18n: { + defaultDropdownText: __('Choose a template'), + }, +}; +</script> +<template> + <gl-dropdown + id="service-desk-template-select" + :text="selectedTemplate || $options.i18n.defaultDropdownText" + :header-text="$options.i18n.defaultDropdownText" + data-qa-selector="service_desk_template_dropdown" + :block="true" + class="service-desk-template-select" + toggle-class="gl-m-0" + > + <template #header> + <gl-search-box-by-type v-model.trim="searchTerm" /> + </template> + <template v-for="item in templateOptions"> + <gl-dropdown-section-header v-if="!Array.isArray(item)" :key="item"> + {{ item }} + </gl-dropdown-section-header> + <template v-else> + <gl-dropdown-item + v-for="template in item" + :key="template.key" + :is-check-item="true" + :is-checked=" + template.project_id === selectedFileTemplateProjectId && + template.name === selectedTemplate + " + @click="() => templateClick(template)" + > + {{ template.name }} + </gl-dropdown-item> + </template> + </template> + </gl-dropdown> +</template> diff --git a/app/assets/javascripts/projects/settings_service_desk/index.js b/app/assets/javascripts/projects/settings_service_desk/index.js index f842ffaaa2b..e14cdee17ce 100644 --- a/app/assets/javascripts/projects/settings_service_desk/index.js +++ b/app/assets/javascripts/projects/settings_service_desk/index.js @@ -18,6 +18,7 @@ export default () => { outgoingName, projectKey, selectedTemplate, + selectedFileTemplateProjectId, templates, } = el.dataset; @@ -32,6 +33,7 @@ export default () => { outgoingName, projectKey, selectedTemplate, + selectedFileTemplateProjectId: parseInt(selectedFileTemplateProjectId, 10) || null, templates: JSON.parse(templates), }, render: (createElement) => createElement(ServiceDeskRoot), |