Commit 52c414ea authored by hh1966's avatar hh1966
Browse files

Clean up research plan component and add ResearchPlanDetailsAttachments

parent f8e778d4
Pipeline #50581 failed with stage
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Dropzone from 'react-dropzone';
import {
FormGroup, ControlLabel, FormControl, Panel, ListGroup, ListGroupItem,
ButtonToolbar, Button, Tooltip, OverlayTrigger, Glyphicon, Row, Col, Tabs, Tab
} from 'react-bootstrap';
import { Panel, ListGroup, ListGroupItem, ButtonToolbar, Button, Tooltip, OverlayTrigger, Row, Col, Tabs, Tab } from 'react-bootstrap';
import SVG from 'react-inlinesvg';
import { includes, last, findKey, values } from 'lodash';
import ElementCollectionLabels from './ElementCollectionLabels';
......@@ -15,37 +11,16 @@ import ElementActions from './actions/ElementActions';
import DetailActions from './actions/DetailActions';
import ResearchPlansFetcher from './fetchers/ResearchPlansFetcher';
import ResearchPlansLiteratures from './DetailsTabLiteratures';
import QuillEditor from './QuillEditor';
import Attachment from './models/Attachment';
import Utils from './utils/Functions';
import EditorFetcher from './fetchers/EditorFetcher';
import SpinnerPencilIcon from './common/SpinnerPencilIcon';
import ImageModal from './common/ImageModal';
import LoadingActions from './actions/LoadingActions';
import ResearchPlanDetailsAttachments from './research_plan/ResearchPlanDetailsAttachments';
import ResearchPlanDetailsBody from './research_plan/ResearchPlanDetailsBody';
import ResearchPlanDetailsNameField from './research_plan/ResearchPlanDetailsNameField';
import ResearchPlanDetailsStatic from './research_plan/ResearchPlanDetailsStatic';
const editorTooltip = exts => <Tooltip id="editor_tooltip">Available extensions: {exts}</Tooltip>;
const downloadTooltip = <Tooltip id="download_tooltip">Download attachment</Tooltip>;
const imageStyle = {
style: {
position: 'absolute',
width: 60,
height: 60
}
};
const previewImage = (attachment) => {
const noAttSvg = '/images/wild_card/no_attachment.svg';
if (attachment.thumb) {
return `/images/thumbnail/${attachment.identifier}`;
}
return noAttSvg;
};
export default class ResearchPlanDetails extends Component {
constructor(props) {
......@@ -53,18 +28,8 @@ export default class ResearchPlanDetails extends Component {
const { research_plan } = props;
this.state = {
research_plan,
showStructureEditor: false,
loadingMolecule: false,
attachmentEditor: false,
extension: null,
edit: false
edit: true
};
this.editorInitial = this.editorInitial.bind(this);
}
componentDidMount() {
this.editorInitial();
}
componentWillReceiveProps(nextProps) {
......@@ -72,15 +37,7 @@ export default class ResearchPlanDetails extends Component {
this.setState({ research_plan });
}
editorInitial() {
EditorFetcher.initial()
.then((result) => {
this.setState({
attachmentEditor: result.installed,
extension: result.ext
});
});
}
// handle functions
handleSubmit() {
const { research_plan } = this.state;
......@@ -98,210 +55,6 @@ export default class ResearchPlanDetails extends Component {
}
}
documentType(filename) {
const { extension } = this.state;
const ext = last(filename.split('.'));
const docType = findKey(extension, o => o.includes(ext));
if (typeof (docType) === 'undefined' || !docType) {
return null;
}
return docType;
}
dropzone() {
return (
<Dropzone
onDrop={files => this.handleFileDrop(files)}
style={{ height: 50, width: '100%', border: '3px dashed lightgray' }}
>
<div style={{ textAlign: 'center', paddingTop: 12, color: 'gray' }}>
Drop Files, or Click to Select.
</div>
</Dropzone>
);
}
handleFileDrop(files) {
const { research_plan } = this.state;
research_plan.changed = true;
const attachments = files.map(f => Attachment.fromFile(f));
research_plan.attachments = research_plan.attachments.concat(attachments);
this.setState({ research_plan });
}
handleUndo(attachment) {
const { research_plan } = this.state;
const index = research_plan.attachments.indexOf(attachment);
research_plan.attachments[index].is_deleted = false;
this.setState({ research_plan });
}
handleEdit(attachment) {
const { research_plan } = this.state;
const fileType = last(attachment.filename.split('.'));
const docType = this.documentType(attachment.filename);
EditorFetcher.startEditing({ attachment_id: attachment.id })
.then((result) => {
if (result.token) {
const url = `/editor?id=${attachment.id}&docType=${docType}&fileType=${fileType}&title=${attachment.filename}&key=${result.token}`;
window.open(url, '_blank');
attachment.aasm_state = 'oo_editing';
attachment.updated_at = new Date();
research_plan.attachments.map((a) => {
if (a.id === attachment.id) return attachment;
})
this.setState({ research_plan });
this.forceUpdate();
} else {
alert('Unauthorized to edit this file.');
}
});
}
attachments() {
const { research_plan } = this.state;
if (research_plan.attachments && research_plan.attachments.length > 0) {
return (
<ListGroup>
{research_plan.attachments.map(attachment => {
return (
<ListGroupItem key={attachment.id}>
{this.listGroupItem(attachment)}
</ListGroupItem>
)
})}
</ListGroup>
);
}
return (
<div style={{ padding: 5 }}>
There are currently no Datasets.<br />
</div>
);
}
listGroupItem(attachment) {
const updateTime = new Date(attachment.updated_at);
updateTime.setTime(updateTime.getTime() + (15 * 60 * 1000));
const hasPop = false;
const fetchNeeded = false;
const fetchId = attachment.id;
const previewImg = previewImage(attachment);
const isEditing = attachment.aasm_state === 'oo_editing' && new Date().getTime() < updateTime
const { attachmentEditor } = this.state;
const docType = this.documentType(attachment.filename);
const editDisable = !attachmentEditor || isEditing || attachment.is_new || docType === null
const styleEditorBtn = !attachmentEditor || docType === null ? 'none' : ''
if (attachment.is_deleted) {
return (
<div>
<Row>
<Col md={10}>
<strike>{attachment.filename}</strike>
</Col>
<Col md={2}>
<Button
bsSize="xsmall"
bsStyle="success"
disabled
>
</Button>{' '}
<Button
bsSize="xsmall"
bsStyle="danger"
onClick={() => this.handleUndo(attachment)}
>
<i className="fa fa-undo" />
</Button>
</Col>
</Row>
</div>
);
}
return (
<div>
<Row>
<Col md={1}>
<div className="analysis-header order" style={{ width: '60px', height: '60px' }}>
<div className="preview" style={{ width: '60px', height: '60px' }} >
<ImageModal
imageStyle={imageStyle}
hasPop={hasPop}
preivewObject={{
src: previewImg
}}
popObject={{
title: attachment.filename,
src: previewImg,
fetchNeeded,
fetchId
}}
/>
</div>
</div>
</Col>
<Col md={9}>
{attachment.filename}
</Col>
<Col md={2}>
{this.removeAttachmentButton(attachment)}
<OverlayTrigger placement="top" overlay={downloadTooltip} >
<Button
bsSize="xsmall"
className="button-right"
bsStyle="primary"
onClick={() => this.handleAttachmentDownload(attachment)}
>
<i className="fa fa-download" />
</Button>
</OverlayTrigger>
<OverlayTrigger placement="left" overlay={editorTooltip(values(this.state.extension).join(','))} >
<Button
style={{ display: styleEditorBtn }}
bsSize="xsmall"
className="button-right"
bsStyle="success"
disabled={editDisable}
onClick={() => this.handleEdit(attachment)}
>
<SpinnerPencilIcon spinningLock={!attachmentEditor || isEditing} />
</Button>
</OverlayTrigger>
</Col>
</Row>
</div>
);
}
handleAttachmentRemove(attachment) {
const { research_plan } = this.state;
const index = research_plan.attachments.indexOf(attachment);
research_plan.changed = true;
research_plan.attachments[index].is_deleted = true;
this.setState({ research_plan });
}
removeAttachmentButton(attachment) {
return (
<Button bsSize="xsmall" bsStyle="danger" className="button-right" onClick={() => this.handleAttachmentRemove(attachment)}>
<i className="fa fa-trash-o" />
</Button>
);
}
handleAttachmentDownload(attachment) {
Utils.downloadFile({contents: `/api/v1/attachments/${attachment.id}`, name: attachment.filename});
}
handleSelect(eventKey) {
UIActions.selectTab({tabKey: eventKey, type: 'screen'});
this.setState({
......@@ -309,16 +62,26 @@ export default class ResearchPlanDetails extends Component {
})
}
toggleEdit() {
let {edit} = this.state
this.setState({
edit: !edit
});
}
// handle name actions
handleNameChange(value) {
let {research_plan} = this.state
research_plan.changed = true
research_plan.name = value
this.setState({
research_plan: research_plan
});
this.setState({ research_plan });
}
// handle body actions
handleBodyChange(value, id) {
let {research_plan} = this.state
let index = research_plan.body.findIndex(field => field.id == id)
......@@ -326,9 +89,7 @@ export default class ResearchPlanDetails extends Component {
research_plan.body[index].value = value
research_plan.changed = true
this.setState({
research_plan: research_plan
});
this.setState({ research_plan });
}
handleBodyDrop(source, target) {
......@@ -337,9 +98,7 @@ export default class ResearchPlanDetails extends Component {
research_plan.body.splice(target, 0, research_plan.body.splice(source, 1)[0]);
research_plan.changed = true
this.setState({
research_plan: research_plan
});
this.setState({ research_plan });
}
handleBodyAdd(type) {
......@@ -348,9 +107,7 @@ export default class ResearchPlanDetails extends Component {
research_plan.addBodyField(type)
research_plan.changed = true
this.setState({
research_plan: research_plan
});
this.setState({ research_plan });
}
handleBodyDelete(id) {
......@@ -360,21 +117,65 @@ export default class ResearchPlanDetails extends Component {
research_plan.body.splice(index, 1)
research_plan.changed = true
this.setState({ research_plan });
}
// handle attachment actions
handleAttachmentDrop(files) {
let { research_plan } = this.state;
research_plan.changed = true;
files.map(file => {
let attachment = Attachment.fromFile(file)
research_plan.attachments.push(attachment)
})
this.setState({ research_plan });
}
handleAttachmentDelete(attachment) {
let { research_plan } = this.state;
let index = research_plan.attachments.indexOf(attachment);
research_plan.changed = true;
research_plan.attachments[index].is_deleted = true;
this.setState({ research_plan });
}
handleAttachmentUndoDelete(attachment) {
let { research_plan } = this.state;
let index = research_plan.attachments.indexOf(attachment);
research_plan.attachments[index].is_deleted = false;
this.setState({
research_plan: research_plan
});
}
toggleEdit() {
let {edit} = this.state
handleAttachmentDownload(attachment) {
Utils.downloadFile({contents: `/api/v1/attachments/${attachment.id}`, name: attachment.filename});
}
this.setState({
edit: !edit
});
handleAttachmentEdit(attachment) {
const { research_plan } = this.state;
// update only this attachment
research_plan.attachments.map((current_attachment) => {
if (current_attachment.id === attachment.id) return attachment;
})
this.setState({ research_plan });
this.forceUpdate();
}
// render functions
renderResearchPlanInfo(research_plan) {
const style = {height: 'auto'};
return (
<Row style={style}>
<Col md={6}>
......@@ -385,29 +186,29 @@ export default class ResearchPlanDetails extends Component {
}
renderPropertiesTab(research_plan) {
const { name, body } = research_plan;
const submitLabel = research_plan.isNew ? "Create" : "Save";
let { name, body, attachments } = research_plan;
let submitLabel = research_plan.isNew ? "Create" : "Save";
return (
<ListGroup fill="true">
<ListGroupItem>
<ResearchPlanDetailsNameField value={name} disabled={research_plan.isMethodDisabled('name')}
onChange={this.handleNameChange.bind(this)} />
<ResearchPlanDetailsNameField value={name}
disabled={research_plan.isMethodDisabled('name')}
onChange={this.handleNameChange.bind(this)} />
<ResearchPlanDetailsBody body={body}
disabled={research_plan.isMethodDisabled('body')}
onChange={this.handleBodyChange.bind(this)}
onDrop={this.handleBodyDrop.bind(this)}
onAdd={this.handleBodyAdd.bind(this)}
onDelete={this.handleBodyDelete.bind(this)} />
<Row>
<Col md={12}>
<FormGroup>
<ControlLabel>Files</ControlLabel>
{this.attachments()}
{this.dropzone()}
</FormGroup>
</Col>
</Row>
disabled={research_plan.isMethodDisabled('body')}
onChange={this.handleBodyChange.bind(this)}
onDrop={this.handleBodyDrop.bind(this)}
onAdd={this.handleBodyAdd.bind(this)}
onDelete={this.handleBodyDelete.bind(this)} />
<ResearchPlanDetailsAttachments attachments={attachments}
onDrop={this.handleAttachmentDrop.bind(this)}
onDelete={this.handleAttachmentDelete.bind(this)}
onUndoDelete={this.handleAttachmentUndoDelete.bind(this)}
onDownload={this.handleAttachmentDownload.bind(this)}
onEdit={this.handleAttachmentEdit.bind(this)} />
</ListGroupItem>
</ListGroup>
);
......
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Dropzone from 'react-dropzone'
import { FormGroup, Button, Row, Col, Tooltip, ControlLabel, ListGroup, ListGroupItem, OverlayTrigger } from 'react-bootstrap'
import { includes, last, findKey, values } from 'lodash'
import EditorFetcher from '../fetchers/EditorFetcher'
import ImageModal from '../common/ImageModal';
import SpinnerPencilIcon from '../common/SpinnerPencilIcon';
const editorTooltip = exts => <Tooltip id="editor_tooltip">Available extensions: {exts}</Tooltip>
const downloadTooltip = <Tooltip id="download_tooltip">Download attachment</Tooltip>
const imageStyle = {
style: {
position: 'absolute',
width: 60,
height: 60
}
}
const previewImage = (attachment) => {
const noAttSvg = '/images/wild_card/no_attachment.svg';
if (attachment.thumb) {
return `/images/thumbnail/${attachment.identifier}`;
}
return noAttSvg;
}
export default class ResearchPlanDetailsAttachments extends Component {
constructor(props) {
super(props);
const { attachments, onDrop, onDelete, onUndoDelete, onDownload, onEdit } = props;
this.state = {
attachments,
onDrop,
onDelete,
onUndoDelete,
onDownload,
onEdit,
attachmentEditor: false,
extension: null,
};
this.editorInitial = this.editorInitial.bind(this);
}
componentDidMount() {
this.editorInitial();
}
editorInitial() {
EditorFetcher.initial()
.then((result) => {
this.setState({
attachmentEditor: result.installed,
extension: result.ext
});
});
}
documentType(filename) {
const { extension } = this.state;
const ext = last(filename.split('.'));
const docType = findKey(extension, o => o.includes(ext));
if (typeof (docType) === 'undefined' || !docType) {
return null;
}
return docType;
}
handleEdit(attachment) {
const { onEdit } = this.state;
const fileType = last(attachment.filename.split('.'));
const docType = this.documentType(attachment.filename);
EditorFetcher.startEditing({ attachment_id: attachment.id })
.then((result) => {
if (result.token) {
const url = `/editor?id=${attachment.id}&docType=${docType}&fileType=${fileType}&title=${attachment.filename}&key=${result.token}`;
window.open(url, '_blank');
attachment.aasm_state = 'oo_editing';
attachment.updated_at = new Date();
onEdit(attachment)
} else {
alert('Unauthorized to edit this file.');
}
});
}
renderRemoveAttachmentButton(attachment) {
const { onDelete } = this.state;
return (
<Button bsSize="xsmall" bsStyle="danger" className="button-right" onClick={() => on