Skip to content

Commit

Permalink
Allow event date editing after creation (#2191)
Browse files Browse the repository at this point in the history
* Allow event date editing after creation (#2188)

* handle event date updates

* fix e2e

---------

Co-authored-by: Konstantin Markov <[email protected]>
  • Loading branch information
petrjasek and thecalcc authored Jan 27, 2025
1 parent a939213 commit 1c20237
Show file tree
Hide file tree
Showing 18 changed files with 210 additions and 79 deletions.
5 changes: 0 additions & 5 deletions client/actions/events/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -651,11 +651,6 @@ const save = (original, updates) => (
const originalItem = eventUtils.modifyForServer(cloneDeep(originalEvent), true);
const eventUpdates = eventUtils.getEventDiff(originalItem, updates);

if (get(originalItem, 'lock_action') === EVENTS.ITEM_ACTIONS.EDIT_EVENT.lock_action &&
!isTemporaryId(originalItem._id)
) {
delete eventUpdates.dates;
}
eventUpdates.update_method = eventUpdates.update_method == null ?
EVENTS.UPDATE_METHODS[0].value :
eventUpdates.update_method?.value ?? eventUpdates.update_method;
Expand Down
11 changes: 4 additions & 7 deletions client/actions/tests/autosave_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,23 +173,20 @@ describe('actions.autosave', () => {
}))
.then((updatedItem) => {
let expectedItem = {
...data.event_autosave[0],
slugline: 'Newest Event Slugline',
name: 'Test Name',
lock_action: 'edit',
lock_user: store.initialState.session.identity._id,
lock_session: store.initialState.session.sessionId,
dates: {
...data.event_autosave[0].dates,
tz: moment.tz.guess(),
},
files: [],
};

delete expectedItem._etag;

const autosaveItem = jasmine.objectContaining(expectedItem);

expect(updatedItem).toEqual(autosaveItem);
expect(updatedItem.dates.tz).toEqual(moment.tz.guess());
expect(updatedItem.dates.start.toDate()).toEqual(data.event_autosave[0].dates.start.toDate());
expect(updatedItem.dates.end.toDate()).toEqual(data.event_autosave[0].dates.end.toDate());

expect(store.dispatch.args[0]).toEqual([{
type: 'AUTOSAVE_RECEIVE',
Expand Down
39 changes: 39 additions & 0 deletions client/components/Contacts/ActionBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import PropTypes from 'prop-types';
import {gettext} from 'core/utils';

export const ActionBar: React.FunctionComponent<any> = ({svc, readOnly, dirty, valid, onSave, onCancel}) => (
<div className="action-bar show">
<div className="button-group button-group--end button-group--comfort">
<button
id="cancel-edit-btn"
type="button"
className="btn"
onClick={onCancel}
>
{gettext('Cancel')}
</button>

{!readOnly && (
<button
id="save-edit-btn"
type="button"
className="btn btn--primary"
onClick={onSave}
disabled={!valid || !dirty}
>
{gettext('Save')}
</button>
)}
</div>
</div>
);

ActionBar.propTypes = {
svc: PropTypes.object.isRequired,
onSave: PropTypes.func,
onCancel: PropTypes.func,
readOnly: PropTypes.bool,
dirty: PropTypes.bool,
valid: PropTypes.bool,
};
3 changes: 2 additions & 1 deletion client/components/Contacts/ContactEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {gettext} from '../../../utils';
import * as ContactFormComponents from 'superdesk-core/scripts/apps/contacts/components/Form';
import {IContact} from 'superdesk-core/scripts/apps/contacts/Contacts';
import ng from 'superdesk-core/scripts/core/services/ng';
import {ActionBar} from '../ActionBar';

interface IProps {
currentContact: IContact;
Expand Down Expand Up @@ -75,7 +76,7 @@ export class ContactEditor extends React.Component<IProps, IState> {
}

render() {
const {ContactFormContainer, ActionBar} = ContactFormComponents;
const {ContactFormContainer} = ContactFormComponents;
const {currentContact} = this.props;

// Provides required services for Contact components
Expand Down
9 changes: 8 additions & 1 deletion client/components/Events/EventEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {EventEditorHeader} from './EventEditorHeader';
import {ContentBlock} from '../../UI/SidePanel';
import {EventScheduleSummary} from '../EventScheduleSummary';
import {CreateNewGeoLookup} from '../../GeoLookupInput/CreateNewGeoLookup';
import {appConfig} from 'appConfig';

interface IProps {
original?: IEventItem;
Expand Down Expand Up @@ -129,6 +130,10 @@ class EventEditorComponent extends React.PureComponent<IProps> {
}

renderHeader() {
if (appConfig.planning_event_link_method === 'many_secondary') {
return null;
}

return !this.props.itemExists ? null : (
<React.Fragment>
<EventEditorHeader item={this.props.item} />
Expand Down Expand Up @@ -177,7 +182,9 @@ class EventEditorComponent extends React.PureComponent<IProps> {
required: true,
showAllDay: this.props.formProfile.editor.dates.all_day.enabled,
showTimeZone: true,
enabled: !this.props.itemExists,
enabled: appConfig.planning_event_link_method === 'many_secondary'
? true
: !this.props.itemExists,
onChange: this.onDatesChanged,
},
language: {
Expand Down
2 changes: 1 addition & 1 deletion client/components/Main/ItemEditor/ItemManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ export class ItemManager {
}

startPartialSave(updates) {
const newState = {diff: cloneDeep(updates)};
const newState = {diff: cloneDeep(updates), errorMessages: []};

this.validate(this.props, newState, this.state);

Expand Down
1 change: 0 additions & 1 deletion client/components/UI/Form/DateInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ interface IProps {
remoteTimeZone?: string;
isLocalTimeZoneDifferent?: boolean;
inputAsLabel?: boolean;
dateOnly?: boolean;

onChange(field: string, value: moment.Moment): void;
popupContainer(): HTMLElement;
Expand Down
9 changes: 4 additions & 5 deletions client/components/UI/Form/DateTimeInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ interface IProps {
onToBeConfirmed?(field: string): void;
toBeConfirmed?: boolean;
testId?: string;
dateOnly?: boolean;
allDay?: boolean;
}

/**
Expand Down Expand Up @@ -77,7 +77,7 @@ export const DateTimeInput = ({
}: IProps) => {
let timeValue = timeField ? diff?.timeField : value;

if (props.dateOnly) {
if (props.allDay) {
timeValue = null;
}

Expand Down Expand Up @@ -112,18 +112,17 @@ export const DateTimeInput = ({
onPopupOpen={onPopupOpen}
onPopupClose={onPopupClose}
remoteTimeZone={remoteTimeZone}
isLocalTimeZoneDifferent={isLocalTimeZoneDifferent && !props.dateOnly}
isLocalTimeZoneDifferent={isLocalTimeZoneDifferent && !props.allDay}
refNode={refNode}
halfWidth={!hideTime}
dateOnly={props.dateOnly}
/>

{!hideTime && (
<Field
row={false}
component={TimeInput}
field={timeField ? timeField : `${field}.time`}
value={timeValue}
value={props.allDay ? null : timeValue}
item={item}
diff={diff}
readOnly={readOnly}
Expand Down
2 changes: 1 addition & 1 deletion client/components/UI/Form/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class Field extends React.Component<IProps, IState> {

const schema = get(formProfile, `schema["${profileField}"]`) || {};
let currentError = (this.state.dirty || showErrors) ? (error || get(errors, field)) : null;
const currentValue = value || get(diff, field);
const currentValue = value !== undefined ? value : get(diff, field);

const Component = component;
const child = (
Expand Down
2 changes: 1 addition & 1 deletion client/components/fields/editor/EndDateTime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface IProps extends IEditorFieldProps {
isLocalTimeZoneDifferent?: boolean;
remoteTimeZone?: string;
onToBeConfirmed?(field: string): void;
dateOnly?: boolean;
allDay?: boolean;
}

export class EditorFieldEndDateTime extends React.PureComponent<IProps> {
Expand Down
4 changes: 2 additions & 2 deletions client/components/fields/editor/EventSchedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ export class EditorFieldEventSchedule extends React.PureComponent<IProps> {
toBeConfirmed={this.props.item[TO_BE_CONFIRMED_FIELD] === true}
isLocalTimeZoneDifferent={isLocalTimeZoneDifferent}
remoteTimeZone={this.props.item.dates?.tz}
dateOnly={this.props.item.dates?.all_day}
allDay={this.props.item.dates?.all_day}
/>
<EditorFieldEndDateTime
{...props}
Expand All @@ -242,7 +242,7 @@ export class EditorFieldEventSchedule extends React.PureComponent<IProps> {
toBeConfirmed={this.props.item[TO_BE_CONFIRMED_FIELD] === true}
isLocalTimeZoneDifferent={isLocalTimeZoneDifferent}
remoteTimeZone={this.props.item.dates?.tz}
dateOnly={this.props.item.dates?.no_end_time || this.props.item.dates?.all_day}
allDay={this.props.item.dates?.no_end_time || this.props.item.dates?.all_day}
/>
<Row
flex={true}
Expand Down
2 changes: 1 addition & 1 deletion client/components/fields/editor/StartDateTime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface IProps extends IEditorFieldProps {
isLocalTimeZoneDifferent?: boolean;
remoteTimeZone?: string;
onToBeConfirmed?(field: string): void;
dateOnly?: boolean;
allDay?: boolean;
}

export class EditorFieldStartDateTime extends React.PureComponent<IProps> {
Expand Down
4 changes: 2 additions & 2 deletions client/components/fields/editor/base/dateTime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface IProps extends IEditorFieldProps {
remoteTimeZone?: string;
singleValue?: boolean;
onToBeConfirmed?(field: string): void;
dateOnly?: boolean;
allDay?: boolean;
}

export class EditorFieldDateTime extends React.PureComponent<IProps> {
Expand Down Expand Up @@ -62,7 +62,7 @@ export class EditorFieldDateTime extends React.PureComponent<IProps> {
refNode={(node) => {
this.node = node;
}}
dateOnly={this.props.dateOnly}
allDay={this.props.allDay}
/>
);
}
Expand Down
5 changes: 3 additions & 2 deletions client/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {assignPlanningConstantTranslations} from './planning';
import {assignAssignmentConstantTranslations} from './assignments';
import {assignTooltipConstantTranslations} from './tooltips';
import {assignCoverageConstantTranslations} from './coverages';
import {IPlanningPubstatus, IWorkflowState} from 'interfaces';

export {PRIVILEGES} from './privileges';
export {PLANNING} from './planning';
Expand Down Expand Up @@ -40,7 +41,7 @@ export const DATE_FORMATS = {
DISPLAY_CDATE_TBC_FORMAT: 'D. MMMM @ TBC',
};

export const WORKFLOW_STATE = {
export const WORKFLOW_STATE: {[key: string]: IWorkflowState} = {
DRAFT: 'draft',
INGESTED: 'ingested',
SCHEDULED: 'scheduled',
Expand All @@ -51,7 +52,7 @@ export const WORKFLOW_STATE = {
SPIKED: 'spiked',
};

export const POST_STATE = {
export const POST_STATE: {[key: string]: IPlanningPubstatus} = {
USABLE: 'usable',
CANCELLED: 'cancelled',
};
Expand Down
16 changes: 12 additions & 4 deletions client/utils/events.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import moment from 'moment-timezone';
import RRule from 'rrule';
import {get, isNil, sortBy, cloneDeep, omitBy, find, isEqual, pickBy, flatten, noop} from 'lodash';
import {get, isNil, sortBy, cloneDeep, omitBy, find, isEqual, pickBy, flatten} from 'lodash';
import {IMenuItem} from 'superdesk-ui-framework/react/components/Menu';

import {IVocabularyItem} from 'superdesk-api';
Expand Down Expand Up @@ -1261,6 +1261,14 @@ function modifyForServer(event: IEventItem, removeNullLinks: boolean = false) {
event.dates.tz
);
}
} else {
if (event.dates?.start != null && moment.isMoment(event.dates.start)) {
event.dates.start = event.dates.start.toISOString();
}

if (event.dates?.end != null && moment.isMoment(event.dates.end)) {
event.dates.end = event.dates.end.toISOString();
}
}

return event;
Expand Down Expand Up @@ -1515,18 +1523,18 @@ function getEventDiff(original: IEventItem, updates: Partial<IEventItem>): Parti
const originalItem = modifyForServer(cloneDeep(original), true);

// clone the updates as we're going to modify it
let eventUpdates = modifyForServer(cloneDeep(updates), true);
const eventUpdates = modifyForServer(cloneDeep(updates), true);

originalItem.location = originalItem.location ? [originalItem.location] : null;

// remove all properties starting with `_`
// and updates that are the same as original
eventUpdates = pickBy(eventUpdates, (value, key) => (
const diff = pickBy(eventUpdates, (value, key) => (
(key === TO_BE_CONFIRMED_FIELD || key === '_planning_item' || !key.startsWith('_')) &&
!isEqual(eventUpdates[key] ?? '', originalItem[key] ?? '')
));

return eventUpdates;
return diff;
}

function convertCoverageToEventEmbedded(coverage: IPlanningCoverageItem): IEmbeddedCoverageItem {
Expand Down
2 changes: 1 addition & 1 deletion client/utils/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ function isEventInDifferentTimeZone(event: Partial<IEventItem>): boolean {
const dateInEventTimeZone = getDateInRemoteTimeZone(event?.dates?.start, event?.dates?.tz);
const dateInLocalTimeZone = getDateInRemoteTimeZone(event?.dates?.start);

return dateInEventTimeZone.format('Z') !== dateInLocalTimeZone.format('Z');
return dateInEventTimeZone.format('Z') !== dateInLocalTimeZone.format('Z') && !event?.dates?.all_day;
}

function localTimeZone(): string {
Expand Down
Loading

0 comments on commit 1c20237

Please sign in to comment.