CLOSER-Cohorts/archivist

View on GitHub
react/src/components/BuildContainer.js

Summary

Maintainability
B
5 hrs
Test Coverage
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux'
import { Instrument } from '../actions'
import { Dashboard } from '../components/Dashboard'
import { CreateNewBuildObjectButtons } from '../components/CreateNewBuildObjectButtons'
import { get, isNil, forEach } from "lodash";
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import ListItemText from '@material-ui/core/ListItemText';
import Chip from '@material-ui/core/Chip';
import { useHistory } from 'react-router-dom';
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import { Loader } from '../components/Loader'
import { Alert, AlertTitle } from '@material-ui/lab';

import { makeStyles } from '@material-ui/core/styles';

const useStyles = makeStyles((theme) => ({
  root: {
    width: '100%',
    backgroundColor: theme.palette.background.paper
  },
  control: {
    width: '100%',
    padding: theme.spacing(2),
  },
  truncate: {
    width: 70,
    'white-space': 'nowrap',
    overflow: 'hidden',
    'text-overflow': 'ellipsis'
  },
  list: {
    height: 1500,
    overflow: 'hidden',
    'overflow': 'scroll',
  },
  expandable: {
    marginTop: '20px',
  }

}));

export const BuildContainer = (props) => {
  let history = useHistory();

  const { instrumentId, selectionPath = () => { }, heading = "Code Lists", itemId, itemType, objectType = "CodeList", stateKey = "codeLists", fetch = [], formRenderer = () => { }, defaultValues = { used_by: [], min_responses: 1, max_responses: 1 }} = props;
  const { findSelectedItem = (items, itemId, itemType) => { return get(items, itemId, {}) }, listItemLabel = (item) => { return item.label }, listItemValue = (item) => { return item.used_by.length }, headingContent = (instrumentId) => { return '' } } = props;
  const dispatch = useDispatch()
  const classes = useStyles();

  const instrument = useSelector(state => get(state.instruments, instrumentId));

  const mergedItems = (keys, selector) => {
    var items = {};

    forEach([keys].flat(), (key)=>{
      items = {...items, ...get(selector(state => state[key]), instrumentId, {})}
    })

    return items;
  }

  const items = mergedItems(stateKey, useSelector)
  const selectedItem = findSelectedItem(items, itemId, itemType)
  const [dataLoaded, setDataLoaded] = useState(false);

  useEffect(() => {
    dispatch(Instrument.show(instrumentId));
    Promise.all(fetch).then(() => {
      setDataLoaded(true)
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const BuildListItem = (props) => {
    const { label, value, id, type } = props
    const classes = useStyles();

    return (
      <ListItem>
        <ListItemText className= { classes.truncate } primary = { label } onClick = {()=>{ handleItemSelection(id, type) }}/>
        { value !== '' && (
          < ListItemSecondaryAction ><Chip label={value} /></ListItemSecondaryAction>
        )}
      </ListItem>
    )
  }

  const handleItemSelection = (id, type=undefined) => {
    const path = selectionPath(instrumentId, id, type)
    history.push(path);
  }

  const Expandable = () => {
    const classes = useStyles();

    return (expanded) ? (<ChevronLeftIcon className= { classes.expandable } onClick = {() => { setExpanded(!expanded) }} />) : (<ChevronRightIcon className={classes.expandable} onClick={() => { setExpanded(!expanded) }} / >)
  }

  const [expanded, setExpanded] = useState(false);

  return (
    <div style= {{ height: 500, width: '100%' }}>
      <Dashboard title={ instrumentId } instrumentId = { instrumentId } >
        <Grid container spacing = { 3} >
          <Grid item xs = {(expanded) ? 6 : 3 }>
            <Paper className={ classes.control }>
              <Grid container >
                <Grid item xs = { 11} > <h2>{heading} {headingContent(instrumentId)}</h2></Grid >
                <Grid item xs = { 1} > <Expandable /></Grid >
              </Grid>
              {instrument && !instrument.signed_off && (
                < CreateNewBuildObjectButtons instrumentId = { instrumentId } objectTypes = { [objectType].flat()} />
              )}
              {!dataLoaded
                ? <Loader />
                : (
                  <List dense={true} className={classes.list} >
                    {
                      Object.values(items).sort((a, b) => a.label.localeCompare(b.label)).map((item) => {
                        return (
                          <BuildListItem label={listItemLabel(item)} value={listItemValue(item)} id={item.id} type={item.type} />
                        )
                      })}
                  </List>
                )}
            </Paper>
          </Grid>
          < Grid item xs = {(expanded) ? 6 : 9}>
            {instrument && instrument.signed_off && (
              <div>
                <Alert severity="error">
                  <AlertTitle>Read Only</AlertTitle>
                  Instrument has been signed off so you can only view this as read-only.
                </Alert>
              </div>
            )}
            {!isNil(selectedItem) && formRenderer(instrumentId, selectedItem, instrument)}
          </Grid>
        </Grid>
      </Dashboard>
    </div>
    );
}