feat: Update agent and team multi-select with new multi-select dropdown (#2516)
* feat: Update agent/team multiselect styles * Component name fixes * Adds key control for our multiselect dropdown and component name spell fix * Minor fixes * Review fixes * Minor fixes * Minor fixes * Review fixes Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com>
This commit is contained in:
parent
672e5874fb
commit
f2b5e328bb
5 changed files with 117 additions and 41 deletions
|
@ -91,6 +91,23 @@
|
||||||
},
|
},
|
||||||
"SEARCH": {
|
"SEARCH": {
|
||||||
"NO_RESULTS": "No results found."
|
"NO_RESULTS": "No results found."
|
||||||
|
},
|
||||||
|
"MULTI_SELECTOR": {
|
||||||
|
"PLACEHOLDER": "None",
|
||||||
|
"TITLE": {
|
||||||
|
"AGENT": "Select agent",
|
||||||
|
"TEAM": "Select team"
|
||||||
|
},
|
||||||
|
"SEARCH": {
|
||||||
|
"NO_RESULTS": {
|
||||||
|
"AGENT": "No agents found",
|
||||||
|
"TEAM": "No teams found"
|
||||||
|
},
|
||||||
|
"PLACEHOLDER": {
|
||||||
|
"AGENT": "Search agents",
|
||||||
|
"TEAM": "Search teams"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,27 +24,21 @@
|
||||||
</woot-button>
|
</woot-button>
|
||||||
</template>
|
</template>
|
||||||
</contact-details-item>
|
</contact-details-item>
|
||||||
<multiselect
|
<multiselect-dropdown
|
||||||
v-model="assignedAgent"
|
|
||||||
:options="agentsList"
|
:options="agentsList"
|
||||||
label="name"
|
:selected-item="assignedAgent"
|
||||||
track-by="id"
|
:multiselector-title="$t('AGENT_MGMT.MULTI_SELECTOR.TITLE.AGENT')"
|
||||||
deselect-label=""
|
:multiselector-placeholder="
|
||||||
select-label=""
|
$t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER')
|
||||||
selected-label=""
|
"
|
||||||
:placeholder="$t('CONVERSATION_SIDEBAR.SELECT.PLACEHOLDER')"
|
:no-search-result="
|
||||||
:allow-empty="true"
|
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.AGENT')
|
||||||
>
|
"
|
||||||
<template slot="option" slot-scope="props">
|
:input-placeholder="
|
||||||
<div class="option__desc">
|
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.AGENT')
|
||||||
<availability-status-badge
|
"
|
||||||
:status="props.option.availability_status"
|
@click="onClickAssignAgent"
|
||||||
/>
|
/>
|
||||||
<span class="option__title">{{ props.option.name }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<span slot="noResult">{{ $t('AGENT_MGMT.SEARCH.NO_RESULTS') }}</span>
|
|
||||||
</multiselect>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="multiselect-wrap--small">
|
<div class="multiselect-wrap--small">
|
||||||
<contact-details-item
|
<contact-details-item
|
||||||
|
@ -52,19 +46,21 @@
|
||||||
icon="ion-ios-people"
|
icon="ion-ios-people"
|
||||||
emoji="🎢"
|
emoji="🎢"
|
||||||
/>
|
/>
|
||||||
<multiselect
|
<multiselect-dropdown
|
||||||
v-model="assignedTeam"
|
|
||||||
:options="teamsList"
|
:options="teamsList"
|
||||||
label="name"
|
:selected-item="assignedTeam"
|
||||||
track-by="id"
|
:multiselector-title="$t('AGENT_MGMT.MULTI_SELECTOR.TITLE.TEAM')"
|
||||||
deselect-label=""
|
:multiselector-placeholder="
|
||||||
select-label=""
|
$t('AGENT_MGMT.MULTI_SELECTOR.PLACEHOLDER')
|
||||||
selected-label=""
|
"
|
||||||
:placeholder="$t('CONVERSATION_SIDEBAR.SELECT.PLACEHOLDER')"
|
:no-search-result="
|
||||||
:allow-empty="true"
|
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.NO_RESULTS.TEAM')
|
||||||
>
|
"
|
||||||
<span slot="noResult">{{ $t('AGENT_MGMT.SEARCH.NO_RESULTS') }}</span>
|
:input-placeholder="
|
||||||
</multiselect>
|
$t('AGENT_MGMT.MULTI_SELECTOR.SEARCH.PLACEHOLDER.TEAM')
|
||||||
|
"
|
||||||
|
@click="onClickAssignTeam"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<conversation-labels :conversation-id="conversationId" />
|
<conversation-labels :conversation-id="conversationId" />
|
||||||
|
@ -138,7 +134,7 @@ import ContactDetailsItem from './ContactDetailsItem.vue';
|
||||||
import ContactInfo from './contact/ContactInfo';
|
import ContactInfo from './contact/ContactInfo';
|
||||||
import ConversationLabels from './labels/LabelBox.vue';
|
import ConversationLabels from './labels/LabelBox.vue';
|
||||||
import ContactCustomAttributes from './ContactCustomAttributes';
|
import ContactCustomAttributes from './ContactCustomAttributes';
|
||||||
import AvailabilityStatusBadge from 'dashboard/components/widgets/conversation/AvailabilityStatusBadge.vue';
|
import MultiselectDropdown from 'shared/components/ui/MultiselectDropdown.vue';
|
||||||
|
|
||||||
import flag from 'country-code-emoji';
|
import flag from 'country-code-emoji';
|
||||||
|
|
||||||
|
@ -149,7 +145,7 @@ export default {
|
||||||
ContactDetailsItem,
|
ContactDetailsItem,
|
||||||
ContactInfo,
|
ContactInfo,
|
||||||
ConversationLabels,
|
ConversationLabels,
|
||||||
AvailabilityStatusBadge,
|
MultiselectDropdown,
|
||||||
},
|
},
|
||||||
mixins: [alertMixin, agentMixin],
|
mixins: [alertMixin, agentMixin],
|
||||||
props: {
|
props: {
|
||||||
|
@ -330,6 +326,21 @@ export default {
|
||||||
};
|
};
|
||||||
this.assignedAgent = selfAssign;
|
this.assignedAgent = selfAssign;
|
||||||
},
|
},
|
||||||
|
onClickAssignAgent(selectedItem) {
|
||||||
|
if (this.assignedAgent && this.assignedAgent.id === selectedItem.id) {
|
||||||
|
this.assignedAgent = null;
|
||||||
|
} else {
|
||||||
|
this.assignedAgent = selectedItem;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onClickAssignTeam(selectedItemTeam) {
|
||||||
|
if (this.assignedTeam && this.assignedTeam.id === selectedItemTeam.id) {
|
||||||
|
this.assignedTeam = null;
|
||||||
|
} else {
|
||||||
|
this.assignedTeam = selectedItemTeam;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import Dropdown from './MutiselectDropdown';
|
import Dropdown from './MultiselectDropdown';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Components/Dropdown/Multiselect Dropdown',
|
title: 'Components/Dropdown/Multiselect Dropdown',
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-on-clickaway="onCloseDropdown" class="selector-wrap">
|
<div
|
||||||
|
v-on-clickaway="onCloseDropdown"
|
||||||
|
class="selector-wrap"
|
||||||
|
@keyup.esc="onCloseDropdown"
|
||||||
|
>
|
||||||
<woot-button
|
<woot-button
|
||||||
variant="hollow"
|
variant="hollow"
|
||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
|
@ -11,14 +11,19 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-scroll-container">
|
<div class="list-scroll-container">
|
||||||
<div class="dropdown-list">
|
<div
|
||||||
|
ref="multiselectDropdown"
|
||||||
|
class="multiselect-dropdown--list"
|
||||||
|
@keyup.up="onArrowUp"
|
||||||
|
@keyup.down="onArrowDown"
|
||||||
|
>
|
||||||
<woot-dropdown-menu>
|
<woot-dropdown-menu>
|
||||||
<woot-dropdown-item
|
<woot-dropdown-item
|
||||||
v-for="option in filteredOptions"
|
v-for="option in filteredOptions"
|
||||||
:key="option.id"
|
:key="option.id"
|
||||||
>
|
>
|
||||||
<woot-button
|
<woot-button
|
||||||
class="dropdown-item"
|
class="multiselect-dropdown--item"
|
||||||
variant="clear"
|
variant="clear"
|
||||||
:class="{
|
:class="{
|
||||||
active: option.id === (selectedItem && selectedItem.id),
|
active: option.id === (selectedItem && selectedItem.id),
|
||||||
|
@ -102,17 +107,50 @@ export default {
|
||||||
return this.filteredOptions.length === 0 && this.search !== '';
|
return this.filteredOptions.length === 0 && this.search !== '';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.focusInput();
|
this.focusInput();
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onclick(option) {
|
onclick(option) {
|
||||||
this.$emit('click', option);
|
this.$emit('click', option);
|
||||||
},
|
},
|
||||||
|
|
||||||
focusInput() {
|
focusInput() {
|
||||||
this.$refs.searchbar.focus();
|
this.$refs.searchbar.focus();
|
||||||
},
|
},
|
||||||
|
onArrowUp() {
|
||||||
|
const allDropdownItems = this.$refs.multiselectDropdown.querySelectorAll(
|
||||||
|
'.dropdown .multiselect-dropdown--item'
|
||||||
|
);
|
||||||
|
const focusedElement = this.$refs.multiselectDropdown.querySelector(
|
||||||
|
'.dropdown .multiselect-dropdown--item:focus'
|
||||||
|
);
|
||||||
|
const activeElementIndex = [...allDropdownItems].indexOf(focusedElement);
|
||||||
|
const lastElementIndex = allDropdownItems.length - 1;
|
||||||
|
|
||||||
|
if (activeElementIndex >= 1) {
|
||||||
|
allDropdownItems[activeElementIndex - 1].focus();
|
||||||
|
} else {
|
||||||
|
allDropdownItems[lastElementIndex].focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onArrowDown() {
|
||||||
|
const allDropdownItems = this.$refs.multiselectDropdown.querySelectorAll(
|
||||||
|
'.dropdown .multiselect-dropdown--item'
|
||||||
|
);
|
||||||
|
const focusedElement = this.$refs.multiselectDropdown.querySelector(
|
||||||
|
'.dropdown .multiselect-dropdown--item:focus'
|
||||||
|
);
|
||||||
|
const activeElementIndex = [...allDropdownItems].indexOf(focusedElement);
|
||||||
|
const lastElementIndex = allDropdownItems.length - 1;
|
||||||
|
|
||||||
|
if (activeElementIndex === lastElementIndex) {
|
||||||
|
allDropdownItems[0].focus();
|
||||||
|
} else {
|
||||||
|
allDropdownItems[activeElementIndex + 1].focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -153,18 +191,24 @@ export default {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-list {
|
.multiselect-dropdown--list {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 12rem;
|
max-height: 12rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
.multiselect-dropdown--item {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
|
background-color: var(--w-50);
|
||||||
|
color: var(--w-900);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background-color: var(--color-background);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-wrap {
|
.user-wrap {
|
||||||
|
|
Loading…
Reference in a new issue