Skip to content

Commit

Permalink
[SDESK-4561] Scheduled Updates: Plan updates to coverage
Browse files Browse the repository at this point in the history
added behave test cases

defaulted config to 'true'

test cases and more preview

refactored and fixed styling

fixed flake error

changes pending
  • Loading branch information
nrvikas committed Aug 22, 2019
1 parent 8c67dc1 commit 5784395
Show file tree
Hide file tree
Showing 29 changed files with 2,238 additions and 369 deletions.
3 changes: 2 additions & 1 deletion client/components/Assignments/AssignmentItem/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ export class AssignmentItem extends React.Component {
}
>
<i className={planningUtils.getCoverageIcon(planningUtils.getCoverageContentType(
assignment, contentTypes) || get(assignment, 'planning.g2_content_type'))} />
assignment, contentTypes) || get(assignment, 'planning.g2_content_type'),
assignment)} />
</OverlayTrigger>
</Column>
<Column grow={true} border={false}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const AssignmentPreviewHeader = ({

const planningSchedule = get(assignment, 'planning.scheduled');
const coverageIcon = planningUtils.getCoverageIcon(planningUtils.getCoverageContentType(
assignment, contentTypes) || get(assignment, 'planning.g2_content_type'));
assignment, contentTypes) || get(assignment, 'planning.g2_content_type'), assignment);

