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
27 changes: 26 additions & 1 deletion client/actions/events/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {get, isEqual, cloneDeep, pickBy, has, find, every} from 'lodash';
import _, {get, isEqual, cloneDeep, pickBy, has, find, every, template} from 'lodash';

devketanpro marked this conversation as resolved.
Show resolved Hide resolved
import {planningApi} from '../../superdeskApi';
import {ISearchSpikeState, IEventSearchParams, IEventItem, IPlanningItem} from '../../interfaces';
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,28 @@ const createEventTemplate = (itemId) => (dispatch, getState, {api, modal, notify
});
};

const PREFERENCES_KEY = 'events_templates:recent';

devketanpro marked this conversation as resolved.
Show resolved Hide resolved
const addEventRecentTemplate = (field, templateId) => (
(dispatch, getState, {preferencesService}) => preferencesService.get()
devketanpro marked this conversation as resolved.
Show resolved Hide resolved
.then((result = {}) => {
result[PREFERENCES_KEY] = result[PREFERENCES_KEY] || {};
result[PREFERENCES_KEY][field] = result[PREFERENCES_KEY][field] || [];
_.remove(result[PREFERENCES_KEY][field], (i) => i === templateId);
result[PREFERENCES_KEY][field].unshift(templateId);
devketanpro marked this conversation as resolved.
Show resolved Hide resolved
return preferencesService.update(result);
})
);

const getEventsRecentTemplates = () => (
(dispatch, getState, {preferencesService}) => preferencesService.get()
.then((result) => {
const templates = _.take(result[PREFERENCES_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 +768,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
54 changes: 49 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, recentTemplates} from '../../selectors/events';
import {Dropdown, IDropdownItem} from '../UI/SubNav';

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

class CreateNewSubnavDropdownFn extends React.PureComponent<IProps> {
constructor(props: IProps) {
super(props);
this.getRecentTemplates = this.getRecentTemplates.bind(this);
}

getRecentTemplates() {
devketanpro marked this conversation as resolved.
Show resolved Hide resolved
const {recentTemplatesId, eventTemplates} = this.props;

if (recentTemplatesId.length !== 0) {
return eventTemplates.filter((template) =>
recentTemplatesId.includes(template._id)
);
}
return [];
}
render() {
const {gettext} = superdeskApi.localization;
const {addEvent, addPlanning, createPlanningOnly, privileges, createEventFromTemplate} = this.props;
const {
addEvent,
addPlanning,
createPlanningOnly,
privileges,
createEventFromTemplate,
recentTemplatesId,
eventTemplates
} = this.props;
const items: Array<IDropdownItem> = [];

const recentTemplates = this.getRecentTemplates().sort(
devketanpro marked this conversation as resolved.
Show resolved Hide resolved
(a, b) => recentTemplatesId.indexOf(a._id) - recentTemplatesId.indexOf(b._id)
);

if (privileges[PRIVILEGES.PLANNING_MANAGEMENT]) {
items.push({
label: gettext('Planning Item'),
Expand All @@ -43,11 +71,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 +119,16 @@ class CreateNewSubnavDropdownFn extends React.PureComponent<IProps> {
function mapStateToProps(state) {
return {
eventTemplates: eventTemplates(state),
recentTemplatesId: recentTemplates(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<string>;
}

export interface IEditorFormState {
Expand Down
5 changes: 5 additions & 0 deletions client/reducers/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const initialState: IEventState = {
currentCalendarId: undefined,
currentFilterId: undefined,
eventTemplates: [],
recentEventTemplates: [],
};

const modifyEventsBeingAdded = (state, payload) => {
Expand Down Expand Up @@ -316,6 +317,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
1 change: 1 addition & 0 deletions client/selectors/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ 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 recentTemplates = (state) => state.events.recentEventTemplates;
export const currentEventFilterId = (state: IPlanningAppState) => state?.events?.currentFilterId;
const isEventsView = (state) => get(state, 'main.filter', '') === MAIN.FILTERS.EVENTS;

Expand Down
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