Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into feature/multiple-e…
Browse files Browse the repository at this point in the history
…vents-in-planning
  • Loading branch information
petrjasek committed Sep 19, 2024
2 parents 295c405 + ed1c8cf commit a524be3
Show file tree
Hide file tree
Showing 39 changed files with 801 additions and 492 deletions.
6 changes: 5 additions & 1 deletion TAGS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# TAG: MULTIPLE_PRIMARY_EVENTS

As we are changing the schema to allow more than one event to be associated to a planning item. To be able to do this gradually, we have introduced event linking types - primary and secondary. Secondary events will only be displayed as linked in the user interface, but will not be used for logic e.g. prevent spiking of a planning item. Events linked via a primary link type will work the same as a single related event used to work before we supported multiple linked events. It is only supported to have one related event that it using primary link. The goal is to eventually support linking multiple events using a primary link. The purpose of this tag is to mark places in the code where that support it missing.
As we are changing the schema to allow more than one event to be associated to a planning item. To be able to do this gradually, we have introduced event linking types - primary and secondary. Secondary events will only be displayed as linked in the user interface, but will not be used for logic e.g. prevent spiking of a planning item. Events linked via a primary link type will work the same as a single related event used to work before we supported multiple linked events. It is only supported to have one related event that it using primary link. The goal is to eventually support linking multiple events using a primary link. The purpose of this tag is to mark places in the code where that support it missing.

# TAG: AUTHORING-ANGULAR

AUTHORING-ANGULAR tag is meant to mark code that has to be removed together with angular based authoring component when time comes.
3 changes: 2 additions & 1 deletion client/actions/agenda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {AGENDA, MODALS, EVENTS} from '../constants';
import {getErrorMessage, gettext, planningUtils} from '../utils';
import {planning, showModal, main} from './index';
import {convertStringFields} from '../utils/strings';
import planningApis from '../actions/planning/api';

