mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-18 10:14:37 -04:00
Working on time tracker
This commit is contained in:
parent
08afd7e4c4
commit
fd306e6104
@ -11,9 +11,13 @@ class TimeTrackerController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$user = auth()->user();
|
||||
$account = $user->account;
|
||||
|
||||
$data = [
|
||||
'title' => trans('texts.time_tracker'),
|
||||
'tasks' => Task::scope()->get(),
|
||||
'account' => $account,
|
||||
];
|
||||
|
||||
return view('time_tracker', $data);
|
||||
|
@ -27,18 +27,20 @@
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header" style="padding-top:12px;padding-bottom:12px;">
|
||||
<ul class="nav navbar-nav navbar-right" style="padding-right:12px; padding-left:10px">
|
||||
<button type='button' class='btn btn-normal btn-lg'>
|
||||
<span data-bind="text: selectedTask().duration, visible: selectedTask"
|
||||
style="font-size:28px;color:white;padding-right:8px;vertical-align:middle;display:none;">test</span>
|
||||
<button type='button' class='btn btn-normal btn-lg' data-bind="visible: selectedTask" style="display:none;">
|
||||
{{ trans('texts.details') }}
|
||||
</button>
|
||||
<button type='button' class='btn btn-success btn-lg' data-bind="click: onStartClick">
|
||||
<button type='button' data-bind="click: onStartClick, css: startClass" class="btn btn-lg">
|
||||
<span data-bind="text: startLabel"></span>
|
||||
<span class='glyphicon glyphicon-play'></span>
|
||||
<span data-bind="css: startIcon"></span>
|
||||
</button>
|
||||
</ul>
|
||||
<div class="input-group input-group-lg">
|
||||
<span class="input-group-addon" style="width:1%;"><span class="glyphicon glyphicon-time"></span></span>
|
||||
<input type="text" class="form-control search" data-bind="event: { input: onFilterChanged }, value: filter, valueUpdate: 'afterkeydown', attr: {placeholder: placeholder}"
|
||||
autocomplete="off" autofocus="autofocus">
|
||||
<input type="text" class="form-control search" autocomplete="off" autofocus="autofocus"
|
||||
data-bind="event: { input: onFilterChanged, keypress: onFilterKeyPress }, value: filter, valueUpdate: 'afterkeydown', attr: {placeholder: placeholder}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -46,7 +48,7 @@
|
||||
|
||||
<div style="height:74px"></div>
|
||||
|
||||
<div class="well" style="padding-bottom:0px;margin-bottom:0px;">
|
||||
<div class="well" style="padding-bottom:0px;margin-bottom:0px;" data-bind="visible: selectedTask">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
{!! Former::select('client')->addOption('', '')->addGroupClass('client-select') !!}
|
||||
@ -63,10 +65,10 @@
|
||||
<div class="list-group" data-bind="foreach: filteredTasks">
|
||||
<a href="#" data-bind="click: $parent.selectTask" class="list-group-item list-group-item-type1">
|
||||
<span class="pull-right">
|
||||
14
|
||||
<span data-bind="text: duration"></span>
|
||||
</span>
|
||||
<h5 class="list-group-item-heading" data-bind="text: description"></h5>
|
||||
<p class="list-group-item-text">...
|
||||
<p class="list-group-item-text">
|
||||
|
||||
</p>
|
||||
</a>
|
||||
@ -81,15 +83,24 @@
|
||||
self.tasks = ko.observableArray();
|
||||
self.filter = ko.observable('');
|
||||
self.selectedTask = ko.observable(false);
|
||||
self.clock = ko.observable(0);
|
||||
|
||||
self.onFilterChanged = function(data) {
|
||||
self.selectedTask(false);
|
||||
}
|
||||
|
||||
self.onFilterKeyPress = function(data, event) {
|
||||
if (event.which == 13) {
|
||||
self.onStartClick();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
self.onStartClick = function() {
|
||||
if (self.selectedTask()) {
|
||||
|
||||
console.log('start w/selected...');
|
||||
} else {
|
||||
console.log('start w/o selected...');
|
||||
var task = new TaskModel();
|
||||
task.description(self.filter());
|
||||
self.addTask(task);
|
||||
@ -98,14 +109,50 @@
|
||||
}
|
||||
}
|
||||
|
||||
self.tock = function(startTime) {
|
||||
console.log('tock..');
|
||||
self.clock(self.clock() + 1);
|
||||
setTimeout(function() {
|
||||
model.tock();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
self.startIcon = ko.computed(function() {
|
||||
if (self.selectedTask()) {
|
||||
if (self.selectedTask().isRunning()) {
|
||||
return 'glyphicon glyphicon-stop';
|
||||
} else {
|
||||
return 'glyphicon glyphicon-play';
|
||||
}
|
||||
} else {
|
||||
return 'glyphicon glyphicon-play';
|
||||
}
|
||||
});
|
||||
|
||||
self.startLabel = ko.computed(function() {
|
||||
if (self.selectedTask()) {
|
||||
return "{{ trans('texts.resume') }}";
|
||||
if (self.selectedTask().isRunning()) {
|
||||
return "{{ trans('texts.stop') }}";
|
||||
} else {
|
||||
return "{{ trans('texts.resume') }}";
|
||||
}
|
||||
} else {
|
||||
return "{{ trans('texts.start') }}";
|
||||
}
|
||||
});
|
||||
|
||||
self.startClass = ko.computed(function() {
|
||||
if (self.selectedTask()) {
|
||||
if (self.selectedTask().isRunning()) {
|
||||
return 'btn-danger';
|
||||
} else {
|
||||
return 'btn-success';
|
||||
}
|
||||
} else {
|
||||
return 'btn-success';
|
||||
}
|
||||
});
|
||||
|
||||
self.placeholder = ko.computed(function() {
|
||||
if (self.selectedTask() && self.selectedTask().description) {
|
||||
return self.selectedTask().description();
|
||||
@ -140,10 +187,112 @@
|
||||
function TaskModel(data) {
|
||||
var self = this;
|
||||
self.description = ko.observable('test');
|
||||
self.time_log = ko.observableArray();
|
||||
|
||||
if (data) {
|
||||
ko.mapping.fromJS(data, {}, this);
|
||||
self.time_log = ko.observableArray();
|
||||
data = JSON.parse(data.time_log);
|
||||
for (var i=0; i<data.length; i++) {
|
||||
self.time_log.push(new TimeModel(data[i]));
|
||||
}
|
||||
console.log('self.time_log.length: ' + self.time_log().length);
|
||||
}
|
||||
|
||||
self.isRunning = ko.computed(function() {
|
||||
if (! self.time_log().length) {
|
||||
return false;
|
||||
}
|
||||
var timeLog = self.time_log();
|
||||
var time = timeLog[timeLog.length-1];
|
||||
return time.isRunning();
|
||||
});
|
||||
|
||||
self.duration = ko.computed(function() {
|
||||
model.clock(); // bind to the clock
|
||||
|
||||
if (! self.time_log().length) {
|
||||
return '00:00:00';
|
||||
}
|
||||
var time = self.time_log()[0];
|
||||
if (time.isRunning()) {
|
||||
var duration = new Date().getTime() - (time.startTime() * 1000);
|
||||
duration = Math.floor(duration / 100) / 10;
|
||||
} else {
|
||||
duration = 0;
|
||||
}
|
||||
|
||||
duration = secondsToTime(duration);
|
||||
return duration.h + ':' + duration.m + ':' + duration.s;
|
||||
});
|
||||
}
|
||||
|
||||
function TimeModel(data) {
|
||||
var self = this;
|
||||
|
||||
var dateTimeFormat = '{{ $account->getMomentDateTimeFormat() }}';
|
||||
var timezone = '{{ $account->getTimezone() }}';
|
||||
|
||||
self.startTime = ko.observable(0);
|
||||
self.endTime = ko.observable(0);
|
||||
self.duration = ko.observable(0);
|
||||
self.actionsVisible = ko.observable(false);
|
||||
self.isStartValid = ko.observable(true);
|
||||
self.isEndValid = ko.observable(true);
|
||||
|
||||
if (data) {
|
||||
self.startTime(data[0]);
|
||||
self.endTime(data[1]);
|
||||
};
|
||||
|
||||
self.isEmpty = ko.computed(function() {
|
||||
return !self.startTime() && !self.endTime();
|
||||
});
|
||||
|
||||
self.isRunning = ko.computed(function() {
|
||||
return self.startTime() && !self.endTime();
|
||||
});
|
||||
|
||||
self.startTime.pretty = ko.computed({
|
||||
read: function() {
|
||||
return self.startTime() ? moment.unix(self.startTime()).tz(timezone).format(dateTimeFormat) : '';
|
||||
},
|
||||
write: function(data) {
|
||||
self.startTime(moment(data, dateTimeFormat).tz(timezone).unix());
|
||||
}
|
||||
});
|
||||
|
||||
self.endTime.pretty = ko.computed({
|
||||
read: function() {
|
||||
return self.endTime() ? moment.unix(self.endTime()).tz(timezone).format(dateTimeFormat) : '';
|
||||
},
|
||||
write: function(data) {
|
||||
self.endTime(moment(data, dateTimeFormat).tz(timezone).unix());
|
||||
}
|
||||
});
|
||||
|
||||
self.setNow = function() {
|
||||
self.startTime(moment.tz(timezone).unix());
|
||||
self.endTime(moment.tz(timezone).unix());
|
||||
}
|
||||
|
||||
self.duration.pretty = ko.computed({
|
||||
read: function() {
|
||||
var duration = false;
|
||||
var start = self.startTime();
|
||||
var end = self.endTime();
|
||||
|
||||
if (start && end) {
|
||||
var duration = end - start;
|
||||
}
|
||||
|
||||
var duration = moment.duration(duration * 1000);
|
||||
return Math.floor(duration.asHours()) + moment.utc(duration.asMilliseconds()).format(":mm:ss")
|
||||
},
|
||||
write: function(data) {
|
||||
self.endTime(self.startTime() + convertToSeconds(data));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(function() {
|
||||
@ -154,6 +303,7 @@
|
||||
model.addTask(taskModel);
|
||||
}
|
||||
ko.applyBindings(model);
|
||||
model.tock();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user