diff --git a/frontend/app/javascript/dashboard/components/ChatList.vue b/frontend/app/javascript/dashboard/components/ChatList.vue
index e4fd99d2..26f2bfde 100644
--- a/frontend/app/javascript/dashboard/components/ChatList.vue
+++ b/frontend/app/javascript/dashboard/components/ChatList.vue
@@ -97,6 +97,7 @@ const allChatList = useMapGetter('getAllStatusChats');
const unAssignedChatsList = useMapGetter('getUnAssignedChats');
const participatingChatsList = useMapGetter('getParticipatingChats');
const chatListLoading = useMapGetter('getChatListLoadingStatus');
+const chatListError = useMapGetter('getChatListErrorStatus');
const activeInbox = useMapGetter('getSelectedInbox');
const conversationStats = useMapGetter('conversationStats/getStats');
const appliedFilters = useMapGetter('getAppliedConversationFiltersV2');
@@ -915,7 +916,13 @@ watch(chatLists, () => {
/>
+ {{ $t('CHAT_LIST.LIST.ERROR') }}
+
+
{{ $t('CHAT_LIST.LIST.404') }}
@@ -933,6 +940,7 @@ watch(chatLists, () => {
[] },
isLoading: { type: Boolean, default: false },
+ hasError: { type: Boolean, default: false },
showEndOfListMessage: { type: Boolean, default: false },
label: { type: String, default: '' },
teamId: { type: [String, Number], default: 0 },
@@ -86,7 +87,7 @@ defineExpose({ conversationListRef });
{{ $t('CHAT_LIST.EOF') }}
diff --git a/frontend/app/javascript/dashboard/i18n/locale/en/chatlist.json b/frontend/app/javascript/dashboard/i18n/locale/en/chatlist.json
index 1384dae2..1ad72e57 100644
--- a/frontend/app/javascript/dashboard/i18n/locale/en/chatlist.json
+++ b/frontend/app/javascript/dashboard/i18n/locale/en/chatlist.json
@@ -4,7 +4,8 @@
"LOAD_MORE_CONVERSATIONS": "Load more conversations",
"EOF": "All conversations loaded 🎉",
"LIST": {
- "404": "There are no active conversations in this group."
+ "404": "There are no active conversations in this group.",
+ "ERROR": "Unable to load conversations. Waiting for the server to reconnect."
},
"FAILED_TO_SEND": "Failed to send",
"TAB_HEADING": "Conversations",
diff --git a/frontend/app/javascript/dashboard/i18n/locale/zh_CN/chatlist.json b/frontend/app/javascript/dashboard/i18n/locale/zh_CN/chatlist.json
index 9d170012..90a3e11e 100644
--- a/frontend/app/javascript/dashboard/i18n/locale/zh_CN/chatlist.json
+++ b/frontend/app/javascript/dashboard/i18n/locale/zh_CN/chatlist.json
@@ -4,7 +4,8 @@
"LOAD_MORE_CONVERSATIONS": "加载更多对话",
"EOF": "所有对话已加载 🎉",
"LIST": {
- "404": "没有有效的对话在这个群组里面"
+ "404": "没有有效的对话在这个群组里面",
+ "ERROR": "无法加载会话,正在等待服务器重新连接。"
},
"FAILED_TO_SEND": "发送失败",
"TAB_HEADING": "会话",
diff --git a/frontend/app/javascript/dashboard/store/modules/conversations/actions.js b/frontend/app/javascript/dashboard/store/modules/conversations/actions.js
index bbc35b5f..d9da8f1d 100644
--- a/frontend/app/javascript/dashboard/store/modules/conversations/actions.js
+++ b/frontend/app/javascript/dashboard/store/modules/conversations/actions.js
@@ -44,6 +44,7 @@ const actions = {
},
fetchAllConversations: async ({ commit, state, dispatch }, explicitFilters) => {
+ commit(types.CLEAR_LIST_ERROR_STATUS);
commit(types.SET_LIST_LOADING_STATUS);
try {
const params = explicitFilters || state.conversationFilters;
@@ -56,12 +57,19 @@ const actions = {
data,
params.assigneeType
);
+ commit(types.CLEAR_LIST_ERROR_STATUS);
} catch (error) {
- // Handle error
+ // Stop the infinite-scroll observer from immediately retrying a failed
+ // page while the backend is unavailable. Reconnect/manual fetches clear
+ // this flag before trying again.
+ commit(types.SET_LIST_ERROR_STATUS);
+ } finally {
+ commit(types.CLEAR_LIST_LOADING_STATUS);
}
},
fetchFilteredConversations: async ({ commit, dispatch }, params) => {
+ commit(types.CLEAR_LIST_ERROR_STATUS);
commit(types.SET_LIST_LOADING_STATUS);
try {
const { data } = await ConversationApi.filter(params);
@@ -71,8 +79,11 @@ const actions = {
data,
'appliedFilters'
);
+ commit(types.CLEAR_LIST_ERROR_STATUS);
} catch (error) {
- // Handle error
+ commit(types.SET_LIST_ERROR_STATUS);
+ } finally {
+ commit(types.CLEAR_LIST_LOADING_STATUS);
}
},
diff --git a/frontend/app/javascript/dashboard/store/modules/conversations/getters.js b/frontend/app/javascript/dashboard/store/modules/conversations/getters.js
index 5e85c423..60aa6e75 100644
--- a/frontend/app/javascript/dashboard/store/modules/conversations/getters.js
+++ b/frontend/app/javascript/dashboard/store/modules/conversations/getters.js
@@ -138,6 +138,7 @@ const getters = {
});
},
getChatListLoadingStatus: ({ listLoadingStatus }) => listLoadingStatus,
+ getChatListErrorStatus: ({ listErrorStatus }) => listErrorStatus,
getAllMessagesLoaded(_state) {
const [chat] = getSelectedChatConversation(_state);
return !chat || chat.allMessagesLoaded === undefined
diff --git a/frontend/app/javascript/dashboard/store/modules/conversations/index.js b/frontend/app/javascript/dashboard/store/modules/conversations/index.js
index 8a13940c..2dfe0f13 100644
--- a/frontend/app/javascript/dashboard/store/modules/conversations/index.js
+++ b/frontend/app/javascript/dashboard/store/modules/conversations/index.js
@@ -12,6 +12,7 @@ const state = {
allConversations: [],
attachments: {},
listLoadingStatus: true,
+ listErrorStatus: false,
chatStatusFilter: wootConstants.STATUS_TYPE.OPEN,
chatSortFilter: wootConstants.SORT_BY_TYPE.LATEST,
currentInbox: null,
@@ -274,6 +275,14 @@ export const mutations = {
_state.listLoadingStatus = false;
},
+ [types.SET_LIST_ERROR_STATUS](_state) {
+ _state.listErrorStatus = true;
+ },
+
+ [types.CLEAR_LIST_ERROR_STATUS](_state) {
+ _state.listErrorStatus = false;
+ },
+
[types.UPDATE_MESSAGE_UNREAD_COUNT](
_state,
{ id, lastSeen, unreadCount = 0 }
diff --git a/frontend/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js b/frontend/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js
index fa052ec1..89bffb9e 100644
--- a/frontend/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js
+++ b/frontend/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js
@@ -431,15 +431,101 @@ describe('#actions', () => {
});
describe('#fetchFilteredConversations', () => {
- it('fetches filtered conversations with a mock commit', async () => {
+ it('fetches filtered conversations and clears the loading state', async () => {
+ const localCommit = vi.fn();
+ const localDispatch = vi.fn();
axios.post.mockResolvedValue({
data: dataReceived,
});
- await actions.fetchFilteredConversations({ commit }, dataToSend);
- expect(commit).toHaveBeenCalledTimes(2);
- expect(commit.mock.calls).toEqual([
- ['SET_LIST_LOADING_STATUS'],
- ['SET_ALL_CONVERSATION', dataReceived.payload],
+ await actions.fetchFilteredConversations(
+ { commit: localCommit, dispatch: localDispatch },
+ dataToSend
+ );
+ expect(localCommit.mock.calls).toEqual([
+ [types.CLEAR_LIST_ERROR_STATUS],
+ [types.SET_LIST_LOADING_STATUS],
+ [types.SET_ALL_CONVERSATION, dataReceived.payload],
+ [
+ `contacts/${types.SET_CONTACTS}`,
+ dataReceived.payload.map(conversation => conversation.meta.sender),
+ ],
+ [types.CLEAR_LIST_ERROR_STATUS],
+ [types.CLEAR_LIST_LOADING_STATUS],
+ ]);
+ expect(localDispatch).toHaveBeenCalledWith(
+ 'conversationStats/set',
+ dataReceived.meta
+ );
+ });
+
+ it('clears the loading state when the API request fails', async () => {
+ const localCommit = vi.fn();
+ axios.post.mockRejectedValue(new Error('Request failed'));
+
+ await actions.fetchFilteredConversations(
+ { commit: localCommit, dispatch: vi.fn() },
+ dataToSend
+ );
+
+ expect(localCommit.mock.calls).toEqual([
+ [types.CLEAR_LIST_ERROR_STATUS],
+ [types.SET_LIST_LOADING_STATUS],
+ [types.SET_LIST_ERROR_STATUS],
+ [types.CLEAR_LIST_LOADING_STATUS],
+ ]);
+ });
+ });
+
+ describe('#fetchAllConversations', () => {
+ it('fetches conversations and clears the loading state', async () => {
+ const localCommit = vi.fn();
+ const localDispatch = vi.fn();
+ const filters = {
+ inboxId: 4,
+ status: 'open',
+ assigneeType: 'all',
+ page: 1,
+ };
+ axios.get.mockResolvedValue({ data: { data: dataReceived } });
+
+ await actions.fetchAllConversations({
+ commit: localCommit,
+ dispatch: localDispatch,
+ state: { conversationFilters: filters },
+ });
+
+ expect(localCommit.mock.calls).toEqual([
+ [types.CLEAR_LIST_ERROR_STATUS],
+ [types.SET_LIST_LOADING_STATUS],
+ [types.SET_ALL_CONVERSATION, dataReceived.payload],
+ [
+ `contacts/${types.SET_CONTACTS}`,
+ dataReceived.payload.map(conversation => conversation.meta.sender),
+ ],
+ [types.CLEAR_LIST_ERROR_STATUS],
+ [types.CLEAR_LIST_LOADING_STATUS],
+ ]);
+ expect(localDispatch).toHaveBeenCalledWith(
+ 'conversationStats/set',
+ dataReceived.meta
+ );
+ });
+
+ it('clears the loading state when the API request fails', async () => {
+ const localCommit = vi.fn();
+ axios.get.mockRejectedValue(new Error('Request failed'));
+
+ await actions.fetchAllConversations({
+ commit: localCommit,
+ dispatch: vi.fn(),
+ state: { conversationFilters: {} },
+ });
+
+ expect(localCommit.mock.calls).toEqual([
+ [types.CLEAR_LIST_ERROR_STATUS],
+ [types.SET_LIST_LOADING_STATUS],
+ [types.SET_LIST_ERROR_STATUS],
+ [types.CLEAR_LIST_LOADING_STATUS],
]);
});
});
diff --git a/frontend/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js b/frontend/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js
index fc1c61b3..77867bb0 100644
--- a/frontend/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js
+++ b/frontend/app/javascript/dashboard/store/modules/specs/conversations/mutations.spec.js
@@ -1066,6 +1066,26 @@ describe('#mutations', () => {
});
});
+ describe('#SET_LIST_ERROR_STATUS', () => {
+ it('marks the conversation list request as failed', () => {
+ const state = { listErrorStatus: false };
+
+ mutations[types.SET_LIST_ERROR_STATUS](state);
+
+ expect(state.listErrorStatus).toBe(true);
+ });
+ });
+
+ describe('#CLEAR_LIST_ERROR_STATUS', () => {
+ it('clears the conversation list request error', () => {
+ const state = { listErrorStatus: true };
+
+ mutations[types.CLEAR_LIST_ERROR_STATUS](state);
+
+ expect(state.listErrorStatus).toBe(false);
+ });
+ });
+
describe('#CHANGE_CHAT_STATUS_FILTER', () => {
it('should update chat status filter', () => {
const state = {
diff --git a/frontend/app/javascript/dashboard/store/mutation-types.js b/frontend/app/javascript/dashboard/store/mutation-types.js
index 1597b7ea..88b928ea 100644
--- a/frontend/app/javascript/dashboard/store/mutation-types.js
+++ b/frontend/app/javascript/dashboard/store/mutation-types.js
@@ -15,6 +15,8 @@ export default {
SET_CONV_TAB_META: 'SET_CONV_TAB_META',
CLEAR_LIST_LOADING_STATUS: 'CLEAR_LIST_LOADING_STATUS',
SET_LIST_LOADING_STATUS: 'SET_LIST_LOADING_STATUS',
+ CLEAR_LIST_ERROR_STATUS: 'CLEAR_LIST_ERROR_STATUS',
+ SET_LIST_ERROR_STATUS: 'SET_LIST_ERROR_STATUS',
SET_ALL_MESSAGES_LOADED: 'SET_ALL_MESSAGES_LOADED',
CLEAR_ALL_MESSAGES_LOADED: 'CLEAR_ALL_MESSAGES_LOADED',
CHANGE_CHAT_STATUS_FILTER: 'CHANGE_CHAT_STATUS_FILTER',