Add team option in bulk actions (#5885)

This commit is contained in:
Fayaz Ahmed 2022-11-18 14:44:36 +05:30 committed by GitHub
parent 47676c3cce
commit 33aacb3401
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 287 additions and 20 deletions

View file

@ -102,6 +102,7 @@
@assign-agent="onAssignAgent" @assign-agent="onAssignAgent"
@update-conversations="onUpdateConversations" @update-conversations="onUpdateConversations"
@assign-labels="onAssignLabels" @assign-labels="onAssignLabels"
@assign-team="onAssignTeamsForBulk"
/> />
<div <div
ref="activeConversation" ref="activeConversation"
@ -685,6 +686,21 @@ export default {
this.showAlert(this.$t('BULK_ACTION.LABELS.ASSIGN_FAILED')); this.showAlert(this.$t('BULK_ACTION.LABELS.ASSIGN_FAILED'));
} }
}, },
async onAssignTeamsForBulk(team) {
try {
await this.$store.dispatch('bulkActions/process', {
type: 'Conversation',
ids: this.selectedConversations,
fields: {
team_id: team.id,
},
});
this.selectedConversations = [];
this.showAlert(this.$t('BULK_ACTION.TEAMS.ASSIGN_SUCCESFUL'));
} catch (err) {
this.showAlert(this.$t('BULK_ACTION.TEAMS.ASSIGN_FAILED'));
}
},
async onUpdateConversations(status) { async onUpdateConversations(status) {
try { try {
await this.$store.dispatch('bulkActions/process', { await this.$store.dispatch('bulkActions/process', {

View file

@ -1,6 +1,6 @@
<template> <template>
<div class="bulk-action__agents"> <div class="bulk-action__agents">
<div class="triangle"> <div class="triangle" :style="cssVars">
<svg height="12" viewBox="0 0 24 12" width="24"> <svg height="12" viewBox="0 0 24 12" width="24">
<path <path
d="M20 12l-8-8-12 12" d="M20 12l-8-8-12 12"
@ -105,13 +105,14 @@ import { mapGetters } from 'vuex';
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue'; import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
import Spinner from 'shared/components/Spinner'; import Spinner from 'shared/components/Spinner';
import { mixin as clickaway } from 'vue-clickaway'; import { mixin as clickaway } from 'vue-clickaway';
import bulkActionsMixin from 'dashboard/mixins/bulkActionsMixin.js';
export default { export default {
components: { components: {
Thumbnail, Thumbnail,
Spinner, Spinner,
}, },
mixins: [clickaway], mixins: [clickaway, bulkActionsMixin],
props: { props: {
selectedInboxes: { selectedInboxes: {
type: Array, type: Array,
@ -233,7 +234,7 @@ export default {
z-index: var(--z-index-one); z-index: var(--z-index-one);
position: absolute; position: absolute;
top: calc(var(--space-slab) * -1); top: calc(var(--space-slab) * -1);
right: var(--space-micro); right: var(--triangle-position);
text-align: left; text-align: left;
} }
} }

View file

@ -43,25 +43,26 @@
variant="smooth" variant="smooth"
color-scheme="secondary" color-scheme="secondary"
icon="person-assign" icon="person-assign"
class="margin-right-smaller"
@click="toggleAgentList" @click="toggleAgentList"
/> />
<woot-button
v-tooltip="$t('BULK_ACTION.ASSIGN_TEAM_TOOLTIP')"
size="tiny"
variant="smooth"
color-scheme="secondary"
icon="people-team-add"
@click="toggleTeamsList"
/>
</div> </div>
<transition name="popover-animation"> <transition name="popover-animation">
<label-actions <label-actions
v-if="showLabelActions" v-if="showLabelActions"
triangle-position="8.5"
@assign="assignLabels" @assign="assignLabels"
@close="showLabelActions = false" @close="showLabelActions = false"
/> />
</transition> </transition>
<transition name="popover-animation">
<agent-selector
v-if="showAgentsList"
:selected-inboxes="selectedInboxes"
:conversation-count="conversations.length"
@select="submit"
@close="showAgentsList = false"
/>
</transition>
<transition name="popover-animation"> <transition name="popover-animation">
<update-actions <update-actions
v-if="showUpdateActions" v-if="showUpdateActions"
@ -70,10 +71,29 @@
:show-resolve="!showResolvedAction" :show-resolve="!showResolvedAction"
:show-reopen="!showOpenAction" :show-reopen="!showOpenAction"
:show-snooze="!showSnoozedAction" :show-snooze="!showSnoozedAction"
triangle-position="5.6"
@update="updateConversations" @update="updateConversations"
@close="showUpdateActions = false" @close="showUpdateActions = false"
/> />
</transition> </transition>
<transition name="popover-animation">
<agent-selector
v-if="showAgentsList"
:selected-inboxes="selectedInboxes"
:conversation-count="conversations.length"
triangle-position="2.8"
@select="submit"
@close="showAgentsList = false"
/>
</transition>
<transition name="popover-animation">
<team-actions
v-if="showTeamsList"
triangle-position="0.2"
@assign-team="assignTeam"
@close="showTeamsList = false"
/>
</transition>
</div> </div>
<div v-if="allConversationsSelected" class="bulk-action__alert"> <div v-if="allConversationsSelected" class="bulk-action__alert">
{{ $t('BULK_ACTION.ALL_CONVERSATIONS_SELECTED_ALERT') }} {{ $t('BULK_ACTION.ALL_CONVERSATIONS_SELECTED_ALERT') }}
@ -85,11 +105,13 @@
import AgentSelector from './AgentSelector.vue'; import AgentSelector from './AgentSelector.vue';
import UpdateActions from './UpdateActions.vue'; import UpdateActions from './UpdateActions.vue';
import LabelActions from './LabelActions.vue'; import LabelActions from './LabelActions.vue';
import TeamActions from './TeamActions.vue';
export default { export default {
components: { components: {
AgentSelector, AgentSelector,
UpdateActions, UpdateActions,
LabelActions, LabelActions,
TeamActions,
}, },
props: { props: {
conversations: { conversations: {
@ -122,6 +144,8 @@ export default {
showAgentsList: false, showAgentsList: false,
showUpdateActions: false, showUpdateActions: false,
showLabelActions: false, showLabelActions: false,
showTeamsList: false,
popoverPositions: {},
}; };
}, },
methods: { methods: {
@ -137,6 +161,9 @@ export default {
assignLabels(labels) { assignLabels(labels) {
this.$emit('assign-labels', labels); this.$emit('assign-labels', labels);
}, },
assignTeam(team) {
this.$emit('assign-team', team);
},
resolveConversations() { resolveConversations() {
this.$emit('resolve-conversations'); this.$emit('resolve-conversations');
}, },
@ -149,6 +176,9 @@ export default {
toggleAgentList() { toggleAgentList() {
this.showAgentsList = !this.showAgentsList; this.showAgentsList = !this.showAgentsList;
}, },
toggleTeamsList() {
this.showTeamsList = !this.showTeamsList;
},
}, },
}; };
</script> </script>

View file

@ -1,6 +1,6 @@
<template> <template>
<div v-on-clickaway="onClose" class="labels-container"> <div v-on-clickaway="onClose" class="labels-container">
<div class="triangle"> <div class="triangle" :style="cssVars">
<svg height="12" viewBox="0 0 24 12" width="24"> <svg height="12" viewBox="0 0 24 12" width="24">
<path <path
d="M20 12l-8-8-12 12" d="M20 12l-8-8-12 12"
@ -75,9 +75,10 @@
<script> <script>
import { mixin as clickaway } from 'vue-clickaway'; import { mixin as clickaway } from 'vue-clickaway';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import bulkActionsMixin from 'dashboard/mixins/bulkActionsMixin.js';
export default { export default {
mixins: [clickaway], mixins: [clickaway, bulkActionsMixin],
data() { data() {
return { return {
query: '', query: '',
@ -160,7 +161,7 @@ export default {
max-width: var(--space-giga); max-width: var(--space-giga);
min-width: var(--space-giga); min-width: var(--space-giga);
position: absolute; position: absolute;
right: 4.5rem; right: var(--space-small);
top: var(--space-larger); top: var(--space-larger);
transform-origin: top right; transform-origin: top right;
width: auto; width: auto;
@ -204,7 +205,7 @@ export default {
.triangle { .triangle {
display: block; display: block;
position: absolute; position: absolute;
right: var(--space-two); right: var(--triangle-position);
text-align: left; text-align: left;
top: calc(var(--space-slab) * -1); top: calc(var(--space-slab) * -1);
z-index: var(--z-index-one); z-index: var(--z-index-one);

View file

@ -0,0 +1,174 @@
<template>
<div v-on-clickaway="onClose" class="bulk-action__teams">
<div class="triangle" :style="cssVars">
<svg height="12" viewBox="0 0 24 12" width="24">
<path
d="M20 12l-8-8-12 12"
fill="var(--white)"
fill-rule="evenodd"
stroke="var(--s-50)"
stroke-width="1px"
/>
</svg>
</div>
<div class="header flex-between">
<span>{{ $t('BULK_ACTION.TEAMS.TEAM_SELECT_LABEL') }}</span>
<woot-button
size="tiny"
variant="clear"
color-scheme="secondary"
icon="dismiss"
@click="onClose"
/>
</div>
<div class="container">
<div class="team__list-container">
<ul>
<li class="search-container">
<div class="agent-list-search flex-between">
<fluent-icon icon="search" class="search-icon" size="16" />
<input
ref="search"
v-model="query"
type="search"
placeholder="Search"
class="agent--search_input"
/>
</div>
</li>
<template v-if="filteredTeams.length">
<li v-for="team in filteredTeams" :key="team.id">
<div class="team__list-item" @click="assignTeam(team)">
<span class="reports-option__title">{{ team.name }}</span>
</div>
</li>
</template>
<li v-else>
<div class="team__list-item">
<span class="reports-option__title">{{
$t('BULK_ACTION.TEAMS.NO_TEAMS_AVAILABLE')
}}</span>
</div>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import { mixin as clickaway } from 'vue-clickaway';
import { mapGetters } from 'vuex';
import bulkActionsMixin from 'dashboard/mixins/bulkActionsMixin.js';
export default {
mixins: [clickaway, bulkActionsMixin],
data() {
return {
query: '',
selectedteams: [],
};
},
computed: {
...mapGetters({ teams: 'teams/getTeams' }),
filteredTeams() {
return [
{ name: 'None', id: 0 },
...this.teams.filter(team =>
team.name.toLowerCase().includes(this.query.toLowerCase())
),
];
},
},
methods: {
assignTeam(key) {
this.$emit('assign-team', key);
},
onClose() {
this.$emit('close');
},
},
};
</script>
<style scoped lang="scss">
.bulk-action__teams {
background-color: var(--white);
border-radius: var(--border-radius-large);
border: 1px solid var(--s-50);
box-shadow: var(--shadow-dropdown-pane);
max-width: 75%;
position: absolute;
right: var(--space-small);
top: var(--space-larger);
transform-origin: top right;
width: auto;
z-index: var(--z-index-twenty);
min-width: var(--space-giga);
.header {
padding: var(--space-one);
span {
font-size: var(--font-size-small);
font-weight: var(--font-weight-medium);
}
}
.container {
max-height: var(--space-giga);
overflow-y: auto;
.team__list-container {
height: 100%;
}
.agent-list-search {
padding: 0 var(--space-one);
border: 1px solid var(--s-100);
border-radius: var(--border-radius-medium);
background-color: var(--s-50);
.search-icon {
color: var(--s-400);
}
.agent--search_input {
border: 0;
font-size: var(--font-size-mini);
margin: 0;
background-color: transparent;
height: unset;
}
}
}
.triangle {
display: block;
z-index: var(--z-index-one);
position: absolute;
top: calc(var(--space-slab) * -1);
right: var(--triangle-position);
text-align: left;
}
}
ul {
margin: 0;
list-style: none;
}
.team__list-item {
display: flex;
align-items: center;
padding: var(--space-one);
cursor: pointer;
&:hover {
background-color: var(--s-50);
}
span {
font-size: var(--font-size-small);
}
}
.search-container {
padding: 0 var(--space-one);
position: sticky;
top: 0;
z-index: var(--z-index-twenty);
background-color: var(--white);
}
</style>

View file

@ -1,6 +1,6 @@
<template> <template>
<div v-on-clickaway="onClose" class="actions-container"> <div v-on-clickaway="onClose" class="actions-container">
<div class="triangle"> <div class="triangle" :style="cssVars">
<svg height="12" viewBox="0 0 24 12" width="24"> <svg height="12" viewBox="0 0 24 12" width="24">
<path <path
d="M20 12l-8-8-12 12" d="M20 12l-8-8-12 12"
@ -45,12 +45,14 @@
import { mixin as clickaway } from 'vue-clickaway'; import { mixin as clickaway } from 'vue-clickaway';
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue'; import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue'; import WootDropdownMenu from 'shared/components/ui/dropdown/DropdownMenu.vue';
import bulkActionsMixin from 'dashboard/mixins/bulkActionsMixin.js';
export default { export default {
components: { components: {
WootDropdownItem, WootDropdownItem,
WootDropdownMenu, WootDropdownMenu,
}, },
mixins: [clickaway], mixins: [clickaway, bulkActionsMixin],
props: { props: {
selectedInboxes: { selectedInboxes: {
type: Array, type: Array,
@ -131,7 +133,7 @@ export default {
box-shadow: var(--shadow-dropdown-pane); box-shadow: var(--shadow-dropdown-pane);
position: absolute; position: absolute;
right: var(--space-small); right: var(--space-small);
top: 48px; top: var(--space-larger);
transform-origin: top right; transform-origin: top right;
width: auto; width: auto;
z-index: var(--z-index-twenty); z-index: var(--z-index-twenty);
@ -152,7 +154,7 @@ export default {
.triangle { .triangle {
display: block; display: block;
position: absolute; position: absolute;
right: 2.8rem; right: var(--triangle-position);
text-align: left; text-align: left;
top: calc(var(--space-slab) * -1); top: calc(var(--space-slab) * -1);
z-index: var(--z-index-one); z-index: var(--z-index-one);

View file

@ -8,6 +8,7 @@
"ASSIGN_LABEL": "Assign", "ASSIGN_LABEL": "Assign",
"YES": "Yes", "YES": "Yes",
"ASSIGN_AGENT_TOOLTIP": "Assign Agent", "ASSIGN_AGENT_TOOLTIP": "Assign Agent",
"ASSIGN_TEAM_TOOLTIP": "Assign team",
"ASSIGN_SUCCESFUL": "Conversations assigned successfully", "ASSIGN_SUCCESFUL": "Conversations assigned successfully",
"ASSIGN_FAILED": "Failed to assign conversations, please try again", "ASSIGN_FAILED": "Failed to assign conversations, please try again",
"RESOLVE_SUCCESFUL": "Conversations resolved successfully", "RESOLVE_SUCCESFUL": "Conversations resolved successfully",
@ -26,6 +27,14 @@
"ASSIGN_SELECTED_LABELS": "Assign selected labels", "ASSIGN_SELECTED_LABELS": "Assign selected labels",
"ASSIGN_SUCCESFUL": "Labels assigned successfully", "ASSIGN_SUCCESFUL": "Labels assigned successfully",
"ASSIGN_FAILED": "Failed to assign labels, please try again" "ASSIGN_FAILED": "Failed to assign labels, please try again"
},
"TEAMS": {
"TEAM_SELECT_LABEL": "Select Team",
"NONE": "None",
"NO_TEAMS_AVAILABLE": "There are no teams added to this account yet.",
"ASSIGN_SELECTED_TEAMS": "Assign selected team",
"ASSIGN_SUCCESFUL": "Teams assiged successfully",
"ASSIGN_FAILED": "Failed to assign team, please try again"
} }
} }
} }

View file

@ -0,0 +1,15 @@
export default {
props: {
trianglePosition: {
type: String,
default: '0',
},
},
computed: {
cssVars() {
return {
'--triangle-position': this.trianglePosition + 'rem',
};
},
},
};

View file

@ -0,0 +1,19 @@
import { mount } from '@vue/test-utils';
import bulkActionsMixin from '../bulkActionsMixin';
describe('bulkActionsMixin', () => {
it('returns the prop and computed values for triangle position:', async () => {
const Component = {
render() {},
title: 'MyComponent',
mixins: [bulkActionsMixin],
};
const wrapper = mount(Component);
await wrapper.setProps({
trianglePosition: '10',
});
expect(wrapper.props().trianglePosition).toEqual('10');
expect(wrapper.vm.cssVars).toEqual({
'--triangle-position': '10rem',
});
});
});