const openAgenda = () => (
(dispatch) => (
Expand Down Expand Up @@ -309,7 +310,7 @@ const createPlanningFromEvent = (
newPlanningItem.agendas = newPlanningItem.agendas.concat(agendas);

return (dispatch) => (
dispatch(planning.api.save({}, newPlanningItem))
dispatch(planningApis.save({}, newPlanningItem))
);
};

Expand Down
4 changes: 2 additions & 2 deletions client/actions/assignments/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {lockUtils, assignmentUtils, gettext, isExistingItem} from '../../utils';
import * as selectors from '../../selectors';
import assignments from './index';
import main from '../main';
import planning from '../planning';
import {hideModal, showModal} from '../index';
import * as actions from '../../actions';
import planningApis from '../planning/api';

const _notifyAssignmentEdited = (assignmentId) => (
(dispatch, getState, {notify}) => {
Expand Down Expand Up @@ -191,7 +191,7 @@ const _updatePlannigRelatedToAssignment = (data) => (
return Promise.resolve();
}

dispatch(planning.api.loadPlanningByIds([data.planning]));
dispatch(planningApis.loadPlanningByIds([data.planning]));
dispatch(main.fetchItemHistory(planningItem));
}
);
Expand Down
41 changes: 23 additions & 18 deletions client/actions/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import {get, isEmpty, isEqual, isNil, omit} from 'lodash';
import moment from 'moment';

import {appConfig} from 'appConfig';
import {appConfig as config} from 'appConfig';

const appConfig = config as IPlanningConfig;

import {IUser} from 'superdesk-api';
import {planningApi, superdeskApi} from '../superdeskApi';
import {
Expand All @@ -18,6 +21,7 @@ import {
ITEM_TYPE,
IEventTemplate,
IEventItem,
IPlanningConfig,
} from '../interfaces';

import {
Expand Down Expand Up @@ -68,6 +72,7 @@ import eventsPlanningUi from './eventsPlanning/ui';
import * as selectors from '../selectors';
import {validateItem} from '../validators';
import {searchParamsToOld} from '../utils/search';
import {searchAndStore} from '../api/combined';

function openForEdit(item: IEventOrPlanningItem, updateUrl: boolean = true, modal: boolean = false) {
return (dispatch, getState) => {
Expand Down Expand Up @@ -716,7 +721,7 @@ const openIgnoreCancelSaveModal = ({
const storedItems = itemType === ITEM_TYPE.EVENT ?
selectors.events.storedEvents(getState()) :
selectors.planning.storedPlannings(getState());
const item = get(storedItems, itemId) || {};
const item = storedItems[itemId] ?? {};

if (!isExistingItem(item)) {
delete item._id;
Expand All @@ -725,19 +730,17 @@ const openIgnoreCancelSaveModal = ({
let promise = Promise.resolve(item);

if (itemType === ITEM_TYPE.EVENT && eventUtils.isEventRecurring(item)) {
const originalEvent = get(storedItems, itemId, {});

promise = dispatch(eventsApi.query({
recurrenceId: originalEvent.recurrence_id,
maxResults: appConfig.max_recurrent_events,
onlyFuture: false,
}))
.then((relatedEvents) => ({
...item,
_recurring: relatedEvents || [item],
_events: [],
_originalEvent: originalEvent,
}));
promise = searchAndStore({
recurrence_id: item.recurrence_id,
max_results: appConfig.max_recurrent_events,
only_future: false,
include_associated_planning: true,
}).then((relatedEvents) => ({
...item,
_recurring: relatedEvents.filter((item) => item.type === 'event') ?? [item],
_events: [],
_originalEvent: item,
}));
}

return promise.then((itemWithAssociatedData) => (
Expand Down Expand Up @@ -1199,8 +1202,10 @@ const openFromLockActions = () => (
if (action) {
/* get the item we're operating on */
dispatch(self.fetchById(sessionLastLock.item_id, sessionLastLock.item_type)).then((item) => {
actionUtils.getActionDispatches({dispatch: dispatch, eventOnly: false,
planningOnly: false})[action[0].actionName](item, false, false);
actionUtils.getActionDispatches({
dispatch: dispatch, eventOnly: false,
planningOnly: false
})[action[0].actionName](item, false, false);
});
}
}
Expand Down Expand Up @@ -1459,7 +1464,7 @@ function onItemUnlocked(
}));

if (getItemType(item) === ITEM_TYPE.PLANNING && selectors.general.currentWorkspace(state)
=== WORKSPACE.AUTHORING) {
=== WORKSPACE.AUTHORING) {
dispatch(self.closePreviewAndEditorForItems([item]));
}
}
Expand Down
2 changes: 1 addition & 1 deletion client/actions/planning/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ const unpost = (original, updates) => (
* Also loads all the associated contacts (if any)
* @param {array, object} plannings - An array of planning item objects
*/
const receivePlannings = (plannings) => (
const receivePlannings = (plannings): any => (
(dispatch) => {
dispatch(actions.contacts.fetchContactsFromPlanning(plannings));
dispatch({
Expand Down
3 changes: 2 additions & 1 deletion client/actions/planning/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {events, fetchAgendas} from '../index';
import main from '../main';
import {showModal, hideModal} from '../index';
import eventsPlanning from '../eventsPlanning';
import planningApis from '../planning/api';

/**
* WS Action when a new Planning item is created
Expand Down Expand Up @@ -103,7 +104,7 @@ const onPlanningLocked = (e: {}, data: IWebsocketMessageData['ITEM_LOCKED']) =>

const sessionId = selectors.general.session(getState()).sessionId;

return dispatch(planning.api.getPlanning(data.item, false))
return dispatch(planningApis.getPlanning(data.item, false))
.then((planInStore) => {
let plan = {
...planInStore,
Expand Down
4 changes: 1 addition & 3 deletions client/actions/planning/tests/api_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import {
restoreSinonStub,
convertEventDatesToMoment,
} from '../../../utils/testUtils';
import {createTestStore} from '../../../utils';
import {PLANNING, SPIKED_STATE, WORKFLOW_STATE} from '../../../constants';
import {PLANNING, SPIKED_STATE} from '../../../constants';
import {MAIN} from '../../../constants';
import * as selectors from '../../../selectors';
import contactsApi from '../../contacts';
import {planningApis} from '../../../api';

Expand Down
5 changes: 2 additions & 3 deletions client/actions/planning/tests/notifications_test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {planningApi} from '../../../superdeskApi';
import planningApis from '../api';
import planningApis from '../../planning/api';
import planningUi from '../ui';
import featuredPlanning from '../featuredPlanning';
import eventsPlanningUi from '../../eventsPlanning/ui';
Expand Down Expand Up @@ -199,7 +199,7 @@ describe('actions.planning.notifications', () => {
describe('onPlanningLocked', () => {
beforeEach(() => {
sinon.stub(planningApi.locks, 'setItemAsLocked').returns(undefined);
sinon.stub(planningApis, 'getPlanning').returns(Promise.resolve(data.plannings[0]));
sinon.stub(planningApis, 'getPlanning').returns(() => Promise.resolve(data.plannings[0]));
});

afterEach(() => {
Expand All @@ -221,7 +221,6 @@ describe('actions.planning.notifications', () => {
))
.then(() => {
expect(planningApi.locks.setItemAsLocked.callCount).toBe(1);
expect(planningApis.getPlanning.callCount).toBe(1);
expect(planningApis.getPlanning.args[0]).toEqual([
'p1',
false,
Expand Down
17 changes: 15 additions & 2 deletions client/api/combined.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import {
IEventOrPlanningItem,
} from '../interfaces';
import {IRestApiResponse} from 'superdesk-api';
import {searchRaw, searchRawGetAll, convertCommonParams, cvsToString, arrayToString} from './search';
import {searchRaw, searchRawGetAll, convertCommonParams, cvsToString, arrayToString, searchRawAndStore} from './search';
import {eventUtils, planningUtils} from '../utils';
import {planningApi} from '../superdeskApi';
import {combinedSearchProfile} from '../selectors/forms';
import {searchPlanningGetAll} from './planning';
import {searchPlanningGetAll, convertPlanningParams} from './planning';
import {searchEventsGetAll} from './events';

type IResponse = IRestApiResponse<IEventOrPlanningItem>;
Expand Down Expand Up @@ -67,6 +67,18 @@ export function searchCombinedGetAll(params: ISearchParams): Promise<Array<IEven
});
}

export function searchAndStore(params: ISearchParams) {
return searchRawAndStore<IEventOrPlanningItem>({
...convertCommonParams(params),
...convertPlanningParams(params),
repo: FILTER_TYPE.COMBINED,
}).then((res) => {
res._items.forEach(modifyItemForClient);

return res._items;
});
}

export function getEventsAndPlanning(params: ISearchParams): Promise<{
events: Array<IEventItem>;
plannings: Array<IPlanningItem>;
Expand Down Expand Up @@ -145,5 +157,6 @@ export const combined: IPlanningAPI['combined'] = {
getRecurringEventsAndPlanningItems: getRecurringEventsAndPlanningItems,
getEventsAndPlanning: getEventsAndPlanning,
getSearchProfile: getCombinedSearchProfile,
searchAndStore: searchAndStore,
};

6 changes: 3 additions & 3 deletions client/api/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {EVENTS, TEMP_ID_PREFIX} from '../constants';
import {arrayToString, convertCommonParams, cvsToString, searchRaw, searchRawGetAll} from './search';
import {eventUtils} from '../utils';
import {eventProfile, eventSearchProfile} from '../selectors/forms';
import * as actions from '../actions';
import planningApis from '../actions/planning/api';

const appConfig = config as IPlanningConfig;

Expand Down Expand Up @@ -161,7 +161,7 @@ function create(updates: Partial<IEventItem>): Promise<Array<IEventItem>> {
}).then((planningItems) => {
// Make sure to update the Redux Store with the latest Planning items
// So that the Editor can set the state with these latest items
planningApi.redux.store.dispatch<any>(actions.planning.api.receivePlannings(planningItems));
planningApi.redux.store.dispatch<any>(planningApis.receivePlannings(planningItems));

return events;
});
Expand Down Expand Up @@ -207,7 +207,7 @@ function update(original: IEventItem, updates: Partial<IEventItem>): Promise<Arr
}).then((planningItems) => {
// Make sure to update the Redux Store with the latest Planning items
// So that the Editor can set the state with these latest items
planningApi.redux.store.dispatch<any>(actions.planning.api.receivePlannings(planningItems));
planningApi.redux.store.dispatch<any>(planningApis.receivePlannings(planningItems));

return events;
});
Expand Down
10 changes: 5 additions & 5 deletions client/api/planning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import {planningProfile, planningSearchProfile} from '../selectors/forms';
import {featured} from './featured';
import {PLANNING} from '../constants';
import * as selectors from '../selectors';
import * as actions from '../actions';
import planningApis from '../actions/planning/api';

const appConfig = config as IPlanningConfig;

function convertPlanningParams(params: ISearchParams): Partial<ISearchAPIParams> {
export function convertPlanningParams(params: ISearchParams): Partial<ISearchAPIParams> {
return {
agendas: arrayToString(params.agendas),
no_agenda_assigned: params.no_agenda_assigned,
Expand Down Expand Up @@ -90,7 +90,7 @@ export function getPlanningById(
.then(modifyItemForClient)
.then((item) => {
if (saveToStore) {
dispatch<any>(actions.planning.api.receivePlannings([item]));
dispatch<any>(planningApis.receivePlannings([item]));
}

return item;
Expand Down Expand Up @@ -223,7 +223,7 @@ function bulkAddCoverageToWorkflow(planningItems: Array<IPlanningItem>): Promise

return planning.update(plan, updates)
.then((updatedPlan) => {
dispatch<any>(actions.planning.api.receivePlannings([updatedPlan]));
dispatch<any>(planningApis.receivePlannings([updatedPlan]));

return updatedPlan;
});
Expand Down Expand Up @@ -262,7 +262,7 @@ function addCoverageToWorkflow(
return planning.update(plan, updates)
.then((updatedPlan) => {
notify.success(gettext('Coverage added to workflow.'));
dispatch<any>(actions.planning.api.receivePlannings([updatedPlan]));
dispatch<any>(planningApis.receivePlannings([updatedPlan]));

return updatedPlan;
})
Expand Down
28 changes: 25 additions & 3 deletions client/api/search.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {ISearchAPIParams, ISearchParams} from '../interfaces';
import {superdeskApi} from '../superdeskApi';
import {IEventOrPlanningItem, ISearchAPIParams, ISearchParams} from '../interfaces';
import {superdeskApi, planningApi as sdPlanningApi} from '../superdeskApi';
import {IRestApiResponse} from 'superdesk-api';
import {getDateTimeElasticFormat, getTimeZoneOffset} from '../utils';
import {default as timeUtils} from '../utils/time';
import {appConfig} from 'appConfig';

import planningApi from '../actions/planning/api';
import eventsApi from '../actions/events/api';
import {partition} from 'lodash';

export function cvsToString(items?: Array<{[key: string]: any}>, field: string = 'qcode'): string {
return arrayToString(
Expand Down Expand Up @@ -51,6 +53,7 @@ export function convertCommonParams(params: ISearchParams): Partial<ISearchAPIPa
sort_field: params.sort_field,
tz_offset: params.date_filter ? getTimeZoneOffset() : null,
time_zone: timeUtils.localTimeZone(),
include_associated_planning: params.include_associated_planning,
};
}

Expand All @@ -76,6 +79,25 @@ export function searchRaw<T>(args: ISearchAPIParams): Promise<IRestApiResponse<T
);
}

export const searchRawAndStore = <T>(args: ISearchAPIParams) => {
const {dispatch} = sdPlanningApi.redux.store;

return superdeskApi.dataApi.queryRawJson<IRestApiResponse<T>>(
'events_planning_search',
excludeNullParams(args)
).then((res) => {
const [relatedPlans, events] = partition(res._items, (item: IEventOrPlanningItem) => item.type === 'planning');

if (args.include_associated_planning) {
dispatch(planningApi.receivePlannings(relatedPlans));
}

dispatch(eventsApi.receiveEvents(events));

return res;
});
};

export function searchRawGetAll<T>(args: ISearchAPIParams): Promise<Array<T>> {
const params = excludeNullParams(args);
let items: Array<T> = [];
Expand Down
2 changes: 1 addition & 1 deletion client/components/GeoLookupInput/AddGeoLookupInput.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import Geolookup from 'react-geolookup';
import DebounceInput from 'react-debounce-input';
import {DebounceInput} from 'react-debounce-input';

import {appConfig} from 'appConfig';
import {IRestApiResponse} from 'superdesk-api';
Expand Down
2 changes: 1 addition & 1 deletion client/components/IgnoreCancelSaveModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class IgnoreCancelSaveModalComponent extends React.Component<IProps, ISta
return (
<UpdateRecurringEventsForm
original={this.props.modalProps.item}
updates={this.props.modalProps.updates}
updates={this.props.modalProps.updates as Partial<IEventItem>}
onEventUpdateMethodChange={this.onEventUpdateMethodChange}
onPlanningUpdateMethodChange={this.onPlanningUpdateMethodChange}
modalProps={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ function getRecurringPlanningToUpdate(
updates: Partial<IEventItem>,
plannings: {[planningId: string]: IPlanningItem}
): Array<IPlanningItem['_id']> {
const originalCoverages: IPlanningEmbeddedCoverageMap = (original.planning_ids || [])
const originalCoverages: IPlanningEmbeddedCoverageMap = (original.planning_ids ?? [])
.map((planningId) => plannings[planningId])
.reduce((planningItems, planningItem) => {
planningItems[planningItem._id] = (planningItem.coverages ?? []).reduce(
Expand Down
Loading

0 comments on commit a524be3

Please sign in to comment.