timer in timesheet
diff --git a/erpnext/projects/doctype/timesheet/timesheet.css b/erpnext/projects/doctype/timesheet/timesheet.css
new file mode 100644
index 0000000..9cce5f4
--- /dev/null
+++ b/erpnext/projects/doctype/timesheet/timesheet.css
@@ -0,0 +1,122 @@
+@import 'https://fonts.googleapis.com/css?family=Open+Sans:300,600,800';
+/*html,
+body {
+ background-color: #000;
+ color: #fff;
+ height: 100%;
+ width: 100%;
+ margin: 0;
+ box-sizing: border-box;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+ -webkit-tap-highlight-color: transparent;
+}*/
+
+.stopwatch {
+ height: 50%;
+ width: 100%;
+ display: inline-flex;
+ transition: all .5s ease;
+ -moz-transition: all .5s ease;
+ -ms-transition: all .5s ease;
+ -webkit-transition: all .5s ease;
+ -o-transition: all .5s ease;
+}
+
+.stopwatch span {
+ display: table-cell;
+ width: 33%;
+ font-size: 5em;
+ font-family: 'Open Sans', sans serif;
+ text-align: center;
+ vertical-align: middle;
+ transition: all .5s ease;
+ -moz-transition: all .5s ease;
+ -ms-transition: all .5s ease;
+ -webkit-transition: all .5s ease;
+ -o-transition: all .5s ease;
+}
+
+.stopwatch .hours {
+ font-weight: 800;
+}
+
+.stopwatch .minutes {
+ font-weight: 600;
+}
+
+.stopwatch .seconds {
+ font-weight: 300;
+}
+
+.stopcontrols {
+ height: 50%;
+ width: 100%;
+ display: table;
+ transition: all .5s ease;
+ -moz-transition: all .5s ease;
+ -ms-transition: all .5s ease;
+ -webkit-transition: all .5s ease;
+ -o-transition: all .5s ease;
+}
+
+.stopcontrols div {
+ display: table-cell;
+ width: 50%;
+ text-align: center;
+ vertical-align: middle;
+ cursor: pointer;
+}
+
+.playpause {
+ border-right: 1px dashed #fff;
+ border-bottom: 1px dashed #fff;
+}
+
+.playpause .play {
+ display: inline-block;
+ border-top: 28px solid transparent;
+ border-left: 37px solid #000;
+ border-bottom: 28px solid transparent;
+}
+
+.playpause .pause {
+ border-right: 5px solid #000;
+ border-left: 5px solid #000;
+ padding: 20px 10px;
+}
+
+.stop {
+ border-bottom: 1px dashed #fff;
+}
+
+.stop span {
+ display: inline-block;
+ background-color: #e74c3c;
+ width: 45px;
+ height: 45px;
+}
+
+@media screen and (max-width: 500px) {
+ .stopwatch span {
+ font-size: 6em;
+ }
+}
+
+@media screen and (max-width: 390px) {
+ .stopwatch {
+ height: 75%;
+ display: block;
+ }
+ .stopcontrols {
+ height: 25%;
+ }
+ .stopwatch span {
+ width: 100%;
+ height: 33%;
+ display: block;
+ }
+}
diff --git a/erpnext/projects/doctype/timesheet/timesheet.html b/erpnext/projects/doctype/timesheet/timesheet.html
new file mode 100644
index 0000000..3414fc1
--- /dev/null
+++ b/erpnext/projects/doctype/timesheet/timesheet.html
@@ -0,0 +1,13 @@
+<div class="stopcontrols">
+ <div class="playpause">
+ <span class="play"></span>
+ </div>
+ <div class="stop">
+ <span></span>
+ </div>
+</div>
+<div class="stopwatch">
+ <span class="hours">00</span>
+ <span class="minutes">00</span>
+ <span class="seconds">00</span>
+</div>
\ No newline at end of file
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 99ee2a2..ed8bbb6 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -50,12 +50,127 @@
}
}
+ if (frm.doc.total_hours) {
+ frm.add_custom_button(__('Start Timer'), function() {
+ frm.trigger("timer")
+ }).addClass("btn-primary");
+ }
+
if(frm.doc.per_billed > 0) {
frm.fields_dict["time_logs"].grid.toggle_enable("billing_hours", false);
frm.fields_dict["time_logs"].grid.toggle_enable("billable", false);
}
},
+ timer: function(frm) {
+ let dialog = new frappe.ui.Dialog({
+ title: __("Timer"),
+ fields: [
+ {"fieldtype": "Select", "label": __("Activity"),
+ "fieldname": "activity",
+ "options": frm.doc.time_logs.map(d => d.activity_type),
+ "reqd": 1 },
+ {"fieldtype": "Select", "label": __("Project"),
+ "fieldname": "project",
+ "options": frm.doc.time_logs.map(d => d.project)},
+ {"fieldtype": "Select", "label": __("Hours"),
+ "fieldname": "hours",
+ "options": frm.doc.time_logs.map(d => d.hours)}
+ ]
+ });
+
+ dialog.wrapper.append(frappe.render_template("timesheet"));
+ frm.trigger("control_timer");
+ dialog.show();
+ },
+
+ control_timer: function() {
+ var interval = null;
+ var currentIncrement = 0;
+ var isPaused = false;
+ var initialised = false;
+ var clicked = false;
+ var paused_time = 0;
+
+ $(".playpause").click(function(e) {
+ if (clicked) {
+ e.preventDefault();
+ return false;
+ }
+
+ if (!initialised) {
+ initialised = true;
+ isPaused = false;
+ $(".playpause span").removeClass();
+ $(".playpause span").addClass("pause");
+ initialiseTimer();
+ }
+ else {
+ $(".playpause span").removeClass();
+ if (isPaused) {
+ isPaused = false;
+ $(".playpause span").addClass("pause");
+ }
+ else {
+ isPaused = true;
+ $(".playpause span").addClass("play");
+ paused_time = currentIncrement;
+ }
+ }
+ });
+
+ $(".stop").click(function() {
+ reset();
+ });
+
+ function initialiseTimer() {
+ interval = setInterval(function() {
+ if (isPaused) return;
+ var current = setCurrentIncrement();
+ updateStopwatch(current);
+ }, 1000);
+ }
+
+ function updateStopwatch(increment) {
+ var hours = Math.floor(increment / 3600);
+ var minutes = Math.floor((increment - (hours * 3600)) / 60);
+ var seconds = increment - (hours * 3600) - (minutes * 60);
+ // if(!$('modal-open:visible')){
+ // reset();
+ // }
+ if (!$('.modal-dialog').is(':visible')) {
+ reset();
+ }
+ if(hours > 99)
+ reset();
+ if(cur_dialog && cur_dialog.get_value('hours') == hours) {
+ isPaused = true;
+ initialised = false;
+ frappe.msgprint(__("Timer exceeded the given hours"));
+ }
+ $(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString());
+ $(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString());
+ $(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString());
+ }
+
+ function setCurrentIncrement() {
+ currentIncrement += 1;
+ return currentIncrement;
+ }
+
+ function reset() {
+ currentIncrement = 0;
+ isPaused = true;
+ initialised = false;
+ clearInterval(interval);
+ $(".hours").text("00");
+ $(".minutes").text("00");
+ $(".seconds").text("00");
+ $(".playpause span").removeClass();
+ $(".playpause span").addClass("play");
+ }
+ },
+
make_invoice: function(frm) {
let dialog = new frappe.ui.Dialog({
title: __("Select Item (optional)"),