diff --git a/app/controllers/clock.js b/app/controllers/clock.js
new file mode 100644
index 0000000..c6a8f14
--- /dev/null
+++ b/app/controllers/clock.js
@@ -0,0 +1,42 @@
+var mongoose = require('mongoose'),
+ Clock = mongoose.model('Clock');
+
+module.exports = function(piler) {
+ return {
+ index: function(req, res, next) {
+ host = String(req.headers['x-forwarded-host']);
+ host = host.split(':')[0];
+
+ if (host != 'clock.atlb.co') {
+ return next();
+ }
+
+ if (!req.user) {
+ req.session.redirectUrl = req.url
+ }
+
+ var path = req.path.slice(1);
+
+ res.render('clock.jade', {
+ css: piler.css.renderTags()
+ });
+ },
+ post: function(req, res) {
+ var clock = new Clock({
+ tech: req.user,
+ action: req.body.action,
+ lat: req.body.lat,
+ long: req.body.long,
+ dt: new Date()
+ });
+
+ clock.save(function(err, result) {
+ if (err) {
+ return res.json(500, err);
+ } else {
+ res.json(result);
+ }
+ });
+ }
+ }
+}
diff --git a/app/controllers/users.js b/app/controllers/users.js
index 29ce87b..d45f572 100644
--- a/app/controllers/users.js
+++ b/app/controllers/users.js
@@ -1,7 +1,8 @@
var mongoose = require('mongoose'),
async = require('async'),
- User = mongoose.model('User');
+ User = mongoose.model('User'),
+ Clock = mongoose.model('Clock');
var log = require('log4node');
@@ -163,6 +164,24 @@ module.exports = function(config, directory) {
return res.json(user);
});
});
+ },
+
+ clocks: function(req, res) {
+ var id = req.param('user_id');
+
+ var criteria = {
+ tech: id
+ };
+
+ var query = Clock.find(criteria)
+ .sort('-dt')
+ .exec(function(err, results) {
+ if (err) {
+ res.json(500, err);
+ } else {
+ res.json(results);
+ }
+ });
}
};
};
diff --git a/app/model/clock.js b/app/model/clock.js
new file mode 100644
index 0000000..c7b237e
--- /dev/null
+++ b/app/model/clock.js
@@ -0,0 +1,14 @@
+var mongoose = require('mongoose'),
+ Schema = mongoose.Schema,
+ ObjectId = Schema.ObjectId;
+
+var clockSchema = new Schema({
+ _id: String,
+ tech: { type: ObjectId, ref: 'User' },
+ dt: Date,
+ action: String,
+ lat: Number,
+ long: Number
+}, { versionKey: false })
+
+var Clock = module.exports = mongoose.model('Clock', clockSchema);
diff --git a/app/views/clock.jade b/app/views/clock.jade
new file mode 100644
index 0000000..b2208ad
--- /dev/null
+++ b/app/views/clock.jade
@@ -0,0 +1,40 @@
+doctype 5
+html(lang="en", ng-app="clock", ng-controller="clock.PageCtrl")
+ head
+ title Atlantic Biomedical
+ !{css}
+ link(rel='stylesheet', href='/css/clock.css')
+ meta(name='viewport', content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0')
+ body
+ script(type='text/javascript', src='//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular.js')
+ script(type='text/javascript', src='//ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular-resource.js')
+ script(type='text/javascript', src='//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js')
+ script(type='text/javascript', src='/clock/app.js')
+
+ error-panel
+ .navbar
+ .navbar-inner
+ a.brand(href='/', target='_self') Atlantic Biomedical
+ progress-panel
+
+ .container-fluid
+ h1 Time Clock
+
+ div.loading(ng-show='working')
+ i.loader
+ br
+ | Detecting your location
+
+ div(ng-hide='working')
+ div.error(ng-show='error')
+ {{ error }}
+
+ div.success(ng-show='success')
+ {{ success }}
+
+ div(ng-hide='error || success')
+ .control-group
+ button.btn.btn-primary(ng-click='clockIn()') Clock In
+ button.btn.btn-primary(ng-click='clockOut()') Clock Out
+ div.mapOuter
+ gmap.mapInner(size='600x600', markers='markers', sensor='false', zoom='14')
diff --git a/config/routes.js b/config/routes.js
index 718bd62..01fae67 100644
--- a/config/routes.js
+++ b/config/routes.js
@@ -46,6 +46,7 @@ module.exports = function(app, auth, piler, calendar, directory, config) {
app.get('/api/users/details', users.details);
app.post('/api/users', users.create);
app.post('/api/users/:user_id', users.update);
+ app.get('/api/users/:user_id/clocks', users.clocks);
var account = require('../app/controllers/account');
app.get('/api/account', account.profile);
@@ -56,6 +57,9 @@ module.exports = function(app, auth, piler, calendar, directory, config) {
var tags = require('../app/controllers/tags')(piler);
app.post('/api/tags', tags.post);
+ var clock = require('../app/controllers/clock')(piler);
+ app.post('/api/clock', clock.post);
+
var login = require('../app/controllers/login')(piler);
app.get('/login', login.login);
app.get('/login/error', login.error);
@@ -63,6 +67,6 @@ module.exports = function(app, auth, piler, calendar, directory, config) {
var home = require('../app/controllers/home')(piler);
- app.get('/', tags.index, auth.requiresUiLogin, home.index);
- app.get('*', tags.index, auth.requiresUiLogin, home.index);
+ app.get('/', tags.index, auth.requiresUiLogin, clock.index, home.index);
+ app.get('*', tags.index, auth.requiresUiLogin, clock.index, home.index);
};
diff --git a/public/clock/app.js b/public/clock/app.js
new file mode 100644
index 0000000..1567c25
--- /dev/null
+++ b/public/clock/app.js
@@ -0,0 +1,172 @@
+clock = {};
+
+
+angular.module('clock', ['ngResource'])
+ .factory("Clock", function($resource) {
+ return $resource('/api/clock');
+ })
+ .constant('geolocation_msgs', {
+ 'errors.location.unsupportedBrowser':'Browser does not support location services',
+ 'errors.location.permissionDenied':'You have rejected access to your location',
+ 'errors.location.positionUnavailable':'Unable to determine your location',
+ 'errors.location.timeout':'Service timeout has been reached'
+ })
+ .factory('geolocation', ['$q','$rootScope','$window','geolocation_msgs',function ($q,$rootScope,$window,geolocation_msgs) {
+ return {
+ getLocation: function (opts) {
+ var deferred = $q.defer();
+ if ($window.navigator && $window.navigator.geolocation) {
+ $window.navigator.geolocation.getCurrentPosition(function(position){
+ $rootScope.$apply(function(){deferred.resolve(position);});
+ }, function(error) {
+ switch (error.code) {
+ case 1:
+ $rootScope.$broadcast('error',geolocation_msgs['errors.location.permissionDenied']);
+ $rootScope.$apply(function() {
+ deferred.reject(geolocation_msgs['errors.location.permissionDenied']);
+ });
+ break;
+ case 2:
+ $rootScope.$broadcast('error',geolocation_msgs['errors.location.positionUnavailable']);
+ $rootScope.$apply(function() {
+ deferred.reject(geolocation_msgs['errors.location.positionUnavailable']);
+ });
+ break;
+ case 3:
+ $rootScope.$broadcast('error',geolocation_msgs['errors.location.timeout']);
+ $rootScope.$apply(function() {
+ deferred.reject(geolocation_msgs['errors.location.timeout']);
+ });
+ break;
+ }
+ }, opts);
+ }
+ else
+ {
+ $rootScope.$broadcast('error',geolocation_msgs['errors.location.unsupportedBrowser']);
+ $rootScope.$apply(function(){deferred.reject(geolocation_msgs['errors.location.unsupportedBrowser']);});
+ }
+ return deferred.promise;
+ }
+ };
+ }])
+ .directive('gmap', function($parse) {
+ return {
+ template: '',
+ replace: true,
+ restrict: 'E',
+ controller: 'GMapController',
+ scope: true,
+ link: function postLink(scope, element, attrs, ctrl) {
+ var el = element[0];
+
+ var sizeBits = attrs.size.split('x');
+ el.width = parseInt(sizeBits[0], 10);
+ el.height = parseInt(sizeBits[1], 10);
+
+ scope.$watch(attrs.markers, function(value) {
+ el.src = ctrl.buildSourceString(attrs, value);
+ });
+ }
+ }
+ })
+ .controller('GMapController', function() {
+ var BASE_URL = '//maps.googleapis.com/maps/api/staticmap?';
+ var STYLE_ATTRIBUTES = ['color', 'label', 'size'];
+
+ function makeMarkerStrings(markers) {
+ return markers.map(function(marker) {
+ var str = Object.keys(marker).map(function(key) {
+ if (STYLE_ATTRIBUTES.indexOf(key) > -1) {
+ return key + ':' + marker[key] + '|';
+ }
+ }).join('');
+
+ return str + marker.coords.join(',');
+ });
+ }
+
+ this.buildSourceString = function(attrs, markers) {
+ var markerStrings;
+ if (markers) {
+ if (!angular.isArray(markers)) {
+ markers = [markers];
+ }
+ markerStrings = makeMarkerStrings(markers);
+ }
+
+ var params = Object.keys(attrs).map(function(attr) {
+ if (attr === 'markers' && markerStrings) {
+ return Object.keys(markerStrings).map(function(key) {
+ return 'markers=' + encodeURIComponent(markerStrings[key]);
+ }).join('&');
+ }
+
+ if (attr[0] !== '$' && attr !== 'alt') {
+ return encodeURIComponent(attr) + '=' + encodeURIComponent(attrs[attr]);
+ }
+ });
+
+ return BASE_URL + params.reduce(function(a, b) {
+ if (!a) {
+ return b;
+ }
+
+ if (b !== undefined) {
+ return a + '&' + b;
+ }
+
+ return a;
+ }, '');
+ };
+ });
+
+clock.PageCtrl = function($scope, $rootScope, geolocation, Clock) {
+
+ function save(action) {
+ Clock.save({
+ action: action,
+ lat: $scope.coords.latitude,
+ long: $scope.coords.longitude
+ }, function(success) {
+ $scope.success = 'Request was successful.';
+ }, function(error) {
+ $scope.error = 'Unable to complete request, Try again later';
+ });
+ }
+
+ $scope.working = true;
+
+
+ $scope.clockIn = function() {
+ save('in');
+ };
+
+ $scope.clockOut = function() {
+ save('out');
+ };
+
+ $rootScope.$on('error', function(event, msg) {
+ $scope.working = false;
+
+ console.log("ERROR");
+ console.log(msg);
+
+ $scope.error = msg;
+ });
+
+ geolocation.getLocation().then(function(data) {
+ $scope.working = false;
+
+ $scope.coords = data.coords;
+
+ console.log('Got location Data');
+ console.log(data);
+ $scope.markers = [{
+ color: 'blue',
+ coords: [data.coords.latitude, data.coords.longitude]
+ }];
+ });
+}
+
+
diff --git a/public/css/clock.css b/public/css/clock.css
new file mode 100644
index 0000000..19a52dd
--- /dev/null
+++ b/public/css/clock.css
@@ -0,0 +1,44 @@
+html, body {
+}
+
+.loading {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ margin: auto;
+ text-align: center;
+ height:100px;
+ width:200px;
+}
+
+.btn {
+ width: 100%;
+ margin-bottom: 5px;
+ display: block;
+}
+
+.mapOuter {
+ width: 100%;
+ overflow: hidden;
+ position: absolute;
+ left: 0;
+}
+
+.mapInner {
+ width: 100%;
+ height: auto;
+}
+
+.error {
+ padding: 5px 10px;
+ color: white;
+ background: #ba6d6d;
+}
+
+.success {
+ padding: 5px 10px;
+ color: white;
+ background: #6ebb72;
+}
diff --git a/public/js/app.js b/public/js/app.js
index 6220e68..7064240 100644
--- a/public/js/app.js
+++ b/public/js/app.js
@@ -78,6 +78,10 @@ angular.module('biomed', ['biomed.filters', 'biomed.services', 'biomed.directive
controller: biomed.UsersIndexCtrl,
reloadOnSearch: false
})
+ .when('/admin/users/:id', {
+ templateUrl: '/partials/users/clock.html',
+ controller: biomed.UserClockCtrl
+ })
.otherwise({
redirectTo: '/schedule'
});
diff --git a/public/js/controllers.js b/public/js/controllers.js
index 186de9e..5341a50 100644
--- a/public/js/controllers.js
+++ b/public/js/controllers.js
@@ -172,6 +172,15 @@ biomed.UsersIndexCtrl = function($scope, $filter, $routeParams, $location, Users
};
};
+biomed.UserClockCtrl = function($scope, $routeParams, Users) {
+ Users.index({userid: $routeParams.id}, function(result) {
+ console.log(result);
+ $scope.tech = result[0];
+ });
+
+ $scope.clocks = Users.clocks($routeParams);
+};
+
biomed.ClientIndexCtrl = function($scope, $filter, $routeParams, Clients, LocationBinder) {
$scope.loading = true;
diff --git a/public/js/services.js b/public/js/services.js
index dc1c511..fe95fde 100644
--- a/public/js/services.js
+++ b/public/js/services.js
@@ -32,6 +32,7 @@ angular.module('biomed.services', [])
details: { method: 'GET', params: { cmd: 'details' }, isArray: true },
create: { method: 'POST', params: {} },
update: { method: 'POST', params: { id: 0 } },
+ clocks: { method: 'GET', params: { id: 0, cmd: 'clocks' }, isArray: true }
});
})
.factory("Schedule", function($resource) {
diff --git a/public/partials/users/clock.html b/public/partials/users/clock.html
new file mode 100644
index 0000000..5f97cbc
--- /dev/null
+++ b/public/partials/users/clock.html
@@ -0,0 +1,31 @@
+
Action | +Date | +Location | ++ | |||||||
---|---|---|---|---|---|---|---|---|---|---|
{{clock.action}} | +{{clock.dt | date:'medium'}} | +{{clock.lat}},{{clock.long}} | +View in Google Maps | +