Skip to content
This repository has been archived by the owner on Jan 9, 2023. It is now read-only.

Commit

Permalink
Outpatient (OPD) Module (#904)
Browse files Browse the repository at this point in the history
* Initial functionality to display visits and checkin patients.

* Don't show age if it isn't set

* Only show today's outpatients.

* Added visit reason

* Fixed visit edit and changed age display

* Fixed return to outpatient

* Set end date on outpatient check in.

* Added tabbed navigation to visits.

* Fix adding imaging and lab requests from visit.

* New component to show patient orders

combines labs, imaging and medication into one table.

* Added filtering to sortable columns

* Fixed missing translations

* Added sorting and started filtering.

* Fixed filter display

* Created typeahead filter.

* Fix issue with idb.filesystem

Fixes Expected an identifier but found \'IDB_SUPPORTS_BLOB\' instead

* Added clear for typeahead filter

* Fixed menu translation

* Moved filterList to mixin

* Added filtering to outpatient list.

* Put back history fields

Also renamed reason for visit to chief complaint.

* Don't show charges header on visit

* Fix failing tests.

* Starting changes to show different fields per visit type.

* Starting work on customizing forms per visit type.

* Added time to lab, imaging, and medication requests.

* Fixed sorting on outpatient list.

* Added short display name

* Added hasAppointment and hasDoneOrders

* Change to record check in time

* Added check in time, has appointment, done orders

and stubbed out checkout button.

* Fixed deprecation warning.

* Cleaning up layout

* Set hasAppointment on visit checkin

Resolves #754

* Starting work on custom forms editor.

* Working on custom form editor.

* Changed to work with new custom-form model.

* Added preview and ordering.

* Fix error when there are no fields.

* Fixed colspan layout.

* Fixed column layout

Updated for live preview.
Fix for empty fields.

* Added formType and column validation

* Added index page to list custom forms.

* Added Custom Forms to navigation.

* Fixed custom form delete

* Updates for testing.

* Finished custom form editor tests

Closes #788

* Added capability to run store query for additional models.

* Added custom-form-manager component

Allows adding custom forms to screens.

* Fixed patientVisits computed property

Fixed issue when findPatientVisits is false.

* Added logic to track modifications made to customForms on an object.

* Added custom forms to visit.

* Fixed breaking test

* Updated to use npm uuid

* Moving diagnosis to its own model

Allow adding of diagnosis directly from patient.
For issue #816

* Added logic to edit and delete diagnoses.

* Changed to disable changing diagnosis on existing record.

* Added logic to copy diagnosis from patient

when creating a new visit

* Diagnosis list should be tied to diagnosis model

* Added primaryDiagnoses CP to visit.

primaryDiagnoses should be used instead of primaryDiagnosis which is a
deprecated property.

* Prod build for testing

* Fix for model test

* Replaced observers from patient-submodule mixing.

* Make sure New Patient button shows on patient index page.

* Fix visit edit to only show checkin on checkin

Checkin verbiage was sticking on visit screen after checking patient in.
Also save messages on visit were not accurate when checking a patient
in.
Fixes #836

* Fixed error on patient index.

* Fixed checkout action on outpatient.

* Made diagnosis async = false

Makes sure diagnosis is resolved when creating new visits for a patient.

* Fixed return to patient from visit

* Fix return to outpatient from visit.

* Changes to make sure visit screen works properly for admissions as well as checkins.

* Allow patient checkin from appointment

Partially addresses #835

* Fixed validation when patient is already selected for checkin

* Visit checkin from appointment now updates appointment

For issue #835

* Cleaned up unused data values.

* Add reason for visit

Resolves #835

* Set admitted status on admission.

* Only show check in button if patient isn't already checked in.

* Close modal on diagnosis delete

Also, make sure diagnosis gets deleted.

* Fixed broken tests

* Build for testing purposes only.

* Merging in master

* Fix sex lookup list for patient quick add

Fixes # #878

* Fixed deleting orders/sorting/filtering

Fixes #886

* Added filters to outpatient screen

Resolves #860
Also, fixed outpatient screen to no longer display admission type
visits.

* New build for testing

* Updated allergies/procedures to match with diagnosis layout.

* Updated tests to include diagnosis.

* Add updateButtonIcon option to edit panel component.

* Add description of dateSetAction

* Fixes return from visit under certain circumstances

* Show visit status and discharge/checkout time when applicable.

Resolves #895

* Normalize how checkin/admission works across the app

Resolves #893

* Fixed issue when deleting visits.

* Fixed disabled update button when model isn't valid.

* Make sure visitType isn't set to Admission when checking in from outpatient.

* Add phone and address to patient quick add

Resolves #898

* Added create new patient checkbox for visit

Resolves #899

* Changed layout to inline search button

* Merge branch 'master' into outpatient

* Fixed diagnosis not displaying properly after update

Fixes #903

* Fix for getMinPouchId

* Added tests for outpatient.
  • Loading branch information
jkleinsc committed Dec 27, 2016
1 parent 3786873 commit 51a246a
Show file tree
Hide file tree
Showing 116 changed files with 3,787 additions and 1,107 deletions.
160 changes: 160 additions & 0 deletions app/admin/custom-forms/edit/controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import AbstractEditController from 'hospitalrun/controllers/abstract-edit-controller';
import Ember from 'ember';
import uuid from 'npm:uuid';

const {
computed,
isEmpty
} = Ember;

export default AbstractEditController.extend({
preview: false,
previewModel: Ember.Object.create(),
updateCapability: 'update_config',

afterUpdate() {
this.displayAlert(this.get('i18n').t('admin.customForms.titles.formSaved'), this.get('i18n').t('admin.customForms.messages.formSaved', this.get('model')));
},

actions: {
addField() {
let newField = this.store.createRecord('custom-field');
this.send('openModal', 'admin.custom-forms.field-edit', newField);
},

deleteField(field) {
let fields = this.get('model.fields');
fields.removeObject(field);
},

editField(field) {
if (isEmpty(field)) {
field = this.store.createRecord('custom-field');
}
this.send('openModal', 'admin.custom-forms.field-edit', field);
},

moveFieldDown(field) {
let fields = this.get('model.fields');
let currentFieldIdx = fields.indexOf(field);
let nextField = fields.objectAt(currentFieldIdx + 1);
fields.replace(currentFieldIdx, 2, [nextField, field]);
},

moveFieldUp(field) {
let fields = this.get('model.fields');
let previousFieldIdx = (fields.indexOf(field) - 1);
let previousField = fields.objectAt(previousFieldIdx);
fields.replace(previousFieldIdx, 2, [field, previousField]);
},

togglePreview() {
this.toggleProperty('preview');
},

updateField(field) {
if (field.get('isNew')) {
this._addNewField(field);
}
this.get('model').save();
}
},

formName: computed('model.name', function() {
let i18n = this.get('i18n');
let formName = this.get('model.name');
if (isEmpty(formName)) {
return i18n.t('admin.customForms.labels.newForm');
} else {
return formName;
}
}),

formTypeValues: [
'patient',
'visit'
],

formTypes: computed(function() {
let i18n = this.get('i18n');
let formTypeValues = this.get('formTypeValues');
return formTypeValues.map((formTypeId) => {
return {
id: formTypeId,
value: i18n.t(`admin.customForms.labels.${formTypeId}FormType`)
};
}).sort(function(a, b) {
return Ember.compare(a.value.toString(), b.value.toString());
});
}),

lastFieldIndex: computed('model.fields.length', function() {
return this.get('model.fields.length') - 1;
}),

fieldTypeLabel(fieldType) {
let i18n = this.get('i18n');
return i18n.t(`admin.customForms.labels.${fieldType}`);
},

_addNewField(field) {
let changedAttributes = field.changedAttributes();
let fieldAttributes = {};
let store = this.get('store');
this._generatePropertyNames(field);
Object.keys(changedAttributes).forEach((attributeName) => {
let [, newValue] = changedAttributes[attributeName];
fieldAttributes[attributeName] = newValue;
});
fieldAttributes.property = field.get('property');
let newField = store.push({
data: {
id: uuid.v4(),
type: 'custom-field',
attributes: fieldAttributes
}
});
let formFields = this.get('model.fields');
formFields.addObject(newField);
},

_generatePropertyNames(field) {
let type = field.get('type');
let propertyName = this._getPropertyName(field);
if (type === 'checkbox') {
let values = field.get('values');
values.forEach((value, index) => {
value.set('property', `${propertyName}${(index + 1)}`);
});
} else {
field.set('property', propertyName);
}
},

_getPropertyName(field) {
let camelizedLabel = field.get('label').camelize();
let labelIndex = 1;
let propertyName = camelizedLabel;
while (this._isPropertyUsed(propertyName) && labelIndex < 10) {
propertyName = `${camelizedLabel}${++labelIndex}`;
}
return propertyName;
},

_isPropertyUsed(propertyName) {
let fields = this.get('model.fields');
let existingProperty = fields.findBy('property', propertyName);
if (!isEmpty(existingProperty)) {
return true;
} else {
let checkboxes = fields.filterBy('type', 'checkbox');
return checkboxes.any((checkbox) =>{
existingProperty = checkbox.get('values').findBy('property', propertyName);
if (!isEmpty(existingProperty)) {
return true;
}
});
}
}

});
14 changes: 14 additions & 0 deletions app/admin/custom-forms/edit/route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import AbstractEditRoute from 'hospitalrun/routes/abstract-edit-route';
import { translationMacro as t } from 'ember-i18n';
export default AbstractEditRoute.extend({
hideNewButton: true,
newTitle: t('admin.customForms.titles.newCustomForm'),
editTitle: t('admin.customForms.titles.editCustomForm'),
modelName: 'custom-form',

actions: {
allItems() {
this.transitionTo('admin.custom-forms');
}
}
});
69 changes: 69 additions & 0 deletions app/admin/custom-forms/edit/template.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{{#edit-panel editPanelProps=editPanelProps}}
{{#em-form model=model submitButton=false}}
<div class="row">
{{em-input property="name" label=(t 'admin.customForms.labels.formName') class="form-input-group required col-xs-4 custom-form-name"}}
{{em-select class="col-xs-3 form-input-group required custom-form-type" label=(t 'admin.customForms.labels.formType')
property="formType" content=formTypes prompt=" "
}}
{{em-input property="columns" label=(t 'admin.customForms.labels.columns') class="form-input-group col-xs-2 custom-form-columns"}}
</div>
<h4>
{{t 'admin.customForms.titles.fields'}}
<button class="btn btn-default align-right" {{action 'addField'}}>
{{t 'admin.customForms.buttons.addField'}}
</button>
</h4>
<table class="table">
<tr class="table-header">
<th>{{t 'admin.customForms.labels.label'}}</th>
<th>{{t 'labels.type'}}</th>
<th>{{t 'labels.action'}}</th>
</tr>
{{#each model.fields as |field index|}}
<tr>
<td>{{field.label}}</td>
<td>
{{#if field.type}}
{{t (concat 'admin.customForms.labels.' field.type)}}
{{/if}}
</td>
<td>
<button class="btn btn-default warning" {{action "deleteField" field bubbles=false }}>
<span class="octicon octicon-x"></span>{{t "buttons.delete"}}
</button>
<button class="btn btn-default neutral" {{action 'editField' field bubbles=false }}>
{{t 'buttons.edit'}}
</button>
{{#if (not-eq index 0)}}
<button class="btn btn-default neutral" {{action 'moveFieldUp' field bubbles=false }}>
<span class="glyphicon glyphicon-chevron-up" aria-hidden="true"></span>
{{t 'admin.customForms.buttons.moveUp'}}
</button>
{{/if}}
{{#if (lt index lastFieldIndex)}}
<button class="btn btn-default neutral" {{action 'moveFieldDown' field bubbles=false }}>
<span class="glyphicon glyphicon-chevron-down" aria-hidden="true"></span>
{{t 'admin.customForms.buttons.moveDown'}}
</button>
{{/if}}
</td>
</tr>
{{/each}}
</table>
{{/em-form}}
<button class="btn btn-default neutral" {{action 'togglePreview' bubbles=false }}>
{{t 'admin.customForms.buttons.preview'}}
</button>
{{#if preview}}
<div class="panel panel-primary form-preview">
<div class="panel-heading">
<h3 class="panel-title">{{formName}}</h3>
</div>
<div class="panel-body">
{{#em-form model=previewModel submitButton=false}}
{{custom-form model=previewModel form=model}}
{{/em-form}}
</div>
</div>
{{/if}}
{{/edit-panel}}
66 changes: 66 additions & 0 deletions app/admin/custom-forms/field-edit/controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import AbstractEditController from 'hospitalrun/controllers/abstract-edit-controller';
import Ember from 'ember';

const {
computed,
isEmpty
} = Ember;

export default AbstractEditController.extend({
editController: Ember.inject.controller('admin/custom-forms/edit'),
cancelAction: 'closeModal',
i18n: Ember.inject.service(),

actions: {
addValue() {
let fieldValues = this.get('model.values');
if (isEmpty(fieldValues)) {
let model = this.get('model');
fieldValues = [];
model.set('values', fieldValues);
}
fieldValues.addObject(Ember.Object.create());
},

deleteValue(valueToDelete) {
let fieldValues = this.get('model.values');
fieldValues.removeObject(valueToDelete);
},

selectType(fieldType) {
this.get('model').set('type', fieldType);
},

update() {
this.get('editController').send('updateField', this.get('model'));
this.send('closeModal');
}
},

fieldTypeValues: [
'checkbox',
'radio',
'select',
'text',
'textarea'
],

fieldTypes: computed(function() {
let i18n = this.get('i18n');
let fieldTypeValues = this.get('fieldTypeValues');
return fieldTypeValues.map((fieldTypeId) => {
return {
id: fieldTypeId,
value: i18n.t(`admin.customForms.labels.${fieldTypeId}`)
};
}).sort(function(a, b) {
return Ember.compare(a.value.toString(), b.value.toString());
});
}),

showValues: computed('model.type', function() {
let type = this.get('model.type');
return (type === 'checkbox' || type === 'radio' || type === 'select');
})

});
77 changes: 77 additions & 0 deletions app/admin/custom-forms/field-edit/template.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{{#modal-dialog
isUpdateDisabled=isUpdateDisabled
title=(t (if model.isNew 'admin.lookup.edit.template.addTitle' 'admin.lookup.edit.template.editTitle'))
updateButtonAction=updateButtonAction
updateButtonText= updateButtonText}}
{{#em-form model=model submitButton=false}}
<div class="row">
<div class="form-input-group col-xs-6">
<label for="fieldTypeSelect">{{t 'labels.type'}}</label>
<select onchange={{action 'selectType' value="target.value"}} class="form-control custom-field-select" id="fieldTypeSelect">
<option disabled selected={{is-not model.type}}></option>
{{#each fieldTypes as |fieldType|}}
<option value="{{fieldType.id}}" selected={{eq model.type fieldType.id}}>
{{fieldType.value}}
</option>
{{/each}}
</select>
</div>
{{em-input property="label" class="col-xs-6 required custom-field-label" label=(t 'admin.customForms.labels.label')}}
</div>
<div class="row">
{{em-input property="colSpan" class="col-xs-5 custom-field-colspan" label=(t 'admin.customForms.labels.colSpan')}}
</div>
{{#if showValues}}
<h4>
{{#if (eq model.type 'radio')}}
{{t 'admin.customForms.titles.radioValues'}}
{{/if}}
{{#if (eq model.type 'select')}}
{{t 'admin.customForms.titles.dropDownValues'}}
{{/if}}
{{#if (eq model.type 'checkbox')}}
{{t 'admin.customForms.titles.checkboxValues'}}
{{/if}}
<button class="btn btn-primary align-right" {{action 'addValue'}}>
{{t "buttons.addValue"}}
</button>
</h4>
<table class="table">
<tr class="table-header">
{{#if (eq model.type 'checkbox')}}
<th>{{t 'admin.customForms.labels.label'}}</th>
{{else}}
<th>{{t 'labels.value'}}</th>
{{/if}}
<th>{{t 'labels.action'}}</th>
</tr>
{{#each model.values as |value|}}
<tr>
<td>
{{input value=value.label class="form-control custom-field-value"}}
</td>
<td>
<button class="btn btn-default warning delete-field-value" {{action "deleteValue" value bubbles=false }}>
<span class="octicon octicon-x"></span>{{t "buttons.delete"}}
</button>
</td>
</tr>
{{/each}}
</table>
{{/if}}
{{#if (eq model.type 'radio')}}
<div class="checkbox">
<label>
{{input type="checkbox" checked=model.includeOtherOption}}
{{t 'admin.customForms.labels.includeOtherOption'}}
</label>
</div>
{{#if model.includeOtherOption}}
<div class="form-group">
<label>{{t 'admin.customForms.labels.otherOptionLabel'}}</label>
{{input class="form-control" value=model.otherOptionLabel}}
</div>
{{/if}}
{{/if}}
{{/em-form}}
{{/modal-dialog}}
5 changes: 5 additions & 0 deletions app/admin/custom-forms/index/controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import AbstractPagedController from 'hospitalrun/controllers/abstract-paged-controller';
export default AbstractPagedController.extend({
addPermission: 'update_config',
deletePermission: 'update_config'
});
Loading

0 comments on commit 51a246a

Please sign in to comment.