Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

524 require questionnaire fields #51

Merged
merged 3 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`|

Expand Down
12 changes: 10 additions & 2 deletions src/views/Questionnaire/QuestionnaireForm.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -74,7 +83,6 @@
}

.status-panel {
float:left;
margin-left: 10px;
margin-bottom: 10px;
}
Expand Down
121 changes: 81 additions & 40 deletions src/views/Questionnaire/QuestionnaireForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -225,7 +226,6 @@ export function QuestionnaireForm(props: QuestionnaireProps) {
);
}

console.log(lform);

LForms.Util.addFormToPage(lform, questionnaireFormId);
const specificForm = document.getElementById(questionnaireFormId);
Expand Down Expand Up @@ -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 <Typography fontSize={16}>{tooltip}</Typography>;
};

// 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 (
<div className="submit-button-panel">
<button className="btn submit-button" onClick={() => loadPreviousForm()}>
Load Previous Form
</button>
<button
className="btn submit-button"
onClick={() => {
outputResponse('in-progress');
}}
>
Save to EHR
</button>
<button
className="btn submit-button"
onClick={() => {
outputResponse('completed');
}}
>
Submit REMS Bundle
</button>
<div className='submit-button-panel'>
<div className="btn-row">
<Button variant="outlined" onClick={() => loadPreviousForm()}>
Load Previous Form
</Button>
<Button variant="outlined"
onClick={() => {
outputResponse('in-progress');
}}
>
Save to EHR
</Button>
<Tooltip title={getMissingFieldsTooltip()}>
<span>
<Button variant="outlined" disabled={!isFilledOut()}
onClick={() => {
outputResponse('completed');
}}
>
Submit REMS Bundle
</Button>
</span>
</Tooltip>
</div>
{!isFilledOut() ? <p className='error-text'>You must include a value for {getMissingFields()}</p> : <></>}
</div>
);
} else {
if (props.adFormCompleted) {
return (
<div className="submit-button-panel">
<button
className="btn submit-button"
onClick={() => {
outputResponse('completed');
}}
>
Submit REMS Bundle
</button>
<Tooltip title={getMissingFieldsTooltip()}>
<Button variant="outlined" disabled={!isFilledOut()}
onClick={() => {
outputResponse('completed');
}}
>
Submit REMS Bundle
</Button>
</Tooltip>
</div>
);
} else {
return (
<div className="submit-button-panel">
{isAdaptiveFormWithoutItem() ? (
<button className="btn submit-button" onClick={() => loadPreviousForm()}>
<Button variant="outlined" onClick={() => loadPreviousForm()}>
Load Previous Form
</button>
</Button>
) : null}
{isAdaptiveFormWithItem() ? (
<button
className="btn submit-button"
<Button variant="outlined"
onClick={() => {
outputResponse('in-progress');
}}
>
Save To EHR
</button>
</Button>
) : null}
</div>
);
Expand Down Expand Up @@ -1640,15 +1679,17 @@ export function QuestionnaireForm(props: QuestionnaireProps) {
{!props.adFormCompleted ? (
<div>
{' '}
<button className="btn submit-button" onClick={loadNextQuestions}>
<Button variant='outlined' onClick={loadNextQuestions}>
Next Question
</button>
</Button>
</div>
) : null}
</div>
) : null}
{!isAdaptive ? <div className="status-panel">Form Loaded: {formLoaded}</div> : null}
{getDisplayButtons()}
<div style={{display: 'flex', justifyContent: 'space-between'}}>
{!isAdaptive ? <div className="status-panel">Form Loaded: {formLoaded}</div> : <div/>}
{getDisplayButtons()}
</div>
</div>
);
}
40 changes: 40 additions & 0 deletions src/views/Questionnaire/SmartApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -111,6 +112,9 @@ export function SmartApp(props: SmartAppProps) {
const [adFormCompleted, setAdFormCompleted] = useState<boolean>(false);
const [adFormResponseFromServer, setAdFormResponseFromServer] = useState<QuestionnaireResponse>();
const [formElement, setFormElement] = useState<HTMLElement>();
const [ignoreRequiredCheckbox, setIgnoreRequiredCheckbox] = useState<boolean>(false);

const showRequiredCheckbox: boolean = env.get('REACT_APP_DEVELOPER_MODE').asBool() ? true : false;
const smart = props.smartClient;
let FHIR_VERSION = 'r4';
const toggleOverlay = () => {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -614,6 +640,19 @@ export function SmartApp(props: SmartAppProps) {
ref={onFilterCheckboxRefChange}
></input>
</div>
{ showRequiredCheckbox ?
<div className="task-button">
<label>Ignore required fields</label>{' '}
<input
type="checkbox"
onChange={() => {
updateRequired(false);
}}
id={questionnaire ? `required-fields-checkbox-${questionnaire.id}` : 'required-fields-checkbox'}
ref={onRequiredCheckboxRefChange}
></input>
</div>
: <div/>}
</div>
</div>
);
Expand Down Expand Up @@ -661,6 +700,7 @@ export function SmartApp(props: SmartAppProps) {
renderButtons={renderButtons}
filterFieldsFn={filter}
filterChecked={filterChecked}
ignoreRequiredChecked={ignoreRequiredCheckbox}
formFilled={formFilled}
updateQuestionnaire={updateQuestionnaire}
ehrLaunch={ehrLaunch}
Expand Down
Loading