Commit a65168ca authored by Alejandro De Maria Antolinos's avatar Alejandro De Maria Antolinos

Merge branch 'local-contact' into 'master'

Parcel status change UI now considers local contact as admin

Closes #419

See merge request !444
parents bf3dfb6b 0f177334
Pipeline #33105 passed with stages
in 11 minutes and 21 seconds
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';
import { Alert, Button, Col, Glyphicon, Grid, Row } from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import { useSelector } from 'react-redux';
import { useFetcher, useResource } from 'rest-hooks';
import { setBreadCrumbs } from '../../actions/breadcrumbs';
import { isEditable } from '../../helpers/statusUtils';
import ParcelResource from '../../resources/parcel';
import ShipmentResource from '../../resources/shipment';
......@@ -16,23 +15,26 @@ import StatusTable from './StatusTable';
import styles from './ParcelDetails.module.css';
import AddressPanel from '../Address/AddressPanel';
import AdminPanel from './AdminPanel';
import PageNotFound from '../../containers/PageNotFound';
import InvestigationUserResource from '../../resources/investigationUser';
import { USER_ROLES } from '../../constants';
function ParcelDetails(props) {
const { investigation, investigationId, parcelId } = props;
const { investigation, parcel } = props;
const initParcel = useResource(ParcelResource.detailShape(), {
investigationId,
_id: parcelId,
});
const [parcel, setParcel] = useState(initParcel);
const { id: investigationId } = investigation;
// This is to render the component when the parcel is updated
const [activeParcel, setActiveParcel] = useState(parcel);
const editParcel = useFetcher(ParcelResource.updateShape());
const getParcel = useFetcher(ParcelResource.detailShape());
const refetchParcel = () => getParcel({ investigationId, _id: parcelId });
const resetActiveParcel = async () =>
setActiveParcel(
await getParcel({ investigationId, _id: activeParcel._id })
);
const editParcelWithRefetch = async (params, body) => {
await editParcel(params, body);
setParcel(await refetchParcel());
await resetActiveParcel();
};
const {
......@@ -41,39 +43,26 @@ function ParcelDetails(props) {
returnAddress,
comments,
storageConditions,
} = parcel;
} = activeParcel;
const [shipment] = useResource(ShipmentResource.listShape(), {
investigationId,
});
const investigationUsers = useResource(
InvestigationUserResource.listShape(),
{ investigationId }
);
const [isEditingParcel, setEditingParcel] = useState(false);
const [alert, setAlert] = useState();
const dispatch = useDispatch();
const user = useSelector((state) => state.user);
useEffect(() => {
if (investigation) {
dispatch(
setBreadCrumbs([
{
name: investigation.visitId.toUpperCase(),
link: `/investigation/${investigation.id}/datasets`,
},
{ badge: investigation.name, name: investigation.summary },
{
name: 'Shipping',
link: `/investigation/${investigation.id}/shipping`,
},
{ name: parcel ? parcel.name : 'Not found' },
])
);
}
}, [dispatch, investigation, parcel]);
if (!parcel) {
return <PageNotFound />;
}
const isLocalContact = !!investigationUsers.find(
(investigationUser) =>
investigationUser.role === USER_ROLES.LocalContact &&
investigationUser.name === user.name
);
const isInvestigationAdmin = isLocalContact || user.isAdministrator;
function handleCloseModal() {
setEditingParcel(false);
......@@ -83,8 +72,8 @@ function ParcelDetails(props) {
setAlert(undefined);
try {
const fetcherParams = {
investigationId: parcel.investigationId,
shipmentId: parcel.shipmentId,
investigationId: activeParcel.investigationId,
shipmentId: activeParcel.shipmentId,
};
await editParcelWithRefetch(fetcherParams, values);
......@@ -100,11 +89,11 @@ function ParcelDetails(props) {
function updateStatus(status, body) {
return editParcelWithRefetch(
{
investigationId: parcel.investigationId,
investigationId: activeParcel.investigationId,
status,
parcelId: parcel._id,
parcelId: activeParcel._id,
},
{ _id: parcel._id, ...body }
{ _id: activeParcel._id, ...body }
);
}
......@@ -114,13 +103,13 @@ function ParcelDetails(props) {
{isEditingParcel && (
<ParcelFormModal
shipment={shipment}
parcel={parcel}
parcel={activeParcel}
onSubmitAsync={handleSubmit}
onCloseModal={handleCloseModal}
/>
)}
<ParcelHeader
parcel={parcel}
parcel={activeParcel}
investigation={investigation}
setAlert={setAlert}
/>
......@@ -129,11 +118,16 @@ function ParcelDetails(props) {
<div className={styles.statusCol}>
<span className={styles.label}>Status:</span>
<div className={styles.currentStatus}>
<StatusDisplay status={parcel.status} />
<StatusDisplay status={activeParcel.status} />
</div>
</div>
<div className={styles.statusCol}>
<ParcelStatusButtons parcel={parcel} updateStatus={updateStatus} />
<ParcelStatusButtons
parcel={activeParcel}
investigation={investigation}
updateStatus={updateStatus}
showSafetyActions={isInvestigationAdmin}
/>
</div>
</div>
......@@ -145,7 +139,7 @@ function ParcelDetails(props) {
bsStyle="primary"
aria-label="Edit"
onClick={() => setEditingParcel(true)}
disabled={!isEditable(parcel)}
disabled={!isEditable(activeParcel)}
>
<Glyphicon glyph="pencil" />
<span className={styles.actionLabel}> Edit</span>
......@@ -181,7 +175,7 @@ function ParcelDetails(props) {
<Row style={{ marginTop: '0.5rem' }}>
<Col sm={12} md={6}>
{parcel.containsDangerousGoods && (
{activeParcel.containsDangerousGoods && (
<Alert bsStyle="danger">
<Glyphicon glyph="warning-sign" /> This parcel contains
dangerous goods.
......@@ -219,21 +213,24 @@ function ParcelDetails(props) {
<BasicPanel title="Content">
<ItemTable
setAlert={setAlert}
onItemChange={async () => setParcel(await refetchParcel())}
parcel={parcel}
onItemChange={resetActiveParcel}
parcel={activeParcel}
/>
</BasicPanel>
<BasicPanel title="History">
<Grid fluid>
<Row>
<StatusTable statuses={parcel.statuses} />
<StatusTable statuses={activeParcel.statuses} />
</Row>
</Grid>
</BasicPanel>
{user.isAdministrator && (
<AdminPanel currentStatus={parcel.status} updateStatus={updateStatus} />
<AdminPanel
currentStatus={activeParcel.status}
updateStatus={updateStatus}
/>
)}
</div>
);
......
import React, { useState } from 'react';
import { Glyphicon, HelpBlock } from 'react-bootstrap';
import { useSelector } from 'react-redux';
import { STATUS, STATUS_DEFS } from '../../constants/parcelStatuses';
import DangerousGoodsModal from './DangerousGoodsModal';
import InputModal from './InputModal';
......@@ -8,11 +7,10 @@ import StatusButton from './StatusButton';
import styles from './ParcelStatusButtons.module.css';
function ParcelStatusButtons(props) {
const { parcel, updateStatus } = props;
const { parcel, updateStatus, showSafetyActions } = props;
const { status: currentStatus } = parcel;
const user = useSelector((state) => state.user);
const allowedActions = user.isAdministrator
const allowedActions = showSafetyActions
? STATUS_DEFS[currentStatus].allowedActions(parcel)
: STATUS_DEFS[currentStatus]
.allowedActions(parcel)
......
......@@ -14,6 +14,7 @@ import {
} from 'react-bootstrap';
import { getMintDOI } from '../../api/icat-plus/doi';
import BootstrapTable2 from 'react-bootstrap-table-next';
import { USER_ROLES } from '../../constants';
class DOIForm extends React.Component {
constructor(props) {
......@@ -141,7 +142,9 @@ class DOIForm extends React.Component {
>
{this.props.investigationUsers.map(function (user) {
const bsStyleRole =
user.role === 'Principal investigator' ? 'primary' : 'default';
user.role === USER_ROLES.PrincipalInvestigator
? 'primary'
: 'default';
return (
<MenuItem
style={{ width: 350 }}
......
......@@ -29,3 +29,8 @@ export const ITEM_TYPE_DEFS = {
export const INVESTIGATION_DATE_FORMAT = 'DD/MM/YYYY';
export const BYTE_UNITS = ['PB', 'TB', 'GB', 'MB', 'KB'];
export const USER_ROLES = Object.freeze({
PrincipalInvestigator: 'Principal investigator',
LocalContact: 'Local contact',
});
import React from 'react';
import React, { useEffect } from 'react';
import { useParams } from 'react-router';
import { useResource } from 'rest-hooks';
import ParcelDetails from '../components/Parcels/ParcelDetails';
import InvestigationResource from '../resources/investigation';
import LoadingBoundary from '../components/LoadingBoundary';
import { setBreadCrumbs } from '../actions/breadcrumbs';
import ParcelResource from '../resources/parcel';
import { useDispatch } from 'react-redux';
function ParcelPage() {
const { investigationId, parcelId } = useParams();
const investigation = useResource(InvestigationResource.detailShape(), {
id: investigationId,
});
const parcel = useResource(ParcelResource.detailShape(), {
investigationId,
_id: parcelId,
});
const dispatch = useDispatch();
useEffect(() => {
if (investigation && parcel) {
dispatch(
setBreadCrumbs([
{
name: investigation.visitId.toUpperCase(),
link: `/investigation/${investigation.id}/datasets`,
},
{ badge: investigation.name, name: investigation.summary },
{
name: 'Shipping',
link: `/investigation/${investigation.id}/shipping`,
},
{ name: parcel ? parcel.name : 'Not found' },
])
);
}
}, [dispatch, investigation, parcel]);
return (
<div className="app__inner">
<LoadingBoundary message="Loading parcel...">
<ParcelDetails
// Send the investigationId without waiting for investigation to trigger the fetch of the parcel
investigationId={investigationId}
investigation={investigation}
parcelId={parcelId}
/>
<ParcelDetails investigation={investigation} parcel={parcel} />
</LoadingBoundary>
</div>
);
......
import { Resource } from 'rest-hooks';
import ICATPLUS from '../config/icatPlus';
import { store } from '../store';
export default class InvestigationUserResource extends Resource {
name = undefined;
fullName = '';
role = '';
investigationName = '';
investigationId = undefined;
email = '';
pk() {
return this.name?.toString();
}
static get key() {
return 'InvestigationUserResource';
}
static listUrl(params) {
const { sessionId } = store.getState().user;
const { investigationId } = params;
return `${ICATPLUS.server}/catalogue/${sessionId}/investigation/id/${investigationId}/investigationusers`;
}
}
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