diff --git a/package-lock.json b/package-lock.json index 57bd2a3..e7189a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11463,9 +11463,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dependencies": { "jake": "^10.8.5" }, diff --git a/src/views/Patient/MedReqDropDown/MedReqDropDown.css b/src/views/Patient/MedReqDropDown/MedReqDropDown.css index 3ff812b..319ae36 100644 --- a/src/views/Patient/MedReqDropDown/MedReqDropDown.css +++ b/src/views/Patient/MedReqDropDown/MedReqDropDown.css @@ -1,7 +1,7 @@ .etasuButtonText { margin-bottom: 0px; font-weight: bold; - font-size: 14px; + font-size: 1.2em; } .etasuButtonTimestamp { diff --git a/src/views/Patient/MedReqDropDown/MedReqDropDown.tsx b/src/views/Patient/MedReqDropDown/MedReqDropDown.tsx index 0178a82..add4630 100644 --- a/src/views/Patient/MedReqDropDown/MedReqDropDown.tsx +++ b/src/views/Patient/MedReqDropDown/MedReqDropDown.tsx @@ -314,7 +314,7 @@ function MedReqDropDown({ }; return ( <> - + diff --git a/src/views/Patient/MedReqDropDown/cdsHooksCards/cdsHooksCard.tsx b/src/views/Patient/MedReqDropDown/cdsHooksCards/cdsHooksCard.tsx index 12afee1..55fb2b6 100644 --- a/src/views/Patient/MedReqDropDown/cdsHooksCards/cdsHooksCard.tsx +++ b/src/views/Patient/MedReqDropDown/cdsHooksCards/cdsHooksCard.tsx @@ -1,18 +1,26 @@ -import { useState, useEffect, ReactElement } from 'react'; -import { Button, Card, CardActions, CardContent, Grid, Typography } from '@mui/material'; +import { Box, Button, Card, CardContent, Grid, Typography } from '@mui/material'; +import { ReactElement, useEffect, useState } from 'react'; import axios from 'axios'; import Client from 'fhirclient/lib/Client'; +import AddCircleOutlineRoundedIcon from '@mui/icons-material/AddCircleOutlineRounded'; +import ArrowForwardIosRoundedIcon from '@mui/icons-material/ArrowForwardIosRounded'; +import KeyboardArrowDownRoundedIcon from '@mui/icons-material/KeyboardArrowDownRounded'; +import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf'; +import Accordion from '@mui/material/Accordion'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; import { + Action, Card as HooksCard, Link, - Suggestion, - Action + Suggestion } from '../../../../cds-hooks/resources/HookTypes'; import { SmartApp } from '../../../Questionnaire/SmartApp'; import { AppContext, getAppContext } from '../../../Questionnaire/questionnaireUtil'; -import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf'; // TODO: // - create a css file for better style @@ -234,78 +242,140 @@ const CdsHooksCard = (props: CdsHooksCardProps) => { }); }); } + const decisionCard = { backgroundColor: '#fff', border: '1px solid rgba(0, 0, 0, 0.12)', borderRadius: '4px' }; + const cardSource = { fontSize: '.85rem', fontStyle: 'italic', margin: '0 0 5px' }; const sourceLink = { marginRight: '8px', color: '#4183c4', textDecoration: 'none' }; + return ( - - + + {props.card?.summary} - {props.card?.detail} - + + + {/* Forms */} + {links.filter(link => link.type === 'smart').length > 0 ? ( + + Required Forms + + {links.map((link: Link) => { + if (link.type === 'smart') { + return ( + + buttonClickAction(link)} + endIcon={} + > + {link?.label} + + + ); + } + })} + + + ) : ( + <>> + )} + + {/* Suggestions */} + {suggestions.length > 0 ? ( + + + Suggestions + + + {suggestions.map((suggestion: Suggestion, ind) => { + const buttonId = 'suggestion_button-' + props.cardInd + '-' + ind; + return ( + + } + onClick={() => + buttonClickSuggestion( + suggestion, + buttonId, + suggestions.length, + props.cardInd, + props.selectionBehavior + ) + } + id={buttonId} + > + {suggestion?.label} + + + ); + })} + + + ) : ( + <>> + )} + + {/* Documentation and Guides */} + {links.filter(link => link.type === 'absolute').length > 0 ? ( + + }> + + View documentation and guides + + + + {links.map((link: Link) => { + if (link.type === 'absolute') { + return ( + + } + onClick={() => buttonClickAction(link)} + > + {link?.label} + + + ); + } + })} + + + ) : ( + <>> + )} + + + {'Source '} {props.card?.source?.label} - - - - {links.map((link: Link) => { - if (link.type === 'smart') { - return ( - - buttonClickAction(link)}> - {link?.label} - - - ); - } - return ( - - } onClick={() => buttonClickAction(link)}> - {link?.label} - - - ); - })} - - - - - {suggestions.map((suggestion: Suggestion, ind) => { - const buttonId = 'suggestion_button-' + props.cardInd + '-' + ind; - return ( - - - buttonClickSuggestion( - suggestion, - buttonId, - suggestions.length, - props.cardInd, - props.selectionBehavior - ) - } - id={buttonId} - > - {suggestion?.label} - - - ); - })} - - - - + + + ); }; diff --git a/src/views/Patient/MedReqDropDown/cdsHooksCards/cdsHooksCards.tsx b/src/views/Patient/MedReqDropDown/cdsHooksCards/cdsHooksCards.tsx index 2112313..374b47e 100644 --- a/src/views/Patient/MedReqDropDown/cdsHooksCards/cdsHooksCards.tsx +++ b/src/views/Patient/MedReqDropDown/cdsHooksCards/cdsHooksCards.tsx @@ -15,7 +15,7 @@ interface CdsHooksCardsProps { const CdsHooksCards = (props: CdsHooksCardsProps) => { return ( - + {props.cards.map((card: HooksCard, cardInd) => ( { {props.remsAdminResponse ? ( - {getRequirements().map((param: EtasuParamParam) => ( - - - {param.resource.status === 'success' ? ( - - ) : ( - - )} - - {param.resource.status === 'success' ? ( - - ) : ( - - )} - - ))} + {getRequirements().map((param: EtasuParamParam) => { + if (param.resource) { + return ( + + + {param.resource?.status === 'success' ? ( + + ) : ( + + )} + + {param.resource?.status === 'success' ? ( + + ) : ( + + )} + + ); + } + })} ) : ( 'Not Available' diff --git a/src/views/Questionnaire/QuestionnaireForm.tsx b/src/views/Questionnaire/QuestionnaireForm.tsx index 4031b61..3ce0405 100644 --- a/src/views/Questionnaire/QuestionnaireForm.tsx +++ b/src/views/Questionnaire/QuestionnaireForm.tsx @@ -10,6 +10,7 @@ import { Meta, Organization, Parameters, + Patient, Questionnaire, QuestionnaireItem, QuestionnaireResponse, @@ -44,7 +45,7 @@ import Client from 'fhirclient/lib/Client'; import ConfigData from '../../config.json'; import { SelectPopup } from './components/SelectPopup'; import AlertDialog from './components/AlertDialog'; - +import { RemsAdminResponse } from './components/RemsInterface/RemsInterface'; import { PrepopulationResults } from './SmartApp'; import { v4 as uuid } from 'uuid'; import axios, { AxiosResponse } from 'axios'; @@ -223,10 +224,15 @@ export function QuestionnaireForm(props: QuestionnaireProps) { const [popupState, popupDispatch] = useReducer(reducer, initialPopupState); const [showRxAlert, setShowRxAlert] = useState({ open: false }); const [formValidationErrors, setFormValidationErrors] = useState([]); + const [patient, setPatient] = useState(undefined); const LForms = window.LForms; const questionnaireFormId = `formContainer-${props.questionnaireForm.id}-${props.tabIndex}`; useEffect(() => { + const patientId = getPatient(); + props.smartClient.request(patientId).then(res => { + setPatient(res); + }); // search for any partially completed QuestionnaireResponses if (props.response) { const response = props.response; @@ -1139,7 +1145,7 @@ export function QuestionnaireForm(props: QuestionnaireProps) { // Get tooltip for Submit button const getMissingFieldsTooltip = () => { const tooltip = isFilledOut() ? 'Submit to REMS admin' : 'Fill out missing fields'; - return {tooltip}; + return {tooltip}; }; // Get missing fields to display @@ -1734,8 +1740,42 @@ export function QuestionnaireForm(props: QuestionnaireProps) { specialtyRxBundle, options ) - .then(response => { + .then((response: RemsAdminResponse) => { + const remsCaseUrl = 'http://hl7.org/fhir/sid/rems-case'; // placeholder const proceedToRems = () => { + const caseNumber = response.data?.case_number; + if (caseNumber && patient) { + patient.identifier = patient.identifier?.filter(iden => { + if (iden.system === remsCaseUrl && iden.period) { + if (iden.period?.end) { + const endDate = new Date(iden.period.end); + if (endDate.getMilliseconds() < Date.now()) { + return false; // filter out old identifiers + } + } + } + return true; + }); + const endDate = new Date(Date.now() + 86400000); // 86400000 is 1 day in milliseconds + patient.identifier?.push({ + value: caseNumber, + system: remsCaseUrl, + period: { + start: new Date(Date.now()).toISOString(), + end: endDate.toISOString() + } + }); + // update patient + props.smartClient.request({ + url: patient.resourceType + '/' + patient.id, + method: 'PUT', + headers: { + 'content-type': 'application/json' + }, + body: JSON.stringify(patient) + }); + } + props.setSpecialtyRxBundle(specialtyRxBundle); }; if (response.status == 201) { diff --git a/src/views/Questionnaire/SmartApp.tsx b/src/views/Questionnaire/SmartApp.tsx index d778469..991af3e 100644 --- a/src/views/Questionnaire/SmartApp.tsx +++ b/src/views/Questionnaire/SmartApp.tsx @@ -723,7 +723,7 @@ export function SmartApp(props: SmartAppProps) { } else { return ( - Loading... + Loading... ); } diff --git a/src/views/Questionnaire/components/PatientSelect/PatientStyles.css b/src/views/Questionnaire/components/PatientSelect/PatientStyles.css index bac36f4..d1811b6 100644 --- a/src/views/Questionnaire/components/PatientSelect/PatientStyles.css +++ b/src/views/Questionnaire/components/PatientSelect/PatientStyles.css @@ -7,7 +7,7 @@ height:500px; width:70%; z-index:1; - font-size:14px; + font-size:1.3em; background-color: #f9f9f9; border: 1px solid black; } @@ -16,7 +16,7 @@ position: absolute; left:14%; top:3px; - font-size: 20px; + font-size: 1.5em; padding: 15px; margin: 10px; } diff --git a/src/views/Questionnaire/components/RemsInterface/RemsInterface.css b/src/views/Questionnaire/components/RemsInterface/RemsInterface.css index 445c9c6..746703e 100644 --- a/src/views/Questionnaire/components/RemsInterface/RemsInterface.css +++ b/src/views/Questionnaire/components/RemsInterface/RemsInterface.css @@ -70,7 +70,7 @@ body { .jsonData { line-height: 16px; font-family: 'Courier New', Courier, monospace; - font-size: 14px; + font-size: 1.3em; border-left: 2px solid #dddddd; border-top: 0px; diff --git a/src/views/Questionnaire/components/RemsInterface/RemsInterface.tsx b/src/views/Questionnaire/components/RemsInterface/RemsInterface.tsx index a85bf90..dc993e6 100644 --- a/src/views/Questionnaire/components/RemsInterface/RemsInterface.tsx +++ b/src/views/Questionnaire/components/RemsInterface/RemsInterface.tsx @@ -5,10 +5,26 @@ import Paper from '@mui/material/Paper'; import Button from '@mui/material/Button'; import { Bundle } from 'fhir/r4'; import { Box } from '@mui/material'; +import { AxiosResponse } from 'axios'; interface RemsInterfaceProps { specialtyRxBundle: Bundle; } +export interface RemsAdminResponse extends AxiosResponse { + data: JsonData; +} + +type MetRequirements = { + completed: boolean; + requirementName: string; + requirementDescription: string; +}; + +interface JsonData { + case_number: string; + status: string; + metRequirements: MetRequirements[]; +} export default function RemsInterface(props: RemsInterfaceProps) { const [viewBundle, setViewBundle] = useState(false); diff --git a/src/views/Smart/Index.tsx b/src/views/Smart/Index.tsx index 6e40010..41ec967 100644 --- a/src/views/Smart/Index.tsx +++ b/src/views/Smart/Index.tsx @@ -10,6 +10,7 @@ const Index = () => { FHIR.oauth2.ready().then(client => { setClient(client); }); + document.title = 'REMS SMART on FHIR app'; }, []); return ( diff --git a/src/views/Smart/Launch.tsx b/src/views/Smart/Launch.tsx index 9c9738e..b1816af 100644 --- a/src/views/Smart/Launch.tsx +++ b/src/views/Smart/Launch.tsx @@ -13,6 +13,7 @@ const Launch = () => { ); useEffect(() => { + document.title = 'REMS SMART on FHIR app launch page'; smartLaunch(); }, []); diff --git a/src/views/Smart/Register.tsx b/src/views/Smart/Register.tsx index fa4ba1d..680bbef 100644 --- a/src/views/Smart/Register.tsx +++ b/src/views/Smart/Register.tsx @@ -1,5 +1,5 @@ import { Button, FormControl, FormHelperText, IconButton, TextField } from '@mui/material'; -import React, { FormEvent, memo, useState } from 'react'; +import React, { FormEvent, memo, useState, useEffect } from 'react'; import CloseIcon from '@mui/icons-material/Close'; interface RegisterProps { @@ -9,7 +9,9 @@ interface RegisterProps { const Register = (props: RegisterProps) => { const [clientId, setClientId] = useState(''); const [fhirUrl, setFhirUrl] = useState(props.fhirUrl || ''); - + useEffect(() => { + document.title = 'Registration page for SMART on FHIR clients'; + }); const [currentClients, setCurrentClients] = useState( JSON.parse(localStorage.getItem('clients') || '[]') );
Loading...