diff --git a/.env b/.env
index c2e72b7..d42d2f7 100644
--- a/.env
+++ b/.env
@@ -9,6 +9,7 @@ REACT_APP_SEND_RX_ENABLED = true
PORT=4040
REACT_APP_CLIENT_ID = app-login
REACT_APP_CLIENT_SCOPES = launch openid profile user/Patient.read patient/Patient.read user/Practitioner.read
+REACT_APP_DEVELOPER_MODE = true
GENERATE_SOURCEMAP=false
BROWSER=none
diff --git a/README.md b/README.md
index c2f94cd..5c3b76c 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ Following are a list of modifiable paths:
| REACT_APP_PHARMACY_SERVER_BASE | `http://localhost:5051` |
| REACT_APP_ETASU_STATUS_ENABLED | `true` |
| REACT_APP_PHARMACY_STATUS_ENABLED | `true` |
+| REACT_APP_DEVELOPER_MODE | `true` |
| REACT_APP_SEND_RX_ENABLED | `true` |
| PORT | `4040`|
diff --git a/src/views/Questionnaire/QuestionnaireForm.css b/src/views/Questionnaire/QuestionnaireForm.css
index 644b08e..899d6d0 100644
--- a/src/views/Questionnaire/QuestionnaireForm.css
+++ b/src/views/Questionnaire/QuestionnaireForm.css
@@ -47,10 +47,19 @@
}
.submit-button-panel {
- float:right;
margin-right: 10px;
margin-bottom: 10px;
}
+
+ .btn-row {
+ display: flex;
+ }
+
+ .error-text {
+ text-align: end;
+ font-style: italic;
+ color: #8b0000;
+ }
.submit-button {
margin-left: 5px;
@@ -74,7 +83,6 @@
}
.status-panel {
- float:left;
margin-left: 10px;
margin-bottom: 10px;
}
diff --git a/src/views/Questionnaire/QuestionnaireForm.tsx b/src/views/Questionnaire/QuestionnaireForm.tsx
index fcf33ef..16a74e6 100644
--- a/src/views/Questionnaire/QuestionnaireForm.tsx
+++ b/src/views/Questionnaire/QuestionnaireForm.tsx
@@ -27,6 +27,8 @@ import {
searchQuestionnaire
} from './questionnaireUtil';
import './QuestionnaireForm.css';
+import { Button, Typography } from '@mui/material';
+import Tooltip from '@mui/material/Tooltip';
import Client from 'fhirclient/lib/Client';
import ConfigData from '../../config.json';
@@ -56,6 +58,7 @@ interface QuestionnaireProps {
updateQuestionnaire: (n: Questionnaire) => void;
fhirVersion: string;
filterChecked: boolean;
+ ignoreRequiredChecked: boolean;
filterFieldsFn: (n: boolean) => void;
renderButtons: (n: Element) => void;
adFormResponseFromServer?: QuestionnaireResponse;
@@ -199,8 +202,6 @@ export function QuestionnaireForm(props: QuestionnaireProps) {
}
});
const loadAndMergeForms = (newResponse: QuestionnaireResponse | null) => {
- console.log(JSON.stringify(props.qform));
- console.log(JSON.stringify(newResponse));
let lform = LForms.Util.convertFHIRQuestionnaireToLForms(
props.qform,
@@ -225,7 +226,6 @@ export function QuestionnaireForm(props: QuestionnaireProps) {
);
}
- console.log(lform);
LForms.Util.addFormToPage(lform, questionnaireFormId);
const specificForm = document.getElementById(questionnaireFormId);
@@ -1032,62 +1032,101 @@ export function QuestionnaireForm(props: QuestionnaireProps) {
return isAdaptiveForm() && props.qform && props.qform.item && props.qform.item.length > 0;
};
+ const isFilledOut = () => {
+ // if checked to ignore required fields, return true to enable the submit button
+ if (props.ignoreRequiredChecked) {
+ return true;
+ } else {
+ // check if form is fully filled out based on required fields
+ const requiredFieldErrors = formValidationErrors ? formValidationErrors.filter((error) => {
+ return error.includes('requires a value');
+ }) : [];
+ return !(formValidationErrors && requiredFieldErrors.length);
+ }
+ };
+
+ // Get tooltip for Submit button
+ const getMissingFieldsTooltip = () => {
+ const tooltip = isFilledOut() ? 'Submit to REMS admin' : 'Fill out missing fields';
+ return {tooltip};
+ };
+
+ // Get missing fields to display
+ const getMissingFields = () => {
+ const fields: string[] = [];
+ const requiredFieldErrors = formValidationErrors ? formValidationErrors.filter((error) => {
+ return error.includes('requires a value');
+ }) : [];
+ if (requiredFieldErrors.length) {
+ requiredFieldErrors.forEach((err) => {
+ const name = err.split(' requires a value')[0];
+ fields.push(name);
+ });
+ }
+ return fields.join(', ');
+ };
+
const getDisplayButtons = () => {
if (!isAdaptiveForm()) {
return (
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {!isFilledOut() ?
You must include a value for {getMissingFields()}
: <>>}
);
} else {
if (props.adFormCompleted) {
return (
-
+
+
+
);
} else {
return (
{isAdaptiveFormWithoutItem() ? (
-
) : null}
{isAdaptiveFormWithItem() ? (
- {
outputResponse('in-progress');
}}
>
Save To EHR
-
+
) : null}
);
@@ -1640,15 +1679,17 @@ export function QuestionnaireForm(props: QuestionnaireProps) {
{!props.adFormCompleted ? (
{' '}
-
+
Next Question
-
+
) : null}
) : null}
- {!isAdaptive ? Form Loaded: {formLoaded}
: null}
- {getDisplayButtons()}
+
+ {!isAdaptive ?
Form Loaded: {formLoaded}
:
}
+ {getDisplayButtons()}
+
);
}
diff --git a/src/views/Questionnaire/SmartApp.tsx b/src/views/Questionnaire/SmartApp.tsx
index f9015b3..f6f62db 100644
--- a/src/views/Questionnaire/SmartApp.tsx
+++ b/src/views/Questionnaire/SmartApp.tsx
@@ -25,6 +25,7 @@ import executeElm from './elm/executeElm';
import PatientSelect from './components/PatientSelect/PatientSelect';
import RemsInterface from './components/RemsInterface/RemsInterface';
import { createRoot } from 'react-dom/client';
+import * as env from 'env-var';
interface SmartAppProps {
standalone: boolean;
@@ -111,6 +112,9 @@ export function SmartApp(props: SmartAppProps) {
const [adFormCompleted, setAdFormCompleted] = useState(false);
const [adFormResponseFromServer, setAdFormResponseFromServer] = useState();
const [formElement, setFormElement] = useState();
+ const [ignoreRequiredCheckbox, setIgnoreRequiredCheckbox] = useState(false);
+
+ const showRequiredCheckbox: boolean = env.get('REACT_APP_DEVELOPER_MODE').asBool() ? true : false;
const smart = props.smartClient;
let FHIR_VERSION = 'r4';
const toggleOverlay = () => {
@@ -339,6 +343,19 @@ export function SmartApp(props: SmartAppProps) {
setFormFilled(document.querySelector('input.ng-empty:not([disabled])') == null);
}
};
+
+ // update the ignore required checkbox
+ const updateRequired = (defaultFilter: boolean) => {
+ let checked: boolean, requiredCheckbox: HTMLInputElement;
+ if (!defaultFilter) {
+ requiredCheckbox = document.getElementById('required-fields-checkbox') as HTMLInputElement;
+ checked = requiredCheckbox ? requiredCheckbox.checked : false;
+ } else {
+ checked = true;
+ }
+ setIgnoreRequiredCheckbox(checked);
+ };
+
const fetchResourcesAndExecuteCql = (
order: string,
coverage: string,
@@ -573,6 +590,15 @@ export function SmartApp(props: SmartAppProps) {
filterCheckbox.checked = filterChecked;
}
};
+
+ // update required checkbox ref
+ const onRequiredCheckboxRefChange = () => {
+ const requiredCheckbox = document.getElementById(questionnaire ? `required-fields-checkbox-${questionnaire.id}` : 'required-fields-checkbox') as HTMLInputElement;
+ if (requiredCheckbox != null) {
+ requiredCheckbox.checked = ignoreRequiredCheckbox;
+ }
+ };
+
const getFhirWrapper = (fhirVersion: string): cqlfhir.FHIRWrapper => {
if (fhirVersion == 'r4') {
return cqlfhir.FHIRWrapper.FHIRv400();
@@ -614,6 +640,19 @@ export function SmartApp(props: SmartAppProps) {
ref={onFilterCheckboxRefChange}
>
+ { showRequiredCheckbox ?
+
+ {' '}
+ {
+ updateRequired(false);
+ }}
+ id={questionnaire ? `required-fields-checkbox-${questionnaire.id}` : 'required-fields-checkbox'}
+ ref={onRequiredCheckboxRefChange}
+ >
+
+ : }
);
@@ -661,6 +700,7 @@ export function SmartApp(props: SmartAppProps) {
renderButtons={renderButtons}
filterFieldsFn={filter}
filterChecked={filterChecked}
+ ignoreRequiredChecked={ignoreRequiredCheckbox}
formFilled={formFilled}
updateQuestionnaire={updateQuestionnaire}
ehrLaunch={ehrLaunch}