Vitals module

Vitals

Vitals view
Vitals
API URL
  /api/patients/{patientId}/vitalsigns
GET response
  {
    author: "Robert Tweed",
    dateCreated: 1558703797000,
    diastolicBP: 92,
    heartRate: 92,
    levelOfConsciousness: "Pain",
    newsScore: 13,
    oxygenSaturation: 77,
    oxygenSupplemental: "true",
    respirationRate: 7,
    source: "ethercis",
    sourceId: "ethercis-d2ee07eb-e568-44f7-9c76-45c3e677b8b0",
    systolicBP: 92,
    temperature: 38,
  }
Component structure
import React from "react";
import { DateField, TextField } from "react-admin";

import ListTemplate from "../../../core/common/ResourseTemplates/ListTemplate";
import VitalsCreate from "./VitalsCreate";
import VitalsEdit from "./VitalsEdit";
import VitalsShow from "./VitalsShow";
import VitalsChart from "./VitalsChart";
import VitalsDatagridRow from "./fragments/DatagridRow";

const VitalsList = ({ classes, ...rest }) => (
    <‍ListTemplate
      create={VitalsCreate}
      edit={VitalsEdit}
      show={VitalsShow}
      chartBlock={VitalsChart}
      CustomRow={VitalsDatagridRow}
      resourceUrl="vitalsigns"
      title="Vitals"
      hasChart={true}
      isCustomDatagrid={true}
      {...rest}
    >
        <‍DateField label="#" source="number" />
        <‍DateField label="Date" source="dateCreate" />
        <‍TextField label="NEWS Score" source="newsScore" />
        <‍TextField label="Source" source="source" />
    <‍/ListTemplate>
);

export default VitalsList;

Vitals Detail

Vitals Detail
Vitals Detail
API URL
  /api/patients/{patientId}/vitalsigns/{sourceId}
GET response
  {
    author: "Robert Tweed",
    dateCreated: 1560155742000,
    diastolicBP: 92,
    heartRate: 92,
    levelOfConsciousness: "Voice",
    newsScore: 11,
    oxygenSaturation: 95,
    oxygenSupplemental: true,
    respirationRate: 11,
    source: "ethercis",
    sourceId: "ethercis-f9e52b2f-a5b9-471b-af1f-f75dcb3dd237",
    systolicBP: 92,
    temperature: 37,
  }
Component structure
import React from "react";

import ShowTemplate from "../../../core/common/ResourseTemplates/ShowTemplate";
import Form from "./fragments/Form";

const VitalsShow = ({ classes, ...rest }) => (
    <‍ShowTemplate pageTitle="Vitals" {...rest}>
        <‍Form isDetailsPage={true} {...rest} />
    <‍/ShowTemplate>
);

export default VitalsShow;

Vitals Detail Edit Form

Vitals Edit Form
Vitals Edit Form
API URL
  /api/patients/{patientId}/vitalsigns
PUT data
  {
    author: "Robert Tweed",
    dateCreate: 1560428887,
    diastolicBP: 92,
    heartRate: 92,
    id: "ethercis-f9e52b2f-a5b9-471b-af1f-f75dcb3dd237",
    levelOfConsciousness: "Voice",
    newsScore: "11",
    oxygenSaturation: 95,
    oxygenSupplemental: true,
    respirationRate: 11,
    source: "ethercis",
    systolicBP: 92,
    temperature: 37,
    userId: "9999999801",
  }
Component structure


Vitals Create Form

Vitals Create
Vitals Create Form
API URL
  /api/patients/{patientId}/vitalsigns
POST data
  {
    author: "Robert Tweed",
    dateCreate: 1560429237,
    diastolicBP: "98",
    heartRate: "98",
    levelOfConsciousness: "Voice",
    newsScore: "11",
    oxygenSaturation: "95",
    oxygenSupplemental: true,
    respirationRate: "13",
    systolicBP: "98",
    temperature: "37",
    userId: "9999999801",
  }
Component structure
import React from "react";

import CreateTemplate from "../../../core/common/ResourseTemplates/CreateTemplate";
import Form from "./fragments/Form";

