Skip to content

Commit

Permalink
As a planning user I want to see up to 5 templates recently used by m…
Browse files Browse the repository at this point in the history
…e in the Event creation pull down menu [SDBELGA-763] (#1898)

* register default user prefrence recentevent templates

* add functionality to saved and fetch recent events templates and display instead of all templates

* refactore code

* refactor component for template handling

* fix syntax and add function calls

* Refactore code

* add recent event template selector and refactor code
  • Loading branch information
devketanpro authored Feb 12, 2024
1 parent efc4739 commit 6905c95
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 13 deletions.
30 changes: 28 additions & 2 deletions client/actions/events/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {get, isEqual, cloneDeep, pickBy, has, find, every} from 'lodash';
import {get, isEqual, cloneDeep, pickBy, has, find, every, take} from 'lodash';

import {planningApi} from '../../superdeskApi';
import {ISearchSpikeState, IEventSearchParams, IEventItem, IPlanningItem} from '../../interfaces';
import {ISearchSpikeState, IEventSearchParams, IEventItem, IPlanningItem, IEventTemplate} from '../../interfaces';
import {appConfig} from 'appConfig';

import {
Expand Down Expand Up @@ -689,6 +689,7 @@ const createEventTemplate = (itemId) => (dispatch, getState, {api, modal, notify
})
.then(() => {
dispatch(fetchEventTemplates());
dispatch(getEventsRecentTemplates());
}, (error) => {
notify.error(
getErrorMessage(error, gettext('Failed to save the event template'))
Expand All @@ -713,6 +714,29 @@ const createEventTemplate = (itemId) => (dispatch, getState, {api, modal, notify
});
};

const RECENT_EVENTS_TEMPLATES_KEY = 'events_templates:recent';

const addEventRecentTemplate = (field: string, templateId: IEventTemplate['_id']) => (
(dispatch, getState, {preferencesService}) => preferencesService.get()
.then((result = {}) => {
result[RECENT_EVENTS_TEMPLATES_KEY] = result[RECENT_EVENTS_TEMPLATES_KEY] || {};
result[RECENT_EVENTS_TEMPLATES_KEY][field] = result[RECENT_EVENTS_TEMPLATES_KEY][field] || [];
result[RECENT_EVENTS_TEMPLATES_KEY][field] = result[RECENT_EVENTS_TEMPLATES_KEY][field].filter(
(i) => i !== templateId);
result[RECENT_EVENTS_TEMPLATES_KEY][field].unshift(templateId);
return preferencesService.update(result);
})
);

const getEventsRecentTemplates = () => (
(dispatch, getState, {preferencesService}) => preferencesService.get()
.then((result) => {
const templates = take(result[RECENT_EVENTS_TEMPLATES_KEY]['templates'], 5);

dispatch({type: EVENTS.ACTIONS.EVENT_RECENT_TEMPLATES, payload: templates});
})
);

// eslint-disable-next-line consistent-this
const self = {
loadEventsByRecurrenceId,
Expand Down Expand Up @@ -745,6 +769,8 @@ const self = {
removeFile,
fetchEventTemplates,
createEventTemplate,
addEventRecentTemplate,
getEventsRecentTemplates,
};

export default self;
20 changes: 16 additions & 4 deletions client/actions/events/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,22 @@ const self = {
};

export const planningEventTemplateEvents = {
'events-template:created': () => eventsApi.fetchEventTemplates,
'events-template:updated': () => eventsApi.fetchEventTemplates,
'events-template:replaced': () => eventsApi.fetchEventTemplates,
'events-template:deleted': () => eventsApi.fetchEventTemplates,
'events-template:created': () => {
eventsApi.fetchEventTemplates();
eventsApi.getEventsRecentTemplates();
},
'events-template:updated': () => {
eventsApi.fetchEventTemplates();
eventsApi.getEventsRecentTemplates();
},
'events-template:replaced': () => {
eventsApi.fetchEventTemplates();
eventsApi.getEventsRecentTemplates();
},
'events-template:deleted': () => {
eventsApi.fetchEventTemplates();
eventsApi.getEventsRecentTemplates();
},
};

// Map of notification name and Action Event to execute
Expand Down
35 changes: 30 additions & 5 deletions client/components/Main/CreateNewSubnavDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {IEventTemplate} from '../../interfaces';

import {PRIVILEGES} from '../../constants';
import * as actions from '../../actions';
import {eventTemplates} from '../../selectors/events';
import {eventTemplates, getRecentTemplatesSelector} from '../../selectors/events';
import {Dropdown, IDropdownItem} from '../UI/SubNav';

interface IProps {
Expand All @@ -16,12 +16,21 @@ interface IProps {
privileges: {[key: string]: number};
createEventFromTemplate(template: IEventTemplate): void;
eventTemplates: Array<IEventTemplate>;
recentTemplates?: Array<IEventTemplate>;
}

class CreateNewSubnavDropdownFn extends React.PureComponent<IProps> {
render() {
const {gettext} = superdeskApi.localization;
const {addEvent, addPlanning, createPlanningOnly, privileges, createEventFromTemplate} = this.props;
const {
addEvent,
addPlanning,
createPlanningOnly,
privileges,
createEventFromTemplate,
recentTemplates,
eventTemplates
} = this.props;
const items: Array<IDropdownItem> = [];

if (privileges[PRIVILEGES.PLANNING_MANAGEMENT]) {
Expand All @@ -43,11 +52,22 @@ class CreateNewSubnavDropdownFn extends React.PureComponent<IProps> {
id: 'create_event',
});

this.props.eventTemplates.forEach((template) => {
if (recentTemplates.length !== 0) {
recentTemplates.forEach((template) => {
items.push({
label: template.template_name,
icon: 'icon-event icon--blue',
group: gettext('Recent Templates'),
action: () => createEventFromTemplate(template),
id: template._id,
});
});
}
eventTemplates.forEach((template) => {
items.push({
label: template.template_name,
icon: 'icon-event icon--blue',
group: gettext('From template'),
group: gettext('ALL Templates'),
action: () => createEventFromTemplate(template),
id: template._id,
});
Expand Down Expand Up @@ -80,11 +100,16 @@ class CreateNewSubnavDropdownFn extends React.PureComponent<IProps> {
function mapStateToProps(state) {
return {
eventTemplates: eventTemplates(state),
recentTemplates: getRecentTemplatesSelector(state)
};
}

const mapDispatchToProps = (dispatch) => ({
createEventFromTemplate: (template: IEventTemplate) => dispatch(actions.main.createEventFromTemplate(template)),
createEventFromTemplate: (template: IEventTemplate) => {
dispatch(actions.main.createEventFromTemplate(template));
dispatch(actions.events.api.addEventRecentTemplate('templates', template._id));
dispatch(actions.events.api.getEventsRecentTemplates());
},
});

export const CreateNewSubnavDropdown = connect(
Expand Down
1 change: 1 addition & 0 deletions client/constants/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const EVENTS = {
RECEIVE_CALENDARS: 'RECEIVE_CALENDARS',
RECEIVE_EVENT_TEMPLATES: 'RECEIVE_EVENT_TEMPLATES',
EXPIRE_EVENTS: 'EXPIRE_EVENTS',
EVENT_RECENT_TEMPLATES: 'EVENT_RECENT_TEMPLATES',
},
// Number of ids to look for by single request
// because url length must stay short
Expand Down
1 change: 1 addition & 0 deletions client/controllers/PlanningController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export class PlanningController {
this.store.dispatch(actions.autosave.fetchAll()),
this.store.dispatch(actions.eventsPlanning.ui.fetchFilters()),
this.store.dispatch(eventsApi.fetchEventTemplates()),
this.store.dispatch(eventsApi.getEventsRecentTemplates()),
])
.then(() => (
// Load the current items that are currently open for Preview/Editing
Expand Down
1 change: 1 addition & 0 deletions client/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1654,6 +1654,7 @@ export interface IEventState {
currentCalendarId?: ICalendar['qcode'];
currentFilterId?: ISearchFilter['_id'];
eventTemplates: Array<IEventItem>;
recentEventTemplates?: Array<IEventTemplate['_id']>;
}

export interface IEditorFormState {
Expand Down
4 changes: 4 additions & 0 deletions client/reducers/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,10 @@ const eventsReducer = createReducer<IEventState>(initialState, {
...state,
eventTemplates: payload,
}),
[EVENTS.ACTIONS.EVENT_RECENT_TEMPLATES]: (state, payload) => ({
...state,
recentEventTemplates: payload,
})
});

const onEventPostChanged = (state, payload) => {
Expand Down
23 changes: 21 additions & 2 deletions client/selectors/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {createSelector} from 'reselect';
import {get, sortBy} from 'lodash';

import {appConfig} from 'appConfig';
import {IEventItem, IPlanningAppState, LIST_VIEW_TYPE} from '../interfaces';
import {IEventItem, IEventState, IEventTemplate, IPlanningAppState, LIST_VIEW_TYPE} from '../interfaces';

import {currentPlanning, storedPlannings} from './planning';
import {agendas, userPreferences} from './general';
Expand All @@ -18,7 +18,8 @@ export const eventIdsInList = (state) => get(state, 'events.eventsInList', []);
export const eventHistory = (state) => get(state, 'events.eventHistoryItems');
export const currentSearch = (state) => get(state, 'main.search.EVENTS.currentSearch');
export const fullText = (state) => get(state, 'main.search.EVENTS.fulltext', '');
export const eventTemplates = (state) => state.events.eventTemplates;
export const eventTemplates = (state:IEventState) => state.events.eventTemplates;
export const recentTemplates = (state:IEventState) => state.events.recentEventTemplates;
export const currentEventFilterId = (state: IPlanningAppState) => state?.events?.currentFilterId;
const isEventsView = (state) => get(state, 'main.filter', '') === MAIN.FILTERS.EVENTS;

Expand Down Expand Up @@ -222,3 +223,21 @@ export const defaultCalendarFilter = createSelector(
[usersDefaultCalendar],
(calendar) => calendar || {qcode: EVENTS.FILTER.DEFAULT}
);


export const getRecentTemplatesSelector = createSelector<
IEventState,
Array<IEventTemplate['_id']>,
IEventState,
Array<IEventTemplate>>([recentTemplates, eventTemplates],
(recentTemplatesId, eventTemplates) => {
if (recentTemplatesId && recentTemplatesId.length !== 0) {
return eventTemplates.filter((template) =>
recentTemplatesId.includes(template._id)
).sort(
(a, b) => recentTemplatesId.indexOf(a._id) - recentTemplatesId.indexOf(b._id)
);
}
return [];
}
);
2 changes: 2 additions & 0 deletions server/planning/events/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,6 @@ def init_app(app):
description=lazy_gettext("Ability to create and manage Event templates"),
)

superdesk.register_default_user_preference("events_templates:recent", {})

superdesk.intrinsic_privilege(EventsUnlockResource.endpoint_name, method=["POST"])

0 comments on commit 6905c95

Please sign in to comment.