diff --git a/bower.json b/bower.json
index f54edd9..3937a7c 100644
--- a/bower.json
+++ b/bower.json
@@ -16,6 +16,7 @@
],
"dependencies": {
"angular": "~1.3.15",
- "angular-ui-router": "~0.2.13"
+ "angular-ui-router": "~0.2.13",
+ "angularjs-datepicker": "^2.1.6"
}
}
diff --git a/public/app/app.module.js b/public/app/app.module.js
index 05f7e18..355a43d 100644
--- a/public/app/app.module.js
+++ b/public/app/app.module.js
@@ -102,6 +102,7 @@
angular.module('KDRPoints', [
'ui.router',
+ '720kb.datepicker'
])
.config(['$stateProvider', '$urlRouterProvider', KDRPoints]);
})();
diff --git a/public/app/directives/dateInput.drctv.js b/public/app/directives/dateInput.drctv.js
new file mode 100644
index 0000000..9f7d435
--- /dev/null
+++ b/public/app/directives/dateInput.drctv.js
@@ -0,0 +1,61 @@
+(function() {
+ var months = [
+ { id: 1, name: 'January' }, { id: 2, name: 'February' },
+ { id: 3, name: 'March' }, { id: 4, name: 'April' },
+ { id: 5, name: 'May' }, { id: 6, name: 'June' },
+ { id: 7, name: 'July' }, { id: 8, name: 'August' },
+ { id: 9, name: 'September' }, { id: 10, name: 'October' },
+ { id: 11, name: 'November' }, { id: 12, name: 'December' }
+ ];
+
+ var monthToDays = function(month, year) {
+ if(month === 2) {
+ return (year % 4) ? 28 : 29;
+ } else if(month < 8) {
+ return 30 + month % 2;
+ } else {
+ return 30 + (month+1) % 2;
+ }
+ };
+
+ var verifyDate = function(date) {
+ date.min = parseInt(date.min);
+ var validHour = date.hour <= 12 && date.hour >= 1;
+ var validMin = date.min <=59 && date.min >= 0;
+ var validDay = date.day < monthToDays(date.month, date.year);
+ var currentDate = new Date();
+ return (currentDate >= revParseDate(date)) && validHour && validMin && validDay;
+ };
+
+ var revParseDate = function(date) {
+ return new Date(date.year, date.month-1, date.day, date.hour, date.min);
+ };
+
+ var dateInputDirective = function() {
+ return {
+ restrict: 'E',
+ replace: false,
+ scope: {
+ date: '=',
+ notify: '=',
+ changeFn: '&'
+ },
+ templateUrl: 'app/directives/templates/dateInput.html',
+ link: function(scope) {
+ scope.months = months;
+ scope.changeFunction = function() {
+ scope.notify = {};
+ if(!verifyDate(scope.date)) {
+ scope.notify.error = 'Invalid Date.';
+ return;
+ }
+ if(scope.changeFn) scope.notify.error = scope.changeFn();
+ };
+
+ }
+ }
+ };
+
+ angular.module('KDRPoints')
+ .directive('dateInput', dateInputDirective);
+})();
diff --git a/public/app/directives/templates/dateInput.html b/public/app/directives/templates/dateInput.html
new file mode 100644
index 0000000..3d3767d
--- /dev/null
+++ b/public/app/directives/templates/dateInput.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+ :
+
+
+
+
diff --git a/public/app/services/service.service.js b/public/app/services/service.service.js
index 8c2b780..7596ce5 100644
--- a/public/app/services/service.service.js
+++ b/public/app/services/service.service.js
@@ -22,7 +22,65 @@
});
};
+ var toDateObj = function(date) {
+ var newDate = date.date + ' ' + date.hour + ':' + date.min + ' ' + date.cycle;
+ newDate = new Date(Date.parse(newDate));
+ return newDate.toString() !== 'Invalid Date' ? newDate : false;
+ };
+
+ var submitInProgress = false;
+
return {
+ cleanHour: function(hour, type) {
+ var cleanHour = {};
+ cleanHour.description = hour.description;
+ if(!cleanHour.description) {
+ return 'Description is required.';
+ };
+ if(type === 'Hour') {
+ cleanHour.startTime = toDateObj(hour.start);
+ cleanHour.endTime = toDateObj(hour.end);
+ if(!cleanHour.startTime || !cleanHour.endTime) {
+ return 'Invalid Date';
+ }
+ } else {
+ cleanHour.amount = hour.amount;
+ if(cleanHour.amount < 0) {
+ return 'Amount must be greater than zero.';
+ }
+ }
+ return cleanHour;
+ },
+ calculateHourEquivalent: function(amount) {
+ var amountToMinutes = amount / (5/60);
+ if(amountToMinutes < 0) {
+ return 'Amount must be greater than zero.';
+ }
+ duration = {
+ hours: Math.floor(amountToMinutes / 60),
+ minutes: amountToMinutes % 60
+ };
+ if(duration.minutes < 10) {
+ duration.minutes = '0' + duration.minutes;
+ }
+ return duration;
+ },
+ calculateDuration: function(startDate, endDate) {
+ startDate = toDateObj(startDate);
+ endDate = toDateObj(endDate);
+ var diffMinutes = (endDate - startDate) / 60000;
+ if(diffMinutes < 0) {
+ return 'Start date must be before end date.';
+ }
+ var duration = {
+ hours: Math.floor(diffMinutes / 60),
+ minutes: diffMinutes % 60
+ };
+ if(duration.minutes < 10) {
+ duration.minutes = '0' + duration.minutes;
+ }
+ return duration;
+ },
submit: function(hour) {
return postHttp('/service/submit', hour);
},
diff --git a/public/app/submitService/submitService.ctrl.js b/public/app/submitService/submitService.ctrl.js
index f02ce9d..fcd0237 100644
--- a/public/app/submitService/submitService.ctrl.js
+++ b/public/app/submitService/submitService.ctrl.js
@@ -1,31 +1,91 @@
(function() {
+ var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July',
+ 'August', 'September', 'October', 'November', 'December'];
+ var hours = ['1','2','3','4','5','6','7','8','9','10','11','12'];
+ var minutes = ['00','15','30','45'];
+
+ var parseDate = function(date) {
+ var month = months[date.getMonth()];
+ var day = date.getDate();
+ var year = date.getFullYear();
+ var min = date.getMinutes();
+ min = minutes[Math.floor(min / 15)];
+ var hour = date.getHours();
+ return {
+ date: month + ' ' + day + ', ' + year,
+ hour: (hour % 12).toString(),
+ min: min,
+ cycle: hour / 12 < 1 ? 'AM' : 'PM'
+ }
+ };
+
var submitServiceCtrl = function($scope, serviceService) {
$scope.type = 'Hour';
- $scope.hour = {};
+ $scope.notify = {};
+
+ $scope.hours = hours;
+ $scope.minutes = minutes;
+
+ var submitInProgress = false;
$scope.submit = function() {
- var cleanHour = {};
- var hour = $scope.hour;
- cleanHour.description = hour.description;
- if($scope.type === 'Hour') {
- cleanHour.startTime = hour.startTime;
- cleanHour.endTime = hour.endTime;
- } else {
- cleanHour.amount = hour.amount;
+ if(submitInProgress) {
+ return;
+ }
+ submitInProgress = true;
+ var cleanHour = serviceService.cleanHour($scope.hour, $scope.type);
+ if(typeof cleanHour === 'string') {
+ submitInProgress = false;
+ $scope.notify.error = cleanHour;
+ return;
}
serviceService.submit(cleanHour)
- .then(function() {
- $scope.hour = {};
- $scope.success = true;
+ .then(function(success) {
+ submitInProgress = false;
+ if(success) {
+ resetData();
+ } else {
+ $scope.notify.error = 'Submission failed.';
+ $scope.notify.success = null;
+ }
});
+
+ };
+
+ $scope.calculateDuration = function() {
+ var duration = serviceService.calculateDuration($scope.hour.start,
+ $scope.hour.end);
+ if(typeof duration === 'string') {
+ $scope.notify.error = duration;
+ return;
+ }
+ $scope.hour.duration = duration;
};
- //remove success when new service hour is input
- $scope.$watch('hour', function() {
- $scope.success = false;
- }, true);
+ $scope.calculateEquivalent = function() {
+ var duration = serviceService.calculateHourEquivalent($scope.hour.amount);
+ if(typeof duration === 'string') {
+ $scope.notify.error = duration;
+ return;
+ }
+ $scope.hour.equivalent = duration;
+ };
+
+ var resetData = function() {
+ $scope.hour = {};
+ var endDate = new Date();
+ var startDate = new Date(endDate.getTime() - 15 * 60000);
+
+ $scope.hour.start = parseDate(startDate);
+ $scope.hour.end = parseDate(endDate);
+ $scope.hour.amount = 0;
+
+ $scope.calculateDuration();
+ $scope.calculateEquivalent();
+ };
+ resetData();
}
angular.module('KDRPoints')
diff --git a/public/app/submitService/submitService.html b/public/app/submitService/submitService.html
index 48a2fec..ad59ea3 100644
--- a/public/app/submitService/submitService.html
+++ b/public/app/submitService/submitService.html
@@ -1,23 +1,53 @@
diff --git a/public/css/angular-datepicker.css b/public/css/angular-datepicker.css
new file mode 100644
index 0000000..8cf66c8
--- /dev/null
+++ b/public/css/angular-datepicker.css
@@ -0,0 +1,209 @@
+datepicker a, [datepicker] a, .datepicker a{
+ color:inherit;
+ text-decoration:none;
+}
+datepicker a:hover, [datepicker] a:hover, .datepicker a:hover{
+ text-decoration:none;
+}
+datepicker select, datepicker select:focus, datepicker select:hover,
+.datepicker select, .datepicker select:focus, .datepicker select:hover,
+[datepicker] select, [datepicker] select:focus, [datepicker] select:hover{
+ width:100%;
+ overflow: hidden;
+ background:none;
+ color:#fff;
+ background-color: #138EFA;
+ border-radius:2px;
+ border: 0;
+ margin-top:5px;
+}
+datepicker, .datepicker, [datepicker],
+._720kb-datepicker-calendar-header,
+._720kb-datepicker-calendar-body,
+._720kb-datepicker-calendar-days-header,
+._720kb-datepicker-calendar-years-pagination-pages {
+ font-family: Helvetica Neue, Arial, sans-serif;
+ font-size: 13.5px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ width: 100%;
+ margin: 0 auto;
+ float: left;
+ clear: right;
+ position: relative;
+}
+._720kb-datepicker-calendar {
+ background: white;
+ color: #333;
+ position: absolute;
+ z-index: 999;
+ min-width: 220px;
+ margin: 0 auto;
+ width: 101%;
+ -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1) inset;
+ -moz-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1) inset;
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1) inset;
+ visibility: hidden;
+ overflow:hidden;
+ margin-left:-0.5%;
+ padding: 0 0 2% 0;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+._720kb-datepicker-calendar._720kb-datepicker-open,._720kb-datepicker-calendar._720kb-datepicker-forced-to-open {
+ width: 0px;
+ visibility: visible;
+}
+._720kb-datepicker-calendar-header {
+ text-align: center;
+ font-size: 15px;
+ line-height: 40px;
+}
+._720kb-datepicker-calendar-header:nth-child(odd) {
+ background: #138EFA;
+}
+._720kb-datepicker-calendar-header:nth-child(even) {
+ background: #7BC6FC;
+}
+._720kb-datepicker-calendar-header-left,
+._720kb-datepicker-calendar-header-middle,
+._720kb-datepicker-calendar-header-right {
+ width: 15%;
+ float: left;
+}
+._720kb-datepicker-calendar-header-middle {
+ width: 70%;
+}
+
+._720kb-datepicker-calendar-header-closed-pagination::after {
+ content: " \25BE";
+}
+
+._720kb-datepicker-calendar-header-opened-pagination::after {
+ content: " \25BE";
+ margin-left: 4px;
+ position: relative;
+ bottom: -3px;
+ display:inline-block;
+ -webkit-transform: rotate(180deg);
+ -moz-transform: rotate(180deg);
+ -o-transform: rotate(180deg);
+ -ms-transform: rotate(180deg);
+ transform: rotate(180deg);
+}
+._720kb-datepicker-calendar-body {
+ width: 96%;
+ margin: 2%;
+ text-align: center;
+}
+._720kb-datepicker-calendar-day {
+ cursor: pointer;
+ font-size: 12.5px;
+ width: 12.2%;
+ margin:5px 1%;
+ padding: 1.5% 0;
+ float: left;
+ -webkit-border-radius: 1px;
+ -moz-border-radius: 1px;
+ border-radius: 1px;
+}
+._720kb-datepicker-calendar-day:hover,._720kb-datepicker-calendar-day._720kb-datepicker-active {
+ background: rgba(0, 0, 0, 0.03);
+}
+._720kb-datepicker-calendar-header a, ._720kb-datepicker-calendar-header a:hover {
+ text-decoration:none;
+ padding:3% 9% 4% 9%;
+ font-size: 13.5px;
+ color:rgba(0, 0, 0, 0.55);
+ font-weight: bold;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+._720kb-datepicker-calendar-header a:hover {
+ color:rgba(0, 0, 0, 0.9);
+ background: rgba(255, 255, 255, 0.45);
+}
+._720kb-datepicker-calendar-month {
+ color:#fff;
+}
+._720kb-datepicker-calendar-month span {
+ font-size: 13px;
+ color:rgba(0, 0, 0, 0.4);
+}
+._720kb-datepicker-calendar-month a span i {
+ font-style: normal;
+ font-size:15px;
+}
+._720kb-datepicker-calendar-month a, ._720kb-datepicker-calendar-month a:hover {
+ padding: 3px;
+ margin-left:1%;
+}
+._720kb-datepicker-calendar-years-pagination{
+ padding:2% 0 0 0;
+ float:left;
+ clear: right;
+ width: 100%;
+}
+._720kb-datepicker-calendar-years-pagination a, ._720kb-datepicker-calendar-years-pagination a:hover {
+ font-size:12px;
+ padding:0 7px;
+ font-weight: normal;
+ margin:3px 1% 0 1%;
+ line-height: 20px;
+ display: inline-block;
+}
+._720kb-datepicker-calendar-years-pagination a._720kb-datepicker-active {
+ color:rgba(0, 0, 0, 0.9);
+ font-weight: 500;
+ background: rgba(255, 255, 255, 0.45);
+}
+._720kb-datepicker-calendar-years-pagination-pages a,._720kb-datepicker-calendar-years-pagination-pages a:hover{
+ padding:5px 10px;
+}
+._720kb-datepicker-calendar-days-header{
+ max-width: 100%;
+ margin:0 auto;
+ padding:0 2% 0 2%;
+ background: rgba(19, 142, 250, 0.08);
+ border-bottom:1px solid rgba(0,0,0,0.02);
+}
+._720kb-datepicker-calendar-days-header div{
+ width: 14.18%;
+ font-weight: 500;
+ font-size: 11.5px;
+ padding:10px 0;
+ float:left;
+ text-align: center;
+ color:rgba(0,0,0,0.7);
+}
+._720kb-datepicker-calendar-days
+._720kb-datepicker-default-button{
+ font-size: 18.5px;
+ position: relative;
+ bottom:-0.5px;
+}
+._720kb-datepicker-default-button{
+ padding:0 4.5px;
+}
+._720kb-datepicker-calendar-header-middle._720kb-datepicker-mobile-item{
+ width:95%;
+ float:none;
+ margin:0 auto;
+}
+._720kb-datepicker-item-hidden{
+ visibility:hidden;
+}
+._720kb-datepicker-calendar-day._720kb-datepicker-disabled,
+._720kb-datepicker-calendar-day._720kb-datepicker-disabled:hover,
+._720kb-datepicker-calendar-years-pagination a._720kb-datepicker-disabled,
+._720kb-datepicker-calendar-years-pagination a._720kb-datepicker-disabled:hover,
+._720kb-datepicker-calendar-years-pagination a._720kb-datepicker-active._720kb-datepicker-disabled,
+._720kb-datepicker-calendar-years-pagination a._720kb-datepicker-active._720kb-datepicker-disabled:hover{
+ color:rgba(0,0,0,0.2);
+ background: rgba(25,2,0,0.02);
+ cursor: default;
+}
diff --git a/public/index.html b/public/index.html
index 43b0da6..228def7 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,20 +1,24 @@
+
+
+
+