2017-09-24 14:42:24 +02:00
|
|
|
@extends('master')
|
|
|
|
|
|
|
|
@section('head')
|
|
|
|
@parent
|
|
|
|
|
|
|
|
<link href="{{ asset('css/built.css') }}?no_cache={{ NINJA_VERSION }}" rel="stylesheet" type="text/css"/>
|
2017-09-29 11:59:53 +02:00
|
|
|
<link href="{{ asset('css/jquery.timepicker.css') }}?no_cache={{ NINJA_VERSION }}" rel="stylesheet" type="text/css"/>
|
|
|
|
<script src="{{ asset('js/jquery.timepicker.js') }}?no_cache={{ NINJA_VERSION }}" type="text/javascript"></script>
|
2017-09-24 14:42:24 +02:00
|
|
|
|
|
|
|
@stop
|
|
|
|
|
|
|
|
@section('head_css')
|
|
|
|
@parent
|
|
|
|
|
|
|
|
<style type="text/css">
|
|
|
|
|
2017-09-30 23:13:41 +02:00
|
|
|
/*
|
2017-09-26 09:28:26 +02:00
|
|
|
@media (max-width: 768px) {
|
|
|
|
#formDiv {
|
|
|
|
position: relative;
|
|
|
|
}
|
|
|
|
}
|
2017-09-30 23:13:41 +02:00
|
|
|
*/
|
2017-09-26 09:28:26 +02:00
|
|
|
|
2017-09-26 09:58:34 +02:00
|
|
|
@media (max-width: 768px) {
|
|
|
|
#clock,
|
|
|
|
#startLabel {
|
|
|
|
display: none;
|
|
|
|
}
|
|
|
|
.navbar-right button {
|
|
|
|
padding-left: 0px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-24 14:42:24 +02:00
|
|
|
button .glyphicon {
|
|
|
|
vertical-align: text-top;
|
|
|
|
}
|
|
|
|
|
|
|
|
a:focus {
|
|
|
|
outline: none;
|
|
|
|
}
|
|
|
|
|
2017-09-25 18:59:13 +02:00
|
|
|
.fade-color {
|
|
|
|
animation-name: fadeToYellow;
|
|
|
|
}
|
|
|
|
|
|
|
|
.list-group-item {
|
|
|
|
animation-duration: .5s;
|
|
|
|
}
|
|
|
|
|
2017-09-24 14:42:24 +02:00
|
|
|
.list-group-item.active {
|
|
|
|
background-color: #f8f8f8 !important;
|
|
|
|
color: black !important;
|
|
|
|
border-left-color: #f8f8f8 !important;
|
|
|
|
border-right-color: #f8f8f8 !important;
|
|
|
|
box-shadow: 0 0 0 2px rgba(0,0,0,.1), 0 2px 2px rgba(0,0,0,.2);
|
|
|
|
border-color: #fff !important;
|
|
|
|
}
|
|
|
|
|
2017-09-25 18:59:13 +02:00
|
|
|
.list-group-item.changed {
|
|
|
|
background-color: #ffffaa !important;
|
|
|
|
}
|
|
|
|
|
|
|
|
@keyframes fadeToYellow {
|
|
|
|
from {background-color: #f8f8f8}
|
|
|
|
to {background-color: #ffffaa}
|
|
|
|
}
|
|
|
|
|
2017-09-24 22:25:56 +02:00
|
|
|
.list-group-item.active .list-group-item-text,
|
|
|
|
.list-group-item.active:focus .list-group-item-text,
|
|
|
|
.list-group-item.active:hover .list-group-item-text {
|
|
|
|
color: black !important;
|
|
|
|
}
|
|
|
|
|
2017-09-24 14:42:24 +02:00
|
|
|
span.link {
|
|
|
|
cursor:pointer;
|
|
|
|
color:#337ab7;
|
|
|
|
text-decoration:none;
|
|
|
|
}
|
|
|
|
|
|
|
|
span.link:hover {
|
|
|
|
text-decoration:underline;
|
|
|
|
}
|
|
|
|
|
|
|
|
.no-gutter > [class*='col-'] {
|
|
|
|
padding-right:0;
|
|
|
|
padding-left:0;
|
|
|
|
}
|
|
|
|
|
|
|
|
.list-group-item:before {
|
|
|
|
position: absolute;
|
|
|
|
top: 0;
|
|
|
|
left: 0;
|
|
|
|
bottom: 0;
|
|
|
|
width: 6px;
|
|
|
|
content: "";
|
|
|
|
}
|
|
|
|
|
|
|
|
.list-group-item-type1:before { background-color: #1c9f77; }
|
|
|
|
.list-group-item-type2:before { background-color: #d95d02; }
|
|
|
|
.list-group-item-type3:before { background-color: #716cb1; }
|
|
|
|
.list-group-item-type4:before { background-color: #e62a8b; }
|
|
|
|
.list-group-item-type5:before { background-color: #5fa213; }
|
|
|
|
.list-group-item-type6:before { background-color: #e6aa04; }
|
|
|
|
.list-group-item-type7:before { background-color: #a87821; }
|
|
|
|
.list-group-item-type8:before { background-color: #676767; }
|
|
|
|
|
2017-09-28 11:34:02 +02:00
|
|
|
.list-group-item-running:after {
|
|
|
|
position: absolute;
|
|
|
|
top: 0;
|
|
|
|
right: 0;
|
|
|
|
bottom: 0;
|
|
|
|
width: 6px;
|
|
|
|
content: "";
|
2017-10-02 10:54:22 +02:00
|
|
|
background-color: #36c157; /* green */
|
|
|
|
xbackground-color: #d9534f; /* red */
|
2017-10-01 15:31:19 +02:00
|
|
|
xbackground-color: orange; /* orange */
|
2017-09-28 11:34:02 +02:00
|
|
|
}
|
|
|
|
|
2017-09-24 21:44:20 +02:00
|
|
|
body {
|
|
|
|
margin-bottom: 60px;
|
|
|
|
}
|
|
|
|
|
2017-10-02 11:57:38 +02:00
|
|
|
#taskList {
|
|
|
|
position: fixed;
|
|
|
|
}
|
|
|
|
|
|
|
|
#taskForm {
|
|
|
|
overflow: visible;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-29 11:59:53 +02:00
|
|
|
.times-table td {
|
|
|
|
xmargin: 0px !important;
|
|
|
|
xpadding: 0px !important;
|
|
|
|
xpadding-bottom: 10px !important;
|
|
|
|
}
|
|
|
|
|
2017-10-01 19:26:53 +02:00
|
|
|
.ui-timepicker-wrapper.start-time {
|
|
|
|
width: 140px !important;
|
|
|
|
}
|
|
|
|
|
|
|
|
.ui-timepicker-wrapper.end-time {
|
2017-10-01 12:06:15 +02:00
|
|
|
width: 230px !important;
|
2017-09-30 23:13:41 +02:00
|
|
|
}
|
|
|
|
|
2017-09-24 21:44:20 +02:00
|
|
|
.footer {
|
|
|
|
position: fixed;
|
|
|
|
bottom: 0;
|
|
|
|
width: 100%;
|
|
|
|
height: 60px;
|
|
|
|
background-color: #313131;
|
|
|
|
color: white;
|
|
|
|
border-top-width: 3px;
|
|
|
|
border-top-color: #aaa;
|
|
|
|
border-top-style: ridge;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-24 14:42:24 +02:00
|
|
|
</style>
|
|
|
|
|
|
|
|
@stop
|
|
|
|
|
|
|
|
@section('body')
|
|
|
|
<nav class="navbar navbar-default navbar-fixed-top">
|
|
|
|
<div class="container-fluid">
|
|
|
|
<div class="navbar-collapse" style="padding-top:12px; padding-bottom:12px;">
|
|
|
|
|
|
|
|
<!-- Navbar Buttons -->
|
2017-09-27 14:26:55 +02:00
|
|
|
<ul class="nav navbar-right" style="margin-right:0px; padding-left:12px; float:right; display: none;">
|
2017-09-26 09:58:34 +02:00
|
|
|
<span id="clock" data-bind="text: selectedTask().duration, visible: selectedTask" class="hidden-xs"
|
2017-09-24 14:42:24 +02:00
|
|
|
style="font-size:28px; color:white; padding-right:12px; vertical-align:middle; display:none;"></span>
|
|
|
|
<button type='button' data-bind="click: onStartClick, css: startClass" class="btn btn-lg">
|
2017-09-26 09:58:34 +02:00
|
|
|
<span id="startLabel" data-bind="text: startLabel"></span>
|
2017-09-24 14:42:24 +02:00
|
|
|
<span data-bind="css: startIcon"></span>
|
|
|
|
</button>
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
<!-- Navbar Filter -->
|
|
|
|
<div class="input-group input-group-lg">
|
2017-09-28 07:57:44 +02:00
|
|
|
<span class="input-group-addon" style="width:1%;" data-bind="click: onFilterClick, style: { 'background-color': filterState() != 'all' ? '#ffffaa' : '' }" title="{{ trans('texts.filter_sort') }}"><span class="glyphicon glyphicon-filter"></span></span>
|
2017-09-24 16:04:32 +02:00
|
|
|
<input id="search" type="search" class="form-control search" autocomplete="off" autofocus="autofocus"
|
2017-09-27 13:25:56 +02:00
|
|
|
data-bind="event: { focus: onFilterFocus, input: onFilterChanged, keypress: onFilterKeyPress }, value: filter, valueUpdate: 'afterkeydown', attr: {placeholder: placeholder, style: filterStyle, disabled: formChanged }">
|
2017-09-24 21:02:15 +02:00
|
|
|
<span class="input-group-addon" style="width:1%;" data-bind="click: onRefreshClick" title="{{ trans('texts.refresh') }}"><span class="glyphicon glyphicon-repeat"></span></span>
|
2017-09-24 14:42:24 +02:00
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</nav>
|
|
|
|
|
2017-09-28 19:57:55 +02:00
|
|
|
<div style="height:72px"></div>
|
2017-09-24 14:42:24 +02:00
|
|
|
|
|
|
|
<!--
|
|
|
|
<div data-bind="text: ko.toJSON(model.selectedTask().client_id)"></div>
|
|
|
|
<div data-bind="text: ko.toJSON(model.selectedTask().project_id)"></div>
|
2017-09-25 15:46:02 +02:00
|
|
|
<div data-bind="text: ko.toJSON(model.selectedTask().client)"></div>
|
2017-09-24 14:42:24 +02:00
|
|
|
<div data-bind="text: ko.toJSON(model.selectedTask().project)"></div>
|
2017-09-25 15:46:02 +02:00
|
|
|
Client: <span data-bind="text: ko.toJSON(model.selectedClient().public_id)"></span>
|
|
|
|
Project: <span data-bind="text: ko.toJSON(model.selectedProject().public_id)"></span>
|
2017-09-24 14:42:24 +02:00
|
|
|
-->
|
|
|
|
|
|
|
|
<div class="container" style="margin: 0 auto;width: 100%;">
|
|
|
|
<div class="row no-gutter">
|
|
|
|
|
|
|
|
<!-- Task Form -->
|
2017-10-02 11:57:38 +02:00
|
|
|
<div id="taskForm" class="col-sm-7 col-sm-push-5">
|
2017-09-30 23:13:41 +02:00
|
|
|
<div id="formDiv" class="panel panel-default x-affix" data-bind="visible: selectedTask" style="margin:20px; display:none;">
|
2017-09-24 16:35:24 +02:00
|
|
|
<div class="panel-body">
|
|
|
|
<form id="taskForm">
|
|
|
|
<span data-bind="event: { keypress: onFormKeyPress, change: onFormChange, input: onFormChange }">
|
|
|
|
<div style="padding-bottom: 20px" class="client-select">
|
|
|
|
{!! Former::select('client_id')
|
|
|
|
->addOption('', '')
|
|
|
|
->label('client')
|
|
|
|
->data_bind("dropdown: selectedTask().client_id") !!}
|
|
|
|
</div>
|
|
|
|
<div style="padding-bottom: 20px" class="project-select">
|
|
|
|
{!! Former::select('project_id')
|
|
|
|
->addOption('', '')
|
|
|
|
->data_bind("dropdown: selectedTask().project_id")
|
|
|
|
->label(trans('texts.project')) !!}
|
|
|
|
</div>
|
2017-09-29 11:59:53 +02:00
|
|
|
<div style="padding-bottom: 20px">
|
2017-09-24 16:35:24 +02:00
|
|
|
{!! Former::textarea('description')
|
|
|
|
->data_bind("value: selectedTask().description")
|
|
|
|
->rows(4) !!}
|
2017-09-29 11:59:53 +02:00
|
|
|
</div>
|
|
|
|
|
|
|
|
<label>{{ trans('texts.times') }}</label>
|
|
|
|
|
|
|
|
|
2017-10-01 13:01:37 +02:00
|
|
|
<table class="table times-table" data-bind="event: { change: selectedTask().onChange }" style="margin-bottom: 0px !important;">
|
2017-10-02 10:54:22 +02:00
|
|
|
<tbody data-bind="foreach: selectedTask().sortedTimes">
|
2017-10-01 11:01:31 +02:00
|
|
|
<tr data-bind="event: { mouseover: onMouseOver, mouseout: onMouseOut }">
|
2017-09-29 11:59:53 +02:00
|
|
|
<td style="padding: 0 6px 10px 0">
|
|
|
|
{!! Former::text('date')
|
2017-10-01 12:04:49 +02:00
|
|
|
->placeholder($account->formatDate($account->getDateTime()))
|
2017-10-01 08:55:45 +02:00
|
|
|
->data_bind("datepicker: startDate, valueUpdate: 'afterkeydown'")
|
|
|
|
->data_date_format(Session::get(SESSION_DATE_PICKER_FORMAT, DEFAULT_DATE_PICKER_FORMAT))
|
2017-09-29 11:59:53 +02:00
|
|
|
->raw() !!}
|
|
|
|
</td>
|
|
|
|
<td style="padding: 0 6px 10px 6px">
|
2017-10-01 15:16:27 +02:00
|
|
|
<div data-bind="css: { 'has-error': !isStartValid() }">
|
|
|
|
{!! Former::text('start_time')
|
|
|
|
->placeholder('start_time')
|
2017-10-01 19:26:53 +02:00
|
|
|
->data_bind("timepicker: startTimeOfDay, timepickerOptions: {className: 'start-time', scrollDefault: 'now', timeFormat: '" . ($account->military_time ? 'H:i:s' : 'g:i:s A') . "'}")
|
2017-10-01 15:16:27 +02:00
|
|
|
->raw() !!}
|
|
|
|
</div>
|
2017-09-29 11:59:53 +02:00
|
|
|
</td>
|
|
|
|
<td style="padding: 0 6px 10px 6px">
|
2017-10-01 15:16:27 +02:00
|
|
|
<div data-bind="css: { 'has-error': !isEndValid() }">
|
|
|
|
{!! Former::text('end_time')
|
|
|
|
->placeholder('end_time')
|
2017-10-01 19:26:53 +02:00
|
|
|
->data_bind("timepicker: endTimeOfDay, timepickerOptions: {className: 'end-time', scrollDefault: 'now', timeFormat: '" . ($account->military_time ? 'H:i:s' : 'g:i:s A') . "'}")
|
2017-10-01 15:16:27 +02:00
|
|
|
->raw() !!}
|
|
|
|
</div>
|
2017-09-29 11:59:53 +02:00
|
|
|
</td>
|
|
|
|
<td style="padding: 0 0 10px 6px">
|
|
|
|
{!! Former::text('duration')
|
2017-10-01 11:01:31 +02:00
|
|
|
->placeholder('duration')
|
2017-10-01 19:26:53 +02:00
|
|
|
->addClass('hide-phone')
|
2017-10-01 18:43:05 +02:00
|
|
|
->data_bind("typeahead: duration")
|
2017-09-29 11:59:53 +02:00
|
|
|
->raw() !!}
|
|
|
|
</td>
|
2017-10-01 19:26:53 +02:00
|
|
|
<td style="width:38px; padding-top: 0px; padding-right: 8px" class="hide-phone">
|
2017-10-01 11:01:31 +02:00
|
|
|
<i style="cursor:pointer;float:right" data-bind="click: $root.selectedTask().removeTime, visible: actionButtonVisible" class="fa fa-minus-circle redlink" title="{{ trans('texts.remove') }}"/>
|
|
|
|
</td>
|
2017-09-29 11:59:53 +02:00
|
|
|
|
|
|
|
</tr>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
2017-09-24 16:35:24 +02:00
|
|
|
</span>
|
|
|
|
|
2017-09-26 09:58:34 +02:00
|
|
|
<center id="buttons" style="padding-top: 30px">
|
2017-09-24 16:35:24 +02:00
|
|
|
<span data-bind="visible: showArchive">
|
|
|
|
{!! DropdownButton::normal(trans('texts.archive'))
|
2017-09-24 14:42:24 +02:00
|
|
|
->withAttributes([
|
2017-09-24 16:35:24 +02:00
|
|
|
'class' => 'archive-dropdown',
|
2017-09-24 14:42:24 +02:00
|
|
|
])
|
2017-09-24 16:35:24 +02:00
|
|
|
->large()
|
|
|
|
->withContents([
|
|
|
|
['label' => trans('texts.delete_task'), 'url' => 'javascript:model.onDeleteClick()'],
|
|
|
|
]
|
|
|
|
)->split() !!}
|
|
|
|
</span>
|
|
|
|
{!! Button::normal(trans('texts.cancel'))
|
|
|
|
->appendIcon(Icon::create('remove-circle'))
|
|
|
|
->withAttributes([
|
|
|
|
'data-bind' => 'click: onCancelClick, visible: showCancel',
|
|
|
|
])
|
|
|
|
->large() !!}
|
|
|
|
|
|
|
|
{!! Button::success(trans('texts.save'))
|
|
|
|
->large()
|
|
|
|
->appendIcon(Icon::create('floppy-disk'))
|
|
|
|
->withAttributes([
|
2017-09-26 10:57:25 +02:00
|
|
|
'data-bind' => 'click: onSaveClick, css: { disabled: ! formChanged() || ! isSaveEnabled() }',
|
2017-09-24 16:35:24 +02:00
|
|
|
]) !!}
|
|
|
|
</center>
|
|
|
|
</form>
|
2017-09-24 14:42:24 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Task List -->
|
2017-10-02 11:57:38 +02:00
|
|
|
<div id="taskList" class="list-group col-sm-5 col-sm-pull-7" style="display:none;overflow:auto;margin-bottom:0px;">
|
2017-09-28 11:21:05 +02:00
|
|
|
<div id="filterPanel" style="margin-bottom:0px;padding-top:20px;padding-bottom:0px;padding-left:10px;display:none;">
|
2017-09-27 22:15:42 +02:00
|
|
|
<div class="panel panel-default">
|
|
|
|
<div class="panel-body">
|
|
|
|
<div class="row" xstyle="padding-bottom:22px;">
|
|
|
|
<div class="col-md-12">
|
2017-09-28 07:57:44 +02:00
|
|
|
{!! Former::select('filter_state')
|
|
|
|
->label('filter')
|
2017-09-27 22:15:42 +02:00
|
|
|
->addOption(trans('texts.all'), 'all')
|
|
|
|
->addOption(trans('texts.stopped'), 'stopped')
|
2017-09-28 07:57:44 +02:00
|
|
|
->addOption(trans('texts.running'), 'running')
|
|
|
|
->data_bind('value: filterState') !!}
|
2017-09-27 22:15:42 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="row">
|
|
|
|
<div class="col-md-6" style="padding-top:24px;">
|
2017-09-28 11:01:27 +02:00
|
|
|
{!! Former::select('sort_field')
|
2017-09-28 10:42:20 +02:00
|
|
|
->addOption(trans('texts.date'), 'createdAt')
|
2017-09-27 22:15:42 +02:00
|
|
|
->addOption(trans('texts.duration'), 'duration')
|
|
|
|
->addOption(trans('texts.client'), 'client')
|
|
|
|
->addOption(trans('texts.project'), 'project')
|
2017-09-28 07:57:44 +02:00
|
|
|
->addOption(trans('texts.description'), 'description')
|
2017-09-28 11:01:27 +02:00
|
|
|
->data_bind('value: sortField, event: {change: onSortChange}') !!}
|
2017-09-27 22:15:42 +02:00
|
|
|
</div>
|
|
|
|
<div class="col-md-6" style="padding-top:24px;">
|
2017-09-28 10:42:20 +02:00
|
|
|
{!! Former::select('sort_direction')
|
|
|
|
->addOption(trans('texts.ascending'), 'ascending')
|
|
|
|
->addOption(trans('texts.descending'), 'descending')
|
|
|
|
->data_bind('value: sortDirection, event: {change: onSortChange}') !!}
|
2017-09-27 22:15:42 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
2017-09-25 19:09:23 +02:00
|
|
|
</div>
|
2017-09-27 22:15:42 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div data-bind="foreach: filteredTasks">
|
2017-09-28 11:34:02 +02:00
|
|
|
<a href="#" data-bind="click: $parent.selectTask, event: { mouseover: onMouseOver, mouseout: onMouseOut }, css: listItemState"
|
2017-09-27 22:15:42 +02:00
|
|
|
class="list-group-item">
|
|
|
|
<div class="pull-right" style="text-align:right;">
|
|
|
|
<div data-bind="visible: actionButtonVisible()"
|
|
|
|
data-bindx="style : { visibility : actionButtonVisible() ? '' : 'hidden' }">
|
|
|
|
|
|
|
|
<button type="button" data-bind="css: startClass, click: onStartClick, clickBubble: false"
|
|
|
|
class="btn btn-sm" style="padding-left:0px; padding-right: 12px; padding-bottom: 6px; margin-top:5px;">
|
|
|
|
<span data-bind="css: startIcon"></span>
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="pull-right" style="text-align:right; padding-left: 16px;">
|
|
|
|
<div data-bind="text: totalDuration, style: { fontWeight: isRunning() ? 'bold' : '' }"></div>
|
|
|
|
<div data-bind="text: age, style: { fontWeight: isRunning() ? 'bold' : '' }" style="padding-top: 2px"></div>
|
|
|
|
</div>
|
|
|
|
<div style="white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">
|
|
|
|
<h4 class="list-group-item-heading">
|
|
|
|
<span data-bind="text: description, style: { fontWeight: isRunning() ? 'bold' : '' }"></span>
|
|
|
|
</h4>
|
|
|
|
<p class="list-group-item-text">
|
|
|
|
<span class="link" data-bind="text: clientName, click: $parent.viewClient, clickBubble: false"></span>
|
|
|
|
<span data-bind="visible: clientName && projectName"> | </span>
|
|
|
|
<span class="link" data-bind="text: projectName, click: $parent.viewProject, clickBubble: false"></span>
|
|
|
|
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</a>
|
|
|
|
</div>
|
2017-09-24 14:42:24 +02:00
|
|
|
</div>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
2017-09-24 22:25:56 +02:00
|
|
|
<!--
|
2017-09-24 21:44:20 +02:00
|
|
|
<footer class="footer">
|
|
|
|
<div style="padding-left: 16px; padding-top: 16px;">
|
|
|
|
<div data-bind="text: statistics"></div>
|
|
|
|
</div>
|
|
|
|
</footer>
|
2017-09-24 22:25:56 +02:00
|
|
|
-->
|
2017-09-24 21:44:20 +02:00
|
|
|
|
2017-09-24 14:42:24 +02:00
|
|
|
@include('tasks.time_tracker_knockout')
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
|
|
|
|
|
|
var tasks = {!! $tasks !!};
|
|
|
|
var clients = {!! $clients !!};
|
|
|
|
var projects = {!! $projects !!};
|
|
|
|
var timezone = '{{ $account->getTimezone() }}';
|
|
|
|
|
|
|
|
var clientMap = {};
|
|
|
|
var projectMap = {};
|
|
|
|
var projectsForClientMap = {};
|
|
|
|
|
2017-09-25 12:53:50 +02:00
|
|
|
function refreshClientList() {
|
|
|
|
var $clientSelect = $('select#client_id');
|
|
|
|
$clientSelect.find('option').remove().end().combobox('refresh');
|
|
|
|
$clientSelect.append(new Option('', ''));
|
|
|
|
|
|
|
|
@if (Auth::user()->can('create', ENTITY_CLIENT))
|
2017-09-25 16:26:22 +02:00
|
|
|
//$clientSelect.append(new Option("{{ trans('texts.create_client')}}: $name", '-1'));
|
2017-09-25 12:53:50 +02:00
|
|
|
@endif
|
|
|
|
|
|
|
|
for (var i=0; i<clients.length; i++) {
|
|
|
|
var client = clients[i];
|
|
|
|
var clientName = getClientDisplayName(client);
|
|
|
|
if (!clientName) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$clientSelect.append(new Option(clientName, client.public_id));
|
|
|
|
}
|
|
|
|
$('select#client_id').combobox('refresh');
|
|
|
|
}
|
|
|
|
|
2017-09-24 14:42:24 +02:00
|
|
|
function refreshProjectList(forceClear) {
|
|
|
|
var clientId = $('input[name=client_id]').val();
|
|
|
|
$projectCombobox = $('select#project_id');
|
|
|
|
$projectCombobox.find('option').remove().end().combobox('refresh');
|
|
|
|
$projectCombobox.append(new Option('', ''));
|
2017-09-25 12:53:50 +02:00
|
|
|
|
2017-09-24 14:42:24 +02:00
|
|
|
@if (Auth::user()->can('create', ENTITY_PROJECT))
|
|
|
|
if (clientId) {
|
|
|
|
$projectCombobox.append(new Option("{{ trans('texts.create_project')}}: $name", '-1'));
|
|
|
|
}
|
|
|
|
@endif
|
|
|
|
|
2017-09-25 12:53:50 +02:00
|
|
|
if (clientId && ! forceClear) {
|
|
|
|
var list = projectsForClientMap.hasOwnProperty(clientId) ? projectsForClientMap[clientId] : [];
|
|
|
|
} else {
|
|
|
|
var list = projects;
|
|
|
|
}
|
2017-09-24 14:42:24 +02:00
|
|
|
|
|
|
|
for (var i=0; i<list.length; i++) {
|
|
|
|
var project = list[i];
|
|
|
|
$projectCombobox.append(new Option(project.name, project.public_id));
|
|
|
|
}
|
2017-09-25 12:53:50 +02:00
|
|
|
|
2017-09-24 14:42:24 +02:00
|
|
|
$('select#project_id').combobox('refresh');
|
|
|
|
}
|
|
|
|
|
2017-09-25 12:53:50 +02:00
|
|
|
function addClientToMaps(client) {
|
|
|
|
clientMap[client.public_id] = client;
|
|
|
|
}
|
|
|
|
|
2017-09-24 16:04:32 +02:00
|
|
|
function addProjectToMaps(project) {
|
|
|
|
var client = project.client;
|
|
|
|
projectMap[project.public_id] = project;
|
|
|
|
if (!projectsForClientMap.hasOwnProperty(client.public_id)) {
|
|
|
|
projectsForClientMap[client.public_id] = [];
|
|
|
|
}
|
|
|
|
projectsForClientMap[client.public_id].push(project);
|
|
|
|
}
|
|
|
|
|
2017-09-26 09:12:03 +02:00
|
|
|
function sendKeepAlive() {
|
|
|
|
setTimeout(function() {
|
|
|
|
$.get('{{ URL::to('/keep_alive') }}', function (response) {
|
|
|
|
if (response == '{{ RESULT_SUCCESS }}') {
|
|
|
|
sendKeepAlive()
|
|
|
|
} else {
|
|
|
|
location.reload();
|
|
|
|
}
|
|
|
|
}).fail(function() {
|
|
|
|
location.reload();
|
|
|
|
});
|
|
|
|
}, 1000 * 60 * 15);
|
|
|
|
}
|
|
|
|
|
2017-09-24 14:42:24 +02:00
|
|
|
$(function() {
|
|
|
|
|
|
|
|
// setup clients and project comboboxes
|
|
|
|
for (var i=0; i<projects.length; i++) {
|
2017-09-25 12:53:50 +02:00
|
|
|
addProjectToMaps(projects[i])
|
2017-09-24 14:42:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (var i=0; i<clients.length; i++) {
|
2017-09-25 12:53:50 +02:00
|
|
|
addClientToMaps(clients[i]);
|
2017-09-24 14:42:24 +02:00
|
|
|
}
|
|
|
|
|
2017-09-25 12:53:50 +02:00
|
|
|
var $clientSelect = $('select#client_id');
|
2017-09-24 14:42:24 +02:00
|
|
|
$clientSelect.on('change', function(e) {
|
|
|
|
var clientId = $('input[name=client_id]').val();
|
|
|
|
var projectId = $('input[name=project_id]').val();
|
|
|
|
var client = clientMap[clientId];
|
|
|
|
var project = projectMap[projectId];
|
|
|
|
if (!clientId && (window.model && !model.selectedTask().client())) {
|
|
|
|
e.preventDefault();return;
|
|
|
|
}
|
|
|
|
if (window.model && model.selectedTask()) {
|
2017-09-25 16:26:22 +02:00
|
|
|
var clientModel = new ClientModel(client);
|
|
|
|
if (clientId == -1) {
|
|
|
|
clientModel.name($('#client_name').val());
|
|
|
|
}
|
|
|
|
model.selectedTask().client(clientModel);
|
2017-09-24 14:42:24 +02:00
|
|
|
model.selectedTask().client_id(clientId);
|
|
|
|
model.selectedTask().project_id(0);
|
|
|
|
model.selectedTask().project(false);
|
|
|
|
}
|
|
|
|
refreshProjectList();
|
|
|
|
});
|
|
|
|
|
|
|
|
var $projectSelect = $('select#project_id').on('change', function(e) {
|
|
|
|
$clientCombobox = $('select#client_id');
|
|
|
|
var projectId = $('input[name=project_id]').val();
|
|
|
|
if (projectId == '-1') {
|
|
|
|
$('input[name=project_name]').val(projectName);
|
|
|
|
//var project = new ProjectModel();
|
2017-09-27 22:15:42 +02:00
|
|
|
//project.name = projectName;
|
2017-09-24 14:42:24 +02:00
|
|
|
//model.selectedTask().project = project;
|
|
|
|
//model.selectedTask().project_id(projectId);
|
|
|
|
} else if (projectId) {
|
|
|
|
var project = projectMap[projectId];
|
|
|
|
model.selectedTask().project(new ProjectModel(project));
|
|
|
|
model.selectedTask().project_id(projectId);
|
|
|
|
// when selecting a project make sure the client is loaded
|
|
|
|
if (project && project.client) {
|
|
|
|
var client = clientMap[project.client.public_id];
|
|
|
|
if (client) {
|
|
|
|
project.client = client;
|
|
|
|
model.selectedTask().client(new ClientModel(client));
|
|
|
|
model.selectedTask().client_id(client.public_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$clientSelect.trigger('change');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-09-25 15:46:02 +02:00
|
|
|
$projectSelect.on('change', function(e) {
|
|
|
|
var projectId = $('input[name=project_id]').val();
|
|
|
|
if (window.model && model.selectedTask() && projectId == -1) {
|
|
|
|
var project = new ProjectModel();
|
|
|
|
project.name($('#project_name').val());
|
|
|
|
model.selectedTask().project_id(-1);
|
|
|
|
model.selectedTask().project(project);
|
|
|
|
}
|
|
|
|
refreshProjectList();
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2017-09-24 16:04:32 +02:00
|
|
|
Mousetrap.bind('/', function(e) {
|
|
|
|
event.preventDefault();
|
|
|
|
$('#search').focus();
|
|
|
|
});
|
|
|
|
|
2017-09-24 14:42:24 +02:00
|
|
|
@include('partials/entity_combobox', ['entityType' => ENTITY_PROJECT])
|
|
|
|
|
2017-09-25 12:53:50 +02:00
|
|
|
refreshClientList();
|
2017-09-24 14:42:24 +02:00
|
|
|
$clientSelect.trigger('change');
|
|
|
|
|
|
|
|
window.model = new ViewModel();
|
2017-09-27 14:26:55 +02:00
|
|
|
var taskModels = [];
|
2017-09-24 14:42:24 +02:00
|
|
|
for (var i=0; i<tasks.length; i++) {
|
|
|
|
var task = tasks[i];
|
|
|
|
var taskModel = new TaskModel(task);
|
2017-09-27 14:26:55 +02:00
|
|
|
taskModels.push(taskModel);
|
2017-09-24 14:42:24 +02:00
|
|
|
}
|
2017-09-27 14:26:55 +02:00
|
|
|
ko.utils.arrayPushAll(model.tasks, taskModels);
|
2017-09-24 14:42:24 +02:00
|
|
|
ko.applyBindings(model);
|
2017-09-26 09:19:33 +02:00
|
|
|
model.refreshTitle();
|
2017-09-24 14:42:24 +02:00
|
|
|
model.tock();
|
2017-09-25 12:03:19 +02:00
|
|
|
|
|
|
|
if (isStorageSupported()) {
|
2017-09-28 11:01:27 +02:00
|
|
|
var taskId = localStorage.getItem('last:time_tracker:task_id');
|
2017-09-25 12:03:19 +02:00
|
|
|
var task = model.taskById(taskId);
|
|
|
|
if (task) {
|
|
|
|
setTimeout(function() {
|
|
|
|
model.selectTask(task);
|
|
|
|
}, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-27 14:26:55 +02:00
|
|
|
$('#taskList, .navbar-right').show();
|
2017-09-24 14:42:24 +02:00
|
|
|
|
|
|
|
$('.archive-dropdown:not(.dropdown-toggle)').click(function() {
|
|
|
|
model.onArchiveClick();
|
|
|
|
});
|
2017-09-24 21:44:20 +02:00
|
|
|
|
2017-09-25 10:52:57 +02:00
|
|
|
toastr.options.timeOut = 3000;
|
|
|
|
toastr.options.positionClass = 'toast-bottom-right';
|
|
|
|
|
2017-10-01 21:16:38 +02:00
|
|
|
if (navigator.userAgent != '{{ TIME_TRACKER_USER_AGENT }}') {
|
2017-09-28 15:19:22 +02:00
|
|
|
var link = '{{ config('ninja.time_tracker_web_url') }}';
|
2017-09-26 10:22:29 +02:00
|
|
|
var message = "{{ trans('texts.download_desktop_app') }}";
|
|
|
|
if (isMobile) {
|
2017-09-26 10:44:05 +02:00
|
|
|
toastr.warning("{{ trans('texts.time_tracker_mobile_help')}}", false, {
|
2017-09-28 15:19:22 +02:00
|
|
|
timeOut: 5000,
|
2017-09-26 10:54:56 +02:00
|
|
|
closeButton: true,
|
2017-09-26 10:44:05 +02:00
|
|
|
});
|
2017-09-26 10:22:29 +02:00
|
|
|
if (isIPhone) {
|
|
|
|
link = '{{ NINJA_IOS_APP_URL }}';
|
|
|
|
message = "{{ trans('texts.download_iphone_app') }}";
|
|
|
|
} else if (isAndroid) {
|
|
|
|
link = '{{ NINJA_ANDROID_APP_URL }}';
|
|
|
|
message = "{{ trans('texts.download_android_app') }}";
|
|
|
|
}
|
|
|
|
}
|
2017-09-28 15:19:22 +02:00
|
|
|
if (link) {
|
|
|
|
var options = {
|
|
|
|
timeOut: 5000,
|
|
|
|
closeButton: true,
|
|
|
|
onclick: function() {
|
|
|
|
window.open(link, '_blank');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
toastr.info(message, false, options);
|
|
|
|
}
|
2017-09-26 08:54:36 +02:00
|
|
|
}
|
|
|
|
|
2017-09-26 11:46:23 +02:00
|
|
|
if (model.isDesktop()) {
|
|
|
|
sendKeepAlive();
|
|
|
|
}
|
2017-09-26 09:12:03 +02:00
|
|
|
|
2017-09-26 10:15:03 +02:00
|
|
|
function setButtonSize() {
|
|
|
|
if ($(window).width() > 350) {
|
2017-09-26 09:58:34 +02:00
|
|
|
$('#buttons .btn').addClass('btn-lg');
|
2017-09-26 10:15:03 +02:00
|
|
|
} else {
|
2017-09-26 09:58:34 +02:00
|
|
|
$('#buttons .btn').removeClass('btn-lg');
|
|
|
|
}
|
2017-09-26 10:15:03 +02:00
|
|
|
}
|
2017-10-02 11:57:38 +02:00
|
|
|
function setPanelHeights() {
|
|
|
|
var height = $(window).height() - $('.navbar').height() - 2;
|
|
|
|
$('#taskList').height(height);
|
|
|
|
}
|
2017-09-26 10:15:03 +02:00
|
|
|
$(window).on('resize', function() {
|
|
|
|
setButtonSize();
|
2017-10-02 11:57:38 +02:00
|
|
|
setPanelHeights();
|
2017-09-26 10:15:03 +02:00
|
|
|
});
|
|
|
|
setButtonSize();
|
2017-10-02 11:57:38 +02:00
|
|
|
setPanelHeights();
|
2017-09-26 09:58:34 +02:00
|
|
|
|
2017-09-24 22:48:32 +02:00
|
|
|
$(window).on('beforeunload', function () {
|
2017-09-26 11:46:23 +02:00
|
|
|
if (model.isDesktop()) {
|
2017-09-26 08:43:19 +02:00
|
|
|
return undefined;
|
|
|
|
}
|
2017-09-24 22:48:32 +02:00
|
|
|
if (model.selectedTask() && model.formChanged()) {
|
2017-09-26 08:43:19 +02:00
|
|
|
return "{{ trans('texts.save_or_discard') }}";
|
2017-09-24 22:48:32 +02:00
|
|
|
} else {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
});
|
2017-09-25 08:53:20 +02:00
|
|
|
|
2017-09-24 14:42:24 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
@stop
|