fix: added ordering capability for sidebar sections folders, teams, channels and labels (CW-7193) (#14609)
## Description * Added the ability to sort for 4 sub-sections under conversations folders, teams, channels and labels. * the sort options are basically created at, alphabetical and unread counts along with both directions. * for folders we don't have an unread count, so we sort it by only created and alphabetical. * all the sort preferences are stored on the frontend - easiest implementation for now. Fixes # CW-7193 ## Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? Tested this locally by visually verifying the changes. Also ran the newly added tests for component level changes. Here is the screenshot of the changes: Added the sort option to sub sections in the conversation sidebar: <img width="282" height="848" alt="Screenshot 2026-06-02 at 1 58 12 AM" src="https://github.com/user-attachments/assets/4a7c6061-86e3-438a-92ae-ee643a0128b6" /> The sort options looks like this: <img width="783" height="698" alt="Screenshot 2026-06-02 at 1 58 49 AM" src="https://github.com/user-attachments/assets/a15bb0a7-b810-4423-a88c-fbd84d0476c0" /> ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: iamsivin <iamsivin@gmail.com>
This commit is contained in:
co-authored by
Sivin Varghese
iamsivin
parent
4816e923b6
commit
d8a16278b9
@@ -0,0 +1,82 @@
|
||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||
import {
|
||||
DEFAULT_SIDEBAR_SORT_PREFERENCES,
|
||||
isValidSidebarSort,
|
||||
normalizeSidebarSortPreferences,
|
||||
} from 'dashboard/helper/sidebarSort';
|
||||
|
||||
const STORAGE_NAME = 'chatwoot_sidebar_sort_preferences';
|
||||
export const SET_SIDEBAR_SORT_PREFERENCES = 'SET_SIDEBAR_SORT_PREFERENCES';
|
||||
|
||||
const getPreferenceScope = rootGetters => {
|
||||
const currentUserId = rootGetters.getCurrentUserID;
|
||||
const currentAccountId = rootGetters.getCurrentAccountId;
|
||||
|
||||
if (!currentUserId || !currentAccountId) return null;
|
||||
|
||||
return `${currentUserId}:${currentAccountId}`;
|
||||
};
|
||||
|
||||
export const state = {
|
||||
preferences: { ...DEFAULT_SIDEBAR_SORT_PREFERENCES },
|
||||
storageKey: null,
|
||||
};
|
||||
|
||||
export const getters = {
|
||||
getSectionSort: $state => section => {
|
||||
return (
|
||||
$state.preferences[section] || DEFAULT_SIDEBAR_SORT_PREFERENCES[section]
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
initialize({ commit, rootGetters }) {
|
||||
const storageKey = getPreferenceScope(rootGetters);
|
||||
const storedPreferences = storageKey
|
||||
? LocalStorage.getFromJsonStore(STORAGE_NAME, storageKey)
|
||||
: {};
|
||||
|
||||
commit(SET_SIDEBAR_SORT_PREFERENCES, {
|
||||
preferences: normalizeSidebarSortPreferences(storedPreferences),
|
||||
storageKey,
|
||||
});
|
||||
},
|
||||
setSectionSort({ commit, rootGetters, state: currentState }, payload = {}) {
|
||||
const { section, sortBy } = payload;
|
||||
|
||||
if (!isValidSidebarSort(section, sortBy)) return;
|
||||
|
||||
const storageKey =
|
||||
currentState.storageKey || getPreferenceScope(rootGetters);
|
||||
const preferences = {
|
||||
...currentState.preferences,
|
||||
[section]: sortBy,
|
||||
};
|
||||
|
||||
commit(SET_SIDEBAR_SORT_PREFERENCES, {
|
||||
preferences,
|
||||
storageKey,
|
||||
});
|
||||
|
||||
if (storageKey) {
|
||||
LocalStorage.updateJsonStore(STORAGE_NAME, storageKey, preferences);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const mutations = {
|
||||
[SET_SIDEBAR_SORT_PREFERENCES]($state, payload = {}) {
|
||||
const { preferences = {}, storageKey = null } = payload;
|
||||
$state.preferences = normalizeSidebarSortPreferences(preferences);
|
||||
$state.storageKey = storageKey;
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||
import {
|
||||
DEFAULT_SIDEBAR_SORT_PREFERENCES,
|
||||
SIDEBAR_SORT_KEYS,
|
||||
SIDEBAR_SORT_SECTIONS,
|
||||
} from 'dashboard/helper/sidebarSort';
|
||||
import {
|
||||
SET_SIDEBAR_SORT_PREFERENCES,
|
||||
actions,
|
||||
} from '../../sidebarSortPreferences';
|
||||
|
||||
vi.mock('shared/helpers/localStorage', () => ({
|
||||
LocalStorage: {
|
||||
getFromJsonStore: vi.fn(),
|
||||
updateJsonStore: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
const rootGetters = {
|
||||
getCurrentUserID: 1,
|
||||
getCurrentAccountId: 2,
|
||||
};
|
||||
|
||||
describe('#actions', () => {
|
||||
const commit = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
commit.mockClear();
|
||||
LocalStorage.getFromJsonStore.mockReset();
|
||||
LocalStorage.updateJsonStore.mockReset();
|
||||
});
|
||||
|
||||
describe('#initialize', () => {
|
||||
it('loads scoped preferences from local storage', () => {
|
||||
LocalStorage.getFromJsonStore.mockReturnValue({
|
||||
[SIDEBAR_SORT_SECTIONS.FOLDERS]: SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC,
|
||||
});
|
||||
|
||||
actions.initialize({ commit, rootGetters });
|
||||
|
||||
expect(LocalStorage.getFromJsonStore).toHaveBeenCalledWith(
|
||||
'chatwoot_sidebar_sort_preferences',
|
||||
'1:2'
|
||||
);
|
||||
expect(commit).toHaveBeenCalledWith(SET_SIDEBAR_SORT_PREFERENCES, {
|
||||
preferences: {
|
||||
...DEFAULT_SIDEBAR_SORT_PREFERENCES,
|
||||
[SIDEBAR_SORT_SECTIONS.FOLDERS]: SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC,
|
||||
},
|
||||
storageKey: '1:2',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setSectionSort', () => {
|
||||
it('persists valid preferences to local storage', () => {
|
||||
const state = {
|
||||
preferences: DEFAULT_SIDEBAR_SORT_PREFERENCES,
|
||||
storageKey: '1:2',
|
||||
};
|
||||
|
||||
actions.setSectionSort(
|
||||
{ commit, rootGetters, state },
|
||||
{
|
||||
section: SIDEBAR_SORT_SECTIONS.LABELS,
|
||||
sortBy: SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
|
||||
}
|
||||
);
|
||||
|
||||
const preferences = {
|
||||
...DEFAULT_SIDEBAR_SORT_PREFERENCES,
|
||||
[SIDEBAR_SORT_SECTIONS.LABELS]: SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
|
||||
};
|
||||
|
||||
expect(commit).toHaveBeenCalledWith(SET_SIDEBAR_SORT_PREFERENCES, {
|
||||
preferences,
|
||||
storageKey: '1:2',
|
||||
});
|
||||
expect(LocalStorage.updateJsonStore).toHaveBeenCalledWith(
|
||||
'chatwoot_sidebar_sort_preferences',
|
||||
'1:2',
|
||||
preferences
|
||||
);
|
||||
});
|
||||
|
||||
it('ignores invalid preferences', () => {
|
||||
actions.setSectionSort(
|
||||
{
|
||||
commit,
|
||||
rootGetters,
|
||||
state: {
|
||||
preferences: DEFAULT_SIDEBAR_SORT_PREFERENCES,
|
||||
storageKey: '1:2',
|
||||
},
|
||||
},
|
||||
{
|
||||
section: SIDEBAR_SORT_SECTIONS.FOLDERS,
|
||||
sortBy: SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
|
||||
}
|
||||
);
|
||||
|
||||
expect(commit).not.toHaveBeenCalled();
|
||||
expect(LocalStorage.updateJsonStore).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import {
|
||||
DEFAULT_SIDEBAR_SORT_PREFERENCES,
|
||||
SIDEBAR_SORT_KEYS,
|
||||
SIDEBAR_SORT_SECTIONS,
|
||||
} from 'dashboard/helper/sidebarSort';
|
||||
import { getters } from '../../sidebarSortPreferences';
|
||||
|
||||
describe('#getters', () => {
|
||||
it('returns section sort preference', () => {
|
||||
const state = {
|
||||
preferences: {
|
||||
...DEFAULT_SIDEBAR_SORT_PREFERENCES,
|
||||
[SIDEBAR_SORT_SECTIONS.TEAMS]: SIDEBAR_SORT_KEYS.CREATED_ASC,
|
||||
},
|
||||
};
|
||||
|
||||
expect(getters.getSectionSort(state)(SIDEBAR_SORT_SECTIONS.TEAMS)).toBe(
|
||||
SIDEBAR_SORT_KEYS.CREATED_ASC
|
||||
);
|
||||
});
|
||||
|
||||
it('falls back to default section sort preference', () => {
|
||||
const state = {
|
||||
preferences: {},
|
||||
};
|
||||
|
||||
expect(getters.getSectionSort(state)(SIDEBAR_SORT_SECTIONS.LABELS)).toBe(
|
||||
SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
DEFAULT_SIDEBAR_SORT_PREFERENCES,
|
||||
SIDEBAR_SORT_KEYS,
|
||||
SIDEBAR_SORT_SECTIONS,
|
||||
} from 'dashboard/helper/sidebarSort';
|
||||
import {
|
||||
SET_SIDEBAR_SORT_PREFERENCES,
|
||||
mutations,
|
||||
} from '../../sidebarSortPreferences';
|
||||
|
||||
describe('#mutations', () => {
|
||||
it('sets normalized preferences', () => {
|
||||
const state = {
|
||||
preferences: {},
|
||||
storageKey: null,
|
||||
};
|
||||
|
||||
mutations[SET_SIDEBAR_SORT_PREFERENCES](state, {
|
||||
preferences: {
|
||||
[SIDEBAR_SORT_SECTIONS.FOLDERS]: SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
|
||||
[SIDEBAR_SORT_SECTIONS.LABELS]: 'invalid',
|
||||
},
|
||||
storageKey: '1:2',
|
||||
});
|
||||
|
||||
expect(state).toEqual({
|
||||
preferences: {
|
||||
...DEFAULT_SIDEBAR_SORT_PREFERENCES,
|
||||
[SIDEBAR_SORT_SECTIONS.FOLDERS]: SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
|
||||
},
|
||||
storageKey: '1:2',
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user