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

- 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:
Liang XJ
2026-01-26 11:16:56 +08:00
commit 092fb2e083
7646 changed files with 975643 additions and 0 deletions

View File

@@ -0,0 +1,110 @@
import axios from 'axios';
import { actions } from '../../customViews';
import * as types from '../../../mutation-types';
import { customViewList, updateCustomViewList } from './fixtures';
const commit = vi.fn();
global.axios = axios;
vi.mock('axios');
describe('#actions', () => {
describe('#get', () => {
it('sends correct actions if API is success', async () => {
axios.get.mockResolvedValue({ data: customViewList });
await actions.get({ commit }, 'conversation');
expect(commit.mock.calls).toEqual([
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isFetching: true }],
[
types.default.SET_CUSTOM_VIEW,
{ data: customViewList, filterType: 'conversation' },
],
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isFetching: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.get.mockRejectedValue({ message: 'Incorrect header' });
await actions.get({ commit });
expect(commit.mock.calls).toEqual([
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isFetching: true }],
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isFetching: false }],
]);
});
});
describe('#create', () => {
it('sends correct actions if API is success', async () => {
const firstItem = customViewList[0];
axios.post.mockResolvedValue({ data: firstItem });
await actions.create({ commit }, firstItem);
expect(commit.mock.calls).toEqual([
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: true }],
[
types.default.ADD_CUSTOM_VIEW,
{ data: firstItem, filterType: 'conversation' },
],
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.post.mockRejectedValue({ message: 'Incorrect header' });
await expect(actions.create({ commit })).rejects.toThrow(Error);
expect(commit.mock.calls).toEqual([
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: true }],
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: false }],
]);
});
});
describe('#delete', () => {
it('sends correct actions if API is success', async () => {
axios.delete.mockResolvedValue({ data: customViewList[0] });
await actions.delete({ commit }, { id: 1, filterType: 'contact' });
expect(commit.mock.calls).toEqual([
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isDeleting: true }],
[types.default.DELETE_CUSTOM_VIEW, { data: 1, filterType: 'contact' }],
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isDeleting: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.delete.mockRejectedValue({ message: 'Incorrect header' });
await expect(actions.delete({ commit }, 1)).rejects.toThrow(Error);
expect(commit.mock.calls).toEqual([
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isDeleting: true }],
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isDeleting: false }],
]);
});
});
describe('#update', () => {
it('sends correct actions if API is success', async () => {
const item = updateCustomViewList[0];
axios.patch.mockResolvedValue({ data: item });
await actions.update({ commit }, item);
expect(commit.mock.calls).toEqual([
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: true }],
[
types.default.UPDATE_CUSTOM_VIEW,
{ data: item, filterType: 'conversation' },
],
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: false }],
]);
});
it('sends correct actions if API is error', async () => {
axios.patch.mockRejectedValue({ message: 'Incorrect header' });
await expect(actions.update({ commit }, 1)).rejects.toThrow(Error);
expect(commit.mock.calls).toEqual([
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: true }],
[types.default.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: false }],
]);
});
});
describe('#setActiveConversationFolder', () => {
it('set active conversation folder', async () => {
await actions.setActiveConversationFolder({ commit }, customViewList[0]);
expect(commit.mock.calls).toEqual([
[types.default.SET_ACTIVE_CONVERSATION_FOLDER, customViewList[0]],
]);
});
});
});

View File

@@ -0,0 +1,81 @@
export const contactViewList = [
{
name: 'Custom view 1',
filter_type: 1,
query: {
payload: [
{
attribute_key: 'name',
filter_operator: 'equal_to',
values: ['john doe'],
query_operator: null,
},
],
},
},
];
export const customViewList = [
{
name: 'Custom view',
filter_type: 0,
query: {
payload: [
{
attribute_key: 'assignee_id',
filter_operator: 'equal_to',
values: [45],
query_operator: 'and',
},
{
attribute_key: 'inbox_id',
filter_operator: 'equal_to',
values: [144],
query_operator: 'and',
},
],
},
},
{
name: 'Custom view 1',
filter_type: 0,
query: {
payload: [
{
attribute_key: 'assignee_id',
filter_operator: 'equal_to',
values: [45],
query_operator: 'and',
},
],
},
},
];
export const updateCustomViewList = [
{
id: 1,
name: 'Open',
filter_type: 'conversation',
query: {
payload: [
{
attribute_key: 'status',
attribute_model: 'standard',
filter_operator: 'equal_to',
values: ['open'],
query_operator: 'and',
custom_attribute_type: '',
},
{
attribute_key: 'assignee_id',
filter_operator: 'equal_to',
values: [52],
custom_attribute_type: '',
},
],
},
created_at: '2022-02-08T03:17:38.761Z',
updated_at: '2023-06-05T13:57:48.478Z',
},
];

View File

