Skip to content

Commit

Permalink
#1273 - edit rule applied to checklist item edit
Browse files Browse the repository at this point in the history
  • Loading branch information
petmongrels committed Feb 26, 2024
1 parent cc67434 commit fde5af1
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 49 deletions.
59 changes: 43 additions & 16 deletions packages/openchs-android/src/action/program/ChecklistActions.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,31 @@
import EntityService from "../../service/EntityService";
import _ from 'lodash';
import {ProgramEnrolment, Checklist} from 'avni-models';
import {ChecklistItemActionNames, ChecklistItemActions} from "./ChecklistItemActions";
import {EditFormRuleResponse} from "rules-config";
import RuleEvaluationService from "../../service/RuleEvaluationService";

class ChecklistActions {
static getInitialState() {
return {};
}
function clone(state) {
const checklists = _.clone(state.checklists);

static clone(state) {
const checklists = _.clone(state.checklists);
return {
checklists: checklists,
individual: !_.isNil(state.individual) ? state.individual.cloneForReference() : null,
showSavedToast: false,
promptForSave: false,
editFormRuleResponse: state.editFormRuleResponse
};
}

class ChecklistActions {
static getInitialState() {
return {
checklists: checklists,
individual: !_.isNil(state.individual) ? state.individual.cloneForReference() : null,
showSavedToast: false,
promptForSave: false
editFormRuleResponse: EditFormRuleResponse.createEditAllowedResponse()
};
}

static onLoad(state, action, context) {
const newState = ChecklistActions.clone(state);
const newState = clone(state);
const enrolment = context.get(EntityService).findByUUID(action.enrolmentUUID, ProgramEnrolment.schema.name);

const checklists = [];
Expand All @@ -43,7 +49,7 @@ class ChecklistActions {
}

static onCompletionDateChange(state, action) {
const newState = ChecklistActions.clone(state);
const newState = clone(state);
const checklist = newState.checklists.find((checklist) => {
return checklist.name === action.checklistName
});
Expand All @@ -53,30 +59,51 @@ class ChecklistActions {
}

static onSave(state, action, context) {
const newState = ChecklistActions.clone(state);
const newState = clone(state);
newState.checklists.forEach((checklist) => {
context.get(EntityService).saveOrUpdate(checklist, Checklist.schema.name);
});
newState.showSavedToast = true;
newState.promptForSave = false;
return newState;
}

static onChecklistItemEdit(state, action, context) {
const editFormRuleResponse = context.get(RuleEvaluationService).runEditFormRule(action.checklistItem.detail.form, action.checklistItem, 'ChecklistItem');

if (editFormRuleResponse.isEditAllowed()) {
action.onContinueChecklistItemEdit();
return state;
} else {
const newState = {...state};
newState.editFormRuleResponse = editFormRuleResponse;
return newState;
}
}

static onChecklistItemEditErrorShown(state) {
return {...state, editFormRuleResponse: EditFormRuleResponse.createEditAllowedResponse()}
}
}

const ChecklistActionsNames = {
ON_LOAD: 'Checklist.ON_LOAD',
ON_CHECKLIST_ITEM_COMPLETION_DATE_CHANGE: 'Checklist.ON_CHECKLIST_ITEM_COMPLETION_DATE_CHANGE',
SAVE: 'Checklist.Save'
SAVE: 'Checklist.Save',
ON_CHECKLIST_ITEM_EDIT: 'Checklist.ON_EDIT',
ON_CHECKLIST_ITEM_EDIT_ERROR_SHOWN: 'Checklist.ON_EDIT_ERROR_SHOWN'
};

const ChecklistActionsMap = new Map([
[ChecklistActionsNames.ON_LOAD, ChecklistActions.onLoad],
[ChecklistActionsNames.ON_CHECKLIST_ITEM_COMPLETION_DATE_CHANGE, ChecklistActions.onCompletionDateChange],
[ChecklistActionsNames.SAVE, ChecklistActions.onSave]
[ChecklistActionsNames.SAVE, ChecklistActions.onSave],
[ChecklistActionsNames.ON_CHECKLIST_ITEM_EDIT, ChecklistActions.onChecklistItemEdit],
[ChecklistActionsNames.ON_CHECKLIST_ITEM_EDIT_ERROR_SHOWN, ChecklistActions.onChecklistItemEditErrorShown],
]);

export {
ChecklistActionsNames,
ChecklistActionsMap,
ChecklistActions
};
};
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
import ObservationsHolderActions from '../common/ObservationsHolderActions';
import _ from 'lodash';
import RuleEvaluationService from "../../service/RuleEvaluationService";
import ChecklistItemState from "../../state/ChecklistItemState";
import {ChecklistItem} from "avni-models";
import ChecklistService from "../../service/ChecklistService";
import RuleEvaluationService from "../../service/RuleEvaluationService";
import {ChecklistItem} from "openchs-models";

function filterFormElements(formElementGroup, context, checklistItem) {
const formElementStatuses = context.get(RuleEvaluationService).getFormElementsStatuses(checklistItem, ChecklistItem.schema.name, formElementGroup);
return formElementGroup.filterElements(formElementStatuses);
}

class ChecklistItemActions {
static getInitialState() {
return {};
}

static filterFormElements(formElementGroup, context, checklistItem) {
let formElementStatuses = context.get(RuleEvaluationService).getFormElementsStatuses(checklistItem, ChecklistItem.schema.name, formElementGroup);
return formElementGroup.filterElements(formElementStatuses);
};

static onLoad(state, action, context) {
const form = action.checklistItem.detail.form;

let firstGroupWithAtLeastOneVisibleElement = _.find(_.sortBy(form.nonVoidedFormElementGroups(), [function (o) {
return o.displayOrder
}]), (formElementGroup) => ChecklistItemActions.filterFormElements(formElementGroup, context, action.checklistItem).length !== 0);
}]), (formElementGroup) => filterFormElements(formElementGroup, context, action.checklistItem).length !== 0);

if (_.isNil(firstGroupWithAtLeastOneVisibleElement)) {
return ChecklistItemState.createOnLoadStateForEmptyForm(action.checklistItem, form, false);
}

let filteredElements = ChecklistItemActions.filterFormElements(firstGroupWithAtLeastOneVisibleElement, context, action.checklistItem);
let filteredElements = filterFormElements(firstGroupWithAtLeastOneVisibleElement, context, action.checklistItem);

return ChecklistItemState.createOnLoad(action.checklistItem, form, false, firstGroupWithAtLeastOneVisibleElement, filteredElements);
}
Expand Down
24 changes: 12 additions & 12 deletions packages/openchs-android/src/service/FormMappingService.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import {EncounterType, Form, FormMapping, Program, SubjectType} from "avni-model
import _ from "lodash";
import FormQueryResult from "./FormQueryResult";