const VitalsCreate = props => (
    <‍CreateTemplate isCustom={true} blockTitle="Vitals" {...props}>
        <‍Form isCreate={true} {...props} />
    <‍/CreateTemplate>
);

export default VitalsCreate;

Vitals Chart

Vitals Create
Vitals Chart
Vitals Create
Vitals Chart Detail
import React, { Component } from "react";
import get from "lodash/get";
import moment from "moment";
import { ResponsiveContainer, LineChart, XAxis, YAxis, CartesianGrid, Tooltip, Legend, Line } from "recharts";
import { connect } from 'react-redux';
import { Toolbar } from "react-admin";

import { withStyles } from "@material-ui/core/styles";
import Typography from '@material-ui/core/Typography';

import CreateButton from "../../../core/common/Buttons/CreateButton";

const styles = {
    chartBlock: {
        width: '100%',
        height: 500,
    },
    toolbar: {
        display: 'flex',
        flexDirection: 'row',
        justifyContent: 'flex-end',
    }
};

function getTooltipFormatter(value, name) {
    const row = get(name, 'props.children', null);
    if (!row) {
        return null;
    }
    const nameArray = row.split(', ');
    const parameter = nameArray[0];
    const units = nameArray[1];
    return (
        <‍span>
            <‍Typography variant="h1">{parameter}:<‍/Typography>
            <‍Typography> {value}, {units}<‍/Typography>
        <‍/span>

    );
}

class VitalsChart extends Component {

    state = {
        disabledLines: [],
    };

    toggleLine = e => {
        const { disabledLines } = this.state;
        const dataKey = e.dataKey;
        let newDisabledLinesArray = [];
        if (disabledLines.indexOf(dataKey) !== -1) {
            newDisabledLinesArray = disabledLines.filter(item => item !== dataKey);
        } else {
            newDisabledLinesArray = disabledLines.concat(dataKey);
        }
        this.setState({
            disabledLines: newDisabledLinesArray
        })
    };

    onPointClick = e => {
        const { history } = this.props;
        const sourceId = get(e, 'payload.sourceId', null);
        const detailsUrl = '/vitalsigns/' + sourceId;
        history.push(detailsUrl);
    };

    render() {
        const { classes, vitalsList, history, createUrl } = this.props;
        const { disabledLines } = this.state;

        const vitalsListArray = Object.values(vitalsList);
        let chartData = [];
        for (let i = 0, n = vitalsListArray.length; i < n; i++) {

            let item = vitalsListArray[i];
            chartData.push({
                name: moment(item.dateCreate).format('MM-DD-YYYY'),
                diastolicBP: item.diastolicBP,
                heartRate: item.heartRate,
                oxygenSaturation: item.oxygenSaturation,
                respirationRate: item.respirationRate,
                systolicBP: item.systolicBP,
                temperature: item.temperature,
                sourceId: item.sourceId,
            });
        }

        const DOT_RADIUS = 8;
        const STROKE_WIDTH = 4;

        const linesArray = [
            { dataKey: "respirationRate", color: "#99C78C", label: <‍Typography>Respiration Rate, resps/min<‍/Typography> },
            { dataKey: "oxygenSaturation", color: "#E18FC0", label: <‍Typography>Oxygen Saturation, %<‍/Typography> },
            { dataKey: "heartRate", color: "#ACC2D7", label: <‍Typography>Heart Rate, bpm<‍/Typography> },
            { dataKey: "systolicBP", color: "#EABA97", label: <‍Typography>Systolic BP, mmHg<‍/Typography> },
            { dataKey: "diastolicBP", color: "#99D9DE", label: <‍Typography>Diastolic BP, mmHg<‍/Typography> },
            { dataKey: "temperature", color: "#E3A08F", label: <‍Typography>Temperature, C<‍/Typography> },
        ];

        return (
            <‍div className={classes.chartBlock}>
                <‍ResponsiveContainer width={'99%'}>
                    <‍LineChart data={chartData} margin=>
                        <‍XAxis dataKey="name" tick= />
                        <‍YAxis tick= />
                        <‍Tooltip
                                formatter={(value, name) => getTooltipFormatter(value, name)}
                            labelFormatter={function(value) {
                                return (
                                    <‍Typography>Date: {value}<‍/Typography>
                                );
                            }}
                        />
                        <‍Legend
                                payload={linesArray.map(item => ({
                                dataKey: item.dataKey,
                                color: item.color,
                                value: item.label,
                            }))}
                            onClick={e => this.toggleLine(e)}
                        />
                        <‍CartesianGrid stroke="#e5e5e5" strokeDasharray="5 5"/>
                        {
                            linesArray.map((item, key) => {
                                if (disabledLines.indexOf(item.dataKey) !== -1) {
                                    return null;
                                }
                                return (
                                    <‍Line
                                       key={key}
                                       type="monotone"
                                       name={item.label}
                                       dataKey={item.dataKey}
                                       stroke={item.color}
                                       activeDot=
                                       strokeWidth={STROKE_WIDTH}
                                    />
                                )
                            })
                        }
                    <‍/LineChart>
                <‍/ResponsiveContainer>
                <‍Toolbar className={classes.toolbar}>
                    <‍CreateButton history={history} redirectPath={createUrl} />
                <‍/Toolbar>
            <‍/div>
        );
    }
};

