Skip to content

Commit

Permalink
Merge branch 'develop' into templates-modal-implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
thecalcc committed Feb 12, 2024
2 parents 24dce25 + 0b5f89b commit 41d66d9
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 23 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
25 changes: 19 additions & 6 deletions client/components/Main/CreateNewSubnavDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {ICalendar, 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';
import {showModal} from '@superdesk/common';
import PlanningTemplatesModal from '../PlanningTemplatesModal/PlanningTemplatesModal';
Expand All @@ -19,14 +19,23 @@ interface IProps {
createEventFromTemplate(template: IEventTemplate): void;
eventTemplates: Array<IEventTemplate>;
calendars: Array<ICalendar>;
recentTemplates?: Array<IEventTemplate>;
}

const MORE_TEMPLATES_THRESHOLD = 5;

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 @@ -51,11 +60,10 @@ class CreateNewSubnavDropdownFn extends React.PureComponent<IProps> {
/**
* Sort the templates by their name.
*/
const sortedTemplates = this.props.eventTemplates
const sortedTemplates = eventTemplates
.sort((templ1, templ2) => templ1.template_name.localeCompare(templ2.template_name));

sortedTemplates
.slice(0, MORE_TEMPLATES_THRESHOLD)
recentTemplates
.forEach((template) => {
items.push({
label: template.template_name,
Expand Down Expand Up @@ -113,11 +121,16 @@ function mapStateToProps(state) {
return {
calendars: state.events.calendars,
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
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ interface IProps {
desks: Array<IDesk>;
contentTypes: Array<IG2ContentType>;
lockedItems: ILockedItems;
isAgendaEnabled: boolean;
}

const mapStateToProps = (state) => ({
Expand Down Expand Up @@ -75,14 +76,19 @@ class RelatedPlanningListItemComponent extends React.PureComponent<IProps> {
<span className="sd-list-item__text-strong">{this.props.item.slugline}</span>
</span>
</List.Row>
<List.Row>
<span className="no-padding">
<span className="sd-list-item__text-label">{gettext('Agenda:')}</span>
<span className="sd-overflow-ellipsis sd-list-item__text-strong sd-list-item--element-grow">
<AgendaNameList agendas={this.props.item._agendas} />
{this.props.isAgendaEnabled && (
<List.Row>
<span className="no-padding">
<span className="sd-list-item__text-label">{gettext('Agenda:')}</span>
<span
className="sd-overflow-ellipsis
sd-list-item__text-strong sd-list-item--element-grow"
>
<AgendaNameList agendas={this.props.item._agendas} />
</span>
</span>
</span>
</List.Row>
</List.Row>
)}
</List.Column>
<List.Column>
<List.Row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
IProfileSchemaTypeList,
ISearchProfile
} from '../../../../interfaces';
import {superdeskApi} from '../../../../superdeskApi';
import {planningApi, superdeskApi} from '../../../../superdeskApi';

import {ButtonGroup, Button} from 'superdesk-ui-framework/react';
import {Row} from '../../../UI/Form';
Expand All @@ -32,6 +32,7 @@ interface IProps extends IEditorFieldProps {
export class EditorFieldEventRelatedPlannings extends React.PureComponent<IProps> {
render() {
const {gettext} = superdeskApi.localization;
const isAgendaEnabled = planningApi.planning.getEditorProfile().editor.agendas.enabled;
const disabled = this.props.disabled || this.props.schema?.read_only;

return (
Expand Down Expand Up @@ -88,6 +89,7 @@ export class EditorFieldEventRelatedPlannings extends React.PureComponent<IProps
editorType={this.props.editorType}
profile={this.props.profile}
coverageProfile={this.props.coverageProfile}
isAgendaEnabled={isAgendaEnabled}
/>
))
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ interface IProps {
scrollOnChange: boolean
): void;
addCoverageToWorkflow(original: IPlanningItem, coverage: IPlanningCoverageItem, index: number): void;
isAgendaEnabled: boolean;
}

export class RelatedPlanningItem extends React.PureComponent<IProps> {
Expand Down Expand Up @@ -104,7 +105,7 @@ export class RelatedPlanningItem extends React.PureComponent<IProps> {

render() {
const {gettext} = superdeskApi.localization;
const {item} = this.props;
const {item, isAgendaEnabled} = this.props;
const hideRemoveIcon = !this.props.item._id.startsWith(TEMP_ID_PREFIX) || this.props.disabled;

return (
Expand All @@ -118,6 +119,7 @@ export class RelatedPlanningItem extends React.PureComponent<IProps> {
<Row noPadding={true}>
<RelatedPlanningListItem
item={item}
isAgendaEnabled={isAgendaEnabled}
showIcon={true}
shadow={1}
editPlanningComponent={hideRemoveIcon ? null : (
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 41d66d9

Please sign in to comment.