function getEncounterTypeCriteria(subjectType, formType, entityCriteria) {
let criteria = `voided = false AND ${entityCriteria} AND form.formType="${formType}"`;
if (subjectType) {
criteria = `${criteria} and subjectType.uuid="${subjectType.uuid}"`
}
return criteria;
}

@Service("FormMappingService")
class FormMappingService extends BaseService {
constructor(db, beanStore) {
Expand Down Expand Up @@ -62,23 +70,15 @@ class FormMappingService extends BaseService {
};

findEncounterTypesForProgram(program: Program, subjectType: SubjectType) {
let criteria = this.getEncounterTypeCriteria(subjectType, Form.formTypes.ProgramEncounter, `entityUUID="${program.uuid}"`);
let criteria = getEncounterTypeCriteria(subjectType, Form.formTypes.ProgramEncounter, `entityUUID="${program.uuid}"`);
return this.getEncounterTypesForProgram(criteria);
}

findActiveEncounterTypesForProgram(program: Program, subjectType: SubjectType) {
let criteria = this.getEncounterTypeCriteria(subjectType, Form.formTypes.ProgramEncounter, `entityUUID="${program.uuid}"`);
let criteria = getEncounterTypeCriteria(subjectType, Form.formTypes.ProgramEncounter, `entityUUID="${program.uuid}"`);
return this.getEncounterTypesForProgram(criteria).filter(this.active);
}

getEncounterTypeCriteria(subjectType, formType, entityCriteria) {
let criteria = `voided = false AND ${entityCriteria} AND form.formType="${formType}"`;
if (subjectType) {
criteria = `${criteria} and subjectType.uuid="${subjectType.uuid}"`
}
return criteria;
}

getEncounterTypesForProgram(criteria) {
const formMappings = this.findAllByCriteria(criteria);
return _.uniqBy(formMappings
Expand All @@ -88,12 +88,12 @@ class FormMappingService extends BaseService {
}

findEncounterTypesForSubjectType(subjectType: SubjectType): EncounterType[] {
let criteria = this.getEncounterTypeCriteria(subjectType, Form.formTypes.Encounter, `entityUUID=null`);
let criteria = getEncounterTypeCriteria(subjectType, Form.formTypes.Encounter, `entityUUID=null`);
return this.getEncounterTypesForSubject(criteria);
}

findActiveEncounterTypesForSubjectType(subjectType: SubjectType): EncounterType[] {
let criteria = this.getEncounterTypeCriteria(subjectType, Form.formTypes.Encounter, `entityUUID=null`);
let criteria = getEncounterTypeCriteria(subjectType, Form.formTypes.Encounter, `entityUUID=null`);
return this.getEncounterTypesForSubject(criteria).filter(this.active);
}

Expand Down
18 changes: 8 additions & 10 deletions packages/openchs-android/src/views/program/ChecklistDisplay.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import PropTypes from 'prop-types';
import React from "react";
import {View} from "react-native";
import {Button, Text} from "native-base";
import {Duration} from 'avni-models';
import {Text} from "native-base";
import DGS from "../primitives/DynamicGlobalStyles";
import Fonts from "../primitives/Fonts";
import {ChecklistActions, ChecklistActionsNames as Actions} from "../../action/program/ChecklistActions";
import {ChecklistActionsNames as Actions} from "../../action/program/ChecklistActions";
import {ChecklistItemActionNames} from "../../action/program/ChecklistItemActions";
import Styles from "../primitives/Styles";
import _ from 'lodash';
import Distances from "../primitives/Distances";
import ChecklistItemDisplay from "./ChecklistItemDisplay";


export default (props) => (
export default ({data, i18n, reloadCallback, onChecklistItemEdit}) => (
<View style={{
marginHorizontal: DGS.resizeWidth(Distances.ContentDistanceFromEdge),
backgroundColor: Styles.whiteColor,
Expand All @@ -22,12 +19,12 @@ export default (props) => (
flexWrap: "nowrap",
paddingHorizontal: DGS.resizeWidth(13)
}}>
<Text style={{fontSize: Fonts.Large}}>{props.data.name}</Text>
{Object.entries(props.data.groupedItems).map(([state, items], idx) =>
<Text style={{fontSize: Fonts.Large}}>{data.name}</Text>
{Object.entries(data.groupedItems).map(([state, items], idx) =>
<View key={idx}
style={{marginTop: DGS.resizeHeight(10)}}>
<Text
style={{fontSize: Fonts.Medium}}>{props.i18n.t(_.startCase(state))}</Text>
style={{fontSize: Fonts.Medium}}>{i18n.t(_.startCase(state))}</Text>
<View style={{
flexDirection: 'row',
flexWrap: 'wrap',
Expand All @@ -41,7 +38,8 @@ export default (props) => (
applicableState={item.applicableState}
completionDateAction={Actions.ON_CHECKLIST_ITEM_COMPLETION_DATE_CHANGE}
undoAction={ChecklistItemActionNames.UNDO}
reloadCallback={props.reloadCallback}
reloadCallback={reloadCallback}
onEdit={onChecklistItemEdit}
/>
)}
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class ChecklistItemDisplay extends AbstractComponent {
reloadCallback: PropTypes.func,
style: PropTypes.object,
editable: PropTypes.bool,
onEdit: PropTypes.func.isRequired
};

constructor(props, context) {
Expand All @@ -30,7 +31,7 @@ class ChecklistItemDisplay extends AbstractComponent {
completeChecklistItem(checklistItem) {
return () => {
if (this.props.checklistItem.editable)
CHSNavigator.navigateToChecklistItemView(this, checklistItem);
this.props.onEdit(this.props.checklistItem);
else
Alert.alert(this.I18n.t("voidedChecklistItemDetailAlertTitle"), new ObservationsHolder(checklistItem.observations).toString(this.I18n));
}
Expand Down
14 changes: 13 additions & 1 deletion packages/openchs-android/src/views/program/ChecklistView.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import CHSContainer from "../common/CHSContainer";
import CHSContent from "../common/CHSContent";
import TypedTransition from "../../framework/routing/TypedTransition";
import ChecklistDisplay from "./ChecklistDisplay";
import CHSNavigator from "../../utility/CHSNavigator";
import AvniToast from "../common/AvniToast";

@Path('/ChecklistView')
class ChecklistView extends AbstractComponent {
Expand Down Expand Up @@ -65,11 +67,19 @@ class ChecklistView extends AbstractComponent {
}
}

onChecklistItemEdit(checklistItem) {
this.dispatchAction(Actions.ON_CHECKLIST_ITEM_EDIT, {
checklistItem,
onContinueChecklistItemEdit: () => CHSNavigator.navigateToChecklistItemView(this, checklistItem)
});
}

render() {
General.logDebug('ChecklistView', this.props.enrolmentUUID);
const checklists = this.state.checklists.map((checklist, idx) => <ChecklistDisplay key={idx}
data={checklist} i18n={this.I18n}
reloadCallback={() => this.dispatchAction(Actions.ON_LOAD, this.props)}/>);
reloadCallback={() => this.dispatchAction(Actions.ON_LOAD, this.props)}
onChecklistItemEdit={(checklistItem) => this.onChecklistItemEdit(checklistItem)}/>);
return (
<CHSContainer style={{backgroundColor: Colors.BlackBackground}}>
<CHSContent>
Expand All @@ -79,6 +89,8 @@ class ChecklistView extends AbstractComponent {
<ScrollView>
{checklists}
</ScrollView>
{this.state.editFormRuleResponse.isEditDisallowed() &&
<AvniToast message={this.I18n.t(this.state.editFormRuleResponse.getMessageKey())} onAutoClose={() => this.dispatchAction(Actions.ON_CHECKLIST_ITEM_EDIT_ERROR_SHOWN)}/>}
</CHSContent>
</CHSContainer>
);
Expand Down

0 comments on commit fde5af1

Please sign in to comment.