diff --git a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
index 471d10f3c..6a0c4747e 100644
--- a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
@@ -172,15 +172,32 @@ export default {
showContentTemplates() {
return this.isATwilioWhatsAppChannel && !this.isPrivate;
},
- isPrivate() {
- if (
- this.currentChat.can_reply ||
+ isWithinMessagingWindow() {
+ return !!(
+ this.currentChat?.can_reply ||
this.isAWhatsAppChannel ||
this.isAPIInbox
- ) {
- return this.isOnPrivateNote;
- }
- return true;
+ );
+ },
+ canSendPublicReply() {
+ return (
+ this.isWithinMessagingWindow && !this.isBotOwnedPendingConversation
+ );
+ },
+ isPrivate() {
+ return (
+ !this.canSendPublicReply || this.replyType === REPLY_EDITOR_MODES.NOTE
+ );
+ },
+ isOnPrivateNote() {
+ return this.isBotOwnedPendingConversation
+ ? this.isPrivate
+ : this.replyType === REPLY_EDITOR_MODES.NOTE;
+ },
+ effectiveReplyMode() {
+ return this.isOnPrivateNote
+ ? REPLY_EDITOR_MODES.NOTE
+ : REPLY_EDITOR_MODES.REPLY;
},
hasMeaningfulEditorContent() {
const body = this.message || '';
@@ -197,10 +214,10 @@ export default {
);
return !!stripped.trim();
},
- isReplyRestricted() {
+ isBotOwnedPendingConversation() {
return (
- !this.currentChat?.can_reply &&
- !(this.isAWhatsAppChannel || this.isAPIInbox)
+ this.currentChat?.status === wootConstants.STATUS_TYPE.PENDING &&
+ this.currentChat?.meta?.assignee_type === 'AgentBot'
);
},
inboxId() {
@@ -330,9 +347,6 @@ export default {
showAudioRecorderEditor() {
return this.showAudioRecorder && this.isRecordingAudio;
},
- isOnPrivateNote() {
- return this.replyType === REPLY_EDITOR_MODES.NOTE;
- },
isOnExpandedLayout() {
const {
LAYOUT_TYPES: { CONDENSED },
@@ -375,7 +389,7 @@ export default {
return this.conversationId;
},
editorStateId() {
- return `draft-${this.conversationIdByRoute}-${this.replyType}`;
+ return `draft-${this.conversationIdByRoute}-${this.effectiveReplyMode}`;
},
audioRecordFormat() {
if (this.isAWhatsAppCloudChannel) {
@@ -456,7 +470,6 @@ export default {
},
watch: {
currentChat(conversation, oldConversation) {
- const { can_reply: canReply } = conversation;
if (oldConversation && oldConversation.id !== conversation.id) {
// Only update email fields when switching to a completely different conversation (by ID)
// This prevents overwriting user input (e.g., CC/BCC fields) when performing actions
@@ -470,11 +483,9 @@ export default {
return;
}
- if (canReply || this.isAWhatsAppChannel || this.isAPIInbox) {
- this.replyType = REPLY_EDITOR_MODES.REPLY;
- } else {
- this.replyType = REPLY_EDITOR_MODES.NOTE;
- }
+ this.replyType = this.isWithinMessagingWindow
+ ? REPLY_EDITOR_MODES.REPLY
+ : REPLY_EDITOR_MODES.NOTE;
this.fetchAndSetReplyTo();
},
@@ -492,7 +503,7 @@ export default {
},
conversationIdByRoute(conversationId, oldConversationId) {
if (conversationId !== oldConversationId) {
- this.setToDraft(oldConversationId, this.replyType);
+ this.setToDraft(oldConversationId, this.effectiveReplyMode);
this.getFromDraft();
this.resetRecorderAndClearAttachments();
}
@@ -501,13 +512,25 @@ export default {
// Autosave the current message draft.
this.doAutoSaveDraft();
},
- replyType(updatedReplyType, oldReplyType) {
+ showWhatsappTemplates(isAvailable) {
+ if (!isAvailable) this.hideWhatsappTemplatesModal();
+ },
+ showContentTemplates(isAvailable) {
+ if (!isAvailable) this.hideContentTemplatesModal();
+ },
+ effectiveReplyMode(updatedReplyType, oldReplyType) {
+ this.$store.dispatch('draftMessages/setReplyEditorMode', {
+ mode: updatedReplyType,
+ });
this.setToDraft(this.conversationIdByRoute, oldReplyType);
this.getFromDraft();
},
},
mounted() {
+ this.$store.dispatch('draftMessages/setReplyEditorMode', {
+ mode: this.effectiveReplyMode,
+ });
this.getFromDraft();
// Don't use the keyboard listener mixin here as the events here are supposed to be
// working even if the editor is focussed.
@@ -516,7 +539,7 @@ export default {
this.setCCAndToEmailsFromLastChat();
this.doAutoSaveDraft = debounce(
() => {
- this.saveDraft(this.conversationIdByRoute, this.replyType);
+ this.saveDraft(this.conversationIdByRoute, this.effectiveReplyMode);
},
500,
true
@@ -549,22 +572,22 @@ export default {
methods: {
getDraftKey(
conversationId = this.conversationIdByRoute,
- replyType = this.replyType
+ replyType = this.effectiveReplyMode
) {
return `draft-${conversationId}-${replyType}`;
},
- getCopilotAcceptedMessage(replyType = this.replyType) {
+ getCopilotAcceptedMessage(replyType = this.effectiveReplyMode) {
const key = this.getDraftKey(this.conversationIdByRoute, replyType);
return this.copilotAcceptedMessages[key] || '';
},
- setCopilotAcceptedMessage(message, replyType = this.replyType) {
+ setCopilotAcceptedMessage(message, replyType = this.effectiveReplyMode) {
const key = this.getDraftKey(this.conversationIdByRoute, replyType);
this.copilotAcceptedMessages[key] = trimContent(
message || '',
this.maxLength
);
},
- clearCopilotAcceptedMessage(replyType = this.replyType) {
+ clearCopilotAcceptedMessage(replyType = this.effectiveReplyMode) {
const key = this.getDraftKey(this.conversationIdByRoute, replyType);
delete this.copilotAcceptedMessages[key];
},
@@ -933,12 +956,8 @@ export default {
// This is to prevent from breaking the upload rules
if (this.attachedFiles.length > 0) this.attachedFiles = [];
- const { can_reply: canReply } = this.currentChat;
- this.$store.dispatch('draftMessages/setReplyEditorMode', {
- mode,
- });
- if (canReply || this.isAWhatsAppChannel || this.isAPIInbox)
- this.replyType = mode;
+ this.$store.dispatch('draftMessages/setReplyEditorMode', { mode });
+ if (this.canSendPublicReply) this.replyType = mode;
if (this.isRecordingAudio) {
this.toggleAudioRecorder();
}
@@ -1009,7 +1028,7 @@ export default {
},
onBlur() {
this.isFocused = false;
- this.saveDraft(this.conversationIdByRoute, this.replyType);
+ this.saveDraft(this.conversationIdByRoute, this.effectiveReplyMode);
},
onFocus() {
this.isFocused = true;
@@ -1257,7 +1276,7 @@ export default {
diff --git a/app/javascript/dashboard/components/widgets/conversation/specs/ReplyBox.spec.js b/app/javascript/dashboard/components/widgets/conversation/specs/ReplyBox.spec.js
new file mode 100644
index 000000000..135a0a638
--- /dev/null
+++ b/app/javascript/dashboard/components/widgets/conversation/specs/ReplyBox.spec.js
@@ -0,0 +1,357 @@
+import { shallowMount } from '@vue/test-utils';
+import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
+import { nextTick } from 'vue';
+import { createStore } from 'vuex';
+import ReplyBox from '../ReplyBox.vue';
+import WhatsappTemplates from '../WhatsappTemplates/Modal.vue';
+
+const CHANNELS = [
+ { name: 'WhatsApp Cloud', inbox: { channel_type: 'Channel::Whatsapp' } },
+ {
+ name: 'Twilio WhatsApp',
+ inbox: { channel_type: 'Channel::TwilioSms', medium: 'whatsapp' },
+ },
+ { name: 'API', inbox: { channel_type: 'Channel::Api' } },
+ { name: 'Instagram', inbox: { channel_type: 'Channel::Instagram' } },
+ { name: 'TikTok', inbox: { channel_type: 'Channel::Tiktok' } },
+ { name: 'Facebook', inbox: { channel_type: 'Channel::FacebookPage' } },
+ { name: 'Line', inbox: { channel_type: 'Channel::Line' } },
+ { name: 'Telegram', inbox: { channel_type: 'Channel::Telegram' } },
+ { name: 'Email', inbox: { channel_type: 'Channel::Email' } },
+ { name: 'Web widget', inbox: { channel_type: 'Channel::WebWidget' } },
+];
+
+const exemptFromMessagingWindow = name =>
+ ['WhatsApp Cloud', 'Twilio WhatsApp', 'API'].includes(name);
+
+const REPLIABLE = {
+ id: 1,
+ inbox_id: 1,
+ can_reply: true,
+ status: 'open',
+ meta: { sender: { id: 2 } },
+ messages: [],
+};
+
+const buildStore = ({ inbox, chat, templates, drafts = {} }) =>
+ createStore({
+ state: {
+ chat: { ...REPLIABLE, ...chat },
+ replyEditorMode: REPLY_EDITOR_MODES.REPLY,
+ drafts: { ...drafts },
+ },
+ mutations: {
+ selectChat: (s, c) => {
+ s.chat = c;
+ },
+ setReplyEditorMode: (s, mode) => {
+ s.replyEditorMode = mode;
+ },
+ setDraft: (s, { key, message }) => {
+ s.drafts = { ...s.drafts, [key]: message };
+ },
+ },
+ actions: {
+ 'draftMessages/setReplyEditorMode': ({ commit }, { mode }) =>
+ commit('setReplyEditorMode', mode),
+ 'draftMessages/set': ({ commit }, payload) => commit('setDraft', payload),
+ },
+ getters: {
+ getSelectedChat: s => s.chat,
+ getCurrentUser: () => ({ id: 7, name: 'Agent', accounts: [] }),
+ getCurrentAccountId: () => 1,
+ getMessageSignature: () => '',
+ getUISettings: () => ({}),
+ getLastEmailInSelectedChat: () => null,
+ 'globalConfig/get': () => ({}),
+ 'inboxes/getInbox': () => () => ({ id: 1, ...inbox }),
+ 'inboxes/getWhatsAppTemplates': () => () => templates,
+ 'contacts/getContact': () => () => ({}),
+ 'draftMessages/get': s => key => s.drafts[key] || '',
+ 'draftMessages/getReplyEditorMode': s => s.replyEditorMode,
+ 'accounts/isFeatureEnabledonAccount': () => () => false,
+ 'accounts/getAccount': () => () => ({}),
+ 'portals/allPortals': () => [],
+ 'integrations/getUIFlags': () => ({ isFetching: false }),
+ },
+ });
+
+const mountWith = ({
+ inbox,
+ chat,
+ templates = [{ name: 'greeting' }],
+ drafts,
+}) => {
+ const store = buildStore({ inbox, chat, templates, drafts });
+ const wrapper = shallowMount(ReplyBox, {
+ global: {
+ plugins: [store],
+ mocks: { $t: key => key },
+ // The bottom panel sits inside a , which shallowMount stubs
+ // without rendering its children.
+ stubs: { transition: false },
+ },
+ });
+ return { wrapper, store };
+};
+
+const topPanel = wrapper =>
+ wrapper.findComponent({ name: 'ReplyTopPanel' }).props();
+const bottomPanel = wrapper =>
+ wrapper.findComponent({ name: 'ReplyBottomPanel' }).props();
+const editor = wrapper =>
+ wrapper.findComponent({ name: 'WootMessageEditor' }).props();
+
+describe('ReplyBox', () => {
+ describe.each(CHANNELS)('$name', ({ name, inbox }) => {
+ it('locks the composer and hides template sends when a bot owns a pending conversation', () => {
+ const { wrapper } = mountWith({
+ inbox,
+ chat: {
+ status: 'pending',
+ meta: { sender: { id: 2 }, assignee_type: 'AgentBot' },
+ },
+ });
+
+ expect(topPanel(wrapper).isReplyRestricted).toBe(true);
+ expect(bottomPanel(wrapper).enableWhatsAppTemplates).toBe(false);
+ expect(bottomPanel(wrapper).enableContentTemplates).toBe(false);
+ // The note composer stays usable — this is a restriction, not a lockout.
+ expect(topPanel(wrapper).isEditorDisabled).toBe(false);
+ });
+
+ it('opens directly in note mode when a bot already owns the pending conversation', () => {
+ const { wrapper, store } = mountWith({
+ inbox,
+ chat: {
+ can_reply: false,
+ status: 'pending',
+ meta: { sender: { id: 2 }, assignee_type: 'AgentBot' },
+ },
+ });
+
+ expect(bottomPanel(wrapper).isOnPrivateNote).toBe(true);
+ expect(store.getters['draftMessages/getReplyEditorMode']).toBe(
+ REPLY_EDITOR_MODES.NOTE
+ );
+ expect(topPanel(wrapper).isEditorDisabled).toBe(false);
+ });
+
+ it('opens in reply mode for every other conversation', () => {
+ const { wrapper, store } = mountWith({ inbox });
+
+ expect(bottomPanel(wrapper).isOnPrivateNote).toBe(false);
+ expect(store.getters['draftMessages/getReplyEditorMode']).toBe(
+ REPLY_EDITOR_MODES.REPLY
+ );
+ });
+
+ it.each(['open', 'resolved', 'snoozed'])(
+ 'leaves the composer open when a bot owns a %s conversation',
+ status => {
+ const { wrapper } = mountWith({
+ inbox,
+ chat: {
+ status,
+ meta: { sender: { id: 2 }, assignee_type: 'AgentBot' },
+ },
+ });
+
+ expect(topPanel(wrapper).isReplyRestricted).toBe(false);
+ expect(bottomPanel(wrapper).enableWhatsAppTemplates).toBe(true);
+ }
+ );
+
+ it('leaves the composer open when a human owns a pending conversation', () => {
+ const { wrapper } = mountWith({
+ inbox,
+ chat: {
+ status: 'pending',
+ meta: { sender: { id: 2 }, assignee_type: 'User' },
+ },
+ });
+
+ expect(topPanel(wrapper).isReplyRestricted).toBe(false);
+ expect(bottomPanel(wrapper).enableWhatsAppTemplates).toBe(true);
+ });
+
+ it('matches the existing messaging-window rule when no bot is involved', () => {
+ const { wrapper } = mountWith({
+ inbox,
+ chat: { can_reply: false, status: 'resolved' },
+ });
+
+ const stillRepliable = exemptFromMessagingWindow(name);
+ expect(topPanel(wrapper).isReplyRestricted).toBe(!stillRepliable);
+ expect(bottomPanel(wrapper).enableWhatsAppTemplates).toBe(stillRepliable);
+ // WhatsApp/API disable the editor and steer to templates; everywhere
+ // else the composer falls back to a usable private note.
+ expect(topPanel(wrapper).isEditorDisabled).toBe(stillRepliable);
+ });
+ });
+
+ it('hides the template action when the inbox has no templates synced', () => {
+ const { wrapper } = mountWith({
+ inbox: { channel_type: 'Channel::Whatsapp' },
+ chat: { can_reply: false, status: 'open' },
+ templates: [],
+ });
+
+ expect(bottomPanel(wrapper).enableWhatsAppTemplates).toBe(false);
+ expect(topPanel(wrapper).isReplyRestricted).toBe(false);
+ });
+
+ describe('drafts', () => {
+ const DRAFTS = {
+ 'draft-1-REPLY': 'half typed reply',
+ 'draft-1-NOTE': 'a note',
+ };
+
+ it('loads the note draft while a bot owns the conversation', async () => {
+ const { wrapper } = mountWith({
+ inbox: { channel_type: 'Channel::WebWidget' },
+ chat: {
+ status: 'pending',
+ meta: { sender: { id: 2 }, assignee_type: 'AgentBot' },
+ },
+ drafts: DRAFTS,
+ });
+ await nextTick();
+
+ expect(editor(wrapper)).toMatchObject({
+ editorId: 'draft-1-NOTE',
+ modelValue: 'a note',
+ });
+ });
+
+ it('leaves the saved reply draft intact and restores it on takeover', async () => {
+ const { wrapper, store } = mountWith({
+ inbox: { channel_type: 'Channel::WebWidget' },
+ chat: {
+ status: 'pending',
+ meta: { sender: { id: 2 }, assignee_type: 'AgentBot' },
+ },
+ drafts: DRAFTS,
+ });
+ await nextTick();
+ expect(store.getters['draftMessages/get']('draft-1-REPLY')).toBe(
+ 'half typed reply'
+ );
+
+ store.commit('selectChat', {
+ ...REPLIABLE,
+ status: 'open',
+ meta: { sender: { id: 2 }, assignee_type: 'User' },
+ });
+ await nextTick();
+
+ expect(editor(wrapper)).toMatchObject({
+ editorId: 'draft-1-REPLY',
+ modelValue: 'half typed reply',
+ });
+ });
+ });
+
+ it('offers content templates on Twilio WhatsApp when no bot owns the conversation', () => {
+ const { wrapper } = mountWith({
+ inbox: { channel_type: 'Channel::TwilioSms', medium: 'whatsapp' },
+ chat: { can_reply: true, status: 'open' },
+ });
+
+ expect(bottomPanel(wrapper).enableContentTemplates).toBe(true);
+ });
+
+ describe('on selecting a conversation', () => {
+ const selectChat = async chat => {
+ const { wrapper, store } = mountWith({
+ inbox: { channel_type: 'Channel::WebWidget' },
+ });
+ store.commit('selectChat', { ...REPLIABLE, id: 99, ...chat });
+ await nextTick();
+ return { wrapper, store };
+ };
+
+ it('switches to note mode when a bot owns a pending conversation', async () => {
+ const { wrapper } = await selectChat({
+ status: 'pending',
+ meta: { sender: { id: 2 }, assignee_type: 'AgentBot' },
+ });
+
+ expect(bottomPanel(wrapper).isOnPrivateNote).toBe(true);
+ });
+
+ it('stays in reply mode when a human owns a pending conversation', async () => {
+ const { wrapper } = await selectChat({
+ status: 'pending',
+ meta: { sender: { id: 2 }, assignee_type: 'User' },
+ });
+
+ expect(bottomPanel(wrapper).isOnPrivateNote).toBe(false);
+ });
+
+ it('closes an open template modal when a bot takes over the conversation', async () => {
+ const { wrapper, store } = mountWith({
+ inbox: { channel_type: 'Channel::Whatsapp' },
+ });
+ wrapper
+ .findComponent({ name: 'ReplyBottomPanel' })
+ .vm.$emit('selectWhatsappTemplate');
+ await nextTick();
+ expect(wrapper.findComponent(WhatsappTemplates).props('show')).toBe(true);
+
+ store.commit('selectChat', {
+ ...REPLIABLE,
+ status: 'pending',
+ meta: { sender: { id: 2 }, assignee_type: 'AgentBot' },
+ });
+ await nextTick();
+
+ expect(wrapper.findComponent(WhatsappTemplates).props('show')).toBe(
+ false
+ );
+ });
+
+ it('returns to reply mode once the agent takes over', async () => {
+ const { wrapper, store } = await selectChat({
+ status: 'pending',
+ meta: { sender: { id: 2 }, assignee_type: 'AgentBot' },
+ });
+ expect(bottomPanel(wrapper).isOnPrivateNote).toBe(true);
+
+ store.commit('selectChat', {
+ ...REPLIABLE,
+ id: 99,
+ status: 'open',
+ meta: { sender: { id: 2 }, assignee_type: 'User' },
+ });
+ await nextTick();
+
+ expect(bottomPanel(wrapper).isOnPrivateNote).toBe(false);
+ expect(topPanel(wrapper).isReplyRestricted).toBe(false);
+ expect(store.getters['draftMessages/getReplyEditorMode']).toBe(
+ REPLY_EDITOR_MODES.REPLY
+ );
+ });
+
+ it('keeps the agent in note mode after they chose it themselves', async () => {
+ const { wrapper } = await selectChat({});
+ wrapper
+ .findComponent({ name: 'ReplyTopPanel' })
+ .vm.$emit('setReplyMode', REPLY_EDITOR_MODES.NOTE);
+ await nextTick();
+
+ expect(bottomPanel(wrapper).isOnPrivateNote).toBe(true);
+ });
+
+ it('mirrors the forced note mode into the draftMessages store', async () => {
+ const { store } = await selectChat({
+ status: 'pending',
+ meta: { sender: { id: 2 }, assignee_type: 'AgentBot' },
+ });
+
+ expect(store.getters['draftMessages/getReplyEditorMode']).toBe(
+ REPLY_EDITOR_MODES.NOTE
+ );
+ });
+ });
+});
diff --git a/app/javascript/dashboard/i18n/locale/en/conversation.json b/app/javascript/dashboard/i18n/locale/en/conversation.json
index 54eaa3b82..2ad4317ad 100644
--- a/app/javascript/dashboard/i18n/locale/en/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/en/conversation.json
@@ -36,8 +36,9 @@
"API_HOURS_WINDOW": "You can only reply to this conversation within {hours} hours",
"NOT_ASSIGNED_TO_YOU": "This conversation is not assigned to you. Would you like to assign this conversation to yourself?",
"ASSIGN_TO_ME": "Assign to me",
- "BOT_HANDOFF_MESSAGE": "You are responding to a conversation which is currently handled by an assistant or a bot.",
- "BOT_HANDOFF_ACTION": "Mark open and assign to you",
+ "BOT_HANDOFF_MESSAGE": "This conversation is currently handled by {assigneeName}.",
+ "BOT_HANDOFF_FALLBACK_ASSIGNEE": "a bot",
+ "BOT_HANDOFF_ACTION": "Take over",
"BOT_HANDOFF_REOPEN_ACTION": "Mark conversation open",
"BOT_HANDOFF_SUCCESS": "Conversation has been handed over to you",
"BOT_HANDOFF_ERROR": "Failed to take over the conversation. Please try again.",
diff --git a/app/javascript/dashboard/store/modules/conversations/index.js b/app/javascript/dashboard/store/modules/conversations/index.js
index 4f539e22d..fe25616f4 100644
--- a/app/javascript/dashboard/store/modules/conversations/index.js
+++ b/app/javascript/dashboard/store/modules/conversations/index.js
@@ -112,7 +112,9 @@ export const mutations = {
const chat = getConversationById(_state)(conversationId);
if (chat) {
chat.meta.assignee = assignee;
- chat.meta.assignee_type = assigneeType;
+ const inferredAssigneeType = assignee ? 'User' : null;
+ chat.meta.assignee_type =
+ assigneeType === undefined ? inferredAssigneeType : assigneeType;
}
},
diff --git a/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js
index a97bdad53..a0766d901 100644
--- a/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js
+++ b/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js
@@ -718,6 +718,48 @@ describe('#mutations', () => {
expect(state.allConversations[0].meta.assignee_type).toEqual('AgentBot');
expect(state.allConversations[1].meta.assignee).toBeUndefined();
});
+
+ it('should update assignee type when provided', () => {
+ const assignee = { id: 1, name: 'Agent' };
+ const state = {
+ allConversations: [{ id: 1, meta: { assignee_type: 'AgentBot' } }],
+ };
+
+ mutations[types.ASSIGN_AGENT](state, {
+ conversationId: 1,
+ assignee,
+ assigneeType: 'User',
+ });
+
+ expect(state.allConversations[0].meta.assignee_type).toEqual('User');
+ });
+
+ it('should infer user assignee type when assignee type is omitted', () => {
+ const assignee = { id: 1, name: 'Agent' };
+ const state = {
+ allConversations: [{ id: 1, meta: { assignee_type: 'AgentBot' } }],
+ };
+
+ mutations[types.ASSIGN_AGENT](state, {
+ conversationId: 1,
+ assignee,
+ });
+
+ expect(state.allConversations[0].meta.assignee_type).toEqual('User');
+ });
+
+ it('should clear assignee type when assignee type and assignee are omitted', () => {
+ const state = {
+ allConversations: [{ id: 1, meta: { assignee_type: 'AgentBot' } }],
+ };
+
+ mutations[types.ASSIGN_AGENT](state, {
+ conversationId: 1,
+ assignee: null,
+ });
+
+ expect(state.allConversations[0].meta.assignee_type).toBeNull();
+ });
});
describe('#ASSIGN_PRIORITY', () => {