@@ -0,0 +1,46 @@
import { getters } from '../../customViews';
import { contactViewList, customViewList } from './fixtures';
describe('#getters', () => {
it('getCustomViewsByFilterType', () => {
const state = { contact: { records: contactViewList } };
expect(getters.getCustomViewsByFilterType(state)(1)).toEqual([
{
name: 'Custom view 1',
filter_type: 1,
query: {
payload: [
{
attribute_key: 'name',
filter_operator: 'equal_to',
values: ['john doe'],
query_operator: null,
},
],
},
},
]);
});
it('getUIFlags', () => {
const state = {
uiFlags: {
isFetching: true,
isCreating: false,
isDeleting: false,
},
};
expect(getters.getUIFlags(state)).toEqual({
isFetching: true,
isCreating: false,
isDeleting: false,
});
});
it('getActiveConversationFolder', () => {
const state = { activeConversationFolder: customViewList[0] };
expect(getters.getActiveConversationFolder(state)).toEqual(
customViewList[0]
);
});
});

View File

@@ -0,0 +1,133 @@
import types from '../../../mutation-types';
import { mutations } from '../../customViews';
import { customViewList, updateCustomViewList } from './fixtures';
describe('#mutations', () => {
describe('#SET_CUSTOM_VIEW', () => {
it('[Conversation] set custom view records', () => {
const state = {
records: [],
conversation: { records: [] },
contact: { records: [] },
};
mutations[types.SET_CUSTOM_VIEW](state, {
data: customViewList,
filterType: 'conversation',
});
expect(state.conversation.records).toEqual(customViewList);
expect(state.contact.records).toEqual([]);
});
it('[Contact] set custom view records', () => {
const state = {
records: [],
conversation: { records: [] },
contact: { records: [] },
};
mutations[types.SET_CUSTOM_VIEW](state, {
data: customViewList,
filterType: 'contact',
});
expect(state.contact.records).toEqual(customViewList);
expect(state.conversation.records).toEqual([]);
});
});
describe('#ADD_CUSTOM_VIEW', () => {
it('[Conversation] push newly created custom views to the store', () => {
const state = {
conversation: { records: [customViewList] },
contact: { records: [] },
};
mutations[types.ADD_CUSTOM_VIEW](state, {
data: customViewList[0],
filterType: 'conversation',
});
expect(state.conversation.records).toEqual([
customViewList,
customViewList[0],
]);
expect(state.contact.records).toEqual([]);
});
it('[Contact] push newly created custom views to the store', () => {
const state = {
conversation: { records: [] },
contact: { records: [customViewList] },
};
mutations[types.ADD_CUSTOM_VIEW](state, {
data: customViewList[0],
filterType: 'contact',
});
expect(state.contact.records).toEqual([
customViewList,
customViewList[0],
]);
expect(state.conversation.records).toEqual([]);
});
});
describe('#DELETE_CUSTOM_VIEW', () => {
it('[Conversation] delete custom view record', () => {
const state = {
conversation: { records: [customViewList[0]] },
contact: { records: [] },
};
mutations[types.DELETE_CUSTOM_VIEW](state, {
data: customViewList[0],
filterType: 'conversation',
});
expect(state.conversation.records).toEqual([customViewList[0]]);
expect(state.contact.records).toEqual([]);
});
it('[Contact] delete custom view record', () => {
const state = {
contact: { records: [customViewList[0]] },
conversation: { records: [] },
};
mutations[types.DELETE_CUSTOM_VIEW](state, {
data: customViewList[0],
filterType: 'contact',
});
expect(state.contact.records).toEqual([customViewList[0]]);
expect(state.conversation.records).toEqual([]);
});
});
describe('#UPDATE_CUSTOM_VIEW', () => {
it('[Conversation] update custom view record', () => {
const state = {
conversation: { records: [updateCustomViewList[0]] },
contact: { records: [] },
};
mutations[types.UPDATE_CUSTOM_VIEW](state, {
data: updateCustomViewList[0],
filterType: 'conversation',
});
expect(state.conversation.records).toEqual(updateCustomViewList);
expect(state.contact.records).toEqual([]);
});
it('[Contact] update custom view record', () => {
const state = {
contact: { records: [updateCustomViewList[0]] },
conversation: { records: [] },
};
mutations[types.UPDATE_CUSTOM_VIEW](state, {
data: updateCustomViewList[0],
filterType: 'contact',
});
expect(state.contact.records).toEqual(updateCustomViewList);
expect(state.conversation.records).toEqual([]);
});
});
describe('#SET_ACTIVE_CONVERSATION_FOLDER', () => {
it('set active conversation folder', () => {
const state = { activeConversationFolder: customViewList[0] };
mutations[types.SET_ACTIVE_CONVERSATION_FOLDER](state, customViewList[0]);
expect(state.activeConversationFolder).toEqual(customViewList[0]);
});
});
});