diff --git a/app/assets/javascripts/boards/components/board_content_sidebar.vue b/app/assets/javascripts/boards/components/board_content_sidebar.vue index e0105d63d99..9bbb8a1a1b2 100644 --- a/app/assets/javascripts/boards/components/board_content_sidebar.vue +++ b/app/assets/javascripts/boards/components/board_content_sidebar.vue @@ -3,15 +3,18 @@ import { GlDrawer } from '@gitlab/ui'; import { MountingPortal } from 'portal-vue'; import { mapState, mapActions, mapGetters } from 'vuex'; import SidebarDropdownWidget from 'ee_else_ce/sidebar/components/sidebar_dropdown_widget.vue'; +import { __, sprintf } from '~/locale'; import BoardSidebarLabelsSelect from '~/boards/components/sidebar/board_sidebar_labels_select.vue'; import BoardSidebarTimeTracker from '~/boards/components/sidebar/board_sidebar_time_tracker.vue'; import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.vue'; import { ISSUABLE } from '~/boards/constants'; +import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue'; import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue'; import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue'; import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue'; import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue'; +import SidebarLabelsWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; export default { @@ -23,6 +26,7 @@ export default { SidebarConfidentialityWidget, BoardSidebarTimeTracker, BoardSidebarLabelsSelect, + SidebarLabelsWidget, SidebarSubscriptionsWidget, SidebarDropdownWidget, SidebarTodoWidget, @@ -46,16 +50,20 @@ export default { weightFeatureAvailable: { default: false, }, + allowLabelEdit: { + default: false, + }, }, inheritAttrs: false, computed: { ...mapGetters([ + 'isGroupBoard', 'isSidebarOpen', 'activeBoardItem', 'groupPathForActiveIssue', 'projectPathForActiveIssue', ]), - ...mapState(['sidebarType', 'issuableType']), + ...mapState(['sidebarType', 'issuableType', 'isSettingLabels']), isIssuableSidebar() { return this.sidebarType === ISSUABLE; }, @@ -65,17 +73,48 @@ export default { fullPath() { return this.activeBoardItem?.referencePath?.split('#')[0] || ''; }, + createLabelTitle() { + return sprintf(__('Create %{workspace} label'), { + workspace: this.isGroupBoard ? 'group' : 'project', + }); + }, + manageLabelTitle() { + return sprintf(__('Manage %{workspace} labels'), { + workspace: this.isGroupBoard ? 'group' : 'project', + }); + }, + attrWorkspacePath() { + return this.isGroupBoard ? this.groupPathForActiveIssue : undefined; + }, }, methods: { ...mapActions([ 'toggleBoardItem', 'setAssignees', 'setActiveItemConfidential', + 'setActiveBoardItemLabels', 'setActiveItemWeight', ]), handleClose() { this.toggleBoardItem({ boardItem: this.activeBoardItem, sidebarType: this.sidebarType }); }, + handleUpdateSelectedLabels(input) { + this.setActiveBoardItemLabels({ + iid: this.activeBoardItem.iid, + projectPath: this.projectPathForActiveIssue, + addLabelIds: input.map((label) => getIdFromGraphQLId(label.id)), + removeLabelIds: this.activeBoardItem.labels + .filter((label) => !input.find((selected) => selected.id === label.id)) + .map((label) => label.id), + }); + }, + handleLabelRemove(input) { + this.setActiveBoardItemLabels({ + iid: this.activeBoardItem.iid, + projectPath: this.projectPathForActiveIssue, + removeLabelIds: [input], + }); + }, }, }; @@ -160,7 +199,28 @@ export default { :issuable-type="issuableType" data-testid="sidebar-due-date" /> - + + {{ __('None') }} + + createComponent(BoardApp), }); diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index 270f2ff085b..ca993e75cf9 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -656,6 +656,7 @@ export default { }, setActiveIssueLabels: async ({ commit, getters }, input) => { + commit(types.SET_LABELS_LOADING, true); const { activeBoardItem } = getters; const { data } = await gqlClient.mutate({ mutation: issueSetLabelsMutation, @@ -669,6 +670,8 @@ export default { }, }); + commit(types.SET_LABELS_LOADING, false); + if (data.updateIssue?.errors?.length > 0) { throw new Error(data.updateIssue.errors); } diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js index 928cece19f7..26b785932bb 100644 --- a/app/assets/javascripts/boards/stores/mutation_types.js +++ b/app/assets/javascripts/boards/stores/mutation_types.js @@ -28,6 +28,7 @@ export const ADD_BOARD_ITEM_TO_LIST = 'ADD_BOARD_ITEM_TO_LIST'; export const REMOVE_BOARD_ITEM_FROM_LIST = 'REMOVE_BOARD_ITEM_FROM_LIST'; export const SET_ACTIVE_ID = 'SET_ACTIVE_ID'; export const UPDATE_BOARD_ITEM_BY_ID = 'UPDATE_BOARD_ITEM_BY_ID'; +export const SET_LABELS_LOADING = 'SET_LABELS_LOADING'; export const SET_ASSIGNEE_LOADING = 'SET_ASSIGNEE_LOADING'; export const RESET_ISSUES = 'RESET_ISSUES'; export const REQUEST_GROUP_PROJECTS = 'REQUEST_GROUP_PROJECTS'; diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js index ef5b84b4575..d381c076c19 100644 --- a/app/assets/javascripts/boards/stores/mutations.js +++ b/app/assets/javascripts/boards/stores/mutations.js @@ -195,6 +195,10 @@ export default { Vue.set(state.boardItems[itemId], prop, value); }, + [mutationTypes.SET_LABELS_LOADING](state, isLoading) { + state.isSettingLabels = isLoading; + }, + [mutationTypes.SET_ASSIGNEE_LOADING](state, isLoading) { state.isSettingAssignees = isLoading; }, diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js index 80c51c966d2..2a6605e687b 100644 --- a/app/assets/javascripts/boards/stores/state.js +++ b/app/assets/javascripts/boards/stores/state.js @@ -12,6 +12,7 @@ export default () => ({ listsFlags: {}, boardItemsByListId: {}, backupItemsList: [], + isSettingLabels: false, isSettingAssignees: false, pageInfoByListId: {}, boardItems: {}, diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue index 4aee02e45c8..9d4eddc510a 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue +++ b/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue @@ -293,7 +293,7 @@ export default { :items="roles" :loading="isLoadingRoles" :loading-text="s__('ClusterIntegration|Loading IAM Roles')" - :placeholder="s__('ClusterIntergation|Select service role')" + :placeholder="s__('ClusterIntegration|Select service role')" :search-field-placeholder="s__('ClusterIntegration|Search IAM Roles')" :empty-text="s__('ClusterIntegration|No IAM Roles found')" :has-errors="Boolean(loadingRolesError)" @@ -330,7 +330,7 @@ export default { :disabled-text="s__('ClusterIntegration|Select a region to choose a Key Pair')" :loading="isLoadingKeyPairs" :loading-text="s__('ClusterIntegration|Loading Key Pairs')" - :placeholder="s__('ClusterIntergation|Select key pair')" + :placeholder="s__('ClusterIntegration|Select key pair')" :search-field-placeholder="s__('ClusterIntegration|Search Key Pairs')" :empty-text="s__('ClusterIntegration|No Key Pairs found')" :has-errors="Boolean(loadingKeyPairsError)" @@ -359,7 +359,7 @@ export default { :disabled="vpcDropdownDisabled" :disabled-text="s__('ClusterIntegration|Select a region to choose a VPC')" :loading-text="s__('ClusterIntegration|Loading VPCs')" - :placeholder="s__('ClusterIntergation|Select a VPC')" + :placeholder="s__('ClusterIntegration|Select a VPC')" :search-field-placeholder="s__('ClusterIntegration|Search VPCs')" :empty-text="s__('ClusterIntegration|No VPCs found')" :has-errors="Boolean(loadingVpcsError)" @@ -389,7 +389,7 @@ export default { :disabled="subnetDropdownDisabled" :disabled-text="s__('ClusterIntegration|Select a VPC to choose a subnet')" :loading-text="s__('ClusterIntegration|Loading subnets')" - :placeholder="s__('ClusterIntergation|Select a subnet')" + :placeholder="s__('ClusterIntegration|Select a subnet')" :search-field-placeholder="s__('ClusterIntegration|Search subnets')" :empty-text="s__('ClusterIntegration|No subnet found')" :has-errors="displaySubnetError" @@ -420,7 +420,7 @@ export default { :disabled="securityGroupDropdownDisabled" :disabled-text="s__('ClusterIntegration|Select a VPC to choose a security group')" :loading-text="s__('ClusterIntegration|Loading security groups')" - :placeholder="s__('ClusterIntergation|Select a security group')" + :placeholder="s__('ClusterIntegration|Select a security group')" :search-field-placeholder="s__('ClusterIntegration|Search security groups')" :empty-text="s__('ClusterIntegration|No security group found')" :has-errors="Boolean(loadingSecurityGroupsError)" @@ -451,7 +451,7 @@ export default { :items="instanceTypes" :loading="isLoadingInstanceTypes" :loading-text="s__('ClusterIntegration|Loading instance types')" - :placeholder="s__('ClusterIntergation|Select an instance type')" + :placeholder="s__('ClusterIntegration|Select an instance type')" :search-field-placeholder="s__('ClusterIntegration|Search instance types')" :empty-text="s__('ClusterIntegration|No instance type found')" :has-errors="Boolean(loadingInstanceTypesError)" diff --git a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_network_dropdown.vue b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_network_dropdown.vue index 12b6070a79a..8f18ac29c0f 100644 --- a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_network_dropdown.vue +++ b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_network_dropdown.vue @@ -43,7 +43,7 @@ export default { :loading="isLoadingItems" :has-errors="Boolean(loadingItemsError)" :loading-text="s__('ClusterIntegration|Loading networks')" - :placeholder="s__('ClusterIntergation|Select a network')" + :placeholder="s__('ClusterIntegration|Select a network')" :search-field-placeholder="s__('ClusterIntegration|Search networks')" :empty-text="s__('ClusterIntegration|No networks found')" :error-message="s__('ClusterIntegration|Could not load networks')" diff --git a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_subnetwork_dropdown.vue b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_subnetwork_dropdown.vue index ec7889e2907..dab4adc3789 100644 --- a/app/assets/javascripts/create_cluster/gke_cluster/components/gke_subnetwork_dropdown.vue +++ b/app/assets/javascripts/create_cluster/gke_cluster/components/gke_subnetwork_dropdown.vue @@ -34,7 +34,7 @@ export default { :loading="isLoadingItems" :has-errors="Boolean(loadingItemsError)" :loading-text="s__('ClusterIntegration|Loading subnetworks')" - :placeholder="s__('ClusterIntergation|Select a subnetwork')" + :placeholder="s__('ClusterIntegration|Select a subnetwork')" :search-field-placeholder="s__('ClusterIntegration|Search subnetworks')" :empty-text="s__('ClusterIntegration|No subnetworks found')" :error-message="s__('ClusterIntegration|Could not load subnetworks')" diff --git a/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue b/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue index 858c30649bb..1a470d74b59 100644 --- a/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue +++ b/app/assets/javascripts/feature_flags/components/strategies/flexible_rollout.vue @@ -17,7 +17,7 @@ export default { }, }, i18n: { - percentageDescription: __('Enter an integer number number between 0 and 100'), + percentageDescription: __('Enter an integer number between 0 and 100'), percentageInvalid: __('Percent rollout must be an integer number between 0 and 100'), percentageLabel: __('Percentage'), stickinessDescription: __('Consistency guarantee method'), diff --git a/app/assets/javascripts/issuable_suggestions/components/item.vue b/app/assets/javascripts/issuable_suggestions/components/item.vue index dea7608685a..a01f4f747b9 100644 --- a/app/assets/javascripts/issuable_suggestions/components/item.vue +++ b/app/assets/javascripts/issuable_suggestions/components/item.vue @@ -1,5 +1,4 @@ @@ -172,8 +246,11 @@ export default { -