diff --git a/app/javascript/dashboard/api/inbox/conversation.js b/app/javascript/dashboard/api/inbox/conversation.js index 93dc884f9..bee3cd23b 100644 --- a/app/javascript/dashboard/api/inbox/conversation.js +++ b/app/javascript/dashboard/api/inbox/conversation.js @@ -14,6 +14,7 @@ class ConversationApi extends ApiClient { labels, teamId, conversationType, + sortBy, }) { return axios.get(this.url, { params: { @@ -24,6 +25,7 @@ class ConversationApi extends ApiClient { page, labels, conversation_type: conversationType, + sort_by: sortBy, }, }); } diff --git a/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss b/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss index 97adeed4c..6c37e5677 100644 --- a/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss +++ b/app/javascript/dashboard/assets/scss/widgets/_conversation-view.scss @@ -95,10 +95,6 @@ align-items: center; justify-content: space-between; padding: 0 var(--space-normal); - - .page-title { - margin-bottom: 0; - } } .content-box { diff --git a/app/javascript/dashboard/components/ChatList.vue b/app/javascript/dashboard/components/ChatList.vue index ecf0557e1..7995c91fd 100644 --- a/app/javascript/dashboard/components/ChatList.vue +++ b/app/javascript/dashboard/components/ChatList.vue @@ -11,15 +11,23 @@ class="chat-list__top" :class="{ filter__applied: hasAppliedFiltersOrActiveFolders }" > -

- {{ pageTitle }} -

- -
- +

+ {{ pageTitle }} +

+ + class="conversation--status-pill" + > + {{ + this.$t(`CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.${activeStatus}.TEXT`) + }} + +
+
- +
@@ -167,8 +178,8 @@ + diff --git a/app/javascript/dashboard/components/widgets/conversation/FilterItem.vue b/app/javascript/dashboard/components/widgets/conversation/FilterItem.vue new file mode 100644 index 000000000..efd68134b --- /dev/null +++ b/app/javascript/dashboard/components/widgets/conversation/FilterItem.vue @@ -0,0 +1,55 @@ + + + diff --git a/app/javascript/dashboard/constants/globals.js b/app/javascript/dashboard/constants/globals.js index 71dd68051..20497b039 100644 --- a/app/javascript/dashboard/constants/globals.js +++ b/app/javascript/dashboard/constants/globals.js @@ -12,6 +12,10 @@ export default { SNOOZED: 'snoozed', ALL: 'all', }, + SORT_BY_TYPE: { + LATEST: 'latest', + CREATED_AT: 'sort_on_created_at', + }, ARTICLE_STATUS_TYPES: { DRAFT: 0, PUBLISH: 1, diff --git a/app/javascript/dashboard/helper/actionCable.js b/app/javascript/dashboard/helper/actionCable.js index 9f6a7c6fe..bf15aa9c5 100644 --- a/app/javascript/dashboard/helper/actionCable.js +++ b/app/javascript/dashboard/helper/actionCable.js @@ -105,8 +105,16 @@ class ActionCableConnector extends BaseActionCableConnector { onLogout = () => AuthAPI.logout(); onMessageCreated = data => { + const { + conversation: { last_activity_at: lastActivityAt }, + conversation_id: conversationId, + } = data; DashboardAudioNotificationHelper.onNewMessage(data); this.app.$store.dispatch('addMessage', data); + this.app.$store.dispatch('updateConversationLastActivity', { + lastActivityAt, + conversationId, + }); }; onReload = () => window.location.reload(); diff --git a/app/javascript/dashboard/i18n/locale/en/chatlist.json b/app/javascript/dashboard/i18n/locale/en/chatlist.json index b96cfb976..6312d4c9f 100644 --- a/app/javascript/dashboard/i18n/locale/en/chatlist.json +++ b/app/javascript/dashboard/i18n/locale/en/chatlist.json @@ -35,6 +35,20 @@ "TEXT": "All" } }, + "VIEW_FILTER": "View", + "SORT_TOOLTIP_LABEL": "Sort conversations", + "CHAT_SORT": { + "STATUS": "Status", + "ORDER_BY": "Order by" + }, + "CHAT_SORT_FILTER_ITEMS": { + "latest": { + "TEXT": "Last activity" + }, + "sort_on_created_at": { + "TEXT": "Created at" + } + }, "ATTACHMENTS": { "image": { "CONTENT": "Picture message" @@ -55,6 +69,24 @@ "CONTENT": "has shared a url" } }, + "CHAT_SORT_BY_FILTER": { + "TITLE": "Sort conversation", + "DROPDOWN_TITLE": "Sort by", + "ITEMS": { + "LATEST": { + "NAME": "Last activity at", + "LABEL": "Last activity" + }, + "CREATED_AT": { + "NAME": "Created at", + "LABEL": "Created at" + }, + "LAST_USER_MESSAGE_AT": { + "NAME": "Last user message at", + "LABEL": "Last message" + } + } + }, "RECEIVED_VIA_EMAIL": "Received via email", "VIEW_TWEET_IN_TWITTER": "View tweet in Twitter", "REPLY_TO_TWEET": "Reply to this tweet", diff --git a/app/javascript/dashboard/routes/dashboard/conversation/search/PopOverSearch.vue b/app/javascript/dashboard/routes/dashboard/conversation/search/PopOverSearch.vue index 7979e6832..372e6b812 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/search/PopOverSearch.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/search/PopOverSearch.vue @@ -6,7 +6,9 @@
-

{{ $t('CONVERSATION.SEARCH_MESSAGES') }}

+

+ {{ $t('CONVERSATION.SEARCH_MESSAGES') }} +

- allConversations.sort( - (a, b) => b.messages.last()?.created_at - a.messages.last()?.created_at - ), + getAllConversations: ({ allConversations, chatSortFilter }) => { + const comparator = { + latest: (a, b) => b.last_activity_at - a.last_activity_at, + sort_on_created_at: (a, b) => a.created_at - b.created_at, + }; + + return allConversations.sort(comparator[chatSortFilter]); + }, getSelectedChat: ({ selectedChatId, allConversations }) => { const selectedChat = allConversations.find( conversation => conversation.id === selectedChatId @@ -85,6 +89,7 @@ const getters = { ).length; }, getChatStatusFilter: ({ chatStatusFilter }) => chatStatusFilter, + getChatSortFilter: ({ chatSortFilter }) => chatSortFilter, getSelectedInbox: ({ currentInbox }) => currentInbox, getConversationById: _state => conversationId => { return _state.allConversations.find( diff --git a/app/javascript/dashboard/store/modules/conversations/index.js b/app/javascript/dashboard/store/modules/conversations/index.js index 8e68071ef..04db1fddf 100644 --- a/app/javascript/dashboard/store/modules/conversations/index.js +++ b/app/javascript/dashboard/store/modules/conversations/index.js @@ -10,6 +10,7 @@ const state = { allConversations: [], listLoadingStatus: true, chatStatusFilter: wootConstants.STATUS_TYPE.OPEN, + chatSortFilter: wootConstants.SORT_BY_TYPE.LATEST, currentInbox: null, selectedChatId: null, appliedFilters: [], @@ -77,6 +78,13 @@ export const mutations = { Vue.set(chat.meta, 'team', team); }, + [types.UPDATE_CONVERSATION_LAST_ACTIVITY]( + _state, + { lastActivityAt, conversationId } + ) { + const [chat] = _state.allConversations.filter(c => c.id === conversationId); + Vue.set(chat, 'last_activity_at', lastActivityAt); + }, [types.ASSIGN_PRIORITY](_state, { priority, conversationId }) { const [chat] = _state.allConversations.filter(c => c.id === conversationId); Vue.set(chat, 'priority', priority); @@ -175,6 +183,10 @@ export const mutations = { _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); diff --git a/app/javascript/dashboard/store/mutation-types.js b/app/javascript/dashboard/store/mutation-types.js index 3938061b9..ae15ff572 100644 --- a/app/javascript/dashboard/store/mutation-types.js +++ b/app/javascript/dashboard/store/mutation-types.js @@ -16,6 +16,7 @@ export default { SET_ALL_MESSAGES_LOADED: 'SET_ALL_MESSAGES_LOADED', CLEAR_ALL_MESSAGES_LOADED: 'CLEAR_ALL_MESSAGES_LOADED', CHANGE_CHAT_STATUS_FILTER: 'CHANGE_CHAT_STATUS_FILTER', + CHANGE_CHAT_SORT_FILTER: 'CHANGE_CHAT_SORT_FILTER', UPDATE_ASSIGNEE: 'UPDATE_ASSIGNEE', UPDATE_CONVERSATION_CONTACT: 'UPDATE_CONVERSATION_CONTACT', CLEAR_CONTACT_CONVERSATIONS: 'CLEAR_CONTACT_CONVERSATIONS', @@ -45,6 +46,7 @@ export default { SET_ACTIVE_INBOX: 'SET_ACTIVE_INBOX', UPDATE_CONVERSATION_CUSTOM_ATTRIBUTES: 'UPDATE_CONVERSATION_CUSTOM_ATTRIBUTES', + UPDATE_CONVERSATION_LAST_ACTIVITY: 'UPDATE_CONVERSATION_LAST_ACTIVITY', SET_MISSING_MESSAGES: 'SET_MISSING_MESSAGES', SET_CONVERSATION_CAN_REPLY: 'SET_CONVERSATION_CAN_REPLY', diff --git a/app/javascript/shared/assets/stylesheets/colors.scss b/app/javascript/shared/assets/stylesheets/colors.scss index 5e45d264f..c80f52427 100644 --- a/app/javascript/shared/assets/stylesheets/colors.scss +++ b/app/javascript/shared/assets/stylesheets/colors.scss @@ -103,4 +103,7 @@ --color-whatsapp-brand: #25d366; --color-sms-twilio: #f42f46; --color-cloud-generic: #18b7b0; + + // Tech-debt + --color-medium-gray: #8492a6; } diff --git a/app/javascript/shared/components/FluentIcon/dashboard-icons.json b/app/javascript/shared/components/FluentIcon/dashboard-icons.json index 94679eb9f..42514a8fb 100644 --- a/app/javascript/shared/components/FluentIcon/dashboard-icons.json +++ b/app/javascript/shared/components/FluentIcon/dashboard-icons.json @@ -178,6 +178,7 @@ "translate-outline": "M16.953 5.303a1 1 0 0 0-1.906-.606c-.124.389-.236.899-.324 1.344-.565.012-1.12 0-1.652-.038a1 1 0 1 0-.142 1.995c.46.032.934.048 1.416.047a25.649 25.649 0 0 0-.24 1.698c-1.263.716-2.142 1.684-2.636 2.7-.624 1.283-.7 2.857.239 3.883.675.736 1.704.758 2.499.588.322-.068.654-.176.988-.32a1 1 0 0 0 1.746-.93 13.17 13.17 0 0 0-.041-.115 8.404 8.404 0 0 0 2.735-4.06c.286.251.507.55.658.864.284.594.334 1.271.099 1.91-.234.633-.78 1.313-1.84 1.843a1 1 0 0 0 .895 1.789c1.44-.72 2.385-1.758 2.821-2.94a4.436 4.436 0 0 0-.17-3.464 4.752 4.752 0 0 0-2.104-2.165C19.998 9.22 20 9.11 20 9a1 1 0 0 0-1.974-.23 5.984 5.984 0 0 0-1.796.138c.047-.305.102-.626.166-.964a20.142 20.142 0 0 0 2.842-.473 1 1 0 0 0-.476-1.942c-.622.152-1.286.272-1.964.358.048-.208.1-.409.155-.584Zm-3.686 8.015c.166-.34.414-.697.758-1.037.02.348.053.67.098.973.083.56.207 1.048.341 1.477a3.41 3.41 0 0 1-.674.227c-.429.092-.588.019-.614.006l-.004-.001c-.162-.193-.329-.774.095-1.645Zm4.498-2.562a6.362 6.362 0 0 1-1.568 2.73 7.763 7.763 0 0 1-.095-.525 10.294 10.294 0 0 1-.088-1.904c.033-.013.067-.024.1-.036l1.651-.265Zm0 0-1.651.265c.602-.212 1.155-.29 1.651-.265ZM7.536 6.29a6.342 6.342 0 0 0-4.456.331 1 1 0 0 0 .848 1.811 4.342 4.342 0 0 1 3.049-.222c.364.107.568.248.69.37.12.123.203.27.257.454.067.225.087.446.09.69a8.195 8.195 0 0 0-.555-.117c-1.146-.199-2.733-.215-4.262.64-1.271.713-1.796 2.168-1.682 3.448.12 1.326.94 2.679 2.572 3.136 1.48.414 2.913-.045 3.877-.507l.08-.04a1 1 0 0 0 1.96-.281V10.5c0-.053.002-.12.005-.2.012-.417.034-1.16-.168-1.838a3.043 3.043 0 0 0-.755-1.29c-.394-.398-.91-.694-1.547-.881h-.003Zm-.419 5.288c.344.06.647.143.887.222v2.197a7.021 7.021 0 0 1-.905.524c-.792.38-1.682.605-2.473.384-.698-.195-1.06-.742-1.119-1.389-.062-.693.243-1.286.667-1.523.987-.553 2.06-.569 2.943-.415Z", "eye-show-outline": "M12 9.005a4 4 0 1 1 0 8 4 4 0 0 1 0-8Zm0 1.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5ZM12 5.5c4.613 0 8.596 3.15 9.701 7.564a.75.75 0 1 1-1.455.365 8.503 8.503 0 0 0-16.493.004.75.75 0 0 1-1.455-.363A10.003 10.003 0 0 1 12 5.5Z", "eye-hide-outline": "M2.22 2.22a.75.75 0 0 0-.073.976l.073.084 4.034 4.035a9.986 9.986 0 0 0-3.955 5.75.75.75 0 0 0 1.455.364 8.49 8.49 0 0 1 3.58-5.034l1.81 1.81A4 4 0 0 0 14.8 15.86l5.919 5.92a.75.75 0 0 0 1.133-.977l-.073-.084-6.113-6.114.001-.002-1.2-1.198-2.87-2.87h.002L8.719 7.658l.001-.002-1.133-1.13L3.28 2.22a.75.75 0 0 0-1.06 0Zm7.984 9.045 3.535 3.536a2.5 2.5 0 0 1-3.535-3.535ZM12 5.5c-1 0-1.97.148-2.889.425l1.237 1.236a8.503 8.503 0 0 1 9.899 6.272.75.75 0 0 0 1.455-.363A10.003 10.003 0 0 0 12 5.5Zm.195 3.51 3.801 3.8a4.003 4.003 0 0 0-3.801-3.8Z", + "sort-icon-outline": "m17.25 4l-.1.007a.75.75 0 0 0-.65.743v12.692l-3.22-3.218l-.084-.072a.75.75 0 0 0-.976 1.134l4.504 4.5l.084.072a.75.75 0 0 0 .976-.073l4.497-4.5l.072-.084a.75.75 0 0 0-.073-.977l-.084-.072a.75.75 0 0 0-.977.073L18 17.446V4.75l-.006-.102A.75.75 0 0 0 17.251 4Zm-11.036.22L1.72 8.715l-.073.084a.75.75 0 0 0 .073.976l.084.073a.75.75 0 0 0 .976-.073l3.217-3.218v12.698l.008.102a.75.75 0 0 0 .743.648l.101-.007a.75.75 0 0 0 .649-.743L7.497 6.559l3.223 3.217l.084.072a.75.75 0 0 0 .975-1.134L7.275 4.22l-.085-.072a.75.75 0 0 0-.976.073Z", "wand-outline": "m13.314 7.565l-.136.126l-10.48 10.488a2.27 2.27 0 0 0 3.211 3.208L16.388 10.9a2.251 2.251 0 0 0-.001-3.182l-.157-.146a2.25 2.25 0 0 0-2.916-.007Zm-.848 2.961l1.088 1.088l-8.706 8.713a.77.77 0 1 1-1.089-1.088l8.707-8.713Zm4.386 4.48L16.75 15a.75.75 0 0 0-.743.648L16 15.75v.75h-.75a.75.75 0 0 0-.743.648l-.007.102c0 .38.282.694.648.743l.102.007H16v.75c0 .38.282.694.648.743l.102.007a.75.75 0 0 0 .743-.648l.007-.102V18h.75a.75.75 0 0 0 .743-.648L19 17.25a.75.75 0 0 0-.648-.743l-.102-.007h-.75v-.75a.75.75 0 0 0-.648-.743L16.75 15l.102.007Zm-1.553-6.254l.027.027a.751.751 0 0 1 0 1.061l-.711.713l-1.089-1.089l.73-.73a.75.75 0 0 1 1.043.018ZM6.852 5.007L6.75 5a.75.75 0 0 0-.743.648L6 5.75v.75h-.75a.75.75 0 0 0-.743.648L4.5 7.25c0 .38.282.693.648.743L5.25 8H6v.75c0 .38.282.693.648.743l.102.007a.75.75 0 0 0 .743-.648L7.5 8.75V8h.75a.75.75 0 0 0 .743-.648L9 7.25a.75.75 0 0 0-.648-.743L8.25 6.5H7.5v-.75a.75.75 0 0 0-.648-.743L6.75 5l.102.007Zm12-2L18.75 3a.75.75 0 0 0-.743.648L18 3.75v.75h-.75a.75.75 0 0 0-.743.648l-.007.102c0 .38.282.693.648.743L17.25 6H18v.75c0 .38.282.693.648.743l.102.007a.75.75 0 0 0 .743-.648l.007-.102V6h.75a.75.75 0 0 0 .743-.648L21 5.25a.75.75 0 0 0-.648-.743L20.25 4.5h-.75v-.75a.75.75 0 0 0-.648-.743L18.75 3l.102.007Z", "grab-handle-outline": [ "M5 4C5 3.44772 5.44772 3 6 3H10C10.5523 3 11 3.44772 11 4V7C11 7.55228 10.5523 8 10 8H6C5.44772 8 5 7.55228 5 7V4Z", diff --git a/app/javascript/shared/components/ui/dropdown/DropdownSubMenu.vue b/app/javascript/shared/components/ui/dropdown/DropdownSubMenu.vue index 7a1318404..aa3a3134f 100644 --- a/app/javascript/shared/components/ui/dropdown/DropdownSubMenu.vue +++ b/app/javascript/shared/components/ui/dropdown/DropdownSubMenu.vue @@ -1,7 +1,7 @@