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

Fix qa reported bugs #2216

Open
wants to merge 14 commits into
base: feature/embedded-events-editor
Choose a base branch
from
6 changes: 6 additions & 0 deletions client/actions/autosave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ const save = (original, updates) => (
updateFields.lock_time = moment();
}

// Remove related events that aren't yet saved themselves,
// since the backend needs them to be in the events resource
if (updateFields.related_events) {
updateFields.related_events = cloneDeep(updateFields.related_events).filter((x) => !isTemporaryId(x._id));
}

return api(`${itemType}_autosave`).save(
original || {},
updateFields
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 @@ -375,7 +375,7 @@ const save = (original, planUpdates) => (
const originalItem = cloneDeep(originalPlan);

// remove all properties starting with _ or lock_,
let updates = pickBy(
const updates: Partial<IPlanningItem> = pickBy(
cloneDeep(planUpdates),
(v, k) => ((k === TO_BE_CONFIRMED_FIELD || !k.startsWith('_')) && !k.startsWith('lock_'))
);
Expand Down
4 changes: 2 additions & 2 deletions client/api/editor/item_events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export function getEventsInstance(type: EDITOR_TYPE): IEditorAPI['item']['events
});
}

function removePlanningItem(item: DeepPartial<IPlanningItem>) {
function unlinkPlanning(item: DeepPartial<IPlanningItem>) {
const editor = planningApi.editor(type);
const event = editor.form.getDiff<IEventItem>();
const plans = (event.associated_plannings || []).filter(
Expand Down Expand Up @@ -227,7 +227,7 @@ export function getEventsInstance(type: EDITOR_TYPE): IEditorAPI['item']['events
getGroupsForItem,
getRelatedPlanningDomRef,
addPlanningItem,
removePlanningItem,
unlinkPlanning,
updatePlanningItem,
onEventDatesChanged,
};
Expand Down
35 changes: 33 additions & 2 deletions client/api/editor/item_planning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,36 @@ export function getPlanningInstance(type: EDITOR_TYPE): IEditorAPI['item']['plan
return editor.dom.fields[field];
}

function updateEventItem(
original: DeepPartial<IEventItem>,
updates: DeepPartial<IEventItem>,
scrollOnChange: boolean
) {
const editor = planningApi.editor(type);
const planning = editor.form.getDiff<IPlanningItem>();
const events = cloneDeep(planning.related_events || []);
const index = events.findIndex(
(event) => event._id === original._id
);

if (index < 0) {
return;
}

events[index] = {
...original,
...updates,
};


editor.form.changeField('related_events', events)
.then(() => {
if (scrollOnChange) {
getRelatedEventsDomRef(original._id).current?.scrollIntoView();
}
});
}

function getRelatedEventsDomRef(eventId: IEventItem['_id']): RefObject<AssociatedEventItem> {
const editor = planningApi.editor(type);
const field = `planning-item--${eventId}`;
Expand All @@ -92,7 +122,7 @@ export function getPlanningInstance(type: EDITOR_TYPE): IEditorAPI['item']['plan
return editor.dom.fields[field];
}

function removeEventItem(item: DeepPartial<IEventItem>): void {
function unlinkEvent(item: DeepPartial<IEventItem>): void {
const editor = planningApi.editor(type);
const planning = editor.form.getDiff<IPlanningItem>();
const events = (planning.related_events || []).filter(
Expand Down Expand Up @@ -142,8 +172,9 @@ export function getPlanningInstance(type: EDITOR_TYPE): IEditorAPI['item']['plan
getGroupsForItem,
getCoverageFields,
getRelatedEventsDomRef,
removeEventItem,
unlinkEvent,
getCoverageFieldDomRef,
addCoverages,
updateEventItem,
};
}
2 changes: 1 addition & 1 deletion client/api/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ function create(updates: Partial<IEventItem>): Promise<Array<IEventItem>> {
return superdeskApi.dataApi.create<IEventItem | IRestApiResponse<IEventItem>>('events', {
...updates,
associated_plannings: undefined,
embedded_planning: updates.associated_plannings.map((planning) => ({
embedded_planning: (updates.associated_plannings ?? []).map((planning) => ({
update_method: planning.update_method ?? planningDefaultCreateMethod,
coverages: planning.coverages.map((coverage) => ({
coverage_id: coverage.coverage_id,
Expand Down
4 changes: 2 additions & 2 deletions client/components/AttachmentsInputStandalone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ export class AttachmentsInputStandalone extends React.PureComponent<IProps, ISta
<Spacer v gap="16" noWrap>
<WithLiveResources
resources={[
{resource: 'planning_files', ids: this.props.value},
{resource: 'planning_files', ids: this.props.value ?? []},
]}
>
{(res) => {
const files: Array<IFile> = res[0]._items;
const files: Array<IFile> = res?.[0]?._items ?? [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after superdesk/superdesk-client-core#4762 you can revert this line to how it was


return (
<Spacer v gap="4">
Expand Down
2 changes: 1 addition & 1 deletion client/components/Events/EventEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ class EventEditorComponent extends React.PureComponent<IProps> {
editor.item.events.getRelatedPlanningDomRef(value._id)
),
addPlanningItem: editor.item.events.addPlanningItem,
removePlanningItem: editor.item.events.removePlanningItem,
unlinkPlanning: editor.item.events.unlinkPlanning,
updatePlanningItem: editor.item.events.updatePlanningItem,
},
}}
Expand Down
38 changes: 28 additions & 10 deletions client/components/Main/ItemEditor/ItemManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
IEditorAPI,
IEditorProps,
IEditorState,
IEventOrPlanningItem
IEventOrPlanningItem,
IPlanningRelatedEventLink
} from '../../../interfaces';
import {planningApi} from '../../../superdeskApi';
import {ITEM_TYPE, POST_STATE, UI, WORKFLOW_STATE, WORKSPACE, EVENTS} from '../../../constants';
Expand All @@ -32,6 +33,8 @@ import {
handleEmbeddedItems,
IEmbeddedPlanningsActionType,
} from '../../../components/editor-standalone/save-handling';
import eventsApi from '../../../actions/events/api';
import {initialValues} from 'selectors/forms';

export class ItemManager {
editor: EditorComponent;
Expand Down Expand Up @@ -661,12 +664,18 @@ export class ItemManager {
});
}

const promise: Promise<any> = handleEmbeddedItems(this.props.editorType, 'SAVE', this.props.itemType)
.then(() =>
!updateStates ? Promise.resolve({}) : this.setState({submitting: true, submitFailed: false}),
/**
* Calls internal save of each embedded item - the one of the storage adapter, then returns the saved items.
*/
const promise = handleEmbeddedItems(this.props.editorType, 'SAVE', this.props.itemType)
.then((res) =>
Promise.all([
res,
!updateStates ? {} : this.setState({submitting: true, submitFailed: false})
])
);

return promise.then(() => {
return promise.then(([embeddedItems]) => {
if (this.props.addNewsItemToPlanning) {
return this._saveFromAuthoring({post, unpost});
}
Expand All @@ -693,14 +702,23 @@ export class ItemManager {
updates.update_method = updateMethod;

if (Object.keys(planningUpdateMethods).length > 0) {
updates.associated_plannings?.forEach((planningItem) => {
if (planningUpdateMethods[planningItem._id] != null) {
planningItem.update_method = planningUpdateMethods[planningItem._id];
}
});
updates.associated_plannings?.forEach((planningItem) => {
if (planningUpdateMethods[planningItem._id] != null) {
planningItem.update_method = planningUpdateMethods[planningItem._id];
}
});
}
}

if (updates.type === 'planning' && (updates.related_events ?? []).length > 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are you doing here that wasn't working with the previous version of code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm updating related_events

const updatedLinks = eventUtils.addRelatedEvents(
updates.related_events.filter((x) => !isTemporaryId(x._id)) as Array<IPlanningRelatedEventLink>,
embeddedItems as Array<IEventItem>,
);

updates.related_events = updatedLinks.next;
}

return this.autoSave.flushAutosave()
.then(() => (
this.dispatch<any>(actions.main.save(
Expand Down
3 changes: 2 additions & 1 deletion client/components/Planning/PlanningEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,8 @@ class PlanningEditorComponent extends React.Component<IProps, IState> {
files: this.props.files,
},
associated_event: {
removeEventItem: editor.item.planning.removeEventItem,
updateEventItem: editor.item.planning.updateEventItem,
unlinkEvent: editor.item.planning.unlinkEvent,
field: 'related_events',
events: (this.props.diff.related_events ?? [])
.map((relatedEvent) => this.props.events[relatedEvent._id]),
Expand Down
56 changes: 56 additions & 0 deletions client/components/editor-standalone/field-definitions/all-day.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {IAuthoringFieldV2, ICommonFieldConfig} from 'superdesk-api';
import {superdeskApi} from '../../../superdeskApi';
import moment from 'moment';

export const getAllDayDatesField = () => {
return {
fieldId: 'dates',
getField: ({required, id}) => {
const fieldConfig: ICommonFieldConfig = {
required: required,
};

const field: IAuthoringFieldV2 = {
id: id,
name: superdeskApi.localization.gettext('All Day'),
fieldType: 'boolean',
fieldConfig: fieldConfig,
};

return field;
},
storageAdapterEvent: {
storeValue: (item: IEventItem, operationalValue: boolean | undefined) => {
const dates = item.dates ?? {};
let newStart, newEnd;

newStart = moment((dates.start ?? (dates.tz ? moment.tz(dates.tz) : moment())))
.startOf('day');

// If allDay is enabled, then set the event to all day
if (operationalValue) {
newEnd = moment(dates.end ?? (dates.tz ? moment.tz(dates.tz) : moment()))
.endOf('day');
} else {
// If allDay is disabled, then set the new dates to
// the initial values since last save and time to empty
newEnd = moment(dates?.end ?? newStart)
.hour(0)
.minute(1);
}

// TODO: Figure out support for _startTime _endTime, _time_to_be_confirmed fields
return {
...item,
dates: {
...item.dates,
start: newStart,
end: newEnd,
all_day: operationalValue,
}
};
},
retrieveStoredValue: (item: IEventItem) => item.dates.all_day,
}
};
};
52 changes: 52 additions & 0 deletions client/components/editor-standalone/field-definitions/calendars.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {IAuthoringFieldV2, IDropdownConfigManualSource} from 'superdesk-api';
import {planningApi, superdeskApi} from '../../../superdeskApi';
import {getVocabularyItemFieldTranslated} from '../../../utils/vocabularies';
import {calendars} from '../../../selectors/events';

export const getCalendarsField = () => {
const vocabularyFromStore = calendars(planningApi.redux.store.getState());

return {
fieldId: 'calendars',
getField: ({required, id}) => {
const options = vocabularyFromStore.map(
(option) => ({
id: option.qcode,
label: getVocabularyItemFieldTranslated(
option,
'label',
'en',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

language shouldn't be hardcoded

'name'
),
})
);

const fieldConfig: IDropdownConfigManualSource = {
source: 'manual-entry',
options: options,
type: 'text',
roundCorners: false,
multiple: true,
required: required,
};

const field: IAuthoringFieldV2 = {
id: id,
name: superdeskApi.localization.gettext('Calendars'),
fieldType: 'dropdown',
fieldConfig: fieldConfig,
};

return field;
},
storageAdapterEvent: {
storeValue: (item, operationalValue: Array<string>) => {
return {
...item,
calendars: vocabularyFromStore.filter((x) => operationalValue.includes(x.qcode)),
};
},
retrieveStoredValue: (item) => item.calendars.map((x) => x.qcode),
}
};
};
34 changes: 34 additions & 0 deletions client/components/editor-standalone/field-definitions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ import {getPriorityField} from './priority-field';
import {getLocationsField} from './locations-field';
import {getLinksField} from './link-field';
import {getContactsField} from './contacts';
import {getOccurStatusField} from './occurence-status';
import {getLanguageField} from './lanugage';
import {getCalendarsField} from './calendars';
import {getAllDayDatesField} from './all-day';

export function getFieldDefinitions(profileType: 'event' | 'planning'): IFieldDefinitions {
const {gettext} = superdeskApi.localization;

const result: Array<IFieldDefinition> = [
{
fieldId: 'ednote',
Expand Down Expand Up @@ -65,6 +70,35 @@ export function getFieldDefinitions(profileType: 'event' | 'planning'): IFieldDe
return field;
},
},
{
fieldId: 'references',
getField: ({required, id}) =>
getTextFieldConfig({id: id, label: gettext('External Reference'), required: required}),
},
{
fieldId: 'definition_short',
getField: ({required, id}) =>
getTextFieldConfig({id: id, label: gettext('Description'), required: required}),
},
{
fieldId: 'invitation_details',
getField: ({required, id}) =>
getTextFieldConfig({id: id, label: gettext('Invitation Details'), required: required}),
},
{
fieldId: 'accreditation_info',
getField: ({required, id}) =>
getTextFieldConfig({id: id, label: gettext('Accreditation Info'), required: required}),
},
{
fieldId: 'registration_details',
getField: ({required, id}) =>
getTextFieldConfig({id: id, label: gettext('Registration Details'), required: required}),
},
getAllDayDatesField(),
getCalendarsField(),
getLanguageField(),
getOccurStatusField(),
getPlanningDate(),
getPlaceField(),
getAgendasField(),
Expand Down
Loading
Loading