Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

As a planning user I want to see up to 5 templates recently used by me in the Event creation pull down menu [SDBELGA-763] #1898

Merged
merged 8 commits into from
Feb 12, 2024
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"])
Loading