parent 1023c372
......@@ -12,7 +12,7 @@ export function fetchDatasetsByDOI(sessionId, doi) {
}
export function fetchDatasetsByInvestigationId(sessionId, investigationId) {
return function (dispatch) {
return function (dispatch) {
dispatch( {
type: FECTH_DATASETS_BY_INVESTIGATION,
payload: axios.get(getDatasetByInvestigationId(sessionId, investigationId))
......
......@@ -30,6 +30,10 @@ class EventPager extends React.Component {
let paginationItemArray = [];
let totalPages = this.getTotalPageNumber();
if (totalPages <= 1){
return null;
}
for (let number = 1; number <= totalPages; number++) {
paginationItemArray.push(
<Pagination.Item key={number} id={number} onClick={() => (this.props.onPageClicked(number))} active={number === this.props.activePage}>{number}</Pagination.Item>
......
import React from 'react';
import { getEventIcon } from '../../../helpers/EventHelpers';
class CategoryIcon extends React.Component {
render() {
return (<div className='pull-left' style={{ paddingRight: '8px' }}>
{getEventIcon(this.props.category, "14")}
</div>)
}
}
export default CategoryIcon;
\ No newline at end of file
import React from 'react';
import PropTypes from 'prop-types';
import { getOriginalEvent, getEventIcon, getLastCommentContent, getEventCreationDate, getEventHistoryCreationDate, getContent } from '../../../helpers/EventHelpers'
import EventContentDisplayer from '../EventContentDisplayer';
import { ANNOTATION, NOTIFICATION, EDIT_MODE, DOC_VIEW, EDIT_EVENT_CONTEXT, BASIC_EVENT_CONTEXT } from '../../../constants/EventTypes';
import { OverlayTrigger, Tooltip, Dropdown, MenuItem } from 'react-bootstrap';
/**
* A basic event of the logbook
*/
class ContentEvent extends React.Component {
render() {
let { event, toggleMode, view, investigationId } = this.props;
let paddingTopDate = (event.type === ANNOTATION) ? '6px' : '0px';
if (event.content && (getContent(event.content, 'plainText') !== null || getContent(event.content, 'html') !== null)) {
if (event.type === ANNOTATION) {
return <AnnotationContent event={event} view={view} />
} else if (event.type === NOTIFICATION) {
return <NotificationContent event={event} />
}
}
return <div>
<div style={{ flexGrow: '1', marginLeft: '10px', paddingTop: '4px', paddingBottom: '4px' }} >
<Icon event={event} view={view} />
</div>
<p style={{ margin: '0px', color: '#777777', fontStyle: 'italic' }}>This event has no content</p>
</div>;
}
}
ContentEvent.propTypes = {
/* the event object as received from the ICAT+ server */
event: PropTypes.object.isRequired,
/** whether this event is selected or not */
isSelected: PropTypes.bool,
/* callback functon which trigger a change to another mode */
toggleMode: PropTypes.func.isRequired,
/** the user who is currently logged in */
user: PropTypes.object.isRequired,
/* the current view */
view: PropTypes.string.isRequired,
}
const AnnotationContent = (props) => {
let { event, view } = props;
return (
<div style={{ flexGrow: '1', marginLeft: '10px', paddingTop: '4px', paddingBottom: '4px' }} >
<Icon event={event} view={view} />
<EventContentDisplayer
content={event.content}
useRichTextEditor={false}
isEditionMode={false}
/>
</div>
)
}
const NotificationContent = (props) => {
let { event } = props;
let notificationMessage = getOriginalEvent(event);
let renderedCommentContent;
let lastCommentContent = getLastCommentContent(event);
if (lastCommentContent) {
renderedCommentContent = () => (
<div style={{ clear: 'left', marginLeft: '30px', paddingTop: '8px', paddingBottom: '8px' }}>
{/* <div className='pull-left' style={{ paddingRight: '8px' }}>
{getEventIcon('comment', "20")}
</div> */}
<EventContentDisplayer
content={lastCommentContent}
useRichTextEditor={false}
isEditionMode={false}
/>
</div>)
} else { renderedCommentContent = () => null }
return (
<div style={{ flexGrow: '1', marginLeft: '10px' }} >
<div className='pull-left' style={{ paddingRight: '8px' }}>
{getEventIcon(event.category, "20")}
</div>
<div id='divContainingHTMLNotificationInDocView'>
<EventContentDisplayer
content={notificationMessage.content}
useRichTextEditor={false}
isEditionMode={false}
/>
</div>
{renderedCommentContent()}
</div>)
}
const Icon = (props) => {
let { event, view } = props;
if (view && view === DOC_VIEW) {
return null
}
return (<div className='pull-left' style={{ paddingRight: '8px' }}>
{getEventIcon(event.category, "20")}
</div>
)
}
export default ContentEvent;
\ No newline at end of file
import React from 'react';
import CategoryIcon from './CategoryIcon.js';
/**
* A basic event of the logbook
*/
class EventIcon extends React.Component {
render() {
return <CategoryIcon category={this.props.event.category}></CategoryIcon>
}
}
export default EventIcon;
\ No newline at end of file
import { DOC_VIEW } from '../../../constants/EventTypes';
import React from 'react';
import PropTypes from 'prop-types'
import _ from 'lodash'
import Moment from 'moment'
import { Col, Table, Row, OverlayTrigger, Tooltip } from 'react-bootstrap'
import { getContent, convertImagesToThumbnails } from '../../../helpers/EventHelpers';
import { getOriginalEvent } from '../../../helpers/EventHelpers'
import UserMessage from '../../UserMessage';
import { INFO_MESSAGE_TYPE } from '../../../constants/UserMessages';
import EventIcon from './EventIcon.js';
import LazyLoad from 'react-lazyload';
import EventTimeLine from './EventTimeLine.js';
require("./eventList.css");
/**
* The list of the all events
*/
class EventList extends React.Component {
/** Returns the list of items to be displayed in the table: events + days */
getItems(){
var items = [];
var lastDate = null; // format DDMMYYYY
for (var i = 0; i < this.props.events.length; i++){
var date = Moment(getOriginalEvent(this.props.events[i]).creationDate).format("MMMM Do YYYY");
if (date !== lastDate){
lastDate = date;
items.push({text: date, type : "date", anchor:date});
}
items.push(this.props.events[i]);
}
return items;
}
render() {
if (!this.props.events || this.props.events.length === 0) {
return null;
}
return (
<div>
<div class="sidenav">
<EventTimeLine events={this.props.events}></EventTimeLine>
</div>
<div class="main">
<Table responsive style={{ fontSize:'12px'}} >
<tbody style={{borderRight:'1px solid #F9F9F9'}}> { this.getItems().map((event, index) => {
if (event.type === "date"){
return <tr><td style={{ borderTop:'1px solid #f2f2f2', textAlign:'center', fontSize:'18px', fontWeight:'bold'}} colSpan={3} ><a name={event.anchor}></a> {event.text}</td></tr>;
}
return <tr>
<td style={{ width: '16px', borderTop:'0'}}>
<EventIcon event={event} />
</td>
<td style={{ width: '16px', borderTop:'0', borderRight:'1px solid #f2f2f2'}}>
<OverlayTrigger placement="right" overlay={<Tooltip id="tooltip"> <p> Events created on {Moment(getOriginalEvent(event).creationDate).format("MMMM Do YYYY, h:mm:ss a")} </p> </Tooltip>}>
<span style={{ cursor: 'pointer', color: '#999999', margin: '0px' }}>{Moment(getOriginalEvent(event).creationDate).format("HH:mm:ss")} </span>
</OverlayTrigger>
</td>
<td style={{border:0}}>
<LazyEvent event={event} />
</td>
</tr>
})}</tbody>
</Table>
</div>
</div>
)
}
}
class LazyEvent extends React.Component {
getHTMLContent(event){
return getContent(event.content, 'html') ? convertImagesToThumbnails(getContent(event.content, 'html')) : convertImagesToThumbnails(getContent(event.content, 'plainText'));
}
render(){
var content = this.getHTMLContent(this.props.event);
if (content.indexOf("img") !== -1){
/** For performance only events with images are lazy loaded */
return <LazyLoad once>
<div dangerouslySetInnerHTML={{ __html : content}} />
</LazyLoad>;
}
return <div dangerouslySetInnerHTML={{ __html : content}} />;
}
}
EventList.propTypes = {
/** the array of unsorted events as provided by the ICAT+ server */
events: PropTypes.array
}
export default EventList;
import { DOC_VIEW } from '../../../constants/EventTypes';
import React from 'react';
import PropTypes from 'prop-types'
import _ from 'lodash'
import Moment from 'moment'
import { getContent, convertImagesToThumbnails } from '../../../helpers/EventHelpers';
import { getOriginalEvent } from '../../../helpers/EventHelpers'
import { VerticalTimeline, VerticalTimelineElement } from 'react-vertical-timeline-component';
import 'react-vertical-timeline-component/style.min.css';
import { getEventIcon } from '../../../helpers/EventHelpers';
import CategoryIcon from './CategoryIcon.js';
require("./eventList.css");
/**
* The list of the all events
*/
class EventTimeLine extends React.Component {
/** Returns the list of days and statistics */
getItems(){
var days = {};
for (var i = 0; i < this.props.events.length; i++){
var date = Moment(getOriginalEvent(this.props.events[i]).creationDate).format("MMMM Do YYYY");
if (days[date] == null){
days[date] = {
commandLine : 0,
info : 0,
error : 0,
comment : 0
};
}
switch(this.props.events[i].category){
case "commandLine":
days[date].commandLine = days[date].commandLine + 1;
break;
case "error":
days[date].error = days[date].error + 1;
break;
case "info":
days[date].info = days[date].info + 1;
break;
case "comment":
days[date].comment = days[date].comment + 1;
break;
}
}
return days;
}
getNode(category, items, key){
if (items[key][category] != 0){
return <td style={{width:'25px'}}>
{items[key][category]}
</td>;
}
return <td style={{width:'25px'}}></td>;
}
getNodeIcon(category, items, key){
if (items[key][category] != 0){
return <td style={{width:'25px'}}>
<CategoryIcon category={category}> </CategoryIcon>
</td>;
}
return <td style={{width:'25px'}}></td>;
}
render() {
if (!this.props.events || this.props.events.length === 0) {
return null;
}
var items = this.getItems();
return (
<VerticalTimeline layout='one-column' className='vertical-time-line-custom-general' animate={false}>
{ Object.keys(items).map((key, index) => {
return <VerticalTimelineElement
className="vertical-timeline-element-custom"
iconStyle={{ top:'15px',left:'15px',width:'10px', height:'10px', background: 'rgb(33, 150, 243)', color: '#ccc' }}>
<div>
<a href={'#' + key}> {key}</a>
<table style={{fontSize:'14px'}}>
<tr>
{this.getNode("commandLine", items, key)}
{this.getNodeIcon("commandLine", items, key)}
{this.getNode("error", items, key)}
{this.getNodeIcon("error", items, key)}
{this.getNode("info", items, key)}
{this.getNodeIcon("info", items, key)}
{this.getNode("comment", items, key)}
{this.getNodeIcon("comment", items, key)}
</tr>
</table>
</div>
</VerticalTimelineElement>;
})}
</VerticalTimeline>
)
}
}
EventTimeLine.propTypes = {
/** the array of unsorted events as provided by the ICAT+ server */
events: PropTypes.array
}
export default EventTimeLine;
.sidenav {
height: 60%;
width: 400px;
position: fixed;
z-index: 1;
top: 70;
bottom : 70;
right: 0;
overflow-y: auto;
padding-top: 5px;
margin-right: 25px;
margin-bottom: 25px;
}
.sidenav a {
padding: 6px 8px 6px 16px;
text-decoration: none;
font-size: 10px;
color: #818181;
display: block;
}
.sidenav a:hover {
color: black;
}
.main {
margin-right: 400px; /* Same as the width of the sidenav */
padding: 0px 10px;
}
@media screen and (max-height: 450px) {
.sidenav {padding-top: 15px;}
.sidenav a {font-size: 18px;}
}
.vertical-timeline::before {
/* this is the vertical line */
content: '';
position: absolute;
top: 0;
left: 18px;
height: 100%;
width: 4px;
background: #F2F2F2;
}
.vertical-timeline-element-content::before {
content: '';
position: absolute;
top: 16px;
right: 100%;
height: 0;
width: 0;
border: 7px solid transparent;
border-right: 7px solid #F2F2F2;
}
.vertical-timeline-element-content {
position: relative;
margin-left: 60px;
background: #F2F2F2;
border-radius: .25em;
padding: 1em;
-webkit-box-shadow: 0 3px 0 #ddd;
box-shadow: 0 3px 0 #ddd;
}
.vertical-time-line-custom-general{
font-size : 8px;
line-height : 1;
width : 200px;
}
.vertical-timeline-element-custom{
font-size : 8px;
height : 45px;
width : 250px;
}
......@@ -5,11 +5,11 @@
export function GUI_CONFIG() {
return {
/** Number of logbook events to display per page. EVENTS_PER_PAGE should be lower than EVENTS_PER_DOWNLOAD */
EVENTS_PER_PAGE: 200,
EVENTS_PER_PAGE: 3000,
/** Maximum number of logbook events downloaded from the server. This enables to store a larger set of
events than those required for a single page thus reducing HTTP requests to the server. */
EVENTS_PER_DOWNLOAD: 400,
EVENTS_PER_DOWNLOAD: 6000,
/** Default sorting filter for logbook events.
The format:
......
......@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { URL } from '../api/icat/icatPlus.js';
import _ from 'lodash';
import EventList from '../components/Event/EventList';
import EventList from '../components/Event/List/EventList';
import EventMessage from '../components/Event/EventMessage.js';
import EventActionBar from '../components/Event/EventActionBar.js';
import NewEvent from '../components/Event/NewEvent.js';
......
......@@ -65,9 +65,10 @@ export function getEventHistoryCreationDate(event) {
* @return {event} the original event. If there is no original event (ie the provided event is the original event) returns itself.
*/
export function getOriginalEvent(event) {
let e = event;
if (e) {
while (e.previousVersionEvent) {
while (e.previousVersionEvent) {
e = e.previousVersionEvent;
}
return e;
......@@ -145,7 +146,12 @@ export function getLastCommentContent(event) {
return null;
}
/**
* Images to thumbnails
*/
export function convertImagesToThumbnails(html) {
return html.replace(new RegExp('img src', 'g'), 'img style="height:200px;width:auto" src');
};
/**
* Get the first letters of the event content.
* @param {array} content the event content
......
import React from 'react';
import { render } from 'react-dom'
import { PersistGate } from 'redux-persist/integration/react'
import { createStore, applyMiddleware } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import { Provider } from 'react-redux'
import logger from 'redux-logger'
import thunk from 'redux-thunk'
import reducer from './reducers'
import promise from "redux-promise-middleware"
import { render } from 'react-dom';
import { PersistGate } from 'redux-persist/integration/react';
import { createStore, applyMiddleware } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { Provider } from 'react-redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import reducer from './reducers';
import promise from "redux-promise-middleware";
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
......
......@@ -14,10 +14,12 @@ const datasets = (state = initialState, action) => {
switch (action.type) {
case FECTH_DATASETS_BY_INVESTIGATION_PENDING:{
debugger
state = {...state, data: [], fetched: false, fetching: true};
break;
}
case FECTH_DATASETS_BY_INVESTIGATION_FULFILLED:{
debugger
state = {...state, data : action.payload.data.reverse(), fetched : true, fetching : false};
break;
}
......
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