return (
<div>
Expand Down
1 change: 1 addition & 0 deletions client/components/Coverages/CoverageArrayInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export class CoverageArrayInput extends React.Component {
navigation={coverageNavigation}
openCoverageIds={this.state.openCoverageIds}
autoAssignToWorkflow={autoAssignToWorkflow}
preferredCoverageDesks={preferredCoverageDesks}
{...props}
/>
</ContentBlock>
Expand Down
111 changes: 106 additions & 5 deletions client/components/Coverages/CoverageEditor/CoverageForm.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import {get} from 'lodash';
import {getItemInArrayById, gettext, planningUtils, getItemWorkflowState} from '../../../utils';
import {getItemInArrayById, gettext, planningUtils, getItemWorkflowState, generateTempId} from '../../../utils';
import moment from 'moment';
import {WORKFLOW_STATE} from '../../../constants';
import {Button} from '../../UI';
import {Row, Label, LineInput} from '../../UI/Form';
import {ScheduledUpdate} from '../ScheduledUpdate';


import {
Expand All @@ -22,10 +25,15 @@ export class CoverageForm extends React.Component {
constructor(props) {
super(props);
this.onScheduleChanged = this.onScheduleChanged.bind(this);
this.onAddScheduledUpdate = this.onAddScheduledUpdate.bind(this);
this.onRemoveScheduledUpdate = this.onRemoveScheduledUpdate.bind(this);
this.onScheduledUpdateClose = this.onScheduledUpdateClose.bind(this);
this.onScheduledUpdateOpen = this.onScheduledUpdateOpen.bind(this);
this.dom = {
contentType: null,
popupContainer: null,
};
this.state = {openScheduledUpdates: []};
}

componentDidUpdate(prevProps) {
Expand All @@ -34,24 +42,24 @@ export class CoverageForm extends React.Component {
}
}

onScheduleChanged(f, v) {
const {value, onChange} = this.props;
onScheduleChanged(f, v, value = this.props.value) {
const {onChange} = this.props;
let finalValue = v, fieldStr, relatedFieldStr;

// We will be updating scheduled and _scheduledTime together
// relatedFieldStr will be '_scheduledTime' if date gets changed and vice versa
// Update time only if date is already set
if (f.endsWith('.date')) {
fieldStr = f.slice(0, -5);
relatedFieldStr = fieldStr.replace('scheduled', '_scheduledTime');
relatedFieldStr = f.replace('scheduled.date', '_scheduledTime');
// If there is no current scheduled date, then set the time value to end of the day
if (!get(value, 'planning.scheduled')) {
finalValue = v.add(1, 'hour').startOf('hour');
relatedFieldStr = null;
}
} else if (f.endsWith('._scheduledTime')) {
// If there is no current scheduled date, then set the date to today
relatedFieldStr = f.slice(0, -4).replace('_', '');
relatedFieldStr = f.replace('_scheduledTime', 'scheduled');
fieldStr = f;
if (!get(value, 'planning.scheduled')) {
finalValue = moment().hour(v.hour())
Expand All @@ -72,6 +80,56 @@ export class CoverageForm extends React.Component {
}
}

onAddScheduledUpdate() {
let defaultScheduledUpdate = {
coverage_id: get(this.props, 'value.coverage_id'),
scheduled_update_id: generateTempId(),
planning: {
internal_note: get(this.props, 'value.planning.internal_note'),
genre: ((get(this.props, 'genres') || []).find((g) => g.qcode === 'Update') ||
get(this.props, 'value.planning.genre')),
},
news_coverage_status: this.props.newsCoverageStatus[0],
workflow_status: WORKFLOW_STATE.DRAFT,
};

// Set default desks for coverage type
planningUtils.setDefaultAssignment(defaultScheduledUpdate, this.props.preferredCoverageDesks,
get(this.props, 'value.planning.g2_content_type'), this.props.defaultDesk);

this.props.onChange(`${this.props.field}.scheduled_updates`,
[
...get(this.props, 'value.scheduled_updates', []),
defaultScheduledUpdate,
]);
this.setState({openScheduledUpdates: [
...this.state.openScheduledUpdates,
defaultScheduledUpdate.scheduled_update_id,
]});
}

onRemoveScheduledUpdate(index) {
// Remove the scheduled update at the index
this.props.onChange(`${this.props.field}.scheduled_updates`,
this.props.value.scheduled_updates.filter((s, ind) => ind !== index));
}

onScheduledUpdateOpen(scheduledUpdate) {
if (!this.state.openScheduledUpdates.includes(scheduledUpdate.scheduled_update_id)) {
this.setState({openScheduledUpdates: [
...this.state.openScheduledUpdates,
scheduledUpdate.scheduled_update_id,
]});
}
}

onScheduledUpdateClose(scheduledUpdate) {
this.setState({
openScheduledUpdates: this.state.openScheduledUpdates.filter((s) =>
s !== scheduledUpdate.scheduled_update_id
)});
}

render() {
const {
field,
Expand All @@ -97,6 +155,10 @@ export class CoverageForm extends React.Component {
onFieldFocus,
onPopupOpen,
onPopupClose,
planningAllowScheduledUpdates,
onRemoveAssignment,
setCoverageDefaultDesk,
...props
} = this.props;

const contentTypeQcode = get(value, 'planning.g2_content_type') || null;
Expand Down Expand Up @@ -262,6 +324,40 @@ export class CoverageForm extends React.Component {
readOnly={roFields.flags}
profileName="flags"
/>

{planningAllowScheduledUpdates && contentTypeQcode === 'text' && (
<Row>
<LineInput><Label text={gettext('SCHEDULED UPDATES')}/></LineInput>
{(value.scheduled_updates || []).map((s, i) => (
<ScheduledUpdate
key={i}
value={s}
field={field}
coverageIndex={index}
index={i}
newsCoverageStatus={newsCoverageStatus}
dateFormat={dateFormat}
timeFormat={timeFormat}
readOnly={readOnly}
contentTypes={contentTypes}
onRemoveAssignment={onRemoveAssignment}
setCoverageDefaultDesk={setCoverageDefaultDesk}
onRemove={this.onRemoveScheduledUpdate.bind(null, i)}
onScheduleChanged={this.onScheduleChanged}
genres={genres}
onClose={this.onScheduledUpdateClose}
onOpen={this.onScheduledUpdateOpen}
openScheduledUpdates={this.state.openScheduledUpdates}
{...fieldProps}
{...props} />
))}
<Button
color="primary"
text={gettext('Schedule an update')}
onClick={this.onAddScheduledUpdate}
/>
</Row>)}

</div>
);
}
Expand Down Expand Up @@ -296,6 +392,11 @@ CoverageForm.propTypes = {
index: PropTypes.number,
onPopupOpen: PropTypes.func,
onPopupClose: PropTypes.func,
preferredCoverageDesks: PropTypes.object,
defaultDesk: PropTypes.object,
planningAllowScheduledUpdates: PropTypes.bool,
onRemoveAssignment: PropTypes.func,
setCoverageDefaultDesk: PropTypes.func,
};

CoverageForm.defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class CoverageFormHeader extends React.Component {
const assignmentState = get(value, 'assigned_to.state');
const cancelled = get(value, 'workflow_status') === 'cancelled';
const canEditAssignment = planningUtils.isCoverageDraft(value) ||
(!!addNewsItemToPlanning && !get(value, 'coverage_id'));
(!!addNewsItemToPlanning && !get(value, 'coverage_id') && !get(value, 'scheduled_update_id'));

if (!deskAssigned && (!userAssigned || !coverageProvider)) {
return (
Expand Down
6 changes: 6 additions & 0 deletions client/components/Coverages/CoverageEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ export const CoverageEditor = ({
onFieldFocus={onFocus}
onPopupOpen={onPopupOpen}
onPopupClose={onPopupClose}
onRemoveAssignment={onRemoveAssignment.bind(null, value, index)}
setCoverageDefaultDesk={setCoverageDefaultDesk}
users={users}
desks={desks}
coverageProviders={coverageProviders}
priorities={priorities}
{...props}
/>
);
Expand Down
13 changes: 11 additions & 2 deletions client/components/Coverages/CoverageIcon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const CoverageIcon = ({
const user = getItemInArrayById(users, get(coverage, 'assigned_to.user'));
const desk = getItemInArrayById(desks, get(coverage, 'assigned_to.desk'));
const assignmentStr = desk ? gettext('Desk: ') + desk.name : gettext('Status: Unassigned');
const scheduledStr = get(coverage, 'planning.scheduled') && dateFormat && timeFormat ?
let scheduledStr = get(coverage, 'planning.scheduled') && dateFormat && timeFormat ?
moment(coverage.planning.scheduled).format(dateFormat + ' ' + timeFormat) : null;
const state = getItemWorkflowStateLabel(get(coverage, 'assigned_to'));
const genre = get(coverage, 'planning.genre.name', '');
Expand All @@ -40,6 +40,15 @@ export const CoverageIcon = ({
{genre && <span><br />{gettext('Genre: ') + genre}</span>}
{slugline && <span><br />{gettext('Slugline: ') + slugline}</span>}
{scheduledStr && <span><br />{gettext('Due: ') + scheduledStr}</span>}
{(get(coverage, 'scheduled_updates') || []).map((s) => {
if (get(s, 'planning.scheduled')) {
scheduledStr = dateFormat && timeFormat ?
moment(s.planning.scheduled).format(dateFormat + ' ' + timeFormat) : null;
return (<span><br />{gettext('Due: ') + scheduledStr}</span>);
}

return null;
})}
</Tooltip>
}>
<span className="sd-list-item__inline-icon icn-mix sd-list-item__item-type">
Expand All @@ -50,7 +59,7 @@ export const CoverageIcon = ({
<i className={classNames(
planningUtils.getCoverageIcon(
planningUtils.getCoverageContentType(coverage, contentTypes) ||
get(coverage, 'planning.g2_content_type')),
get(coverage, 'planning.g2_content_type'), coverage),
planningUtils.getCoverageIconColor(coverage),
'sd-list-item__inline-icon')}/>
</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Row as PreviewRow} from '../../UI/Preview';
import moment from 'moment-timezone';
import {get} from 'lodash';
import {getCreator, getItemInArrayById, gettext} from '../../../utils';
import {StateLabel} from '../../index';

export const CoveragePreviewTopBar = ({
item,
coverage,
users,
desks,
newsCoverageStatus,
dateFormat,
timeFormat,
}) => {
const userAssigned = getCreator(coverage, 'assigned_to.user', users);
const deskAssigned = desks.find((d) =>
d._id === get(coverage, 'assigned_to.desk'));

const coverageProvider = get(coverage, 'assigned_to.coverage_provider');
/* eslint-disable camelcase */
const {
assignor_user,
assignor_desk,
assigned_date_user,
assigned_date_desk,
} = get(coverage, 'assigned_to', {});

const deskAssignor = getItemInArrayById(users, assignor_desk);
const userAssignor = getItemInArrayById(users, assignor_user);
const assignmentPriority = get(coverage, 'assigned_to.priority');
let coverageTopBar = (<PreviewRow><label>Unassigned</label></PreviewRow>);

if (deskAssigned || userAssigned) {
coverageTopBar = (
<div>
<div className="TimeAndAuthor">
{ deskAssigned && <div>
{gettext('Desk')}:&nbsp;
<span className="TimeAndAuthor__author">{deskAssigned.name.toUpperCase()}</span>
{' (' + moment(assigned_date_desk).format(timeFormat + ' ' + dateFormat) + ', ' +
get(deskAssignor, 'display_name', '').toUpperCase() + ')'}
</div> }
{ userAssigned && <div>
{gettext('Assignee')}&nbsp;
<span className="TimeAndAuthor__author">
{get(userAssigned, 'display_name', '').toUpperCase()}</span>
{' (' + moment(assigned_date_user).format(timeFormat + ' ' + dateFormat) + ', ' +
get(userAssignor, 'display_name', '').toUpperCase() + ')'}
</div> }
{ coverageProvider && <span> {gettext('Coverage Provider: ') + coverageProvider.name} </span>}
</div>
<PreviewRow>
<span className={'line-input priority-label priority-label--' + assignmentPriority}>
{assignmentPriority}</span>
<StateLabel item={coverage.assigned_to}
verbose={true}
className="pull-right"/>
</PreviewRow>
</div>
);
}

return coverageTopBar;
};

CoveragePreviewTopBar.propTypes = {
coverage: PropTypes.object,
users: PropTypes.array,
desks: PropTypes.array,
newsCoverageStatus: PropTypes.array,
dateFormat: PropTypes.string,
timeFormat: PropTypes.string,
formProfile: PropTypes.object,
noOpen: PropTypes.bool,
active: PropTypes.bool,
scrollInView: PropTypes.bool,
onClick: PropTypes.func,
inner: PropTypes.bool,
index: PropTypes.number,
item: PropTypes.object,
planningAllowScheduledUpdates: PropTypes.bool,
};


CoveragePreviewTopBar.defaultProps = {
dateFormat: 'DD/MM/YYYY',
timeFormat: 'HH:mm',
};

0 comments on commit 5784395

Please sign in to comment.