Skip to content

Commit

Permalink
feat: initial confirm modal implementation
Browse files Browse the repository at this point in the history
This commit adds in a modal replacement for $window.confirm() for better
security.  See #150 for reference.

The modal is available in the ModalService, which also contains some
TODOs for later development.  The service exposes a single method,
`confirm()` that will resolve with a boolean value, depending on what
the user click - "true" for accept and "false" for cancel.
  • Loading branch information
jniles committed Mar 27, 2016
1 parent 18053e7 commit 3c53437
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 16 deletions.
5 changes: 5 additions & 0 deletions client/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1874,6 +1874,11 @@
"REFERENCE_LOOKUP" : "Reference Lookup",
"NO_RECEIPT" : "No receipt matches that reference.",
"SUCCESS" : "Successfully found a receipt!"
},
"CONFIRM" : {
"TITLE" : "Action Confirmation",
"PROMPT" : "Are you sure you would like to perform that action?",
"ACCEPT" : "Accept"
}
},
"NO_EXCHANGE": {
Expand Down
59 changes: 59 additions & 0 deletions client/src/js/services/ModalService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
angular.module('bhima.services')
.service('ModalService', ModalService);

ModalService.$inject = [ '$uibModal' ];

/**
* Modal Service
*
* A service to house generic modals useful through out the application. These
* will replace a lot of the native JavaScript alerts/confirms to allow easier
* translation, testing, and functionality.
*
* @todo - build following methods/modals:
* - alert() to show a generic alert with "dismiss" or "acknowledge" button.
* It might be useful to have an associated icon and state (error, info,
* warning, etc).
*
* - sudo() to bring up a modal requiring correct user password entry and set
* the application state into super user mode (if appropriate)
*
* - confirmText() to bring up a "type this text to confirm" input that will
* only allow a user to enter text and only enable the "confirm" button once
* the text matches exactly what is anticipated.
*
*/
function ModalService(Modal) {
var service = this;

service.confirm = confirm;

/**
* Opens a "confirm delete" modal with a button for "Confirm" or "Cancel".
* The modal is a safe replacement for $window.confirm(), since you cannot
* disable javascript alerts from within it.
*
* @param {String} prompt - a translateable message to pass the template
* @param {Object} options - optional object with properties to configure the
* ui-bootstrap modal.
* @returns {Promise} result - a promise resolved by the modal instance
*/
function confirm(prompt, options) {

// default options for modal rendering
var opts = options || {};

var instance = Modal.open({
animation : opts.animation || false,
keyboard : opts.keyboard || true,
size : opts.size || 'md',
controller : 'ConfirmModalController as ConfirmModalCtrl',
resolve : { prompt : function provider() { return prompt;} },
templateUrl : '/partials/templates/modals/confirm.modal.html'
});

return instance.result;
}


}
2 changes: 0 additions & 2 deletions client/src/partials/billing_services/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
<div class="toolbar">

<div class="toolbar-item">

<!-- @todo - hide this on the create/update state -->
<a
class="btn btn-default"
data-method="create"
Expand Down
41 changes: 27 additions & 14 deletions client/src/partials/billing_services/list/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ angular.module('bhima.controllers')
.controller('BillingServicesListController', BillingServicesListController);

BillingServicesListController.$inject = [
'$translate', 'BillingServicesService', 'AccountService'
'$translate', 'BillingServicesService', 'AccountService', 'ModalService'
];

/**
Expand All @@ -11,7 +11,7 @@ BillingServicesListController.$inject = [
* This is the default controller for the billing services URL endpoint. It
* downloads and displays all billing services in the application via a ui-grid.
*/
function BillingServicesListController($translate, BillingServices, Accounts) {
function BillingServicesListController($translate, BillingServices, Accounts, Modals) {
var vm = this;

var editTemplate =
Expand Down Expand Up @@ -40,6 +40,14 @@ function BillingServicesListController($translate, BillingServices, Accounts) {
// default loading state - false;
vm.loading = false;

// delete handler (calls confirm modal)
vm.delete = del;

// HTTP error handler - binds error to view
function handler(response) {
vm.error = response.data;
}

// fired on state init
function startup() {

Expand All @@ -58,29 +66,34 @@ function BillingServicesListController($translate, BillingServices, Accounts) {
// populate the grid
vm.options.data = billingServices;
})
.catch(function (error) {
vm.error = error;
})
.catch(handler)
.finally(function () {
toggleLoadingIndicator();
});
}

// toggle the grid's loading indicator
function toggleLoadingIndicator() {
vm.loading = !vm.loading;
}

function edit(row) {
console.log('Clicked edit with:', row);
}

// handle deletes in the view
function del(row) {
console.log('Clicked delete with:', row);
Modals.confirm(null, { size : 'sm' })
.then(function (bool) {
// if the user did choose "accept", return early
if (!bool) { return bool; }

/** @todo - refactor this into a proper promise chain */
BillingServices.delete(row.id)
.then(function () {
vm.options.data = vm.options.data.filter(function (service) {
return service.id !== row.id;
});
});
})
.catch(handler);
}

vm.edit = edit;
vm.delete = del;


startup();
}
19 changes: 19 additions & 0 deletions client/src/partials/templates/modals/confirm.modal.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<div class="modal-header">
<h4>{{ "MODALS.CONFIRM.TITLE" | translate }}</h4>
</div>

<div class="modal-body">
<p class="text-left">
<span class="glyphicon glyphicon-info-sign text-primary"></span>
{{ (ConfirmModalCtrl.prompt || 'MODALS.CONFIRM.PROMPT') | translate }}
</p>
</div>

<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="ConfirmModalCtrl.close(true)">
{{ "MODALS.CONFIRM.ACCEPT" | translate }}
</button>
<button type="button" class="btn btn-default" ng-click="ConfirmModalCtrl.close(false)">
{{ "FORM.CANCEL" | translate }}
</button>
</div>
22 changes: 22 additions & 0 deletions client/src/partials/templates/modals/confirm.modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
angular.module('bhima.controllers')
.controller('ConfirmModalController', ConfirmModalController);

ConfirmModalController.$inject = ['$uibModalInstance', 'prompt'];

/**
* Confirm Modal Controller
*
* This controller provides bindings for the confirm modal.
*/
function ConfirmModalController(Instance, prompt) {
var vm = this;

/**
* bind the prompt to the view, if provided
* @todo - should this be done automatically with controllerAs?
*/
vm.prompt = prompt;

// bind modal controls
vm.close = Instance.close;
}

0 comments on commit 3c53437

Please sign in to comment.