Commit 56d2c7dc authored by Maxime Chaillet's avatar Maxime Chaillet

Merge react component for editing with react component for creating event.

parent 684452e3
This diff is collapsed.
This diff is collapsed.
import React from 'react';
import { EDIT_EVENT_CONTEXT, ANNOTATION, NOTIFICATION, NEW_EVENT_CONTEXT } from '../../constants/EventTypes';
import { Label, FormControl, InputGroup } from 'react-bootstrap';
import EventContentDisplayer from './EventContentDisplayer';
import PropTypes from 'prop-types';
import { getOriginalEvent, getPreviousVersionNumber, } from '../../helpers/EventHelpers';
import HTMLEditor from './HTMLEditor';
class EventContentPanel extends React.Component {
render() {
let { event, context, investigationId, user, onEventDataChanged } = this.props;
if (context === EDIT_EVENT_CONTEXT) {
if (event) {
if (event.type === ANNOTATION) {
return (<AnnotationContent
canEnableSaveButton={onEventDataChanged}
event={event}
investigationId={investigationId}
setTitleInput={this.setTitleInput}
user={user}
/>);
}
if (event.type === NOTIFICATION) {
return (<NotificationContent
canEnableSaveButton={onEventDataChanged}
event={event}
investigationId={investigationId}
user={user}
/>);
}
}
} else if (context === NEW_EVENT_CONTEXT) {
return (<HTMLEditor
canEnableSaveButton={onEventDataChanged}
investigationId={investigationId}
isEditionMode={true}
user={user}
/>
);
}
return null;
}
}
export default EventContentPanel;
EventContentPanel.proptype = {
/* investigation identifier */
investigationId: PropTypes.string,
/* user who is using this component */
user: PropTypes.object,
}
/**
* React component which represents the content of an annotation
* @param {*} props the props passed to this component
*/
class AnnotationContent extends React.Component {
constructor(props) {
super(props);
this.state = {
inputTitleValue: this.props.event.title || ''
};
this.onChangeInputValue = this.onChangeInputValue.bind(this);
}
onChangeInputValue(e) {
this.setState({ inputTitleValue: e.target.value });
}
render() {
let { event, investigationId, user } = this.props;
if (event.type && event.type === ANNOTATION) {
return (
<div>
<EventContentDisplayer
content={event.content}
useRichTextEditor={true}
isEditionMode={true}
user={user}
investigationId={investigationId}
canEnableSaveButton={this.props.canEnableSaveButton}
/>
<div style={{ paddingTop: '8px' }}>
<InputGroup>
<InputGroup.Addon style={{ backgroundColor: 'transparent', border: 'none' }}> <Label> Title </Label> </InputGroup.Addon>
<FormControl
type='text'
value={this.state.inputTitleValue}
onChange={this.onChangeInputValue}
placeholder='Optional title here'
ref={this.props.setTitleInput}
/>
</InputGroup>
</div>
</div>
);
};
}
componentDidUpdate() {
this.props.canEnableSaveButton();
}
}
AnnotationContent.propTypes = {
/* the callback function which activates the save button */
canEnableSaveButton: PropTypes.func.isRequired,
/* The event to be shown */
event: PropTypes.object.isRequired,
/* the investigationId */
investigationId: PropTypes.string.isRequired,
/* the callback function which adds a ref to this */
setTitleInput: PropTypes.func.isRequired,
/* the user */
user: PropTypes.object.isRequired
};
/**
* React component which represents the content of an notification
* @param {*} props the props passed to this component
*/
const NotificationContent = (props) => {
let { event, investigationId, user } = props;
let notificationMessage = getOriginalEvent(event);
if (event.type && event.type === NOTIFICATION) {
let editorContent;
if (getPreviousVersionNumber(event) === 0) {
let fakeContent = [{ format: 'html', text: '<p> </p>' }];
editorContent = (<EventContentDisplayer
content={fakeContent}
useRichTextEditor={true}
isEditionMode={true}
user={user}
investigationId={investigationId}
canEnableSaveButton={props.canEnableSaveButton}
/>);
} else {
editorContent = (<EventContentDisplayer
content={event.content}
useRichTextEditor={true}
isEditionMode={true}
user={user}
investigationId={investigationId}
canEnableSaveButton={props.canEnableSaveButton}
/>);
};
return (
<div>
<div style={{ marginLeft: '10px' }} >
<div id='divContainingHTMLNotificationInDocView'>
<EventContentDisplayer
content={notificationMessage.content}
useRichTextEditor={false}
isEditionMode={false}
/>
</div>
</div>
{editorContent}
</div>
);
};
};
NotificationContent.propTypes = {
/* the callback function which activates the save button */
canEnableSaveButton: PropTypes.func.isRequired,
/* The event to be shown */
event: PropTypes.object.isRequired,
/* the investigationId */
investigationId: PropTypes.string.isRequired,
/* the user */
user: PropTypes.object.isRequired
};
\ No newline at end of file
......@@ -3,7 +3,7 @@ import { Row, Glyphicon, Panel } from 'react-bootstrap'
import PropTypes from 'prop-types'
import { getEventIcon, getEventHistoryCreationDate } from '../../helpers/EventHelpers'
import { NEW_EVENT_CONTEXT, EVENT_HISTORY_ORIGINAL_VERSION_CONTEXT, EVENT_HISTORY_MIDDLE_VERSION_CONTEXT, EVENT_HISTORY_LATEST_VERSION_CONTEXT } from '../../constants/EventTypes';
import { NEW_EVENT_CONTEXT, EVENT_HISTORY_ORIGINAL_VERSION_CONTEXT, EVENT_HISTORY_MIDDLE_VERSION_CONTEXT, EVENT_HISTORY_LATEST_VERSION_CONTEXT, EDIT_EVENT_CONTEXT } from '../../constants/EventTypes';
/**
* A class which represents the event header. This component renders something different in the header based on the context prop.
......@@ -40,7 +40,7 @@ class EventHeader extends React.Component {
&nbsp; Written on {getEventHistoryCreationDate(event)} (latest version), the current comment is:
</div>
</Row>
</Panel.Heading>)
</Panel.Heading>)
} else if (context === EVENT_HISTORY_MIDDLE_VERSION_CONTEXT) {
return (<Panel.Heading >
<Row>
......@@ -59,6 +59,8 @@ class EventHeader extends React.Component {
return (<Panel.Heading >
<b> New comment </b>
</Panel.Heading>)
} else if (context === EDIT_EVENT_CONTEXT) {
return (<Panel.Heading> <b> Edit </b> </Panel.Heading>);
}
}
}
......
import React from 'react';
import { Row, Col, OverlayTrigger, Popover, Button, Well, Label } from 'react-bootstrap';
import { getPreviousVersionNumber, } from '../../helpers/EventHelpers';
import Popup from 'reactjs-popup';
import Moment from 'moment';
import EventVersion from './EventVersion';
/**
* React component which renders event history label and the correwsponding modal.
*/
class EventHistory extends React.Component {
render() {
if (this.props.event) {
return (
<div >
<OverlayTrigger
trigger='click'
placement='top'
overlay={eventVersionsPopover(this.props.event)}
rootClose={true} >
<Label> History <div className='arrow-down' /> </Label>
</OverlayTrigger>
</div>);
}
return null;
}
}
/**
* Render the popup which displays all event versions of the event
* @param {*} event the event under investigation
*/
function eventVersionsPopover(event) {
let e = event;
/* build the different lines representing the previous versions */
let lines = [];
while (e.previousVersionEvent != null) {
if (lines.length === 0) {
lines.push(<EventVersionItem event={e} type='edition' mostRecent={true} />);
} else {
lines.push(<EventVersionItem event={e} type='edition' mostRecent={false} />);
};
e = e.previousVersionEvent;
}
lines.push(<EventVersionItem event={e} type='creation' />);
/* build the popover */
return (<Popover id='mypopover'>
<div>
<p style={{ marginBottom: '5px' }}> This log has {getPreviousVersionNumber(event) + 1} versions. </p>
<hr style={{ marginTop: '5px' }} />
{lines.map((item, index) => <Popup
key={index}
trigger={<div> {item} </div>}
modal
closeOnDocumentClick={false}
contentStyle={{ width: '90%', height: '90%' }}
>
{close => (
<div className='fullPage'>
<a className='close' onClick={close}> &times; </a>
<div className='content'>
<EventVersion
event={item.props.event}
isLatestVersion={(index === 1) ? true : false}
isOriginalVersion={(index === lines.length - 1) ? true : false}
/>
</div>
</div>
)}
</Popup>
)}
<hr style={{ marginBottom: '5px' }} />
<Popup
trigger={<div style={{ display: 'inline' }}> <Button bsStyle='link'> See all versions </Button> </div>}
modal
closeOnDocumentClick={false}
contentStyle={{ width: '90%', height: '90%' }}
>
{close => (
<div className='fullPage'>
<a className='close' onClick={close}> &times; </a>
<div className='content'>
<CompleteEventHistory event={event} />
</div>
</div>
)}
</Popup>
</div>
</Popover>);
}
/**
* React component which represents the commentBy part.
* @param {*} props
*/
const CommentBy = (props) => {
let { event } = props;
if (getPreviousVersionNumber(event) === 0) {
return (
<div>
<Label> Created by </Label>
<div style={{ paddingTop: '5px', paddingLeft: '10px' }}>
<p style={{ color: '#666666' }}> {props.event.username} </p>
</div>
<hr id='hrEvents' />
</div>
);
};
if (getPreviousVersionNumber(event) !== 0) {
return (
<div>
<Label> Commented by </Label>
<div style={{ paddingTop: '5px', paddingLeft: '10px' }}>
<p style={{ color: '#666666' }}> {props.event.username} </p>
</div>
<hr id='hrEvents' />
</div>
);
};
return null;
};
/**
* React component which represents on item of the event history
* @param {*} props the props passed to this component
*/
class EventVersionItem extends React.Component {
render() {
function buildTheDisplay(event, type, mostRecent) {
return (
<Well bsSize='small' style={{ marginBottom: '5px', cursor: 'pointer' }}>
<b> {event.username} </b>
{type === 'creation' ? 'created on ' : ''}
{type === 'edition' ? 'edited on ' : ''}
{Moment(event.creationDate).format('MMMM DD HH:mm')}
{mostRecent === true ? ' (most recent)' : ''}
{event.machine ? <span style={{ color: '#777777', fontStyle: 'italic' }}> from {event.machine} </span> : ''}
</Well>
);
};
return (
buildTheDisplay(this.props.event, this.props.type, this.props.mostRecent)
);
};
};
/**
* React component which displays the complete event history
*/
const CompleteEventHistory = (props) => {
let { event } = props;
let eventVersions = [];
/* first push the current event which is the latest version of the event*/
//eventHistoryItems.push(<EventListViewExpanded event={event} isFullPage={true} />)
eventVersions.push(<EventVersion event={event} isLatestVersion={true} />);
/* then display the previous version of this event */
while (event.previousVersionEvent != null) {
event = event.previousVersionEvent;
eventVersions.push(<div class='margin-bottom-10'>
<Row>
<Col xs={1}> </Col>
<Col xs={11}>
<EventVersion event={event} isOriginalVersion={event.previousVersionEvent === null ? true : false} />
</Col>
</Row>
</div>);
};
return eventVersions;
};
export default EventHistory;
\ No newline at end of file
......@@ -29,14 +29,14 @@ class NewEvent extends React.Component {
render() {
const { investigationId, user, onNewEventUploaded } = this.props;
if (this.state.status === UPLOADED_STATUS) {
this.setState({ status: IDLE_STATUS });
onNewEventUploaded()
return null;
} else {
// if (this.state.status === UPLOADED_STATUS) {
// this.setState({ status: IDLE_STATUS });
// onNewEventUploaded()
// return null;
// } else {
return (
<Panel bsStyle='primary' style={{ marginBottom: '0px' }}>
<EventHeader context={NEW_EVENT_CONTEXT} />
{/* <EventHeader context={NEW_EVENT_CONTEXT} /> */}
<div>
<div style={{ padding: '1px' }}>
<HTMLEditor
......@@ -84,62 +84,62 @@ class NewEvent extends React.Component {
</Panel>
)
}
// }
}
changeStateToUploaded = () => {
this.setState({ status: UPLOADED_STATUS })
}
/** Fuction triggered when the user clicks the 'cancel' button */
onCancelNewEventClicked = () => {
localStorage.removeItem('plainText');
localStorage.removeItem('HTMLText');
this.props.setNewEventVisibility(NEW_EVENT_INVISIBLE);
}
/**
* Callback method called when the editor content changed from empty to not empty and vice versa.
* @param {*} change the object representing the change
*/
canEnableSaveButton = (change) => {
if (change) {
if ("hasText" in change) {
this.setState({ hasText: change.hasText })
}
}
}
/**
* Create the new event
*/
createEvent = () => {
let investigationId = this.props.investigationId;
let currentTagIds = this.tagContainer.state.selectedTags.map((tag) => tag._id);
let newEvent = {
category: EVENT_CATEGORY_COMMENT,
content: [
{
format: "plainText",
text: localStorage.getItem('plainText')
},
{
format: "html",
text: localStorage.getItem('HTMLText')
}
],
creationDate: Date(),
investigationId: investigationId,
title: '', //this.inputTitle.value,
tag: currentTagIds,
type: ANNOTATION,
username: this.props.user.username,
}
this.props.createEvent(newEvent, this.props.user.sessionId, this.props.investigationId, this.changeStateToUploaded)
}
// /** Fuction triggered when the user clicks the 'cancel' button */
// onCancelNewEventClicked = () => {
// localStorage.removeItem('plainText');
// localStorage.removeItem('HTMLText');
// this.props.setNewEventVisibility(NEW_EVENT_INVISIBLE);
// }
// /**
// * Callback method called when the editor content changed from empty to not empty and vice versa.
// * @param {*} change the object representing the change
// */
// canEnableSaveButton = (change) => {
// if (change) {
// if ("hasText" in change) {
// this.setState({ hasText: change.hasText })
// }
// }
// }
// /**
// * Create the new event
// */
// createEvent = () => {
// let investigationId = this.props.investigationId;
// let currentTagIds = this.tagContainer.state.selectedTags.map((tag) => tag._id);
// let newEvent = {
// category: EVENT_CATEGORY_COMMENT,
// content: [
// {
// format: "plainText",
// text: localStorage.getItem('plainText')
// },
// {
// format: "html",
// text: localStorage.getItem('HTMLText')
// }
// ],
// creationDate: Date(),
// investigationId: investigationId,
// title: '', //this.inputTitle.value,
// tag: currentTagIds,
// type: ANNOTATION,
// username: this.props.user.username,
// }
// this.props.createEvent(newEvent, this.props.user.sessionId, this.props.investigationId, this.changeStateToUploaded)
// }
setTagContainer = (element) => {
this.tagContainer = element
......
import React from 'react';
import PropTypes from 'prop-types';
import { Panel, Label, Glyphicon, OverlayTrigger, Tooltip, Grid, Row, Col } from 'react-bootstrap';
import EventHeader from './EventHeader';
import EventFooter from './EventFooter';
import TagContainer from '../../containers/TagContainer';
import EventContentPanel from './EventContentPanel';
import { EDIT_EVENT_INVISIBLE, NEW_EVENT_CONTEXT, EDIT_EVENT_CONTEXT, NEW_EVENT_INVISIBLE, EVENT_CATEGORY_COMMENT, ANNOTATION } from '../../constants/EventTypes';
import EventHistory from './EventHistory';
import { getEventCreationDate, getEventHistoryCreationDate, getOriginalEvent, getPreviousVersionNumber } from '../../helpers/EventHelpers';
/**
* React component which renders a Panel for creating of editing an event
*/
class NewOrEditEventPanel extends React.Component {
constructor(props) {
super(props);
this.state = {
hasText: false //whether the editor contains some text or not
}
}
render() {
let { event, user, context, investigationId, setEditEventVisibility } = this.props;
return (
<Panel bsStyle='primary' style={{ marginBottom: '0px' }}>
<EventHeader context={context} />
<div>
<div style={{ padding: '1px' }}>
<EventContentPanel
context={context}
event={event}
investigationId={investigationId}
onEventDataChanged={this.onEventDataChanged}
user={user}
/>
</div>
<div>
<Label style={{ margin: '6px 6px 6px 12px', paddingRight: '2px' }}> Tags </Label>
<OverlayTrigger
placement='top'
overlay={<Tooltip id='tooltip'> <p> Manage tags </p> </Tooltip>}>
<a href={"/investigation/" + this.props.investigationId + "/events/tagManager"} target="_blank" style={{ fontSize: '12px' }}> <Glyphicon glyph='cog' /> </a>
</OverlayTrigger>
<TagContainer
canEnableSaveButton={this.onEventDataChanged}
context={context}
event={event}
investigationId={this.props.investigationId}
setTagContainer={this.setTagContainer}
/>
</div>
<Grid fluid>
<Row>
<Col xs={4}> <CreationDate event={event} /> </Col>
<Col xs={4}> <CommentBy event={event} /> </Col>
<Col xs={4}> <EventHistory event={event} /> </Col>
</Row>
</Grid>
</div>
<EventFooter
isSaveButtonEnabled={this.state.hasText}
onCancelButtonClicked={() => this.onCancelButtonClicked()}
onSaveButtonClicked={() => this.onSaveButtonClicked()} />
</Panel>
)
}
/**
* Callback method called when current event data has just changed. This could original from a key press in the editor, a change in tags, ...
* @param {*} change the object representing the change
*/
onEventDataChanged = (change) => {
if (change) {
if ("hasText" in change) {
this.setState({ hasText: change.hasText })
}
}
}
/**
* Callback function triggered when the user clicks on the cancel button
*/
onCancelButtonClicked = () => {
if (this.props.context) {
if (this.props.context === NEW_EVENT_CONTEXT) {