const mapStateToProps = state => {
    return {
        vitalsList: get(state, 'admin.resources.vitalsigns.data', []),
    }
};

export default connect(mapStateToProps, null)(withStyles(styles)(VitalsChart));

Vitals Popover

Vitals Popover
Vitals Popover
import React from "react";
import get from "lodash/get";

import { withStyles } from '@material-ui/core/styles';
import Popover from "@material-ui/core/Popover";
import Typography from "@material-ui/core/Typography";

import rangeLine from "../../../images/range-line.jpeg";
import { rangeLineSettings } from "./settings";

const styles = theme => ({
    popover: {
        pointerEvents: 'none',
    },
    paper: {
        display: "block",
        width: 490,
        padding: 0,
        margin: 0,
        borderRadius: 0,
    },
    blockTitle: {
        display: "flex",
        alignItems: "center",
        height: 49,
        color: theme.palette.paperColor,
        backgroundColor: theme.palette.secondaryMainColor,
        paddingLeft: 15,
    },
    title: {
        color: theme.palette.paperColor,
        fontSize: 18,
        fontWeight: 700,
    },
    rangeAxisItemTop: {
        display: "flex",
        flexDirection: "column",
        justifyContent: "flex-start",
        width: 70,
        height: 65,
        float: "left"
    },
    rangeAxisItemBottom: {
        display: "flex",
        flexDirection: "column",
        justifyContent: "flex-end",
        width: 70,
        height: 65,
        float: "left"
    },
    content: {
        margin: 10,
        height: 60,
    },
    axis: {
        display: "flex",
        flexDirection: "row",
        justifyContent: "space-between",
        backgroundImage: `url(${rangeLine})`,
        backgroundRepeat: "no-repeat",
        backgroundPosition: "center",
        backgroundSize: '490px 10px',
    },
});

const RangeLinePopover = ({ classes, anchorEl, open, handleClose, label, model }) => {
    const rangeLineValues = get(rangeLineSettings, model, null);
    return (
        <‍Popover
                open={open}
                className={classes.popover}
                classes=
                anchorEl={anchorEl}
                onClose={handleClose}
                anchorOrigin=
            transformOrigin=
        >
            <‍div>
                <‍div className={classes.blockTitle}>
                    <‍Typography className={classes.title}>{label}<‍/Typography>
                <‍/div>
                <‍div className={classes.content}>
                    <‍div className={classes.axis}>
                        {
                            rangeLineValues && rangeLineValues.map(item => {
                                return (
                                    <‍div className={classes[item.position]}>
                                        <‍Typography variant="body2">{item.label}<‍/Typography>
                                    <‍/div>
                                );
                            })
                        }
                    <‍/div>
                <‍/div>
            <‍/div>
        <‍/Popover>
    );
};

export default withStyles(styles)(RangeLinePopover);