diff options
Diffstat (limited to 'app/assets/javascripts/sidebar/components/severity')
4 files changed, 279 insertions, 0 deletions
diff --git a/app/assets/javascripts/sidebar/components/severity/constants.js b/app/assets/javascripts/sidebar/components/severity/constants.js new file mode 100644 index 00000000000..4f58ff38121 --- /dev/null +++ b/app/assets/javascripts/sidebar/components/severity/constants.js @@ -0,0 +1,41 @@ +import { __, s__ } from '~/locale'; + +export const INCIDENT_SEVERITY = { + CRITICAL: { + value: 'CRITICAL', + icon: 'critical', + label: s__('IncidentManagement|Critical - S1'), + }, + HIGH: { + value: 'HIGH', + icon: 'high', + label: s__('IncidentManagement|High - S2'), + }, + MEDIUM: { + value: 'MEDIUM', + icon: 'medium', + label: s__('IncidentManagement|Medium - S3'), + }, + LOW: { + value: 'LOW', + icon: 'low', + label: s__('IncidentManagement|Low - S4'), + }, + UNKNOWN: { + value: 'UNKNOWN', + icon: 'unknown', + label: s__('IncidentManagement|Unknown'), + }, +}; + +export const ISSUABLE_TYPES = { + INCIDENT: 'incident', +}; + +export const I18N = { + UPDATE_SEVERITY_ERROR: s__('SeverityWidget|There was an error while updating severity.'), + TRY_AGAIN: __('Please try again'), + EDIT: __('Edit'), + SEVERITY: s__('SeverityWidget|Severity'), + SEVERITY_VALUE: s__('SeverityWidget|Severity: %{severity}'), +}; diff --git a/app/assets/javascripts/sidebar/components/severity/graphql/mutations/update_issuable_severity.mutation.graphql b/app/assets/javascripts/sidebar/components/severity/graphql/mutations/update_issuable_severity.mutation.graphql new file mode 100644 index 00000000000..750e757971f --- /dev/null +++ b/app/assets/javascripts/sidebar/components/severity/graphql/mutations/update_issuable_severity.mutation.graphql @@ -0,0 +1,9 @@ +mutation updateIssuableSeverity($projectPath: ID!, $severity: IssuableSeverity!, $iid: String!) { + issueSetSeverity(input: { iid: $iid, severity: $severity, projectPath: $projectPath }) { + errors + issue { + iid + severity + } + } +} diff --git a/app/assets/javascripts/sidebar/components/severity/severity.vue b/app/assets/javascripts/sidebar/components/severity/severity.vue new file mode 100644 index 00000000000..7e7d62256c9 --- /dev/null +++ b/app/assets/javascripts/sidebar/components/severity/severity.vue @@ -0,0 +1,42 @@ +<script> +import { GlIcon } from '@gitlab/ui'; + +export default { + components: { + GlIcon, + }, + props: { + severity: { + type: Object, + required: true, + validator(severity) { + const { value, label, icon } = severity; + return value && label && icon; + }, + }, + iconSize: { + type: Number, + required: false, + default: 12, + }, + iconOnly: { + type: Boolean, + required: false, + default: false, + }, + }, +}; +</script> + +<template> + <div + class="incident-severity gl-display-inline-flex gl-align-items-center gl-justify-content-between" + > + <gl-icon + :size="iconSize" + :name="`severity-${severity.icon}`" + :class="[`icon-${severity.icon}`, { 'gl-mr-3': !iconOnly }]" + /> + <span v-if="!iconOnly">{{ severity.label }}</span> + </div> +</template> diff --git a/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue b/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue new file mode 100644 index 00000000000..8f3610b912a --- /dev/null +++ b/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue @@ -0,0 +1,187 @@ +<script> +import { + GlDropdown, + GlDropdownItem, + GlLoadingIcon, + GlTooltip, + GlSprintf, + GlLink, +} from '@gitlab/ui'; +import { INCIDENT_SEVERITY, ISSUABLE_TYPES, I18N } from './constants'; +import updateIssuableSeverity from './graphql/mutations/update_issuable_severity.mutation.graphql'; +import SeverityToken from './severity.vue'; +import createFlash from '~/flash'; + +export default { + i18n: I18N, + components: { + GlLoadingIcon, + GlTooltip, + GlSprintf, + GlDropdown, + GlDropdownItem, + GlLink, + SeverityToken, + }, + props: { + projectPath: { + type: String, + required: true, + }, + iid: { + type: String, + required: true, + }, + initialSeverity: { + type: String, + required: false, + default: INCIDENT_SEVERITY.UNKNOWN.value, + }, + issuableType: { + type: String, + required: false, + default: ISSUABLE_TYPES.INCIDENT, + validator: value => { + // currently severity is supported only for incidents, but this list might be extended + return [ISSUABLE_TYPES.INCIDENT].includes(value); + }, + }, + }, + data() { + return { + isDropdownShowing: false, + isUpdating: false, + severity: this.initialSeverity, + }; + }, + computed: { + severitiesList() { + switch (this.issuableType) { + case ISSUABLE_TYPES.INCIDENT: + return Object.values(INCIDENT_SEVERITY); + default: + return []; + } + }, + dropdownClass() { + return this.isDropdownShowing ? 'show' : 'gl-display-none'; + }, + selectedItem() { + return this.severitiesList.find(severity => severity.value === this.severity); + }, + }, + mounted() { + document.addEventListener('click', this.handleOffClick); + }, + beforeDestroy() { + document.removeEventListener('click', this.handleOffClick); + }, + methods: { + handleOffClick(event) { + if (!this.isDropdownShowing) { + return; + } + + if (!this.$refs.sidebarSeverity.contains(event.target)) { + this.hideDropdown(); + } + }, + hideDropdown() { + this.isDropdownShowing = false; + const event = new Event('hidden.gl.dropdown'); + this.$el.dispatchEvent(event); + }, + toggleFormDropdown() { + this.isDropdownShowing = !this.isDropdownShowing; + }, + updateSeverity(value) { + this.hideDropdown(); + this.isUpdating = true; + this.$apollo + .mutate({ + mutation: updateIssuableSeverity, + variables: { + iid: this.iid, + severity: value, + projectPath: this.projectPath, + }, + }) + .then(resp => { + const { + data: { + issueSetSeverity: { + errors = [], + issue: { severity }, + }, + }, + } = resp; + + if (errors[0]) { + throw errors[0]; + } + this.severity = severity; + }) + .catch(() => + createFlash({ + message: `${this.$options.i18n.UPDATE_SEVERITY_ERROR} ${this.$options.i18n.TRY_AGAIN}`, + }), + ) + .finally(() => { + this.isUpdating = false; + }); + }, + }, +}; +</script> + +<template> + <div ref="sidebarSeverity" class="block"> + <div ref="severity" class="sidebar-collapsed-icon" @click="toggleFormDropdown"> + <severity-token :severity="selectedItem" :icon-size="14" :icon-only="true" /> + <gl-tooltip :target="() => $refs.severity" boundary="viewport" placement="left"> + <gl-sprintf :message="$options.i18n.SEVERITY_VALUE"> + <template #severity> + {{ selectedItem.label }} + </template> + </gl-sprintf> + </gl-tooltip> + </div> + + <div class="hide-collapsed"> + <p class="title gl-display-flex gl-justify-content-space-between"> + {{ $options.i18n.SEVERITY }} + <gl-link + data-testid="editButton" + href="#" + @click="toggleFormDropdown" + @keydown.esc="hideDropdown" + > + {{ $options.i18n.EDIT }} + </gl-link> + </p> + + <gl-dropdown + :class="dropdownClass" + block + :text="selectedItem.label" + toggle-class="dropdown-menu-toggle gl-mb-2" + @keydown.esc.native="hideDropdown" + > + <gl-dropdown-item + v-for="option in severitiesList" + :key="option.value" + data-testid="severityDropdownItem" + :is-check-item="true" + :is-checked="option.value === severity" + @click="updateSeverity(option.value)" + > + <severity-token :severity="option" /> + </gl-dropdown-item> + </gl-dropdown> + + <gl-loading-icon v-if="isUpdating" :inline="true" /> + + <severity-token v-else-if="!isDropdownShowing" :severity="selectedItem" /> + </div> + </div> +</template> |