diff --git a/app/javascript/dashboard/components/ui/ContextMenu.vue b/app/javascript/dashboard/components/ui/ContextMenu.vue
index 0773e4fd2..9ce6866cb 100644
--- a/app/javascript/dashboard/components/ui/ContextMenu.vue
+++ b/app/javascript/dashboard/components/ui/ContextMenu.vue
@@ -77,6 +77,15 @@ const handleClose = () => {
emit('close');
};
+const handleFocusOut = event => {
+ // Keep the menu open while focus stays inside it (e.g. the label search
+ // input); close it once focus leaves the menu entirely.
+ if (menuRef.value?.contains(event.relatedTarget)) {
+ return;
+ }
+ handleClose();
+};
+
onUnmounted(() => {
isLocked.value = false;
});
@@ -89,7 +98,7 @@ onUnmounted(() => {
class="fixed outline-none z-[9999] cursor-pointer"
:style="position"
tabindex="0"
- @blur="handleClose"
+ @focusout="handleFocusOut"
>
diff --git a/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue b/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue
index 34009935c..351ca6d2c 100644
--- a/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/contextMenu/Index.vue
@@ -7,10 +7,13 @@ import {
getSortedAgentsByAvailability,
getAgentsByUpdatedPresence,
} from 'dashboard/helper/agentHelper.js';
+import { picoSearch } from '@scmmishra/pico-search';
import MenuItem from './menuItem.vue';
import MenuItemWithSubmenu from './menuItemWithSubmenu.vue';
import wootConstants from 'dashboard/constants/globals';
import AgentLoadingPlaceholder from './agentLoadingPlaceholder.vue';
+import NextInput from 'dashboard/components-next/input/Input.vue';
+import Icon from 'dashboard/components-next/icon/Icon.vue';
const MENU = {
MARK_AS_READ: 'mark-as-read',
@@ -31,6 +34,8 @@ export default {
MenuItem,
MenuItemWithSubmenu,
AgentLoadingPlaceholder,
+ NextInput,
+ Icon,
},
props: {
chatId: {
@@ -87,6 +92,7 @@ export default {
data() {
return {
MENU,
+ labelSearchQuery: '',
STATUS_TYPE: wootConstants.STATUS_TYPE,
readOption: {
label: this.$t('CONVERSATION.CARD_CONTEXT_MENU.MARK_AS_READ'),
@@ -216,6 +222,14 @@ export default {
// Don't show snooze if the conversation is already snoozed/resolved/pending
return this.status === wootConstants.STATUS_TYPE.OPEN;
},
+ filteredLabels() {
+ const labels = this.labelSearchQuery
+ ? picoSearch(this.labels, this.labelSearchQuery, ['title'])
+ : this.labels;
+ // Assigned labels first, keeping each group's existing order.
+ const isAssigned = label => this.conversationLabels.includes(label.title);
+ return [...labels].sort((a, b) => isAssigned(b) - isAssigned(a));
+ },
},
mounted() {
this.$store.dispatch('inboxAssignableAgents/fetch', [this.inboxId]);
@@ -335,21 +349,49 @@ export default {
:option="labelMenuConfig"
:sub-menu-available="!!labels.length"
>
-
+
+
+
+
+
+
+
+
+
+
+ {{ $t('CONVERSATION.CARD_CONTEXT_MENU.NO_LABELS_FOUND') }}
+
+
-