Initial commit: Add logistics and order_detail message types
Some checks failed
Lock Threads / action (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Run Linux nightly installer / nightly (push) Has been cancelled
Some checks failed
Lock Threads / action (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot EE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot EE docker images / merge (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Publish Chatwoot CE docker images / build (linux/arm64, ubuntu-22.04-arm) (push) Has been cancelled
Publish Chatwoot CE docker images / merge (push) Has been cancelled
Run Chatwoot CE spec / lint-backend (push) Has been cancelled
Run Chatwoot CE spec / lint-frontend (push) Has been cancelled
Run Chatwoot CE spec / frontend-tests (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (0, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (1, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (10, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (11, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (12, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (13, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (14, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (15, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (2, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (3, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (4, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (5, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (6, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (7, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (8, 16) (push) Has been cancelled
Run Chatwoot CE spec / backend-tests (9, 16) (push) Has been cancelled
Run Linux nightly installer / nightly (push) Has been cancelled
- Add Logistics component with progress tracking - Add OrderDetail component for order information - Support data-driven steps and actions - Add blue color scale to widget SCSS - Fix node overflow and progress bar rendering issues - Add English translations for dashboard components Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
361
app/javascript/dashboard/store/modules/conversations/index.js
Normal file
361
app/javascript/dashboard/store/modules/conversations/index.js
Normal file
@@ -0,0 +1,361 @@
|
||||
import types from '../../mutation-types';
|
||||
import getters, { getSelectedChatConversation } from './getters';
|
||||
import actions from './actions';
|
||||
import { findPendingMessageIndex } from './helpers';
|
||||
import { MESSAGE_STATUS } from 'shared/constants/messages';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import { BUS_EVENTS } from '../../../../shared/constants/busEvents';
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
import { CONTENT_TYPES } from 'dashboard/components-next/message/constants.js';
|
||||
|
||||
const state = {
|
||||
allConversations: [],
|
||||
attachments: {},
|
||||
listLoadingStatus: true,
|
||||
chatStatusFilter: wootConstants.STATUS_TYPE.OPEN,
|
||||
chatSortFilter: wootConstants.SORT_BY_TYPE.LATEST,
|
||||
currentInbox: null,
|
||||
selectedChatId: null,
|
||||
appliedFilters: [],
|
||||
contextMenuChatId: null,
|
||||
conversationParticipants: [],
|
||||
conversationLastSeen: null,
|
||||
syncConversationsMessages: {},
|
||||
conversationFilters: {},
|
||||
copilotAssistant: {},
|
||||
};
|
||||
|
||||
const getConversationById = _state => conversationId => {
|
||||
return _state.allConversations.find(c => c.id === conversationId);
|
||||
};
|
||||
|
||||
// mutations
|
||||
export const mutations = {
|
||||
[types.SET_ALL_CONVERSATION](_state, conversationList) {
|
||||
const newAllConversations = [..._state.allConversations];
|
||||
conversationList.forEach(conversation => {
|
||||
const indexInCurrentList = newAllConversations.findIndex(
|
||||
c => c.id === conversation.id
|
||||
);
|
||||
if (indexInCurrentList < 0) {
|
||||
newAllConversations.push(conversation);
|
||||
} else if (conversation.id !== _state.selectedChatId) {
|
||||
// If the conversation is already in the list, replace it
|
||||
// Added this to fix the issue of the conversation not being updated
|
||||
// When reconnecting to the websocket. If the selectedChatId is not the same as
|
||||
// the conversation.id in the store, replace the existing conversation with the new one
|
||||
newAllConversations[indexInCurrentList] = conversation;
|
||||
} else {
|
||||
// If the conversation is already in the list and selectedChatId is the same,
|
||||
// replace all data except the messages array, attachments, dataFetched, allMessagesLoaded
|
||||
const existingConversation = newAllConversations[indexInCurrentList];
|
||||
newAllConversations[indexInCurrentList] = {
|
||||
...conversation,
|
||||
allMessagesLoaded: existingConversation.allMessagesLoaded,
|
||||
messages: existingConversation.messages,
|
||||
dataFetched: existingConversation.dataFetched,
|
||||
};
|
||||
}
|
||||
});
|
||||
_state.allConversations = newAllConversations;
|
||||
},
|
||||
[types.EMPTY_ALL_CONVERSATION](_state) {
|
||||
_state.allConversations = [];
|
||||
_state.selectedChatId = null;
|
||||
},
|
||||
[types.SET_ALL_MESSAGES_LOADED](_state) {
|
||||
const [chat] = getSelectedChatConversation(_state);
|
||||
chat.allMessagesLoaded = true;
|
||||
},
|
||||
|
||||
[types.CLEAR_ALL_MESSAGES_LOADED](_state) {
|
||||
const [chat] = getSelectedChatConversation(_state);
|
||||
chat.allMessagesLoaded = false;
|
||||
},
|
||||
[types.CLEAR_CURRENT_CHAT_WINDOW](_state) {
|
||||
_state.selectedChatId = null;
|
||||
},
|
||||
|
||||
[types.SET_PREVIOUS_CONVERSATIONS](_state, { id, data }) {
|
||||
if (data.length) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === id);
|
||||
chat.messages.unshift(...data);
|
||||
}
|
||||
},
|
||||
[types.SET_ALL_ATTACHMENTS](_state, { id, data }) {
|
||||
_state.attachments[id] = [...data];
|
||||
},
|
||||
[types.SET_MISSING_MESSAGES](_state, { id, data }) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === id);
|
||||
if (!chat) return;
|
||||
chat.messages = data;
|
||||
},
|
||||
|
||||
[types.SET_CURRENT_CHAT_WINDOW](_state, activeChat) {
|
||||
if (activeChat) {
|
||||
_state.selectedChatId = activeChat.id;
|
||||
}
|
||||
},
|
||||
|
||||
[types.ASSIGN_AGENT](_state, assignee) {
|
||||
const [chat] = getSelectedChatConversation(_state);
|
||||
chat.meta.assignee = assignee;
|
||||
},
|
||||
|
||||
[types.ASSIGN_TEAM](_state, { team, conversationId }) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
||||
chat.meta.team = team;
|
||||
},
|
||||
|
||||
[types.UPDATE_CONVERSATION_LAST_ACTIVITY](
|
||||
_state,
|
||||
{ lastActivityAt, conversationId }
|
||||
) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
||||
if (chat) {
|
||||
chat.last_activity_at = lastActivityAt;
|
||||
}
|
||||
},
|
||||
[types.ASSIGN_PRIORITY](_state, { priority, conversationId }) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
||||
chat.priority = priority;
|
||||
},
|
||||
|
||||
[types.UPDATE_CONVERSATION_CUSTOM_ATTRIBUTES](_state, custom_attributes) {
|
||||
const [chat] = getSelectedChatConversation(_state);
|
||||
chat.custom_attributes = custom_attributes;
|
||||
},
|
||||
|
||||
[types.CHANGE_CONVERSATION_STATUS](
|
||||
_state,
|
||||
{ conversationId, status, snoozedUntil }
|
||||
) {
|
||||
const conversation =
|
||||
getters.getConversationById(_state)(conversationId) || {};
|
||||
conversation.snoozed_until = snoozedUntil;
|
||||
conversation.status = status;
|
||||
},
|
||||
|
||||
[types.MUTE_CONVERSATION](_state) {
|
||||
const [chat] = getSelectedChatConversation(_state);
|
||||
chat.muted = true;
|
||||
},
|
||||
|
||||
[types.UNMUTE_CONVERSATION](_state) {
|
||||
const [chat] = getSelectedChatConversation(_state);
|
||||
chat.muted = false;
|
||||
},
|
||||
|
||||
[types.ADD_CONVERSATION_ATTACHMENTS](_state, message) {
|
||||
// early return if the message has not been sent, or has no attachments
|
||||
if (
|
||||
message.status !== MESSAGE_STATUS.SENT ||
|
||||
!message.attachments?.length
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = message.conversation_id;
|
||||
const existingAttachments = _state.attachments[id] || [];
|
||||
|
||||
const attachmentsToAdd = message.attachments.filter(attachment => {
|
||||
// if the attachment is not already in the store, add it
|
||||
// this is to prevent duplicates
|
||||
return !existingAttachments.some(
|
||||
existingAttachment => existingAttachment.id === attachment.id
|
||||
);
|
||||
});
|
||||
|
||||
// replace the attachments in the store
|
||||
_state.attachments[id] = [...existingAttachments, ...attachmentsToAdd];
|
||||
},
|
||||
|
||||
[types.DELETE_CONVERSATION_ATTACHMENTS](_state, message) {
|
||||
if (message.status !== MESSAGE_STATUS.SENT) return;
|
||||
|
||||
const { conversation_id: id } = message;
|
||||
const existingAttachments = _state.attachments[id] || [];
|
||||
if (!existingAttachments.length) return;
|
||||
|
||||
_state.attachments[id] = existingAttachments.filter(attachment => {
|
||||
return attachment.message_id !== message.id;
|
||||
});
|
||||
},
|
||||
|
||||
[types.ADD_MESSAGE]({ allConversations, selectedChatId }, message) {
|
||||
const { conversation_id: conversationId } = message;
|
||||
const [chat] = getSelectedChatConversation({
|
||||
allConversations,
|
||||
selectedChatId: conversationId,
|
||||
});
|
||||
if (!chat) return;
|
||||
|
||||
const pendingMessageIndex = findPendingMessageIndex(chat, message);
|
||||
if (pendingMessageIndex !== -1) {
|
||||
chat.messages[pendingMessageIndex] = message;
|
||||
} else {
|
||||
chat.messages.push(message);
|
||||
chat.timestamp = message.created_at;
|
||||
const { conversation: { unread_count: unreadCount = 0 } = {} } = message;
|
||||
chat.unread_count = unreadCount;
|
||||
if (selectedChatId === conversationId) {
|
||||
emitter.emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
[types.ADD_CONVERSATION](_state, conversation) {
|
||||
_state.allConversations.push(conversation);
|
||||
},
|
||||
|
||||
[types.DELETE_CONVERSATION](_state, conversationId) {
|
||||
_state.allConversations = _state.allConversations.filter(
|
||||
c => c.id !== conversationId
|
||||
);
|
||||
},
|
||||
|
||||
[types.UPDATE_CONVERSATION](_state, conversation) {
|
||||
const { allConversations } = _state;
|
||||
const index = allConversations.findIndex(c => c.id === conversation.id);
|
||||
|
||||
if (index > -1) {
|
||||
const selectedConversation = allConversations[index];
|
||||
|
||||
// ignore out of order events
|
||||
if (conversation.updated_at < selectedConversation.updated_at) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { messages, ...updates } = conversation;
|
||||
allConversations[index] = { ...selectedConversation, ...updates };
|
||||
if (_state.selectedChatId === conversation.id) {
|
||||
emitter.emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
|
||||
}
|
||||
} else {
|
||||
_state.allConversations.push(conversation);
|
||||
}
|
||||
},
|
||||
|
||||
[types.SET_LIST_LOADING_STATUS](_state) {
|
||||
_state.listLoadingStatus = true;
|
||||
},
|
||||
|
||||
[types.CLEAR_LIST_LOADING_STATUS](_state) {
|
||||
_state.listLoadingStatus = false;
|
||||
},
|
||||
|
||||
[types.UPDATE_MESSAGE_UNREAD_COUNT](
|
||||
_state,
|
||||
{ id, lastSeen, unreadCount = 0 }
|
||||
) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === id);
|
||||
if (chat) {
|
||||
chat.agent_last_seen_at = lastSeen;
|
||||
chat.unread_count = unreadCount;
|
||||
}
|
||||
},
|
||||
[types.CHANGE_CHAT_STATUS_FILTER](_state, data) {
|
||||
_state.chatStatusFilter = data;
|
||||
},
|
||||
|
||||
[types.CHANGE_CHAT_SORT_FILTER](_state, data) {
|
||||
_state.chatSortFilter = data;
|
||||
},
|
||||
|
||||
// Update assignee on action cable message
|
||||
[types.UPDATE_ASSIGNEE](_state, payload) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === payload.id);
|
||||
chat.meta.assignee = payload.assignee;
|
||||
},
|
||||
|
||||
[types.UPDATE_CONVERSATION_CONTACT](_state, { conversationId, ...payload }) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
||||
if (chat) {
|
||||
chat.meta.sender = payload;
|
||||
}
|
||||
},
|
||||
|
||||
[types.UPDATE_CONVERSATION_CALL_STATUS](
|
||||
_state,
|
||||
{ conversationId, callStatus }
|
||||
) {
|
||||
const chat = getConversationById(_state)(conversationId);
|
||||
if (!chat) return;
|
||||
|
||||
chat.additional_attributes = {
|
||||
...chat.additional_attributes,
|
||||
call_status: callStatus,
|
||||
};
|
||||
},
|
||||
|
||||
[types.UPDATE_MESSAGE_CALL_STATUS](_state, { conversationId, callStatus }) {
|
||||
const chat = getConversationById(_state)(conversationId);
|
||||
if (!chat) return;
|
||||
|
||||
const lastCall = (chat.messages || []).findLast(
|
||||
m => m.content_type === CONTENT_TYPES.VOICE_CALL
|
||||
);
|
||||
|
||||
if (!lastCall) return;
|
||||
|
||||
lastCall.content_attributes ??= {};
|
||||
lastCall.content_attributes.data = {
|
||||
...lastCall.content_attributes.data,
|
||||
status: callStatus,
|
||||
};
|
||||
},
|
||||
|
||||
[types.SET_ACTIVE_INBOX](_state, inboxId) {
|
||||
_state.currentInbox = inboxId ? parseInt(inboxId, 10) : null;
|
||||
},
|
||||
|
||||
[types.SET_CONVERSATION_CAN_REPLY](_state, { conversationId, canReply }) {
|
||||
const [chat] = _state.allConversations.filter(c => c.id === conversationId);
|
||||
if (chat) {
|
||||
chat.can_reply = canReply;
|
||||
}
|
||||
},
|
||||
|
||||
[types.CLEAR_CONTACT_CONVERSATIONS](_state, contactId) {
|
||||
const chats = _state.allConversations.filter(
|
||||
c => c.meta.sender.id !== contactId
|
||||
);
|
||||
_state.allConversations = chats;
|
||||
},
|
||||
|
||||
[types.SET_CONVERSATION_FILTERS](_state, data) {
|
||||
_state.appliedFilters = data;
|
||||
},
|
||||
|
||||
[types.CLEAR_CONVERSATION_FILTERS](_state) {
|
||||
_state.appliedFilters = [];
|
||||
},
|
||||
|
||||
[types.SET_LAST_MESSAGE_ID_IN_SYNC_CONVERSATION](
|
||||
_state,
|
||||
{ conversationId, messageId }
|
||||
) {
|
||||
_state.syncConversationsMessages[conversationId] = messageId;
|
||||
},
|
||||
|
||||
[types.SET_CONTEXT_MENU_CHAT_ID](_state, chatId) {
|
||||
_state.contextMenuChatId = chatId;
|
||||
},
|
||||
|
||||
[types.SET_CHAT_LIST_FILTERS](_state, data) {
|
||||
_state.conversationFilters = data;
|
||||
},
|
||||
[types.UPDATE_CHAT_LIST_FILTERS](_state, data) {
|
||||
_state.conversationFilters = { ..._state.conversationFilters, ...data };
|
||||
},
|
||||
[types.SET_INBOX_CAPTAIN_ASSISTANT](_state, data) {
|
||||
_state.copilotAssistant = data.assistant;
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
};
|
||||
Reference in New Issue
Block a user