initial implementation of staged, arrived, and delivered tabs
authorGalen Charlton <gmc@equinoxinitiative.org>
Thu, 28 May 2020 14:52:05 +0000 (10:52 -0400)
committerGalen Charlton <gmc@equinoxinitiative.org>
Thu, 28 May 2020 14:52:05 +0000 (10:52 -0400)
TODO:

* hook up arrived and delivered button on the staged tab
* add a progress bar when the delivered action is invoked
* add more error handling for the delivered action
* add 'checked out' language to the delivered buttons?
* include list of holds on the arrived tab?

Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/templates/staff/circ/curbside/index.tt2
Open-ILS/src/templates/staff/circ/curbside/t_arrived_manager.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/circ/curbside/t_delivered_manager.tt2 [new file with mode: 0644]
Open-ILS/src/templates/staff/circ/curbside/t_main.tt2
Open-ILS/src/templates/staff/circ/curbside/t_staged_manager.tt2 [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/circ/curbside/directives/arrived_manager.js [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/circ/curbside/directives/delivered_manager.js [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/circ/curbside/directives/staged_manager.js [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/circ/curbside/services/core.js

index 4ad4410..7955e26 100644 (file)
@@ -9,11 +9,20 @@
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/curbside/services/core.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/curbside/app.js"></script>
 <script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/curbside/directives/to_be_staged_manager.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/curbside/directives/staged_manager.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/curbside/directives/arrived_manager.js"></script>
+<script src="[% ctx.media_prefix %]/js/ui/default/staff/circ/curbside/directives/delivered_manager.js"></script>
 <script>
 angular.module('egCoreMod').run(['egStrings', function(s) {
     s.SUCCESS_CURBSIDE_MARK_STAGED = "[% l('Marked curbside appointment {{slot_id}} as staged') %]";
     s.NOTFOUND_CURBSIDE_MARK_STAGED = "[% l('Could not find curbside appointment {{slot_id}} to mark as staged') %]";
     s.FAILED_CURBSIDE_MARK_STAGED = "[% l('Failed to mark curbside appointment {{slot_id}} as staged: {{evt_code}}') %]";
+    s.SUCCESS_CURBSIDE_MARK_ARRIVED = "[% l('Marked curbside appointment {{slot_id}} as patron arrived') %]";
+    s.NOTFOUND_CURBSIDE_MARK_ARRIVED = "[% l('Could not find curbside appointment {{slot_id}} to mark as patron arrived') %]";
+    s.FAILED_CURBSIDE_MARK_ARRIVED = "[% l('Failed to mark curbside appointment {{slot_id}} as patron arrived: {{evt_code}}') %]";
+    s.SUCCESS_CURBSIDE_MARK_DELIVERED = "[% l('Marked curbside appointment {{slot_id}} as delivered') %]";
+    s.NOTFOUND_CURBSIDE_MARK_DELIVERED = "[% l('Could not find curbside appointment {{slot_id}} to mark as delivered') %]";
+    s.FAILED_CURBSIDE_MARK_DELIVERED = "[% l('Failed to mark curbside appointment {{slot_id}} as delivered: {{evt_code}}') %]";
 }]);
 </script>
 [% END %]
diff --git a/Open-ILS/src/templates/staff/circ/curbside/t_arrived_manager.tt2 b/Open-ILS/src/templates/staff/circ/curbside/t_arrived_manager.tt2
new file mode 100644 (file)
index 0000000..e34e9b4
--- /dev/null
@@ -0,0 +1,31 @@
+<div>
+  <div ng-style="{visibility : refreshNeeded ? 'visible' : 'hidden'}" class="alert alert-warning">
+    [% l('Updates to the list of appointments whose patron has arrived are available. Please refresh.') %]
+  </div>
+  <eg-grid
+    id-field="slot_id"
+    features="-sort,-multisort,-picker,-multiselect"
+    items-provider="gridDataProvider"
+    grid-controls="gridControls"
+    dateformat="{{$root.egDateAndTimeFormat}}">
+
+    <eg-grid-menu-item handler="refresh_arrived" standalone="true"
+        label="[% l('Refresh List')%]"></eg-grid-menu-item>
+
+    <eg-grid-field label="[% l('Pickup Date/Time') %]" path="slot.slot" datatype="timestamp"></eg-grid-field>
+    <eg-grid-field label="[% l('Patron') %]" path="slot.patron">
+      <a href="./circ/patron/{{item.slot.patron().id()}}/holds" target="_blank">
+        {{item.slot.patron().family_name()}} / {{item.slot.patron().card().barcode()}}
+        <span class="glyphicon glyphicon-new-window"></span>
+      </a>
+    </eg-grid-field>
+    <eg-grid-field label="[% l('Appointment ID') %]" path="slot.id"></eg-grid-field>
+    <eg-grid-field label="[% l('Action') %]" handlers="gridCellHandlers" compiled>
+      <button class="btn btn-sm btn-primary"
+        ng-disabled="col.handlers.wasHandled(item['slot_id'])"
+        ng-click="col.handlers.mark_delivered(item['slot_id'])">
+        [% l('Mark As Items Delivered') %]
+      </button>
+    </eg-grid-field>
+  </eg-grid>
+</div>
diff --git a/Open-ILS/src/templates/staff/circ/curbside/t_delivered_manager.tt2 b/Open-ILS/src/templates/staff/circ/curbside/t_delivered_manager.tt2
new file mode 100644 (file)
index 0000000..d37f4b5
--- /dev/null
@@ -0,0 +1,24 @@
+<div>
+  <div ng-style="{visibility : refreshNeeded ? 'visible' : 'hidden'}" class="alert alert-warning">
+    [% l('Updates to the list of appointments whose items were delivered are available. Please refresh.') %]
+  </div>
+  <eg-grid
+    id-field="id"
+    features="-sort,-multisort,-picker,-multiselect"
+    items-provider="gridDataProvider"
+    grid-controls="gridControls"
+    dateformat="{{$root.egDateAndTimeFormat}}">
+
+    <eg-grid-menu-item handler="refresh_delivered" standalone="true"
+        label="[% l('Refresh List')%]"></eg-grid-menu-item>
+
+    <eg-grid-field label="[% l('Pickup Date/Time') %]" path="slot" datatype="timestamp"></eg-grid-field>
+    <eg-grid-field label="[% l('Patron') %]" path="patron">
+      <a href="./circ/patron/{{item.patron().id()}}/holds" target="_blank">
+        {{item.patron().family_name()}} / {{item.patron().card().barcode()}}
+        <span class="glyphicon glyphicon-new-window"></span>
+      </a>
+    </eg-grid-field>
+    <eg-grid-field label="[% l('Appointment ID') %]" path="id"></eg-grid-field>
+  </eg-grid>
+</div>
index 5f7eb3b..5f81f3b 100644 (file)
       </uib-tab>
       <uib-tab index="'staged'" heading="[% l('Staged And Ready') %]">
         <div class="container-fluid">
-           XXX
+           <eg-curbside-staged-manager ng-if="active_tab === 'staged'"></eg-curbside-staged-manager>
         </div>
       </uib-tab>
       <uib-tab index="'arrived'" heading="[% l('Patron Is Outside') %]">
         <div class="container-fluid">
-           XXX
+           <eg-curbside-arrived-manager ng-if="active_tab === 'arrived'"></eg-curbside-arrived-manager>
         </div>
       </uib-tab>
       <uib-tab index="'delivered'" heading="[% l('Delivered Today') %]">
         <div class="container-fluid">
-           XXX
+           <eg-curbside-delivered-manager ng-if="active_tab === 'delivered'"></eg-curbside-delivered-manager>
         </div>
       </uib-tab>
       <uib-tab index="'schedule'" heading="[% l('Schedule Pickup') %]">
diff --git a/Open-ILS/src/templates/staff/circ/curbside/t_staged_manager.tt2 b/Open-ILS/src/templates/staff/circ/curbside/t_staged_manager.tt2
new file mode 100644 (file)
index 0000000..ce7b8b5
--- /dev/null
@@ -0,0 +1,36 @@
+<div>
+  <div ng-style="{visibility : refreshNeeded ? 'visible' : 'hidden'}" class="alert alert-warning">
+    [% l('Updates to the list of staged and ready appointments are available. Please refresh.') %]
+  </div>
+  <eg-grid
+    id-field="slot_id"
+    features="-sort,-multisort,-picker,-multiselect"
+    items-provider="gridDataProvider"
+    grid-controls="gridControls"
+    dateformat="{{$root.egDateAndTimeFormat}}">
+
+    <eg-grid-menu-item handler="refresh_staged" standalone="true"
+        label="[% l('Refresh List')%]"></eg-grid-menu-item>
+
+    <eg-grid-field label="[% l('Pickup Date/Time') %]" path="slot.slot" datatype="timestamp"></eg-grid-field>
+    <eg-grid-field label="[% l('Patron') %]" path="slot.patron">
+      <a href="./circ/patron/{{item.slot.patron().id()}}/holds" target="_blank">
+        {{item.slot.patron().family_name()}} / {{item.slot.patron().card().barcode()}}
+        <span class="glyphicon glyphicon-new-window"></span>
+      </a>
+    </eg-grid-field>
+    <eg-grid-field label="[% l('Appointment ID') %]" path="slot.id"></eg-grid-field>
+    <eg-grid-field label="[% l('Action') %]" handlers="gridCellHandlers" compiled>
+      <button class="btn btn-sm btn-primary"
+        ng-disabled="col.handlers.wasHandled(item['slot_id'])"
+        ng-click="col.handlers.mark_arrived(item['slot_id'])">
+        [% l('Mark As Patron Arrived') %]
+      </button>
+      <button class="btn btn-sm btn-primary"
+        ng-disabled="col.handlers.wasHandled(item['slot_id'])"
+        ng-click="col.handlers.mark_delivered(item['slot_id'])">
+        [% l('Mark As Patron Arrived and Items Delivered') %]
+      </button>
+    </eg-grid-field>
+  </eg-grid>
+</div>
diff --git a/Open-ILS/web/js/ui/default/staff/circ/curbside/directives/arrived_manager.js b/Open-ILS/web/js/ui/default/staff/circ/curbside/directives/arrived_manager.js
new file mode 100644 (file)
index 0000000..77d940e
--- /dev/null
@@ -0,0 +1,92 @@
+angular.module('egCurbsideAppDep')
+
+.directive('egCurbsideArrivedManager', function() {
+    return {
+        transclude: true,
+        restrict:   'E',
+        scope: { },
+        templateUrl: './circ/curbside/t_arrived_manager',
+        controller:
+       ['$scope','$q','egCurbsideCoreSvc','egCore','egGridDataProvider',
+        '$uibModal','$timeout','$location','egConfirmDialog','ngToast','$interval',
+function($scope , $q , egCurbsideCoreSvc , egCore , egGridDataProvider ,
+         $uibModal , $timeout , $location , egConfirmDialog , ngToast , $interval) {
+
+    $scope.gridControls = {};
+
+    $scope.wasHandled = {};
+    $scope.refreshNeeded = false;
+
+    latestTime = undefined;
+    var checkRefresh = undefined;
+    function startRefreshCheck() {
+        if (!angular.isDefined(checkRefresh)) {
+            checkRefresh = $interval(function() {
+                egCurbsideCoreSvc.get_latest_arrived().then(function(latest) {
+                    if (angular.isDefined(latest)) {
+                        if (angular.isDefined(latestTime) && latestTime != latest) {
+                            $scope.refreshNeeded = true;
+                            stopRefreshCheck();
+                        }
+                        latestTime = latest;
+                    }
+                });
+            }, 15000);
+        }
+    }
+    function stopRefreshCheck() {
+        if (angular.isDefined(checkRefresh)) {
+            $interval.cancel(checkRefresh);
+            checkRefresh = undefined;
+        }
+    }
+    this.$onInit = function() {
+        startRefreshCheck();
+    }
+    this.$onDestroy = function() {
+        stopRefreshCheck();
+    }
+
+    $scope.gridDataProvider = egGridDataProvider.instance({
+        get : function(offset, count) {
+            $scope.wasHandled = {};
+            $scope.refreshNeeded = false;
+            startRefreshCheck();
+            return egCurbsideCoreSvc.get_arrived(offset, count);
+        }
+    });
+
+    $scope.refresh_arrived = function() {
+        $scope.gridControls.refresh();
+    }
+
+    $scope.gridCellHandlers = { };
+    $scope.gridCellHandlers.mark_delivered = function(id) {
+        egCurbsideCoreSvc.mark_delivered(id).then(function(resp) {
+            if (evt = egCore.evt.parse(resp)) {
+                ngToast.danger(egCore.strings.$replace(
+                    egCore.strings.FAILED_CURBSIDE_MARK_DELIVERED,
+                    { slot_id : id, evt_code : evt.code }
+                ));
+                return;
+            } 
+            if (!angular.isDefined(resp)) {
+                ngToast.warning(egCore.strings.$replace(
+                    egCore.strings.NOTFOUND_CURBSIDE_MARK_DELIVERED,
+                    { slot_id : id }
+                ));
+                return;
+            }
+            ngToast.success(egCore.strings.$replace(
+                egCore.strings.SUCCESS_CURBSIDE_MARK_DELIVERED,
+                { slot_id : id }
+            ));
+            $scope.wasHandled[id] = true;
+            $timeout(function() { $scope.refresh_arrived() }, 500);
+        });
+    }
+    $scope.gridCellHandlers.wasHandled = function(id) {
+        return $scope.wasHandled[id];
+    }
+
+}]}});
diff --git a/Open-ILS/web/js/ui/default/staff/circ/curbside/directives/delivered_manager.js b/Open-ILS/web/js/ui/default/staff/circ/curbside/directives/delivered_manager.js
new file mode 100644 (file)
index 0000000..447725b
--- /dev/null
@@ -0,0 +1,63 @@
+angular.module('egCurbsideAppDep')
+
+.directive('egCurbsideDeliveredManager', function() {
+    return {
+        transclude: true,
+        restrict:   'E',
+        scope: { },
+        templateUrl: './circ/curbside/t_delivered_manager',
+        controller:
+       ['$scope','$q','egCurbsideCoreSvc','egCore','egGridDataProvider',
+        '$uibModal','$timeout','$location','egConfirmDialog','ngToast','$interval',
+function($scope , $q , egCurbsideCoreSvc , egCore , egGridDataProvider ,
+         $uibModal , $timeout , $location , egConfirmDialog , ngToast , $interval) {
+
+    $scope.gridControls = {};
+
+    $scope.refreshNeeded = false;
+
+    latestTime = undefined;
+    var checkRefresh = undefined;
+    function startRefreshCheck() {
+        if (!angular.isDefined(checkRefresh)) {
+            checkRefresh = $interval(function() {
+                egCurbsideCoreSvc.get_latest_delivered().then(function(latest) {
+                    if (angular.isDefined(latest)) {
+                        if (angular.isDefined(latestTime) && latestTime != latest) {
+                            $scope.refreshNeeded = true;
+                            stopRefreshCheck();
+                        }
+                        latestTime = latest;
+                    }
+                });
+            }, 15000);
+        }
+    }
+    function stopRefreshCheck() {
+        if (angular.isDefined(checkRefresh)) {
+            $interval.cancel(checkRefresh);
+            checkRefresh = undefined;
+        }
+    }
+    this.$onInit = function() {
+        startRefreshCheck();
+    }
+    this.$onDestroy = function() {
+        stopRefreshCheck();
+    }
+
+    $scope.gridDataProvider = egGridDataProvider.instance({
+        get : function(offset, count) {
+            $scope.refreshNeeded = false;
+            startRefreshCheck();
+            return egCurbsideCoreSvc.get_delivered(offset, count);
+        }
+    });
+
+    $scope.refresh_delivered = function() {
+        $scope.gridControls.refresh();
+    }
+
+    $scope.gridCellHandlers = { };
+
+}]}});
diff --git a/Open-ILS/web/js/ui/default/staff/circ/curbside/directives/staged_manager.js b/Open-ILS/web/js/ui/default/staff/circ/curbside/directives/staged_manager.js
new file mode 100644 (file)
index 0000000..db5d988
--- /dev/null
@@ -0,0 +1,92 @@
+angular.module('egCurbsideAppDep')
+
+.directive('egCurbsideStagedManager', function() {
+    return {
+        transclude: true,
+        restrict:   'E',
+        scope: { },
+        templateUrl: './circ/curbside/t_staged_manager',
+        controller:
+       ['$scope','$q','egCurbsideCoreSvc','egCore','egGridDataProvider',
+        '$uibModal','$timeout','$location','egConfirmDialog','ngToast','$interval',
+function($scope , $q , egCurbsideCoreSvc , egCore , egGridDataProvider ,
+         $uibModal , $timeout , $location , egConfirmDialog , ngToast , $interval) {
+
+    $scope.gridControls = {};
+
+    $scope.wasHandled = {};
+    $scope.refreshNeeded = false;
+
+    latestTime = undefined;
+    var checkRefresh = undefined;
+    function startRefreshCheck() {
+        if (!angular.isDefined(checkRefresh)) {
+            checkRefresh = $interval(function() {
+                egCurbsideCoreSvc.get_latest_staged().then(function(latest) {
+                    if (angular.isDefined(latest)) {
+                        if (angular.isDefined(latestTime) && latestTime != latest) {
+                            $scope.refreshNeeded = true;
+                            stopRefreshCheck();
+                        }
+                        latestTime = latest;
+                    }
+                });
+            }, 15000);
+        }
+    }
+    function stopRefreshCheck() {
+        if (angular.isDefined(checkRefresh)) {
+            $interval.cancel(checkRefresh);
+            checkRefresh = undefined;
+        }
+    }
+    this.$onInit = function() {
+        startRefreshCheck();
+    }
+    this.$onDestroy = function() {
+        stopRefreshCheck();
+    }
+
+    $scope.gridDataProvider = egGridDataProvider.instance({
+        get : function(offset, count) {
+            $scope.wasHandled = {};
+            $scope.refreshNeeded = false;
+            startRefreshCheck();
+            return egCurbsideCoreSvc.get_staged(offset, count);
+        }
+    });
+
+    $scope.refresh_staged = function() {
+        $scope.gridControls.refresh();
+    }
+
+    $scope.gridCellHandlers = { };
+    $scope.gridCellHandlers.mark_arrived = function(id) {
+        egCurbsideCoreSvc.mark_arrived(id).then(function(resp) {
+            if (evt = egCore.evt.parse(resp)) {
+                ngToast.danger(egCore.strings.$replace(
+                    egCore.strings.FAILED_CURBSIDE_MARK_ARRIVED,
+                    { slot_id : id, evt_code : evt.code }
+                ));
+                return;
+            } 
+            if (!angular.isDefined(resp)) {
+                ngToast.warning(egCore.strings.$replace(
+                    egCore.strings.NOTFOUND_CURBSIDE_MARK_ARRIVED,
+                    { slot_id : id }
+                ));
+                return;
+            }
+            ngToast.success(egCore.strings.$replace(
+                egCore.strings.SUCCESS_CURBSIDE_MARK_ARRIVED,
+                { slot_id : id }
+            ));
+            $scope.wasHandled[id] = true;
+            $timeout(function() { $scope.refresh_staged() }, 500);
+        });
+    }
+    $scope.gridCellHandlers.wasHandled = function(id) {
+        return $scope.wasHandled[id];
+    }
+
+}]}});
index 87f2bbb..d52b7a3 100644 (file)
@@ -28,6 +28,78 @@ function(egCore , orderByFilter , $q , $filter , $uibModal , ngToast , egConfirm
         });
     }
 
+    service.get_staged = function(offset, count) {
+        return egCore.net.request(
+            'open-ils.curbside',
+            'open-ils.curbside.fetch_staged',
+            egCore.auth.token(),
+            egCore.auth.user().ws_ou(),
+            count, // yep, count first
+            offset
+        );
+    };
+    service.get_latest_staged = function() {
+        return egCore.net.request(
+            'open-ils.curbside',
+            'open-ils.curbside.fetch_staged.latest',
+            egCore.auth.token()
+        ).then(function(resp) {
+            if (evt = egCore.evt.parse(resp)) {
+                return undefined;
+            } else {
+                return resp;
+            }
+        });
+    }
+
+    service.get_arrived = function(offset, count) {
+        return egCore.net.request(
+            'open-ils.curbside',
+            'open-ils.curbside.fetch_arrived',
+            egCore.auth.token(),
+            egCore.auth.user().ws_ou(),
+            count, // yep, count first
+            offset
+        );
+    };
+    service.get_latest_arrived = function() {
+        return egCore.net.request(
+            'open-ils.curbside',
+            'open-ils.curbside.fetch_arrived.latest',
+            egCore.auth.token()
+        ).then(function(resp) {
+            if (evt = egCore.evt.parse(resp)) {
+                return undefined;
+            } else {
+                return resp;
+            }
+        });
+    }
+
+    service.get_delivered = function(offset, count) {
+        return egCore.net.request(
+            'open-ils.curbside',
+            'open-ils.curbside.fetch_delivered',
+            egCore.auth.token(),
+            egCore.auth.user().ws_ou(),
+            count, // yep, count first
+            offset
+        );
+    };
+    service.get_latest_delivered = function() {
+        return egCore.net.request(
+            'open-ils.curbside',
+            'open-ils.curbside.fetch_delivered.latest',
+            egCore.auth.token()
+        ).then(function(resp) {
+            if (evt = egCore.evt.parse(resp)) {
+                return undefined;
+            } else {
+                return resp;
+            }
+        });
+    }
+
     service.mark_staged = function(slot_id) {
         return egCore.net.request(
             'open-ils.curbside',
@@ -36,6 +108,23 @@ function(egCore , orderByFilter , $q , $filter , $uibModal , ngToast , egConfirm
             slot_id
         );
     }
+    service.mark_arrived = function(slot_id) {
+        return egCore.net.request(
+            'open-ils.curbside',
+            'open-ils.curbside.mark_arrived',
+            egCore.auth.token(),
+            slot_id
+        );
+    }
+    service.mark_delivered = function(slot_id) {
+        return egCore.net.request(
+            'open-ils.curbside',
+            'open-ils.curbside.mark_delivered',
+            egCore.auth.token(),
+            slot_id
+        );
+    }
+
 
     return service;
 }])