Commit 14ffd8d4 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '263452-display-busy-status-in-issue-sidebar' into 'master'

[FE] Set user availability - Add busy indicator issue sidebar

See merge request gitlab-org/gitlab!54165
parents ea30dd56 e7c254d0
...@@ -51,7 +51,7 @@ export default { ...@@ -51,7 +51,7 @@ export default {
<div> <div>
<collapsed-assignee-list :users="sortedAssigness" :issuable-type="issuableType" /> <collapsed-assignee-list :users="sortedAssigness" :issuable-type="issuableType" />
<div class="value hide-collapsed"> <div data-testid="expanded-assignee" class="value hide-collapsed">
<template v-if="hasNoUsers"> <template v-if="hasNoUsers">
<span class="assign-yourself no-value qa-assign-yourself"> <span class="assign-yourself no-value qa-assign-yourself">
{{ __('None') }} {{ __('None') }}
......
...@@ -44,6 +44,11 @@ export default { ...@@ -44,6 +44,11 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
assigneeAvailabilityStatus: {
type: Object,
required: false,
default: () => ({}),
},
}, },
data() { data() {
return { return {
...@@ -101,6 +106,13 @@ export default { ...@@ -101,6 +106,13 @@ export default {
return new Flash(__('Error occurred when saving assignees')); return new Flash(__('Error occurred when saving assignees'));
}); });
}, },
exposeAvailabilityStatus(users) {
return users.map(({ username, ...rest }) => ({
...rest,
username,
availability: this.assigneeAvailabilityStatus[username] || '',
}));
},
}, },
}; };
</script> </script>
...@@ -123,7 +135,7 @@ export default { ...@@ -123,7 +135,7 @@ export default {
<assignees <assignees
v-if="!store.isFetching.assignees" v-if="!store.isFetching.assignees"
:root-path="relativeUrlRoot" :root-path="relativeUrlRoot"
:users="store.assignees" :users="exposeAvailabilityStatus(store.assignees)"
:editable="store.editable" :editable="store.editable"
:issuable-type="issuableType" :issuable-type="issuableType"
class="value" class="value"
......
...@@ -30,6 +30,28 @@ function getSidebarOptions(sidebarOptEl = document.querySelector('.js-sidebar-op ...@@ -30,6 +30,28 @@ function getSidebarOptions(sidebarOptEl = document.querySelector('.js-sidebar-op
return JSON.parse(sidebarOptEl.innerHTML); return JSON.parse(sidebarOptEl.innerHTML);
} }
/**
* Extracts the list of assignees with availability information from a hidden input
* field and converts to a key:value pair for use in the sidebar assignees component.
* The assignee username is used as the key and their busy status is the value
*
* e.g { root: 'busy', admin: '' }
*
* @returns {Object}
*/
function getSidebarAssigneeAvailabilityData() {
const sidebarAssigneeEl = document.querySelectorAll('.js-sidebar-assignee-data input');
return Array.from(sidebarAssigneeEl)
.map((el) => el.dataset)
.reduce(
(acc, { username, availability = '' }) => ({
...acc,
[username]: availability,
}),
{},
);
}
function mountAssigneesComponent(mediator) { function mountAssigneesComponent(mediator) {
const el = document.getElementById('js-vue-sidebar-assignees'); const el = document.getElementById('js-vue-sidebar-assignees');
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
...@@ -39,6 +61,7 @@ function mountAssigneesComponent(mediator) { ...@@ -39,6 +61,7 @@ function mountAssigneesComponent(mediator) {
if (!el) return; if (!el) return;
const { iid, fullPath } = getSidebarOptions(); const { iid, fullPath } = getSidebarOptions();
const assigneeAvailabilityStatus = getSidebarAssigneeAvailabilityData();
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new Vue({ new Vue({
el, el,
...@@ -56,6 +79,7 @@ function mountAssigneesComponent(mediator) { ...@@ -56,6 +79,7 @@ function mountAssigneesComponent(mediator) {
signedIn: el.hasAttribute('data-signed-in'), signedIn: el.hasAttribute('data-signed-in'),
issuableType: issuableType:
isInIssuePage() || isInIncidentPage() || isInDesignPage() ? 'issue' : 'merge_request', isInIssuePage() || isInIncidentPage() || isInDesignPage() ? 'issue' : 'merge_request',
assigneeAvailabilityStatus,
}, },
}), }),
}); });
......
...@@ -9,6 +9,7 @@ import { ...@@ -9,6 +9,7 @@ import {
AJAX_USERS_SELECT_PARAMS_MAP, AJAX_USERS_SELECT_PARAMS_MAP,
} from 'ee_else_ce/users_select/constants'; } from 'ee_else_ce/users_select/constants';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown'; import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import { isUserBusy } from '~/set_status_modal/utils';
import { fixTitle, dispose } from '~/tooltips'; import { fixTitle, dispose } from '~/tooltips';
import ModalStore from '../boards/stores/modal_store'; import ModalStore from '../boards/stores/modal_store';
import axios from '../lib/utils/axios_utils'; import axios from '../lib/utils/axios_utils';
...@@ -795,13 +796,17 @@ UsersSelect.prototype.renderRow = function ( ...@@ -795,13 +796,17 @@ UsersSelect.prototype.renderRow = function (
? `data-container="body" data-placement="left" data-title="${tooltip}"` ? `data-container="body" data-placement="left" data-title="${tooltip}"`
: ''; : '';
const name =
user?.availability && isUserBusy(user.availability)
? sprintf(__('%{name} (Busy)'), { name: user.name })
: user.name;
return ` return `
<li data-user-id=${user.id}> <li data-user-id=${user.id}>
<a href="#" class="dropdown-menu-user-link d-flex align-items-center ${linkClasses}" ${tooltipAttributes}> <a href="#" class="dropdown-menu-user-link d-flex align-items-center ${linkClasses}" ${tooltipAttributes}>
${this.renderRowAvatar(issuableType, user, img)} ${this.renderRowAvatar(issuableType, user, img)}
<span class="d-flex flex-column overflow-hidden"> <span class="d-flex flex-column overflow-hidden">
<strong class="dropdown-menu-user-full-name gl-font-weight-bold"> <strong class="dropdown-menu-user-full-name gl-font-weight-bold">
${escape(user.name)} ${escape(name)}
</strong> </strong>
${ ${
username username
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
= _('Assignee') = _('Assignee')
= loading_icon(css_class: 'gl-vertical-align-text-bottom') = loading_icon(css_class: 'gl-vertical-align-text-bottom')
.selectbox.hide-collapsed .js-sidebar-assignee-data.selectbox.hide-collapsed
- if assignees.none? - if assignees.none?
= hidden_field_tag "#{issuable_type}[assignee_ids][]", 0, id: nil = hidden_field_tag "#{issuable_type}[assignee_ids][]", 0, id: nil
- else - else
......
---
title: Display user busy status in issue sidebar
merge_request: 54165
author:
type: added
...@@ -199,6 +199,38 @@ RSpec.describe 'User edit profile' do ...@@ -199,6 +199,38 @@ RSpec.describe 'User edit profile' do
expect(busy_status.checked?).to eq(true) expect(busy_status.checked?).to eq(true)
end end
context 'with user status set to busy' do
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project, author: user) }
before do
toggle_busy_status
submit_settings
project.add_developer(user)
visit project_issue_path(project, issue)
end
it 'shows author as busy in the assignee dropdown' do
find('.block.assignee .edit-link').click
wait_for_requests
page.within '.dropdown-menu-user' do
expect(page).to have_content("#{user.name} (Busy)")
end
end
it 'displays the assignee busy status' do
click_button 'assign yourself'
wait_for_requests
visit project_issue_path(project, issue)
wait_for_requests
expect(page.find('[data-testid="expanded-assignee"]')).to have_text("#{user.name} (Busy)")
end
end
context 'with set_user_availability_status feature flag disabled' do context 'with set_user_availability_status feature flag disabled' do
before do before do
stub_feature_flags(set_user_availability_status: false) stub_feature_flags(set_user_availability_status: false)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment