Compare commits
9
Commits
master
...
fix/CW-7647
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81dfa2a3db | ||
|
|
8e9f4ff04b | ||
|
|
bae20ca83e | ||
|
|
b32e27a43d | ||
|
|
6f37c55623 | ||
|
|
7920ef0da4 | ||
|
|
9ff0a2fe64 | ||
|
|
954e5844a8 | ||
|
|
e6812eb92f |
@@ -77,6 +77,15 @@ const handleClose = () => {
|
|||||||
emit('close');
|
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(() => {
|
onUnmounted(() => {
|
||||||
isLocked.value = false;
|
isLocked.value = false;
|
||||||
});
|
});
|
||||||
@@ -89,7 +98,7 @@ onUnmounted(() => {
|
|||||||
class="fixed outline-none z-[9999] cursor-pointer"
|
class="fixed outline-none z-[9999] cursor-pointer"
|
||||||
:style="position"
|
:style="position"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@blur="handleClose"
|
@focusout="handleFocusOut"
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,10 +7,13 @@ import {
|
|||||||
getSortedAgentsByAvailability,
|
getSortedAgentsByAvailability,
|
||||||
getAgentsByUpdatedPresence,
|
getAgentsByUpdatedPresence,
|
||||||
} from 'dashboard/helper/agentHelper.js';
|
} from 'dashboard/helper/agentHelper.js';
|
||||||
|
import { picoSearch } from '@scmmishra/pico-search';
|
||||||
import MenuItem from './menuItem.vue';
|
import MenuItem from './menuItem.vue';
|
||||||
import MenuItemWithSubmenu from './menuItemWithSubmenu.vue';
|
import MenuItemWithSubmenu from './menuItemWithSubmenu.vue';
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
import AgentLoadingPlaceholder from './agentLoadingPlaceholder.vue';
|
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 = {
|
const MENU = {
|
||||||
MARK_AS_READ: 'mark-as-read',
|
MARK_AS_READ: 'mark-as-read',
|
||||||
@@ -31,6 +34,8 @@ export default {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
MenuItemWithSubmenu,
|
MenuItemWithSubmenu,
|
||||||
AgentLoadingPlaceholder,
|
AgentLoadingPlaceholder,
|
||||||
|
NextInput,
|
||||||
|
Icon,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
chatId: {
|
chatId: {
|
||||||
@@ -87,6 +92,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
MENU,
|
MENU,
|
||||||
|
labelSearchQuery: '',
|
||||||
STATUS_TYPE: wootConstants.STATUS_TYPE,
|
STATUS_TYPE: wootConstants.STATUS_TYPE,
|
||||||
readOption: {
|
readOption: {
|
||||||
label: this.$t('CONVERSATION.CARD_CONTEXT_MENU.MARK_AS_READ'),
|
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
|
// Don't show snooze if the conversation is already snoozed/resolved/pending
|
||||||
return this.status === wootConstants.STATUS_TYPE.OPEN;
|
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() {
|
mounted() {
|
||||||
this.$store.dispatch('inboxAssignableAgents/fetch', [this.inboxId]);
|
this.$store.dispatch('inboxAssignableAgents/fetch', [this.inboxId]);
|
||||||
@@ -335,21 +349,49 @@ export default {
|
|||||||
:option="labelMenuConfig"
|
:option="labelMenuConfig"
|
||||||
:sub-menu-available="!!labels.length"
|
:sub-menu-available="!!labels.length"
|
||||||
>
|
>
|
||||||
<MenuItem
|
<div class="pb-1 w-[12.5rem]">
|
||||||
v-for="label in labels"
|
<NextInput
|
||||||
:key="label.id"
|
v-model="labelSearchQuery"
|
||||||
:option="generateMenuLabelConfig(label, 'label')"
|
type="search"
|
||||||
:variant="
|
size="sm"
|
||||||
conversationLabels.includes(label.title)
|
class="w-full"
|
||||||
? 'label-assigned'
|
custom-input-class="!ps-8 !text-xs"
|
||||||
: 'label'
|
:placeholder="$t('CONVERSATION.CARD_CONTEXT_MENU.SEARCH_LABELS')"
|
||||||
"
|
@click.stop
|
||||||
@click.stop="
|
@keydown.stop
|
||||||
conversationLabels.includes(label.title)
|
>
|
||||||
? $emit('removeLabel', label)
|
<template #prefix>
|
||||||
: $emit('assignLabel', label)
|
<Icon
|
||||||
"
|
icon="i-lucide-search"
|
||||||
/>
|
class="absolute z-10 -translate-y-1/2 pointer-events-none size-3.5 text-n-slate-10 top-1/2 start-2"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</NextInput>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-x-hidden overflow-y-auto max-h-[12.5rem]">
|
||||||
|
<MenuItem
|
||||||
|
v-for="label in filteredLabels"
|
||||||
|
:key="label.id"
|
||||||
|
:option="generateMenuLabelConfig(label, 'label')"
|
||||||
|
:variant="
|
||||||
|
conversationLabels.includes(label.title)
|
||||||
|
? 'label-assigned'
|
||||||
|
: 'label'
|
||||||
|
"
|
||||||
|
@mousedown.prevent
|
||||||
|
@click.stop="
|
||||||
|
conversationLabels.includes(label.title)
|
||||||
|
? $emit('removeLabel', label)
|
||||||
|
: $emit('assignLabel', label)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<p
|
||||||
|
v-if="!filteredLabels.length"
|
||||||
|
class="px-2 py-2 m-0 text-xs text-center text-n-slate-11"
|
||||||
|
>
|
||||||
|
{{ $t('CONVERSATION.CARD_CONTEXT_MENU.NO_LABELS_FOUND') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</MenuItemWithSubmenu>
|
</MenuItemWithSubmenu>
|
||||||
<MenuItemWithSubmenu
|
<MenuItemWithSubmenu
|
||||||
v-if="isAllowed([MENU.AGENT])"
|
v-if="isAllowed([MENU.AGENT])"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ defineProps({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="menu text-n-slate-12 min-h-7 min-w-0" role="button">
|
<div class="menu group text-n-slate-12 min-h-7 min-w-0" role="button">
|
||||||
<fluent-icon
|
<fluent-icon
|
||||||
v-if="variant === 'icon' && option.icon"
|
v-if="variant === 'icon' && option.icon"
|
||||||
:icon="option.icon"
|
:icon="option.icon"
|
||||||
@@ -52,7 +52,7 @@ defineProps({
|
|||||||
<Icon
|
<Icon
|
||||||
v-if="variant === 'label-assigned'"
|
v-if="variant === 'label-assigned'"
|
||||||
icon="i-lucide-check"
|
icon="i-lucide-check"
|
||||||
class="flex-shrink-0 size-3.5 mr-1"
|
class="flex-shrink-0 size-3.5 text-n-brand group-hover:text-white"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -193,6 +193,8 @@
|
|||||||
},
|
},
|
||||||
"ASSIGN_AGENT": "Assign agent",
|
"ASSIGN_AGENT": "Assign agent",
|
||||||
"ASSIGN_LABEL": "Assign label",
|
"ASSIGN_LABEL": "Assign label",
|
||||||
|
"SEARCH_LABELS": "Search labels",
|
||||||
|
"NO_LABELS_FOUND": "No labels found",
|
||||||
"AGENTS_LOADING": "Loading agents...",
|
"AGENTS_LOADING": "Loading agents...",
|
||||||
"ASSIGN_TEAM": "Assign team",
|
"ASSIGN_TEAM": "Assign team",
|
||||||
"DELETE": "Delete conversation",
|
"DELETE": "Delete conversation",
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@
|
|||||||
"@amplitude/analytics-browser": "^2.11.10",
|
"@amplitude/analytics-browser": "^2.11.10",
|
||||||
"@breezystack/lamejs": "^1.2.7",
|
"@breezystack/lamejs": "^1.2.7",
|
||||||
"@chatwoot/ninja-keys": "1.2.3",
|
"@chatwoot/ninja-keys": "1.2.3",
|
||||||
"@chatwoot/prosemirror-schema": "1.3.23",
|
"@chatwoot/prosemirror-schema": "1.3.28",
|
||||||
"@chatwoot/utils": "^0.0.56",
|
"@chatwoot/utils": "^0.0.56",
|
||||||
"@formkit/core": "^1.7.2",
|
"@formkit/core": "^1.7.2",
|
||||||
"@formkit/vue": "^1.7.2",
|
"@formkit/vue": "^1.7.2",
|
||||||
|
|||||||
Generated
+5
-5
@@ -25,8 +25,8 @@ importers:
|
|||||||
specifier: 1.2.3
|
specifier: 1.2.3
|
||||||
version: 1.2.3
|
version: 1.2.3
|
||||||
'@chatwoot/prosemirror-schema':
|
'@chatwoot/prosemirror-schema':
|
||||||
specifier: 1.3.23
|
specifier: 1.3.28
|
||||||
version: 1.3.23
|
version: 1.3.28
|
||||||
'@chatwoot/utils':
|
'@chatwoot/utils':
|
||||||
specifier: ^0.0.56
|
specifier: ^0.0.56
|
||||||
version: 0.0.56
|
version: 0.0.56
|
||||||
@@ -452,8 +452,8 @@ packages:
|
|||||||
'@chatwoot/ninja-keys@1.2.3':
|
'@chatwoot/ninja-keys@1.2.3':
|
||||||
resolution: {integrity: sha512-xM8d9P5ikDMZm2WbaCTk/TW5HFauylrU3cJ75fq5je6ixKwyhl/0kZbVN/vbbZN4+AUX/OaSIn6IJbtCgIF67g==}
|
resolution: {integrity: sha512-xM8d9P5ikDMZm2WbaCTk/TW5HFauylrU3cJ75fq5je6ixKwyhl/0kZbVN/vbbZN4+AUX/OaSIn6IJbtCgIF67g==}
|
||||||
|
|
||||||
'@chatwoot/prosemirror-schema@1.3.23':
|
'@chatwoot/prosemirror-schema@1.3.28':
|
||||||
resolution: {integrity: sha512-jGxbWELCdlVI64BJiE1wT84ekJHYDXXKiluQIKT3aKPEjPwMR48umKF3A0yHjKoR7IIxCC9oM77TvXOA0ebLtw==}
|
resolution: {integrity: sha512-BaK5u13BPcXNDXFwpml7b1G6u8t6dfRqT6TGpZqk+4NiAfWpPHREmG8TovPKpgTmnuPmcH3j2NkSeVYpArEqgg==}
|
||||||
|
|
||||||
'@chatwoot/utils@0.0.56':
|
'@chatwoot/utils@0.0.56':
|
||||||
resolution: {integrity: sha512-A6dmPLfTSrW4qYNY73btyi4PqpfzcXRSaucscZTQdzNqF6G/QUdgnBmHtho8HeiYby/kSHXaSxLJj+0dx3yEQQ==}
|
resolution: {integrity: sha512-A6dmPLfTSrW4qYNY73btyi4PqpfzcXRSaucscZTQdzNqF6G/QUdgnBmHtho8HeiYby/kSHXaSxLJj+0dx3yEQQ==}
|
||||||
@@ -5124,7 +5124,7 @@ snapshots:
|
|||||||
hotkeys-js: 3.8.7
|
hotkeys-js: 3.8.7
|
||||||
lit: 2.2.6
|
lit: 2.2.6
|
||||||
|
|
||||||
'@chatwoot/prosemirror-schema@1.3.23':
|
'@chatwoot/prosemirror-schema@1.3.28':
|
||||||
dependencies:
|
dependencies:
|
||||||
markdown-it-sup: 2.0.0
|
markdown-it-sup: 2.0.0
|
||||||
prosemirror-commands: 1.7.1
|
prosemirror-commands: 1.7.1
|
||||||
|
|||||||
Reference in New Issue
Block a user