diff --git a/client/src/i18n/en.json b/client/src/i18n/en.json index befb36c8dc..46b3084f17 100644 --- a/client/src/i18n/en.json +++ b/client/src/i18n/en.json @@ -1722,12 +1722,10 @@ }, "LOCATION": { "ADD" : "Add", - "COUNTRY" : "Country", "CURRENT" : "Current", "DESCRIPTION" : "Location Manager Configuration is used to create location and to store it in the system, locations are very importants when you register a patient and so one. Use this module to", - "ET" : "and", - "INFO_CREATE" : "create", - "INFO_MODIFY" : "modify", + "INFO_CREATE" : "Create", + "INFO_MODIFY" : "Modify", "LOCALITY" : "Locality", "LOCATION" : "Location", "MANAGER_CONFIGURATION" : "Location Manager Configuration", @@ -1737,15 +1735,16 @@ "MANAGE_VILLAGE" : "Manage Village", "NEW_FORM" : "New Form", "ORIGIN" : "Origin", - "PROVINCE" : "Province", - "REGISTER" : "Register a Location", + "REGISTER" : "Register a new Location", "REGISTERED" : "Registered Locations", - "REMOVE" : "remove", - "SECTOR" : "Sector", + "REMOVE" : "Remove", "TITLE" : "Location Manager", - "VILLAGE" : "Village", "VILLAGE_NAME" : "Village Name", - "VILLE" : "Township/Commune" + "VILLE" : "Township/Commune", + "COUNTRY" : "Country", + "PROVINCE" : "Province", + "SECTOR" : "Sector", + "VILLAGE" : "Village" }, "NO_EXCHANGE": { "CURRENT_DATE" : "Current Date", @@ -2584,9 +2583,9 @@ "TITLE" : "Sector Manager" }, "SELECT": { - "ACCOUNT" : "Select Account", - "ACCOUNT_REFERENTIIAL" : "Select the accounts referential", - "ACCOUNT_TYPE" : "Select Account Type", + "ACCOUNT" : "Select an Account", + "ACCOUNT_REFERENTIIAL" : "Select the Referential Account", + "ACCOUNT_TYPE" : "Select an Account Type", "ALL" : "All", "BALANCE_SECTION" : "Select Balance Sheet Section", "CASH" : "Select a Cash", @@ -2650,7 +2649,12 @@ "VALUE" : "Select a value", "VILLAGE" : "Select a Village", "WHOLE_YEAR_BUDGET" : "Select the annual budget", - "ZS" : "Select an health zone" + "ZS" : "Select an health zone", + "VILLAGE" : "Select a Village", + "SECTOR" : "Select a Sector", + "PROVINCE" : "Select a Province", + "COUNTRY" : "Select a Country", + "EMPTY" : "No matching records!" }, "SERVICE": { "COST_CENTER_SELECT" : "Cost center", diff --git a/client/src/js/directives/bhLocationSelect.js b/client/src/js/directives/bhLocationSelect.js new file mode 100644 index 0000000000..2c033182b2 --- /dev/null +++ b/client/src/js/directives/bhLocationSelect.js @@ -0,0 +1,240 @@ +angular.module('bhima.directives') + +/** + * Location Selection Component - bhLocationSelect + */ +.component('bhLocationSelect', { + templateUrl : 'partials/templates/bhLocationSelect.tmpl.html', + controller : LocationSelectController, + bindings: { + locationUuid: '=', // two-way binding + disable: '<', // one-way binding + validationTrigger: '<', // one-way binding + } +}); + +LocationSelectController.$inject = [ 'LocationService', '$scope' ]; + +/** + * Location Select Controller + * + * This component allows easy selection and validation of locations to be used + * throughout bhima. + * + * COMPONENT LIFECYCLE + * + * 1. On startup, all countries are downloaded and bound the view. If a + * location-uuid was provided, the location is immediately downloaded and + * selected in the view. + * + * 2. As the user changes each will fire off + * an HTTP request to load fresh data from the server. It will also clear the + * previous selections from dependent selects. + * + * 3. When the user finally selects a village, the location-uuid is updated + * with the village's uuid. + * + * BINDINGS + * + * 1. [location-uuid] : A two-way bound location uuid. The parent controller + * should expect this ID to contain the selected location. + * 2. [disable] : A hook to allow an external controller to disable the entire + * component. + * 3. [validation-trigger] : A hook to trigger validation on the component. + * Will usually be ParentForm.$submitted. + * + * @constructor + * @example + * + * + * + */ +function LocationSelectController(Locations, $scope) { + var vm = this; + + /** loading indicator */ + vm.loading = false; + + /** methods */ + vm.loadVillages = loadVillages; + vm.loadSectors = loadSectors; + vm.loadProvinces = loadProvinces; + vm.updateLocationUuid = updateLocationUuid; + + /** disabled bindings for individual component messages to be translated + * if there is no data, indicate that to the user. + */ + var selectCountry = 'SELECT.COUNTRY'; + var selectProvince = 'SELECT.PROVINCE'; + var selectSector = 'SELECT.SECTOR'; + var selectVillage = 'SELECT.VILLAGE'; + var noData = 'SELECT.EMPTY'; + vm.messages = { + country: selectCountry, + province: selectVillage, + sector: selectSector, + village: selectVillage, + }; + + // load the countries once, at startup + Locations.countries() + .then(function (countries) { + vm.countries = countries; + + // if there are countries to select, show a "select a country" message + // however, if there isn't any data, show a "no data" message. This pattern + // is used throughout the component. + vm.messages.country = (countries.length > 0) ? + selectCountry : + noData; + }); + + /** load the provinces, based on the country selected */ + function loadProvinces() { + + // don't send an HTTP request if there is no country + if (!vm.country || !vm.country.uuid) { return; } + + // allow the elements + vm.sectors = []; + vm.villages = []; + }); + } + + /** load the sectors, based on the province selected */ + function loadSectors() { + + // don't send an HTTP request if there is no province + if (!vm.province || !vm.province.uuid) { return; } + + // allow the to be selected + vm.disabled.village = false; + + // fetch the villages from the server + Locations.villages({ sector : vm.sector.uuid }) + .then(function (villages) { + vm.villages = villages; + + // show the appropriate message to the user + vm.messages.village = (villages.length > 0) ? + selectVillage : + noData; + }); + } + + /** updates the exposed location uuid for the client to use */ + function updateLocationUuid() { + vm.locationUuid = vm.village.uuid; + console.log('Location updated to:', vm.locationUuid); + } + + /** + * If a location has been provided or changes, reload the datasource with the + * provided location uuid. + * @method loadLocation + * @private + */ + function loadLocation() { + + // make sure we actually have an initial location (prevents needless firing + // during $scope churn). + if (!vm.locationUuid) { return; } + + // if the location is already selected, do not reload all datasoures. This + // condition will occur when we manually called updateLocationUuid() from + // the village elementin the view + vm.village = { + uuid : initial.villageUuid, + village : initial.village, + }; + + vm.sector = { + uuid : initial.sectorUuid, + sector : initial.sector, + }; + + vm.province = { + uuid : initial.provinceUuid, + province : initial.province, + }; + + vm.country = { + uuid : initial.countryUuid, + country : initial.country, + }; + + // refresh all data sources to allow a user to use the class="form-control" id="location-select-<%CONFIGID%>">' + - // '' + - ''; - var configurationList = Object.keys(config).reverse(); - - configurationList.forEach(function (key) { - var configObject = config[key]; - var component = componentStructure; - - var changeSubmit = lookupDependency(key) ? - '\"locationSelect.<%NAMESPACE%>.fetchLocationData(\'' + lookupDependency(key) + '\', locationSelect.<%NAMESPACE%>.locationStore.' + configObject.id + '.value)\"' : - '\"locationSelect.<%NAMESPACE%>.submitVillage(locationSelect.<%NAMESPACE%>.locationStore.' + configObject.id + '.value)\"'; - - changeSubmit = changeSubmit.replace(/<%NAMESPACE%>/g, submitCallback); - - component = component.replace(/<%CONFIGID%>/g, configObject.id); - component = component.replace(/<%CONFIGLABEL%>/g, configObject.label); - component = component.replace(/<%CONFIGCOLUMN%>/g, configObject.column); - component = component.replace(/<%CONFIGDEPEND%>/g, lookupDependency(key)); - component = component.replace(/<%NAMESPACE%>/g, submitCallback); - component = component.replace(/<%CONFIGCHANGE%>/g, changeSubmit); - - compile = compile.concat(component); - }); - - return compile; - } - - namespace.lookupModel = lookupModel; - namespace.fetchLocationData = fetchLocationData; - namespace.submitVillage = submitVillage; - } - }; -}]); -*/ diff --git a/client/src/js/services/LocationService.js b/client/src/js/services/LocationService.js index 14bdf6431b..22aa4c85e0 100644 --- a/client/src/js/services/LocationService.js +++ b/client/src/js/services/LocationService.js @@ -22,8 +22,8 @@ function LocationService($http, util) { * wrapper for HTTP requests made to the baseUrl endpoint * @private */ - function request(path) { - return $http.get(baseUrl.concat(path)) + function request(path, options) { + return $http.get(baseUrl.concat(path), options) .then(util.unwrapHttpResponse); } @@ -31,8 +31,8 @@ function LocationService($http, util) { * fetch a list of villages from the server * @public */ - function villages() { - return request('/villages'); + function villages(options) { + return request('/villages', { params : options }); } @@ -40,16 +40,16 @@ function LocationService($http, util) { * fetch a list of sectors from the server * @public */ - function sectors() { - return request('/sectors'); + function sectors(options) { + return request('/sectors', { params : options }); } /** * fetch a list of provinces from the server * @public */ - function provinces() { - return request('/provinces'); + function provinces(options) { + return request('/provinces', { params : options }); } /** @@ -68,7 +68,7 @@ function LocationService($http, util) { * @public */ function location(uuid) { - return request('detail/'.concat(uuid)); + return request('/detail/'.concat(uuid)); } return service; diff --git a/client/src/partials/locations/locations.html b/client/src/partials/locations/locations.html index 507129d64b..a49f73f7b9 100644 --- a/client/src/partials/locations/locations.html +++ b/client/src/partials/locations/locations.html @@ -61,7 +61,6 @@

{{ 'LOCATION.MANAGER_CONFIGURATION' | translate }}

{{ 'LOCATION.DESCRIPTION' | translate }} {{ 'LOCATION.INFO_CREATE' | translate }}, {{ 'LOCATION.INFO_MODIFY' | translate }} - {{ 'LOCATION.ET' | translate }} {{ 'LOCATION.REMOVE' | translate }} {{ 'LOCATION.LOCATION' | translate }}.

diff --git a/client/src/partials/patients/registration/registration.html b/client/src/partials/patients/registration/registration.html index 72d5d84581..23e7d4dda6 100644 --- a/client/src/partials/patients/registration/registration.html +++ b/client/src/partials/patients/registration/registration.html @@ -1,18 +1,18 @@
- +
  1. {{ "TREE.HOSPITAL" | translate }}
  2. {{ "PATIENT_REG.PAGE_TITLE" | translate }}
- +
- +
@@ -20,9 +20,9 @@
- + -
@@ -31,9 +31,9 @@
- + -
@@ -42,9 +42,9 @@
- + -
@@ -55,27 +55,27 @@
-
- +
- +

{{ "UTIL.PATIENT_EXIT" | translate }}

-
- +
@@ -86,19 +86,19 @@
- +
- +
- + -
@@ -118,39 +118,37 @@ +
- +
- + -

{{ "PATIENT_REG.ORIGIN_LOCATION" | translate }}

-
- - - - + +
- +

- +

{{ "PATIENT_REG.CURRENT_LOCATION" | translate }}

-
- - - + +
- + -
+
{{ "PATIENT_REG.CURRENT_LOCATION" | translate }}
- +
{{ "PATIENT_REG.OPTIONAL_INFO" | translate }}
- +
diff --git a/client/src/partials/templates/bhLocationSelect.tmpl.html b/client/src/partials/templates/bhLocationSelect.tmpl.html new file mode 100644 index 0000000000..42fc0fa3b4 --- /dev/null +++ b/client/src/partials/templates/bhLocationSelect.tmpl.html @@ -0,0 +1,87 @@ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +

+ + + {{ "LOCATION.REGISTER_LOCATION" | translate }} + +

+ + +

+

{{ "FORM.REQUIRED" | translate }}

+

+
diff --git a/server/config/express.js b/server/config/express.js index 0d8e44675c..28db042e1b 100644 --- a/server/config/express.js +++ b/server/config/express.js @@ -46,9 +46,8 @@ exports.configure = function configure(app) { next(); }); - // NOTE -- EXPERIMENTAL - // reject PUTs and POSTs with empty objects in the data - // property with a 400 error + // reject PUTs and POSTs with empty objects in the data property with a 400 + // error app.use(function (req, res, next) { if (req.method !== 'PUT' && req.method !== 'POST') { return next(); @@ -67,7 +66,7 @@ exports.configure = function configure(app) { // provide a stream for morgan to write to winston.stream = { write : function (message, encoding) { - winston.log('info', message.trim()); + winston.info(message.trim()); } }; diff --git a/server/config/routes.js b/server/config/routes.js index 1252e7b1d7..b205e8af2c 100644 --- a/server/config/routes.js +++ b/server/config/routes.js @@ -15,7 +15,7 @@ var winston = require('winston'); var auth = require('../controllers/auth'); var data = require('../controllers/data'); var users = require('../controllers/users'); -var locations = require('../controllers/location'); +var locations = require('../controllers/locations'); var tree = require('../controllers/tree'); var createPurchase = require('../controllers/finance/purchase'); var createSale = require('../controllers/finance/sale'); diff --git a/server/controllers/locations.js b/server/controllers/locations.js index c07193f1e9..c84f31f599 100644 --- a/server/controllers/locations.js +++ b/server/controllers/locations.js @@ -114,7 +114,8 @@ exports.countries = function countries(req, res, next) { 'use strict'; var sql = - 'SELECT country.uuid, country.country_en AS name FROM country;'; + 'SELECT country.uuid, country.country_en AS name FROM country ' + + 'ORDER BY country.country_en ASC;'; db.exec(sql) .then(function (data) {