Commit f8b6b897 authored by Alejandro De Maria Antolinos's avatar Alejandro De Maria Antolinos Committed by Marjolaine Bodin
Browse files

It closes #512

parent b242a84e
......@@ -18,8 +18,9 @@ import SearchPage from './containers/SearchPage';
import OpenDataPage from './containers/OpenData/OpenDataPage';
import ClosedDataPage from './containers/ClosedData/ClosedDataPage';
import DatasetsPage from './containers/DatasetsPage';
import EventTagPage from './containers/EventTagPage';
import TagPage from './containers/TagPage';
import EventsPage from './containers/EventsPage';
import InstrumentLogbookPage from './containers/InstrumentLogbookPage';
import SelectionPage from './containers/Selection/SelectionPage';
import CameraPage from './containers/CameraPage';
import DataStatisticsPage from './containers/DataStatisticsPage';
......@@ -148,6 +149,18 @@ function App() {
<Route exact path="/addresses" component={AddressesPage} />
)}
<Route exact path="/beamline/:instrumentName/events">
<LoadingBoundary message="Loading logbook..." spacedOut>
<InstrumentLogbookPage />
</LoadingBoundary>
</Route>
<Route path="/tag">
<LoadingBoundary message="Loading tags..." spacedOut>
<TagPage />
</LoadingBoundary>
</Route>
<Route path="/investigation">
<LoadingBoundary message="Loading investigation..." spacedOut>
<Route
......@@ -163,10 +176,6 @@ function App() {
}
/>
<Route path="/investigation/:investigationId/events/tagManager">
<EventTagPage />
</Route>
<Route exact path="/investigation/:investigationId/events">
<EventsPage />
</Route>
......
......@@ -5,6 +5,7 @@ import {
SET_AUTOMATIC_COLLAPSING,
SET_AUTOMATIC_REFRESH,
SET_IS_SORTING_LATESTS_EVENTS_FIRST,
SHOW_USER_LOGS_BEAMLINE_LOGBOOK,
} from '../constants/actionTypes';
/**
......@@ -75,3 +76,14 @@ export function setIsSortingLatestEventsFirst(isSortingLatestEventsFirst) {
isSortingLatestEventsFirst,
};
}
/**
* Displays or hides the user's logs (linked to investigations in the logbook of the beamlines)
* @param {*} isVisible
*/
export function setBeamlineLogbookShowUserLogs(isVisible) {
return {
type: SHOW_USER_LOGS_BEAMLINE_LOGBOOK,
isVisible,
};
}
import ICATPLUS from '../../config/icatPlus';
import { getURLParamsByDictionary } from '../../helpers/url';
/**
* Get URL used to update an event on a given investigation on ICAT+
* @param {String} sessionId the session identifier
......@@ -15,17 +15,22 @@ export function getEventURL(
sortBy,
types,
format,
search
search,
instrumentName,
filterInvestigation
) {
const params = new URLSearchParams();
params.set('investigationId', investigationId);
if (limit) params.set('limit', limit);
if (sortBy) params.set('sortBy', sortBy);
if (sortOrder) params.set('sortOrder', sortOrder);
if (types) params.set('types', types);
if (skip) params.set('skip', skip);
if (format) params.set('format', format);
if (search) params.set('search', search);
const params = getURLParamsByDictionary({
investigationId,
limit,
sortBy,
sortOrder,
types,
skip,
format,
search,
instrumentName,
});
params.set('filterInvestigation', filterInvestigation);
return `${ICATPLUS.server}/logbook/${sessionId}/event?${params.toString()}`;
}
......@@ -57,18 +62,14 @@ export function getCountLogbookStatisticsURL(
* @param {string} sessionId session identifier
* @param {*} investigationId investigation identifier
*/
export function getTagsByInvestigationId(sessionId, investigationId) {
return `${ICATPLUS.server}/logbook/${sessionId}/investigation/id/${investigationId}/tag`;
}
/** Create a given tag associated to a given investigation */
export function createTagsByInvestigationId(sessionId) {
return `${ICATPLUS.server}/logbook/${sessionId}/tag/create`;
}
export function getTagURL(sessionId, investigationId, instrumentName, id) {
const params = getURLParamsByDictionary({
investigationId,
instrumentName,
id,
});
/** Update a given tag associated to a given investigation */
export function updateTagsByInvestigationId(sessionId, investigationId, tagId) {
return `${ICATPLUS.server}/logbook/${sessionId}/investigation/id/${investigationId}/tag/id/${tagId}/tag/update`;
return `${ICATPLUS.server}/logbook/${sessionId}/tag?${params.toString()}`;
}
export function createEventFromBase64(sessionId, investigationId) {
......@@ -77,7 +78,7 @@ export function createEventFromBase64(sessionId, investigationId) {
/** Get the statistics of the logbook usage
* @param {string} sessionId session identifier
* @param {string} startDate the startDate of the period
* @param {string} startDate the startDate of the periodr
* @param {string} endDate endDate of the period
*/
export function getStats(sessionId, startDate, endDate) {
......
......@@ -6,7 +6,7 @@ import UI from '../../config/ui';
import { INVESTIGATION_DATE_FORMAT } from '../../constants';
import styles from './InvestigationDateFilter.module.css';
const getHrefByInstrument = (instrumentName) => {
const getStatisticsHrefByInstrument = (instrumentName) => {
try {
if (instrumentName) {
return `/manager/stats/data?instrumentName=${instrumentName.replace(
......@@ -18,6 +18,10 @@ const getHrefByInstrument = (instrumentName) => {
console.log(e);
}
};
const getLogbookHrefByInstrument = (instrumentName) => {
return `/beamline/${instrumentName}/events`;
};
function InvestigationDateFilter(props) {
const {
rootClassName,
......@@ -32,6 +36,7 @@ function InvestigationDateFilter(props) {
instrumentName,
showStatisticsMenu,
withJupyter,
showLogbookMenu,
} = props;
return (
......@@ -77,9 +82,11 @@ function InvestigationDateFilter(props) {
>
<Glyphicon glyph="remove" />
</Button>
{showStatisticsMenu && (
<Button href={getHrefByInstrument(instrumentName)} bsStyle="primary">
<Button
href={getStatisticsHrefByInstrument(instrumentName)}
bsStyle="default"
>
<Glyphicon glyph="signal" style={{ margin: 5 }} />
Data Statistics
</Button>
......@@ -99,6 +106,16 @@ function InvestigationDateFilter(props) {
</a>
</Button>
)}
{showLogbookMenu && (
<Button
style={{ marginLeft: 10 }}
href={getLogbookHrefByInstrument(instrumentName)}
bsStyle="default"
>
<Glyphicon glyph="book" style={{ fontWeight: 'bold', margin: 5 }} />
{`${instrumentName.toUpperCase()} Logbook`}
</Button>
)}
</div>
);
}
......
......@@ -168,6 +168,7 @@ function InvestigationTable(props) {
withProposalLinks = false,
withInvestigationStats = false,
showStatisticsMenu = false,
showLogbookMenu = false,
instrumentName,
withUserPortalLink = false,
withJupyter = false,
......@@ -300,6 +301,7 @@ function InvestigationTable(props) {
onEndClear={handleEndDateClear}
endPlaceHolder={'Filter between dates'}
showStatisticsMenu={showStatisticsMenu}
showLogbookMenu={showLogbookMenu}
instrumentName={instrumentName}
withJupyter={withJupyter}
/>
......
......@@ -13,7 +13,7 @@ import EventTextBox from './EventTextBox';
*/
function Event(props) {
let events = [props.event];
const { isReleased, search } = props;
const { isReleased, search, isBeamlineLogbook } = props;
const history = useHistory();
const query = useQuery();
......@@ -54,30 +54,33 @@ function Event(props) {
setCollapsed(!collapsed);
};
return events.map((event, index) => (
<tr id={event._id} key={index} style={{ backgroundColor: '#f0f0f6' }}>
return events.map((myEvent, index) => (
<tr id={myEvent._id} key={index} style={{ backgroundColor: '#f0f0f6' }}>
<td
style={{ width: 16 }}
className={styles.borderTopSeparatorBetweenEvents}
>
<Button
bsStyle="default"
bsSize="small"
style={{ width: 25, position: 'static', padding: 0 }}
onClick={() => {
query.set('edit', event._id);
history.push({ search: query.toString() });
}}
>
{getButtonIcon(event)}
</Button>
{(!isBeamlineLogbook ||
(isBeamlineLogbook && !myEvent.investigationId)) && (
<Button
bsStyle="default"
bsSize="small"
style={{ width: 25, position: 'static', padding: 0 }}
onClick={() => {
query.set('edit', myEvent._id);
history.push({ search: query.toString() });
}}
>
{getButtonIcon(myEvent)}
</Button>
)}
</td>
<td
className={styles.borderTopSeparatorBetweenEvents}
style={{ width: 16, backgroundColor: 'white' }}
>
{getTimeComponent(event)}
{getTimeComponent(myEvent)}
</td>
<td
......@@ -88,9 +91,9 @@ function Event(props) {
}}
>
<div style={{ marginLeft: 5, backgroundColor: 'white' }}>
<EventTextBox event={event} search={search} />
<EventTextBox event={myEvent} search={search} />
{event.events && collapsed && (
{myEvent.events && collapsed && (
<Label
style={{
color: 'blue',
......@@ -99,16 +102,48 @@ function Event(props) {
}}
onClick={onHandleClick}
>
.... {event.events.length} command lines more
.... {myEvent.events.length} command lines more
</Label>
)}
</div>
</td>
{isBeamlineLogbook && myEvent.investigationId && (
<td
className={styles.borderTopSeparatorBetweenEvents}
style={{
paddingBottom: 0,
backgroundColor: 'white',
}}
>
{' '}
<div>
<a
target="_blank"
rel="noopener noreferrer"
href={`/investigation/${myEvent.investigationId}/events`}
style={{ fontWeight: 'bold' }}
>
{myEvent.investigationName}
</a>
</div>
</td>
)}
{isBeamlineLogbook && !myEvent.investigationId && (
<td
className={styles.borderTopSeparatorBetweenEvents}
style={{
paddingBottom: 0,
backgroundColor: 'white',
}}
></td>
)}
<td
className={styles.borderTopSeparatorBetweenEvents}
style={{ width: 50, backgroundColor: 'white' }}
>
<TagListInLine tags={event.tag} />
<TagListInLine tags={myEvent.tag} />
</td>
</tr>
));
......
......@@ -86,7 +86,13 @@ function getItems(events, automaticCollapsing) {
* The list of the all events
*/
function EventList(props) {
const { events, isReleased, search, automaticCollapsing } = props;
const {
events,
isReleased,
search,
automaticCollapsing,
isBeamlineLogbook = false,
} = props;
if (!events) {
return null;
......@@ -137,6 +143,7 @@ function EventList(props) {
event={event}
isReleased={isReleased}
onEventClicked={props.onEventClicked}
isBeamlineLogbook={isBeamlineLogbook}
/>
);
})}
......
......@@ -99,13 +99,13 @@ function getTextColor(category) {
}
if (category.toLowerCase() === EVENT_CATEGORY_INFO) {
return 'primary';
return 'text-info';
}
if (category.toLowerCase() === EVENT_CATEGORY_DEBUG) {
return 'text-muted';
}
return '';
return 'text-success';
}
EventTextBox.propTypes = {
......
......@@ -46,6 +46,9 @@ function EventListMenu(props) {
automaticCollapsing,
automaticRefresh,
isSortingLatestEventsFirst,
isBeamlineLogbook,
bsStyle = 'primary',
showUserLogsOnBeamlineLogbook,
} = props;
/**
......@@ -109,9 +112,14 @@ function EventListMenu(props) {
<Nav style={{ marginLeft: 0 }}>
<NavItem eventKey={1} href="#" className="logbookNavItem">
<EventListMenuButton
bsStyle={bsStyle}
text="New"
glyph="plus"
tooltipText="Create a new event"
tooltipText={
isBeamlineLogbook
? 'Create a new log in the beamline logbook'
: 'Create a new log'
}
isEnabled={isNewButtonEnabled}
isVisible={!isReleased}
/>
......@@ -124,6 +132,7 @@ function EventListMenu(props) {
className="logbookNavItem"
>
<EventListMenuButton
bsStyle={bsStyle}
text="Take a photo"
glyph="camera"
tooltipText="Take a photo"
......@@ -138,41 +147,44 @@ function EventListMenu(props) {
<Nav>
<NavItem eventKey={3} href="#" className="logbookNavItem">
<EventListMenuButton
bsStyle={bsStyle}
text="Settings"
glyph="cog"
tooltipText={getSettingsTooltip()}
isEnabled
/>
</NavItem>
<NavItem
eventKey={4}
href={
!isNewButtonEnabled || eventCountBySelectionFilter === 0
? null
: getEventURL(
sessionId,
investigationId,
null,
null,
sortOrder,
sortBy,
types,
'pdf'
)
}
target="_blank"
className="logbookNavItem"
>
<EventListMenuButton
text="PDF"
glyph="download"
tooltipText="Download as PDF"
isEnabled={
!(!isNewButtonEnabled || eventCountBySelectionFilter === 0)
{!isBeamlineLogbook && (
<NavItem
eventKey={4}
href={
!isNewButtonEnabled || eventCountBySelectionFilter === 0
? null
: getEventURL(
sessionId,
investigationId,
null,
null,
sortOrder,
sortBy,
types,
'pdf'
)
}
/>
</NavItem>
target="_blank"
className="logbookNavItem"
>
<EventListMenuButton
bsStyle={bsStyle}
text="PDF"
glyph="download"
tooltipText="Download as PDF"
isEnabled={
!(!isNewButtonEnabled || eventCountBySelectionFilter === 0)
}
/>
</NavItem>
)}
<NavItem
eventKey={4}
href={`https://confluence.esrf.fr/display/DATAPOLWK/Electronic+Logbook`}
......@@ -184,6 +196,7 @@ function EventListMenu(props) {
glyph="question-sign"
tooltipText="Go to the documentation"
isEnabled
bsStyle={bsStyle}
/>
</NavItem>
{false && (
......@@ -202,7 +215,7 @@ function EventListMenu(props) {
<NavItem>
<InputGroup style={{ marginTop: '-7px' }}>
<InputGroup.Button>
<Button bsStyle="primary">
<Button bsStyle={bsStyle}>
<Glyphicon glyph="search" />
</Button>
</InputGroup.Button>
......@@ -249,6 +262,8 @@ function EventListMenu(props) {
automaticCollapsing={automaticCollapsing}
automaticRefresh={automaticRefresh}
isSortingLatestEventsFirst={isSortingLatestEventsFirst}
isBeamlineLogbook={isBeamlineLogbook}
showUserLogsOnBeamlineLogbook={showUserLogsOnBeamlineLogbook}
></SettingLogbookMenuPanel>
)}
</Navbar>
......
......@@ -5,7 +5,14 @@ import { Glyphicon, OverlayTrigger, Tooltip } from 'react-bootstrap';
/* A React component which renders a button in the event list menu. It does not handle event associated to the button.*/
class EventListMenuButton extends React.Component {
render() {
const { isEnabled, isVisible, text, glyph, tooltipText } = this.props;
const {
isEnabled,
isVisible,
text,
glyph,
tooltipText,
bsStyle = 'danger',
} = this.props;
if (!isVisible) {
return null;
......@@ -13,7 +20,7 @@ class EventListMenuButton extends React.Component {
if (!isEnabled) {
return (
<div className="btn btn-sm btn-primary" disabled>
<div className={`btn btn-sm btn-${bsStyle}`} disabled>
{' '}
<Glyphicon glyph={glyph} />
{text}{' '}
......@@ -28,7 +35,7 @@ class EventListMenuButton extends React.Component {
overlay={<Tooltip id="tooltip"> {tooltipText} </Tooltip>}
>
{/* <Button bsSize="small" bsStyle="primary" disabled={false} > <Glyphicon glyph={glyph} /> {text} </Button> */}
<div className="btn btn-sm btn-primary">
<div className={`btn btn-sm btn-${bsStyle}`}>
{' '}
<Glyphicon glyph={glyph} /> {text}{' '}
</div>
......
......@@ -17,6 +17,7 @@ import {
setCategoryTypes,
setIsSortingLatestEventsFirst,
unsetCategoryTypes,
setBeamlineLogbookShowUserLogs,
} from '../../../actions/logbook';
import Switch from 'react-switch';
......@@ -63,6 +64,8 @@ export default function SettingLogbookMenuPanel(props) {
automaticCollapsing,
automaticRefresh,
isSortingLatestEventsFirst,
isBeamlineLogbook = false,
showUserLogsOnBeamlineLogbook = true,
} = props;
const getValueByName = (name) =>
......@@ -161,23 +164,23 @@ export default function SettingLogbookMenuPanel(props) {
{isCommandLineDisplayed() && (
<Row>
<Col md={12} sm={12} xs={12}>
<Switch
onColor={toggleColor}
onChange={() => {
dispatch(
setAutomaticCollapsing(!automaticCollapsing)
);
}}
checked={automaticCollapsing}
width={switchWidth}
height={switchHeight}
className={styles.logbookSwitch}
/>
</Col>
<Col md={12} sm={12} xs={12}>
<div className={styles.logbookTextSwitch}>
Group together command lines
</div>
<span>
<Switch
onColor={toggleColor}
onChange={() => {
dispatch(
setAutomaticCollapsing(!automaticCollapsing)
);
}}
checked={automaticCollapsing}
width={switchWidth}
height={switchHeight}
className={styles.logbookSwitch}
/>
<span style={{ margin: '10px' }}>
Group command lines
</span>
</span>
</Col>
</Row>
)}
......@@ -185,7 +188,7 @@ export default function SettingLogbookMenuPanel(props) {
</Col>
)}
<Col md={2} sm={6} xs={12}>
<h4>Sort</h4>
<h4>Sorting</h4>
</