Commit 3161b719 authored by Maxime Chaillet's avatar Maxime Chaillet

Change repository name Event -> Logbook for react components. Display newly...

Change repository name Event -> Logbook for react components. Display newly arrived event count in the event list menu.
parent 2882bd96
import { DOC_VIEW } from '../../constants/EventTypes';
import React from 'react'
import ReactDOM from 'react-dom'
import { Grid, Row, OverlayTrigger, Tooltip } from 'react-bootstrap'
import PropTypes from 'prop-types'
import _ from 'lodash'
import Moment from 'moment'
import { getOriginalEvent } from '../../helpers/EventHelpers'
import Event from './Event';
import UserMessage from '../UserMessage';
import { INFO_MESSAGE_TYPE } from '../../constants/UserMessages';
require("./event.css");
const VERTICAL_BAR_EXTRA_LENGTH_ON_TOP = 10;
const VERTICAL_BAR_EXTRA_LENGTH_ON_BOTTOM = 15;
const VERTICAL_BAR_LENGTH_UNIT = 'px';
/**
* The list of the all events
*/
class EventList extends React.Component {
constructor(props) {
super(props)
this.state = {
verticalBarLength: '0px',
events: this.props.events, // the displayed (ie filtered) event list. The original list is in this.props.eventList
}
this.adjustVerticalBarLength = this.adjustVerticalBarLength.bind(this);
// this.onEventUpdated = this.onEventUpdated.bind(this);
}
/**
* Executed after all the events have been properly displayed
*/
componentDidMount() {
this.adjustVerticalBarLength();
}
componentDidUpdate() {
this.adjustVerticalBarLength();
}
render() {
const {
events,
investigationId,
onEventUpdated,
onEventClicked,
reverseEventsSortingByCreationDate,
selectedEventId,
user,
updateEvent,
view,
} = this.props;
/**
* Get existing events grouped by date
*
*/
function getGroupedEvents() {
//Groups events by day. For each group (=each day), events are still sorted from the youngest to the oldest
//groupedSortedEvents is an object : { "Aug 3":{ [{json event1}, {json event2}] }, "Aug 1":{ [{json event3}] } }
let groupedSortedEvents = _.groupBy(events, function (o) {
return Moment(getOriginalEvent(o).creationDate).format("MMMM D, YYYY");
});
// Iterates over each of the properties of the groupedSortedEvents object and create an array containing React components
let eventsAndDates = [];
let dateKey = 0;
for (var propertyName in groupedSortedEvents) {
eventsAndDates.push(
<div key={dateKey}>
<TimeAxisMarks userView={view} value={propertyName} />
{groupedSortedEvents[propertyName].map(
(event, eventKey) => {
return (<div key={eventKey}>
<Event
event={event}
investigationId={investigationId}
isSelected={event._id && event._id === selectedEventId}
onEventUpdated={onEventUpdated}
onEventClicked={onEventClicked}
reverseEventsSortingByCreationDate={reverseEventsSortingByCreationDate}
user={user}
updateEvent={updateEvent}
view={view}
/>
</div>)
}
)}
</div>
)
dateKey += 1;
}
return eventsAndDates;
}
let verticalBarLengthOntop = VERTICAL_BAR_EXTRA_LENGTH_ON_TOP + VERTICAL_BAR_LENGTH_UNIT;
if (!events || events.length === 0) {
return (
<Grid fluid={true} ref={(element) => { this.grid = element }} >
<Row style={{ marginLeft: '30px', marginRight: '30px', marginTop: '20px' }}>
<UserMessage
message="The logbook is currently empty."
isTextCentered={true}
type={INFO_MESSAGE_TYPE}
/>
</Row>
</Grid >
)
}
return (
<Grid fluid={true} ref={(element) => { this.grid = element }} >
<Row style={{ height: verticalBarLengthOntop }}>
<div ref={(element) => this.verticalBar = element}
style={{
/* this is the vertical line */
content: '',
position: 'relative',
height: this.state.verticalBarLength,
width: '2px',
background: '#CCCCCC',
zIndex: '1',
marginLeft: '120px',
}} />
</Row>
{getGroupedEvents()}
</Grid >
)
}
/**
* Determine the grid height and deduce the vertical bar height.
*/
adjustVerticalBarLength() {
var node = ReactDOM.findDOMNode(this.grid);
if (node) {
let gridLength = node.clientHeight;
let barLengthValue = gridLength + VERTICAL_BAR_EXTRA_LENGTH_ON_BOTTOM;
let barLength = barLengthValue + VERTICAL_BAR_LENGTH_UNIT;
if (barLength !== this.state.verticalBarLength) {
this.setState({ verticalBarLength: barLength });
}
}
}
}
const TimeAxisMarks = (props) => {
let userView = props.userView; // the view the user has choosen 'doc' or 'monitoring'
if (userView === DOC_VIEW) {
/* to be removed */
return (<div style={{ marginTop: '10px', marginBottom: '10px' }}>
<OverlayTrigger placement="right" overlay={<Tooltip id="tooltip"> <p> Events created on {props.value} </p> </Tooltip>}>
<span style={{ color: '#999999', marginLeft: '10px', fontStyle: 'italic', fontSize: '16px' }}> <strong> {Moment(props.value).format("MMM D")} </strong> </span>
</OverlayTrigger>
</div>)
} else {
// used in 'monitoring view'
return (<div style={{ marginTop: '10px', marginBottom: '10px' }}>
<OverlayTrigger placement="right" overlay={<Tooltip id="tooltip"> <p> Events created on {props.value} </p> </Tooltip>}>
<span style={{ color: '#999999', marginLeft: '0px', fontStyle: 'italic', fontSize: '16px' }}> <strong> {Moment(props.value).format("MMM D")} </strong> </span>
</OverlayTrigger>
</div>
)
}
}
EventList.propTypes = {
/** the array of unsorted events as provided by the ICAT+ server */
events: PropTypes.array,
/** the investigationId the provided events belong to. */
investigationId: PropTypes.string.isRequired,
/* Callback function triggered when the user updated an event */
onEventUpdated: PropTypes.func,
/** the callback functon which refresh the event list */
reloadEvents: PropTypes.func.isRequired,
/** callback function which reverse the sorting of events by date */
reverseEventsSortingByCreationDate: PropTypes.func.isRequired,
/** the user who is currently logged in */
user: PropTypes.object.isRequired,
/* the selected view to display the events in*/
view: PropTypes.string.isRequired,
}
export default EventList;
import React from 'react';
import { Well, Navbar, Nav, NavItem, MenuItem, DropdownButton, Radio } from 'react-bootstrap';
import EventListMenuButton from './EventListMenuButton';
import { getPDF } from '../../api/icat/icatPlus';
import EventListMenuButton from '../EventListMenuButton';
import { getPDF } from '../../../api/icat/icatPlus';
import { ComboSearch } from 'react-combo-search';
import PropTypes from 'prop-types';
import _ from 'lodash';
import { DOC_VIEW, LIST_VIEW, SORT_EVENTS_FROM_YOUNGEST, SORT_EVENTS_FROM_OLDEST, NEW_EVENT_VISIBLE } from '../../constants/EventTypes';
import { DOC_VIEW, LIST_VIEW, SORT_EVENTS_FROM_YOUNGEST, SORT_EVENTS_FROM_OLDEST, NEW_EVENT_VISIBLE } from '../../../constants/EventTypes';
import PeriodicRefresherDialogue from './NewlyAvailableEventsDialogue';
import NewlyAvailableEventsDialogue from './NewlyAvailableEventsDialogue';
// styles
require('./react-datetime.css');
......@@ -47,7 +49,7 @@ class EventListMenu extends React.Component {
expanded={this.state.isNavbarExpanded}
onSelect={this.onSelectNavbar}
onToggle={this.onToggleNavbar}
style={{ background: 'none', border: 'none', WebkitBoxShadow: 'none', boxShadow: 'none', position: 'sticky', top: '0px', backgroundColor:'white' }}>
style={{ background: 'none', border: 'none', WebkitBoxShadow: 'none', boxShadow: 'none', position: 'sticky', top: '0px', backgroundColor: 'white' }}>
<Navbar.Header>
<Navbar.Toggle />
......@@ -78,6 +80,12 @@ class EventListMenu extends React.Component {
</NavItem>
<NavItem eventKey={3} href={getPDF(sessionId, investigationId, selectionFilter)} target='_blank' className="logbookNavItem" >
<EventListMenuButton text='PDF' glyph='download' tooltipText='Download as PDF' isEnabled={!(isNewEventVisible === undefined || isNewEventVisible === true || numberOfMatchingEventsFound === 0)} />
</NavItem>
<NavItem>
<NewlyAvailableEventsDialogue eventCount={this.props.periodicData} />
</NavItem>
</Nav>
......
import React from 'react';
class NewlyAvailableEventsDialogue extends React.Component {
render() {
return (<span style={{ color: '#888888' }}>
{this.props.newlyArrivedEventCount ? this.props.newlyArrivedEventCount : 0} new logs available.
</span>)
}
}
export default NewlyAvailableEventsDialogue;
\ No newline at end of file
import React from 'react';
import OverlayBox from '../OverlayBox';
class availableEventsDialogue extends React.Component {
render() {
return (<span style={{ color: '#888888' }}>
{this.props.newlyArrivedEventCount ? this.props.newlyArrivedEventCount : 0} new logs available.
</span>)
}
}
export default availableEventsDialogue;
\ No newline at end of file
......@@ -5,22 +5,21 @@ import PropTypes from 'prop-types';
import { getEventsByInvestigationId, getEventCountByInvestigationId, createEvent, updateEvent } from '../../api/icat/icatPlus.js';
import _ from 'lodash';
import EventList from '../../components/Event/List/EventList';
import EventListMenu from '../../components/Event/EventListMenu.js';
import EventList from '../../components/Logbook/List/EventList';
import EventListMenu from '../../components/Logbook/Menu/EventListMenu.js';
import { NEW_EVENT_INVISIBLE, NEW_EVENT_VISIBLE, LIST_VIEW, ERROR_MESSAGE_TYPE, EDIT_EVENT_CONTEXT, EDIT_EVENT_VISIBLE, EDIT_EVENT_INVISIBLE, NEW_EVENT_CONTEXT } from '../../constants/EventTypes.js';
import EventPager from '../../components/Event/EventPager.js';
import EventPager from '../../components/Logbook/EventPager.js';
import { convertPageToPageEventIndexes, isRangeAvailableOnTheClient } from '../../helpers/PaginationHelper.js';
import { GUI_CONFIG } from '../../config/gui.config.js';
import Loading from '../../components/Loading.js';
import UserMessage from '../../components/UserMessage.js';
import { getSelectionFiltersBySearchCriteria, getSelectionFiltersForMongoQuery } from './SelectionFilterHelper.js';
import { clearAvailableTagAction } from '../../actions/logbook.js';
import OverlayBox from '../../components/Event/OverlayBox.js';
import NewOrEditEventPanel from '../../components/Event/NewOrEditEventPanel.js';
import "../../components/Event/event.css"
import OverlayBox from '../../components/Logbook/OverlayBox.js';
import NewOrEditEventPanel from '../../components/Logbook/NewOrEditEventPanel.js';
import "../../components/Logbook/event.css"
import { INFO_MESSAGE_TYPE } from '../../constants/UserMessages.js';
import PeriodicRefresher from './PeriodicRefresher.js';
import PeriodicRefresherDialogue from './PeriodicRefresherDialogue.js';
/**
* This class represents the event container component. It's role is to retrieve events from the server asynchronuously.
......@@ -80,20 +79,30 @@ class LogbookContainer extends React.Component {
<UserMessage type={ERROR_MESSAGE_TYPE} message={this.state.errorMessage} />
</OverlayBox>
<EventListMenu
availableTags={this.props.availableTags}
investigationId={investigationId}
isNewEventVisible={this.state.isNewEventVisible}
numberOfMatchingEventsFound={this.state.foundEventCount}
reverseEventsSortingByCreationDate={this.reverseEventsSortingByCreationDate}
searchEvents={this.searchEvents}
sessionId={user.sessionId}
setNewEventVisibility={this.setNewEventVisibility}
setView={this.setView}
sortingFilter={this.state.sortingFilter}
selectionFilter={selectionFilter}
view={this.state.view}
/>
<PeriodicRefresher
initialValue={this.state.pageEvents.length}
intervalInMilliSeconds={2000}
isEnabled={true}
periodicCallback={(onSuccess, onFailure) => this.getEventsByInvestigationIdCopy(investigationId, user.sessionId, GUI_CONFIG().EVENTS_PER_DOWNLOAD, 0, selectionFilter, GUI_CONFIG().DEFAULT_SORTING_FILTER, onFailure, onSuccess)}>
<EventListMenu
availableTags={this.props.availableTags}
investigationId={investigationId}
isNewEventVisible={this.state.isNewEventVisible}
numberOfMatchingEventsFound={this.state.foundEventCount}
reverseEventsSortingByCreationDate={this.reverseEventsSortingByCreationDate}
searchEvents={this.searchEvents}
sessionId={user.sessionId}
setNewEventVisibility={this.setNewEventVisibility}
setView={this.setView}
sortingFilter={this.state.sortingFilter}
selectionFilter={selectionFilter}
view={this.state.view}
/>
</PeriodicRefresher>
<OverlayBox open={this.state.isNewEventVisible === true || (this.state.eventBeingEdited ? true : false)} classNames={{ overlay: 'newOrEditOverlayClass', modal: 'newOrEditModalClass' }}>
<div style={{ height: this.getPanelHeightInPercent(NEW_EVENT_CONTEXT), paddingBottom: this.state.eventBeingEdited ? '5px' : '0px' }}>
......@@ -134,18 +143,9 @@ class LogbookContainer extends React.Component {
{(this.state.pageEvents.length === 0) ?
<UserMessage message='The logbook is empty.' type={INFO_MESSAGE_TYPE} isTextCentered={true} />
:
<div>
<PeriodicRefresher isEnabled={true}
initialValue={this.state.pageEvents.length}
intervalInMilliSeconds={2000}
periodicCallback={(onSuccess, onFailure) => this.getEventsByInvestigationIdCopy(investigationId, user.sessionId, GUI_CONFIG().EVENTS_PER_DOWNLOAD, 0, selectionFilter, GUI_CONFIG().DEFAULT_SORTING_FILTER, onFailure, onSuccess)}>
<PeriodicRefresherDialogue />
</PeriodicRefresher>
<EventList
events={this.state.pageEvents}
onEventClicked={this.onEventClicked} />
</div>
: <EventList
events={this.state.pageEvents}
onEventClicked={this.onEventClicked} />
}
<EventPager
......
......@@ -9,7 +9,7 @@ class PeriodicRefresher extends React.Component {
render() {
let timer = setTimeout(() => { return this.props.periodicCallback(this.onSuccess, this.onFailure) }, this.props.intervalInMilliSeconds);
return React.cloneElement(this.props.children, { data: this.state.data, initialValue: this.props.initialValue });
return React.cloneElement(this.props.children, { periodicData: this.state.data, initialValue: this.props.initialValue });
}
onSuccess = (data) => {
......
import React from 'react';
import OverlayBox from '../../components/Event/OverlayBox';
class PeriodicRefresherDialogue extends React.Component {
render() {
return (<div>
<OverlayBox open={true} onClose={() => null} showCloseIcon={false} classNames={{ overlay: 'autoRefreshOverlayClass', modal: 'autoRefreshModalClass' }}>
There are {this.props.data ? this.props.data.length : this.props.initialValue} events in the list
</OverlayBox>
</div>)
}
}
export default PeriodicRefresherDialogue;
\ No newline at end of file
......@@ -2,12 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import axios from 'axios';
import { connect } from 'react-redux';
import TagList from '../components/Event/Tag/TagList';
import TagEditor from '../components/Event/Tag/TagEditor';
import TagList from '../components/Logbook/Tag/TagList';
import TagEditor from '../components/Logbook/Tag/TagEditor';
import { getTagsByInvestigationId, createTagsByInvestigationId, updateTagsByInvestigationId } from '../api/icat/icatPlus';
import _ from 'lodash';
import { INFO_MESSAGE_TYPE, ERROR_MESSAGE_TYPE, TAG_MANAGER_CONTEXT, BASIC_EVENT_CONTEXT, TAG_EDITOR_CONTEXT, TAG_CREATOR_CONTEXT, NEW_EVENT_CONTEXT, EDIT_EVENT_CONTEXT, FETCHED_STATUS, FETCHING_STATUS } from '../constants/EventTypes';
import TagActionBar from '../components/Event/Tag/TagActionBar';
import TagActionBar from '../components/Logbook/Tag/TagActionBar';
import UserMessage from '../components/UserMessage';
import { SUCCESS_MESSAGE_TYPE } from '../constants/UserMessages';
import { setAvailableTagAction } from '../actions/logbook';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment