feat: Add new sidebar for Chatwoot V4 (#10291)

This PR has the initial version of the new sidebar targeted for the next major redesign of the app. This PR includes the following changes

- Components in the `layouts-next` and `base-next` directories in `dashboard/components`
- Two generic components `Avatar` and `Icon`
- `SidebarGroup` component to manage expandable sidebar groups with nested navigation items. This includes handling active states, transitions, and permissions.
- `SidebarGroupHeader` component to display the header of each navigation group with optional icons and active state indication.
- `SidebarGroupLeaf` component for individual navigation items within a group, supporting icons and active state.
- `SidebarGroupSeparator` component to visually separate nested navigation items. (They look a lot like header)
- `SidebarGroupEmptyLeaf` component to render empty state of any navigation groups.

----

Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Pranav <pranavrajs@gmail.com>
This commit is contained in:
Shivam Mishra
2024-10-24 07:02:37 +05:30
committed by GitHub
parent 601a0f8a76
commit 6d3ecfe3c1
47 changed files with 2188 additions and 155 deletions

View File

@@ -2,8 +2,26 @@ import * as MutationHelpers from 'shared/helpers/vuex/mutationHelpers';
import types from '../mutation-types';
import CustomViewsAPI from '../../api/customViews';
const VIEW_TYPES = {
CONVERSATION: 'conversation',
CONTACT: 'contact',
};
// use to normalize the filter type
const FILTER_KEYS = {
0: VIEW_TYPES.CONVERSATION,
1: VIEW_TYPES.CONTACT,
[VIEW_TYPES.CONVERSATION]: VIEW_TYPES.CONVERSATION,
[VIEW_TYPES.CONTACT]: VIEW_TYPES.CONTACT,
};
export const state = {
records: [],
[VIEW_TYPES.CONVERSATION]: {
records: [],
},
[VIEW_TYPES.CONTACT]: {
records: [],
},
uiFlags: {
isFetching: false,
isCreating: false,
@@ -16,11 +34,15 @@ export const getters = {
getUIFlags(_state) {
return _state.uiFlags;
},
getCustomViews(_state) {
return _state.records;
getCustomViewsByFilterType: _state => key => {
const filterType = FILTER_KEYS[key];
return _state[filterType].records;
},
getCustomViewsByFilterType: _state => filterType => {
return _state.records.filter(record => record.filter_type === filterType);
getConversationCustomViews(_state) {
return _state[VIEW_TYPES.CONVERSATION].records;
},
getContactCustomViews(_state) {
return _state[VIEW_TYPES.CONTACT].records;
},
getActiveConversationFolder(_state) {
return _state.activeConversationFolder;
@@ -33,7 +55,7 @@ export const actions = {
try {
const response =
await CustomViewsAPI.getCustomViewsByFilterType(filterType);
commit(types.SET_CUSTOM_VIEW, response.data);
commit(types.SET_CUSTOM_VIEW, { data: response.data, filterType });
} catch (error) {
// Ignore error
} finally {
@@ -44,7 +66,10 @@ export const actions = {
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: true });
try {
const response = await CustomViewsAPI.create(obj);
commit(types.ADD_CUSTOM_VIEW, response.data);
commit(types.ADD_CUSTOM_VIEW, {
data: response.data,
filterType: FILTER_KEYS[obj.filter_type],
});
} catch (error) {
const errorMessage = error?.response?.data?.message;
throw new Error(errorMessage);
@@ -56,7 +81,10 @@ export const actions = {
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isCreating: true });
try {
const response = await CustomViewsAPI.update(obj.id, obj);
commit(types.UPDATE_CUSTOM_VIEW, response.data);
commit(types.UPDATE_CUSTOM_VIEW, {
data: response.data,
filterType: FILTER_KEYS[obj.filter_type],
});
} catch (error) {
const errorMessage = error?.response?.data?.message;
throw new Error(errorMessage);
@@ -68,7 +96,7 @@ export const actions = {
commit(types.SET_CUSTOM_VIEW_UI_FLAG, { isDeleting: true });
try {
await CustomViewsAPI.deleteCustomViews(id, filterType);
commit(types.DELETE_CUSTOM_VIEW, id);
commit(types.DELETE_CUSTOM_VIEW, { data: id, filterType });
} catch (error) {
throw new Error(error);
} finally {
@@ -88,10 +116,18 @@ export const mutations = {
};
},
[types.ADD_CUSTOM_VIEW]: MutationHelpers.create,
[types.SET_CUSTOM_VIEW]: MutationHelpers.set,
[types.UPDATE_CUSTOM_VIEW]: MutationHelpers.update,
[types.DELETE_CUSTOM_VIEW]: MutationHelpers.destroy,
[types.ADD_CUSTOM_VIEW]: (_state, { data, filterType }) => {
MutationHelpers.create(_state[filterType], data);
},
[types.SET_CUSTOM_VIEW]: (_state, { data, filterType }) => {
MutationHelpers.set(_state[filterType], data);
},
[types.UPDATE_CUSTOM_VIEW]: (_state, { data, filterType }) => {
MutationHelpers.update(_state[filterType], data);
},
[types.DELETE_CUSTOM_VIEW]: (_state, { data, filterType }) => {
MutationHelpers.destroy(_state[filterType], data);
},
[types.SET_ACTIVE_CONVERSATION_FOLDER](_state, folder) {
_state.activeConversationFolder = folder;