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

[SDESK-4767] Feature to attach files to coverages #1403

Merged
merged 3 commits into from
Dec 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions client/actions/planning/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -960,15 +960,16 @@ const fetchPlanningFiles = (planning) => (
return Promise.resolve();
}

const filesToFetch = planningUtils.getPlanningFiles(planning);
const filesInStore = selectors.general.files(getState());

if (every(planning.files, (f) => f in filesInStore)) {
if (every(filesToFetch, (f) => f in filesInStore)) {
return Promise.resolve();
}

return api('planning_files').query(
{
where: {$and: [{_id: {$in: planning.files}}]},
where: {$and: [{_id: {$in: filesToFetch}}]},
}
)
.then((data) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {gettext, stringUtils, assignmentUtils} from '../../../utils';
import {InternalNoteLabel} from '../../';
import {ContactsPreviewList} from '../../Contacts/index';
import {Row} from '../../UI/Preview';
import {FileReadOnlyList} from '../../UI';

// eslint-disable-next-line complexity
export const AssignmentPreview = ({
Expand All @@ -15,6 +16,8 @@ export const AssignmentPreview = ({
coverageFormProfile,
planningFormProfile,
planningItem,
files,
createLink,
}) => {
const planning = get(assignment, 'planning', {});

Expand Down Expand Up @@ -86,11 +89,25 @@ export const AssignmentPreview = ({
<Row
enabled={get(coverageFormProfile, 'editor.internal_note.enabled')}
label={gettext('Internal Note')}
noPadding={true}
>
<InternalNoteLabel item={planning} showTooltip={false}/>
<p>{stringUtils.convertNewlineToBreak(planning.internal_note || '-')}</p>
</Row>

<Row
enabled={get(coverageFormProfile, 'editor.files.enabled')}
label={gettext('ATTACHMENTS')}
noPadding={true}
>
<FileReadOnlyList
formProfile={coverageFormProfile}
files={files}
item={planning}
createLink={createLink}
noToggle />
</Row>


</div>
);
};
Expand All @@ -101,4 +118,6 @@ AssignmentPreview.propTypes = {
coverageFormProfile: PropTypes.object,
planningFormProfile: PropTypes.object,
planningItem: PropTypes.object,
files: PropTypes.array,
createLink: PropTypes.func,
};
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {get} from 'lodash';

import * as selectors from '../../../selectors';
import {gettext, stringUtils, timeUtils} from '../../../utils';

import {Datetime} from '../../';
import {Location} from '../../Location';
import {FileReadOnlyList} from '../../UI';
import {Row} from '../../UI/Preview';
import {FileInput, LinkInput} from '../../UI/Form';
import {LinkInput} from '../../UI/Form';
import {ContactsPreviewList} from '../../Contacts';

export const EventPreviewComponent = ({item, formProfile, createLink, streetMapUrl, files}) => {
export const EventPreview = ({item, formProfile, createLink, streetMapUrl, files}) => {
if (!item) {
return null;
}
Expand Down Expand Up @@ -115,22 +113,12 @@ export const EventPreviewComponent = ({item, formProfile, createLink, streetMapU
enabled={get(formProfile, 'editor.files.enabled')}
label={gettext('Attachments')}
>
{get(item, 'files.length', 0) > 0 ? (
<ul>
{get(item, 'files').map((file, index) =>
(<li key={index}>
<FileInput
value={file}
createLink={createLink}
readOnly={true}
files={files}
/>
</li>)
)}
</ul>
) : (
<p><span className="sd-text__info">{gettext('No attached files added.')}</span></p>
)}
<FileReadOnlyList
formProfile={formProfile}
files={files}
item={item}
createLink={createLink}
noToggle />
</Row>

<Row
Expand All @@ -156,19 +144,10 @@ export const EventPreviewComponent = ({item, formProfile, createLink, streetMapU
);
};

EventPreviewComponent.propTypes = {
EventPreview.propTypes = {
item: PropTypes.object,
formProfile: PropTypes.object,
createLink: PropTypes.func,
streetMapUrl: PropTypes.string,
files: PropTypes.object,
};

const mapStateToProps = (state) => ({
createLink: (f) => (selectors.config.getServerUrl(state) + '/upload/' + f.filemeta.media_id + '/raw'),
streetMapUrl: selectors.config.getStreetMapUrl(state),
files: selectors.general.files(state),
});


export const EventPreview = connect(mapStateToProps)(EventPreviewComponent);
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {get} from 'lodash';

import * as selectors from '../../../selectors';
import * as actions from '../../../actions';
import {assignmentUtils, gettext, eventUtils} from '../../../utils';
import {assignmentUtils, gettext, eventUtils, planningUtils} from '../../../utils';
import {ASSIGNMENTS, WORKSPACE} from '../../../constants';

import {AssignmentPreviewHeader} from './AssignmentPreviewHeader';
Expand All @@ -20,6 +20,10 @@ class AssignmentPreviewContainerComponent extends React.Component {
if (eventUtils.shouldFetchFilesForEvent(this.props.eventItem)) {
this.props.fetchEventFiles(this.props.eventItem);
}

if (planningUtils.shouldFetchFilesForPlanning(this.props.planningItem)) {
this.props.fetchPlanningFiles(this.props.planningItem);
}
}

getItemActions() {
Expand Down Expand Up @@ -82,6 +86,9 @@ class AssignmentPreviewContainerComponent extends React.Component {
contentTypes,
session,
privileges,
createLink,
streetMapUrl,
files,
} = this.props;

if (!assignment) {
Expand Down Expand Up @@ -129,6 +136,8 @@ class AssignmentPreviewContainerComponent extends React.Component {
coverageFormProfile={formProfile.coverage}
planningFormProfile={formProfile.planning}
planningItem={planningItem}
createLink={createLink}
files={files}
/>
</ContentBlock>

Expand Down Expand Up @@ -161,6 +170,9 @@ class AssignmentPreviewContainerComponent extends React.Component {
<EventPreview
item={eventItem}
formProfile={formProfile.event}
createLink={createLink}
streetMapUrl={streetMapUrl}
files={files}
/>
</ToggleBox>
</ContentBlock>
Expand Down Expand Up @@ -202,6 +214,10 @@ AssignmentPreviewContainerComponent.propTypes = {
fetchEventFiles: PropTypes.func,
currentWorkspace: PropTypes.string,
contentTypes: PropTypes.array,
fetchPlanningFiles: PropTypes.func,
createLink: PropTypes.func,
streetMapUrl: PropTypes.string,
files: PropTypes.array,
};

const mapStateToProps = (state) => ({
Expand All @@ -223,6 +239,9 @@ const mapStateToProps = (state) => ({
agendas: selectors.general.agendas(state),
currentWorkspace: selectors.general.currentWorkspace(state),
contentTypes: selectors.general.contentTypes(state),
createLink: (f) => (selectors.config.getServerUrl(state) + '/upload/' + f.filemeta.media_id + '/raw'),
streetMapUrl: selectors.config.getStreetMapUrl(state),
files: selectors.general.files(state),
});

const mapDispatchToProps = (dispatch) => ({
Expand All @@ -235,6 +254,7 @@ const mapDispatchToProps = (dispatch) => ({
removeAssignment: (assignment) => dispatch(actions.assignments.ui.showRemoveAssignmentModal(assignment)),
openArchivePreview: (assignment) => dispatch(actions.assignments.ui.openArchivePreview(assignment)),
fetchEventFiles: (event) => dispatch(actions.events.api.fetchEventFiles(event)),
fetchPlanningFiles: (planning) => dispatch(actions.planning.api.fetchPlanningFiles(planning)),
});

export const AssignmentPreviewContainer = connect(
Expand Down
71 changes: 68 additions & 3 deletions client/components/Coverages/CoverageEditor/CoverageForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {get} from 'lodash';
import {getItemInArrayById, gettext, planningUtils, generateTempId, assignmentUtils} from '../../../utils';
import moment from 'moment';
import {WORKFLOW_STATE, DEFAULT_DATE_FORMAT, DEFAULT_TIME_FORMAT, TO_BE_CONFIRMED_FIELD} from '../../../constants';
import {Button} from '../../UI';
import {Row, Label, LineInput} from '../../UI/Form';
import {Button, ToggleBox} from '../../UI';
import {Row, Label, LineInput, FileInput} from '../../UI/Form';
import {ScheduledUpdate} from '../ScheduledUpdate';


Expand All @@ -32,11 +32,18 @@ export class CoverageForm extends React.Component {
this.onRemoveScheduledUpdate = this.onRemoveScheduledUpdate.bind(this);
this.onScheduledUpdateClose = this.onScheduledUpdateClose.bind(this);
this.onScheduledUpdateOpen = this.onScheduledUpdateOpen.bind(this);
this.onAddFiles = this.onAddFiles.bind(this);
this.onRemoveFile = this.onRemoveFile.bind(this);
this.dom = {
contentType: null,
popupContainer: null,
};
this.state = {openScheduledUpdates: []};
this.state = {
openScheduledUpdates: [],
uploading: false,
};
this.filePath = 'value.planning.files';
this.fullFilePath = `coverages[${this.props.index}].planning.files`;
}

componentDidUpdate(prevProps) {
Expand Down Expand Up @@ -142,6 +149,34 @@ export class CoverageForm extends React.Component {
)});
}

onAddFiles(fileList) {
const files = Array.from(fileList).map((f) => [f]);

this.setState({uploading: true});
this.props.uploadFiles(files)
.then((newFiles) => {
this.props.onChange(this.fullFilePath,
[
...get(this.props, this.filePath, []),
...newFiles.map((f) => f._id),
]);
this.setState({uploading: false});
}, () => {
this.props.notifyValidationErrors(['Failed to upload files']);
this.setState({uploading: false});
});
}

onRemoveFile(file) {
const promise = !get(this.props, this.filePath, []).includes(file._id) ?
this.props.removeFile(file) : Promise.resolve();

promise.then(() =>
this.props.onChange(this.fullFilePath,
get(this.props, this.filePath, []).filter((f) => f !== file._id))
);
}

render() {
const {
field,
Expand Down Expand Up @@ -170,6 +205,8 @@ export class CoverageForm extends React.Component {
planningAllowScheduledUpdates,
onRemoveAssignment,
setCoverageDefaultDesk,
createUploadLink,
files,
...props
} = this.props;

Expand Down Expand Up @@ -315,6 +352,29 @@ export class CoverageForm extends React.Component {
{...fieldProps}
/>

{get(formProfile, 'editor.files.enabled') &&
<ToggleBox
isOpen={false}
badgeValue={get(this.props, `${this.filePath}.length`) || null }
title={gettext('Attached Files')}
scrollInView={true}
hideUsingCSS={true} >
<div className={this.state.uploading ? 'sd-loader' : ''}>
{!this.state.uploading && <Field
component={FileInput}
field={`${field}.planning.files`}
profileName="files"
createLink={createUploadLink}
defaultValue={[]}
readOnly={roFields.files}
{...fieldProps}
files={files}
onAddFiles={this.onAddFiles}
onRemoveFile={this.onRemoveFile}
/>}
</div>
</ToggleBox>}

<Field
component={SelectInput}
field={`${field}.news_coverage_status`}
Expand Down Expand Up @@ -430,6 +490,11 @@ CoverageForm.propTypes = {
planningAllowScheduledUpdates: PropTypes.bool,
onRemoveAssignment: PropTypes.func,
setCoverageDefaultDesk: PropTypes.func,
uploadFiles: PropTypes.func,
createUploadLink: PropTypes.func,
removeFile: PropTypes.func,
files: PropTypes.array,
notifyValidationErrors: PropTypes.func,
};

CoverageForm.defaultProps = {
Expand Down
14 changes: 13 additions & 1 deletion client/components/Coverages/CoveragePreview/index.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Row as PreviewRow} from '../../UI/Preview';
import {CollapseBox} from '../../UI';
import {CollapseBox, FileReadOnlyList} from '../../UI';
import {get} from 'lodash';
import {gettext, stringUtils, planningUtils, assignmentUtils} from '../../../utils';
import {ContactsPreviewList} from '../../Contacts/index';
Expand All @@ -28,6 +28,8 @@ export const CoveragePreview = ({
scrollInView,
inner,
planningAllowScheduledUpdates,
files,
createLink,
}) => {
const coverageStatus = get(coverage, 'news_coverage_status.qcode', '') ===
PLANNING.NEWS_COVERAGE_CANCELLED_STATUS.qcode ? PLANNING.NEWS_COVERAGE_CANCELLED_STATUS :
Expand Down Expand Up @@ -146,6 +148,14 @@ export const CoveragePreview = ({
/>
}

{get(formProfile, 'editor.files.enabled') &&
<FileReadOnlyList
formProfile={formProfile}
files={files}
item={coverage.planning}
createLink={createLink} />
}

<PreviewRow
label={gettext('Coverage Status')}
value={coverageStatus.label || ''}
Expand Down Expand Up @@ -214,6 +224,8 @@ CoveragePreview.propTypes = {
index: PropTypes.number,
item: PropTypes.object,
planningAllowScheduledUpdates: PropTypes.bool,
createLink: PropTypes.func,
files: PropTypes.array,
};


Expand Down
3 changes: 2 additions & 1 deletion client/components/Events/EventEditor/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export class EventEditorComponent extends React.Component {
]);
this.setState({uploading: false});
}, () => {
this.notifyValidationErrors('Failed to upload files');
this.props.notifyValidationErrors(['Failed to upload files']);
this.setState({uploading: false});
});
}
Expand Down Expand Up @@ -518,6 +518,7 @@ EventEditorComponent.propTypes = {
onPopupClose: PropTypes.func,
itemManager: PropTypes.object,
original: PropTypes.object,
notifyValidationErrors: PropTypes.func,
};

EventEditorComponent.defaultProps = {
Expand Down
Loading