diff --git a/appinfo/routes.php b/appinfo/routes.php
index 64aad6b..ebb203b 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -8,7 +8,7 @@
* it's instantiated in there
*/
-
+
return [
'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
@@ -18,9 +18,9 @@
['name' => 'dashboard#index', 'url' => '/dashboard', 'verb' => 'GET'],
['name' => 'reports#index', 'url' => '/reports', 'verb' => 'GET'],
['name' => 'timelines#index', 'url' => '/timelines', 'verb' => 'GET'],
-
+
['name' => 'timelinesAdmin#index', 'url' => '/timelines-admin', 'verb' => 'GET'],
-
+
['name' => 'tags#index', 'url' => '/tags', 'verb' => 'GET'],
['name' => 'goals#index', 'url' => '/goals', 'verb' => 'GET'],
@@ -31,8 +31,10 @@
['name' => 'ajax#update_work_interval', 'url' => '/ajax/update-work-interval/{id}', 'verb' => 'POST'],
['name' => 'ajax#add_work_interval', 'url' => '/ajax/add-work-interval/{name}', 'verb' => 'POST'],
['name' => 'ajax#delete_work_interval', 'url' => '/ajax/delete-work-interval/{id}', 'verb' => 'POST'],
+ ['name' => 'ajax#add_cost', 'url' => '/ajax/add-cost/{id}', 'verb' => 'POST'],
+
- ['name' => 'ajax#get_clients', 'url' => '/ajax/clients', 'verb' => 'GET'],
+ ['name' => 'ajax#get_clients', 'url' => '/ajax/clients', 'verb' => 'GET'],
['name' => 'ajax#add_client', 'url' => '/ajax/add-client/{name}', 'verb' => 'POST'],
['name' => 'ajax#edit_client', 'url' => '/ajax/edit-client/{id}', 'verb' => 'POST'],
['name' => 'ajax#delete_client', 'url' => '/ajax/delete-client/{id}', 'verb' => 'POST'],
diff --git a/composer.json b/composer.json
index 95a360a..3036dd7 100644
--- a/composer.json
+++ b/composer.json
@@ -10,5 +10,6 @@
],
"require": {},
"require-dev": {
+ "christophwurst/nextcloud": "^23.0"
}
}
diff --git a/composer.lock b/composer.lock
index c9b161c..5780326 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,9 +4,186 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "2e664be3728bc19c6f91bbed1ff62617",
+ "content-hash": "f406174408d593dde45d93667f53b5bc",
"packages": [],
- "packages-dev": [],
+ "packages-dev": [
+ {
+ "name": "christophwurst/nextcloud",
+ "version": "v23.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ChristophWurst/nextcloud_composer.git",
+ "reference": "7f7b957934d22205aab66c568ac3029bd8f2110d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ChristophWurst/nextcloud_composer/zipball/7f7b957934d22205aab66c568ac3029bd8f2110d",
+ "reference": "7f7b957934d22205aab66c568ac3029bd8f2110d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.3 || ~8.0.0",
+ "psr/container": "^1.0",
+ "psr/event-dispatcher": "^1.0",
+ "psr/log": "^1.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "23.0.0-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "AGPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Christoph Wurst",
+ "email": "christoph@winzerhof-wurst.at"
+ }
+ ],
+ "description": "Composer package containing Nextcloud's public API (classes, interfaces)",
+ "time": "2022-02-22T09:41:29+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf",
+ "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "time": "2021-03-05T17:36:06+00:00"
+ },
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "time": "2019-01-08T18:20:26+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "time": "2021-05-03T11:20:27+00:00"
+ }
+ ],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
@@ -14,5 +191,5 @@
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
- "plugin-api-version": "2.3.0"
+ "plugin-api-version": "1.1.0"
}
diff --git a/js/src/dashboard.js b/js/src/dashboard.js
index c51517e..aa0a93e 100644
--- a/js/src/dashboard.js
+++ b/js/src/dashboard.js
@@ -11,243 +11,309 @@ require('../../css/style.css');
var Chart = require("chart.js");
Chart.plugins.register({
- afterDraw: function(chart) {
- if (chart.data.datasets.length === 0 || chart.data.datasets[0].data.length === 0) {
- // No data is present
- var ctx = chart.chart.ctx;
- var width = chart.chart.width;
- var height = chart.chart.height
- chart.clear();
-
- ctx.save();
- ctx.textAlign = 'center';
- ctx.textBaseline = 'middle';
- ctx.font = "16px normal 'Helvetica Nueue'";
- ctx.fillText('No data to display', width / 2, height / 2);
- ctx.restore();
- }
-}
+ afterDraw: function (chart) {
+ if (chart.data.datasets.length === 0 || chart.data.datasets[0].data.length === 0) {
+ // No data is present
+ var ctx = chart.chart.ctx;
+ var width = chart.chart.width;
+ var height = chart.chart.height
+ chart.clear();
+
+ ctx.save();
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+ ctx.font = "16px normal 'Helvetica Nueue'";
+ ctx.fillText('No data to display', width / 2, height / 2);
+ ctx.restore();
+ }
+ }
});
var dtf = require("./dateformat.js");
-(function() {
- $.ajaxSetup({
- headers: { 'RequestToken': OC.requestToken }
- });
-
- $( function() {
- $(document).ready(function() {
-
- var start = moment().subtract(29, 'days');
- var end = moment();
- var group1 = 'client';
- var group2 = 'project';
- var group3 = '';
- var filterProjectId = '';
- var filterClientId = '';
- var myDoughnutChart = null;
-
- function cb(start, end) {
- $('#report-range span').html(start.format(dtf.dformat()) + ' - ' + end.format(dtf.dformat()));
- }
- $("#report-range").daterangepicker({
- timePicker: false,
- startDate: start,
- endDate: end,
- showCustomRangeLabel: false,
- ranges: {
- 'Today': [moment().startOf('day'), moment().endOf('day')],
- 'This week': [moment().startOf('week'), moment().endOf('week')],
- 'Last 7 Days': [moment().startOf('day').subtract(6, 'days'), moment().endOf('day')],
- 'Last 30 Days': [moment().startOf('day').subtract(29, 'days'), moment().endOf('day')],
- 'Last 90 Days': [moment().startOf('day').subtract(89, 'days'), moment().endOf('day')],
- 'Last 365 Days': [moment().startOf('day').subtract(364, 'days'), moment().endOf('day')],
- 'This Month': [moment().startOf('month'), moment().endOf('day')],
- 'This Year': [moment().startOf('year'), moment().endOf('day')],
- 'Starting last year': [moment().startOf('year').subtract(1, 'year'), moment().endOf('day')],
- 'Last 3 years': [moment().startOf('day').subtract(3, 'year'), moment().endOf('day')],
- 'Last 5 years': [moment().startOf('day').subtract(5, 'year'), moment().endOf('day')],
- },
- locale: {
- format: dtf.dformat(),
- firstDay: firstDay
- }
- },cb);
- $("#report-range").on('apply.daterangepicker', function(ev, picker) {
- //days = Math.round((picker.endDate.unix()-picker.startDate.unix()) / 86400);
- start = picker.startDate;
+(function () {
+ $.ajaxSetup({
+ headers: {'RequestToken': OC.requestToken}
+ });
+
+ $(function () {
+ $(document).ready(function () {
+
+ var start = moment().subtract(29, 'days');
+ var end = moment();
+ var group1 = 'client';
+ var group2 = 'project';
+ var group3 = '';
+ var filterProjectId = '';
+ var filterClientId = '';
+ var myDoughnutChart = null;
+ var costChart = null;
+
+ function cb(start, end) {
+ $('#report-range span').html(start.format(dtf.dformat()) + ' - ' + end.format(dtf.dformat()));
+ }
+
+ $("#report-range").daterangepicker({
+ timePicker: false,
+ startDate: start,
+ endDate: end,
+ showCustomRangeLabel: false,
+ ranges: {
+ 'Today': [moment().startOf('day'), moment().endOf('day')],
+ 'This week': [moment().startOf('week'), moment().endOf('week')],
+ 'Last 7 Days': [moment().startOf('day').subtract(6, 'days'), moment().endOf('day')],
+ 'Last 30 Days': [moment().startOf('day').subtract(29, 'days'), moment().endOf('day')],
+ 'Last 90 Days': [moment().startOf('day').subtract(89, 'days'), moment().endOf('day')],
+ 'Last 365 Days': [moment().startOf('day').subtract(364, 'days'), moment().endOf('day')],
+ 'This Month': [moment().startOf('month'), moment().endOf('day')],
+ 'This Year': [moment().startOf('year'), moment().endOf('day')],
+ 'Starting last year': [moment().startOf('year').subtract(1, 'year'), moment().endOf('day')],
+ 'Last 3 years': [moment().startOf('day').subtract(3, 'year'), moment().endOf('day')],
+ 'Last 5 years': [moment().startOf('day').subtract(5, 'year'), moment().endOf('day')],
+ },
+ locale: {
+ format: dtf.dformat(),
+ firstDay: firstDay
+ }
+ }, cb);
+ $("#report-range").on('apply.daterangepicker', function (ev, picker) {
+ //days = Math.round((picker.endDate.unix()-picker.startDate.unix()) / 86400);
+ start = picker.startDate;
+ getData();
+ });
+ cb(start, end);
+
+
+ var chartData = {};
+ var costChartData = {};
+
getData();
- });
- cb(start, end);
-
-
- var chartData = {};
- getData();
- function getData(){
- var baseUrl = OC.generateUrl('/apps/timetracker/ajax/report?name=&from='+start.unix()+'&to='+end.unix()+'&group1='+group1+'&group2='+group2+'&timegroup='+group3+'&filterProjectId='+filterProjectId+'&filterClientId='+filterClientId);
- var default_colors = [
- '#3366CC',
- '#DC3912',
- '#FF9900',
- '#109618',
- '#990099',
- '#3B3EAC',
- '#0099C6',
- '#DD4477',
- '#66AA00',
- '#B82E2E',
- '#316395',
- '#994499',
- '#22AA99',
- '#AAAA11',
- '#6633CC',
- '#E67300',
- '#8B0707',
- '#329262',
- '#5574A6',
- '#3B3EAC',
- '#C71585',
- '#006400',
- '#4B0082',
- '#8B0000',
- '#FF4500',
- '#BDB76B',
- '#008080',
- '#FFE4E1',
- '#800000',
- '#000080',
- '#2F4F4F',
- '#FF1493',
- '#008000',
- '#800080',
- '#FF0000',
- '#FF8C00',
- '#20B2AA',
- '#FAF0E6',
- '#FFD700',
- '#A52A2A',
- '#708090',
- '#DB7093',
- '#808000',
- '#9400D3',
- '#FFDAB9',
- '#7FFFD4'
- ]
- $.ajax({
- /*headers: {requesttoken: oc_requesttoken},*/
- url: baseUrl,
- method: 'GET',
- dataType: 'json',
- success: function (d) {
+
+ function getData() {
+ var baseUrl = OC.generateUrl('/apps/timetracker/ajax/report?name=&from=' + start.unix() + '&to=' + end.unix() + '&group1=' + group1 + '&group2=' + group2 + '&timegroup=' + group3 + '&filterProjectId=' + filterProjectId + '&filterClientId=' + filterClientId);
+ var default_colors = [
+ '#3366CC',
+ '#DC3912',
+ '#FF9900',
+ '#109618',
+ '#990099',
+ '#3B3EAC',
+ '#0099C6',
+ '#DD4477',
+ '#66AA00',
+ '#B82E2E',
+ '#316395',
+ '#994499',
+ '#22AA99',
+ '#AAAA11',
+ '#6633CC',
+ '#E67300',
+ '#8B0707',
+ '#329262',
+ '#5574A6',
+ '#3B3EAC',
+ '#C71585',
+ '#006400',
+ '#4B0082',
+ '#8B0000',
+ '#FF4500',
+ '#BDB76B',
+ '#008080',
+ '#FFE4E1',
+ '#800000',
+ '#000080',
+ '#2F4F4F',
+ '#FF1493',
+ '#008000',
+ '#800080',
+ '#FF0000',
+ '#FF8C00',
+ '#20B2AA',
+ '#FAF0E6',
+ '#FFD700',
+ '#A52A2A',
+ '#708090',
+ '#DB7093',
+ '#808000',
+ '#9400D3',
+ '#FFDAB9',
+ '#7FFFD4'
+ ]
+ $.ajax({
+ /*headers: {requesttoken: oc_requesttoken},*/
+ url: baseUrl,
+ method: 'GET',
+ dataType: 'json',
+ success: function (d) {
chartData = {
- labels: [],
- datasets: [{ data: [], backgroundColor: []}, { data: [], backgroundColor: []},],
-
- };
- var clientMap = {};
- var nclients = 0;
- // extract clients in clientMap
- var totalMinutes = 0;
- for (var x = 0; x < d.items.length; x++){
+ labels: [],
+ datasets: [{data: [], backgroundColor: []}, {data: [], backgroundColor: []},],
+ };
+ costChartData = {
+ labels: [],
+ datasets: [{data: [], backgroundColor: []}, {data: [], backgroundColor: []},],
+ };
+ var clientMap = {};
+ var nclients = 0;
+ var totalCost = 0;
+ // extract clients in clientMap
+ var totalMinutes = 0;
+ for (var x = 0; x < d.items.length; x++) {
cid = d.items[x].client;
- if (cid == null){
- cid = -1;
+ if (cid == null) {
+ cid = -1;
}
- if (clientMap[cid] === undefined){
- clientMap[cid] = {duration:d.items[x].totalDuration, order:nclients, client:(d.items[x].client == null)?'Not Set':d.items[x].client}
- totalMinutes += d.items[x].totalDuration/60.0;
+ if (clientMap[cid] === undefined) {
+ clientMap[cid] = {
+ duration: d.items[x].totalDuration,
+ order: nclients,
+ client: (d.items[x].client == null) ? 'Not Set' : d.items[x].client,
+ cost: d.items[x].cost
+ }
+ totalMinutes += d.items[x].totalDuration / 60.0;
+ totalCost += d.items[x].cost;
nclients++;
} else {
- clientMap[cid].duration = clientMap[cid].duration + d.items[x].totalDuration;
- totalMinutes += d.items[x].totalDuration/60.0;
+ clientMap[cid].duration = clientMap[cid].duration + d.items[x].totalDuration;
+ clientMap[cid].cost = clientMap[cid].cost + d.items[x].cost;
+ totalMinutes += d.items[x].totalDuration / 60.0;
+ totalCost += d.items[x].cost;
}
- }
- var mx = 0;
- var nindex = nclients;
-
- var sortable = [];
- for (var client in clientMap) {
- sortable.push([client, clientMap[client].order]);
- }
-
- sortable.sort(function(a, b) {
- return a[1] - b[1];
- });
- for (var i = 0; i < sortable.length; i++) {
- t = sortable[i];
- key = t[0];
-
+ }
+ var mx = 0;
+ var nindex = nclients;
+
+ var sortable = [];
+ for (var client in clientMap) {
+ sortable.push([client, clientMap[client].order]);
+ }
+
+ sortable.sort(function (a, b) {
+ return a[1] - b[1];
+ });
+ for (var i = 0; i < sortable.length; i++) {
+ t = sortable[i];
+ key = t[0];
+
// first add clients
chartData.datasets[0].data[clientMap[key].order] = clientMap[key].duration / 60.0;
chartData.datasets[1].data[clientMap[key].order] = 0;
- if (clientMap[key].client == -1){
- chartData.labels[clientMap[key].order] = "Client Not Set";
-
+
+ costChartData.datasets[0].data[clientMap[key].order] = (clientMap[key].cost / 100);
+ costChartData.datasets[1].data[clientMap[key].order] = 0;
+
+ if (clientMap[key].client == -1) {
+ chartData.labels[clientMap[key].order] = "Client Not Set";
+ costChartData.labels[clientMap[key].order] = "Client Not Set";
+
} else {
- chartData.labels[clientMap[key].order] = clientMap[key].client;
+ chartData.labels[clientMap[key].order] = clientMap[key].client;
+ costChartData.labels[clientMap[key].order] = clientMap[key].client;
}
chartData.datasets[0].backgroundColor[clientMap[key].order] = default_colors[clientMap[key].order];
chartData.datasets[1].backgroundColor[clientMap[key].order] = default_colors[clientMap[key].order];
+
+ costChartData.datasets[0].backgroundColor[clientMap[key].order] = default_colors[clientMap[key].order];
+ costChartData.datasets[1].backgroundColor[clientMap[key].order] = default_colors[clientMap[key].order];
// add projects for each client
-
- for (var x = 0; x < d.items.length; x++){
-
- if (d.items[x].client === key || (d.items[x].client == null && key == -1)){
- chartData.datasets[0].data[nindex] = 0;
- chartData.datasets[1].data[nindex] = d.items[x].totalDuration/60.0;
-
- chartData.datasets[1].backgroundColor[nindex] = default_colors[nindex];
- chartData.datasets[0].backgroundColor[nindex] = default_colors[nindex];
- if (d.items[x].project == null){
- chartData.labels[nindex] = "Project Not Set";
- } else {
- chartData.labels[nindex] = d.items[x].project;
+
+ for (var x = 0; x < d.items.length; x++) {
+
+ if (d.items[x].client === key || (d.items[x].client == null && key == -1)) {
+ chartData.datasets[0].data[nindex] = 0;
+ chartData.datasets[1].data[nindex] = d.items[x].totalDuration / 60.0;
+
+ costChartData.datasets[0].data[nindex] = 0;
+ costChartData.datasets[1].data[nindex] = d.items[x].cost;
+
+ chartData.datasets[1].backgroundColor[nindex] = default_colors[nindex];
+ chartData.datasets[0].backgroundColor[nindex] = default_colors[nindex];
+
+ costChartData.datasets[1].backgroundColor[nindex] = default_colors[nindex];
+ costChartData.datasets[0].backgroundColor[nindex] = default_colors[nindex];
+
+
+ if (d.items[x].project == null) {
+ chartData.labels[nindex] = "Project Not Set";
+ costChartData.labels[nindex] = "Project Not Set"
+ } else {
+ chartData.labels[nindex] = d.items[x].project;
+ costChartData.labels[nindex] = d.items[x].project;
+ }
+ nindex++;
}
- nindex++;
- }
}
- };
- if (myDoughnutChart != null){
+ }
+ ;
+
+ console.log(chartData);
+ console.log(costChartData);
+ if (myDoughnutChart != null) {
myDoughnutChart.destroy();
- }
+ }
+
+ if (costChart != null) {
+ costChart.destroy();
+ }
- var ctx = document.getElementById("myChart").getContext("2d");
+ var ctx = document.getElementById("myChart").getContext("2d");
- myDoughnutChart = new Chart(ctx, {
+ myDoughnutChart = new Chart(ctx, {
type: 'doughnut',
data: chartData,
options: {
- tooltips: {
- callbacks: {
- title: function(tooltipItem, data) {
- return data['labels'][tooltipItem[0]['index']];
- },
- label: function(tooltipItem, data) {
-
- mm = data['datasets'][tooltipItem.datasetIndex]['data'][tooltipItem['index']];
- h = Math.trunc(mm / 60);
- m = Math.trunc(mm % 60);
- return (h+" hours "+m+" minutes")
- },
- afterLabel: function(tooltipItem, data) {
- var dataset = data['datasets'][tooltipItem.datasetIndex];
- if (!(0 in dataset["_meta"]))
- return '';
- var percent = Math.round((dataset['data'][tooltipItem['index']] / dataset["_meta"][0]['total']) * 100)
- return '(' + percent + '%)';
- },
- },
-
- }
+ tooltips: {
+ callbacks: {
+ title: function (tooltipItem, data) {
+ return data['labels'][tooltipItem[0]['index']];
+ },
+ label: function (tooltipItem, data) {
+
+ mm = data['datasets'][tooltipItem.datasetIndex]['data'][tooltipItem['index']];
+ h = Math.trunc(mm / 60);
+ m = Math.trunc(mm % 60);
+ return (h + " hours " + m + " minutes")
+ },
+ afterLabel: function (tooltipItem, data) {
+ var dataset = data['datasets'][tooltipItem.datasetIndex];
+ if (!(0 in dataset["_meta"]))
+ return '';
+ var percent = Math.round((dataset['data'][tooltipItem['index']] / dataset["_meta"][0]['total']) * 100)
+ return '(' + percent + '%)';
+ },
+ },
+
+ }
}
});
+
+ var costChartCtx = document.getElementById("costChart").getContext("2d");
+
+ costChart = new Chart(costChartCtx, {
+ type: 'doughnut',
+ data: costChartData,
+ options: {
+ tooltips: {
+ callbacks: {
+ title: function (tooltipItem, data) {
+ return data['labels'][tooltipItem[0]['index']];
+ },
+ label: function (tooltipItem, data) {
+ var c = data['datasets'][tooltipItem.datasetIndex]['data'][tooltipItem['index']];
+ return "Cost: " + (c / 100).toFixed(2);
+ }
+ },
+
+ }
+ }
+ });
+
h = Math.trunc(totalMinutes / 60);
m = Math.trunc(totalMinutes % 60);
- $('#summary').html('Total time: '+h+" hours "+m+" minutes");
- }
- });
- }
- });
- } );
+ $('#summary').html('Total time: ' + h + " hours " + m + " minutes" + "
" + "Total costs: " + (totalCost / 100).toFixed(2));
+ }
+ });
+ }
+ });
+ });
}());
diff --git a/js/src/reports.js b/js/src/reports.js
index be731b8..a154de1 100644
--- a/js/src/reports.js
+++ b/js/src/reports.js
@@ -52,7 +52,7 @@ var dtf = require("./dateformat.js");
'The Month Before Last': [moment().subtract(2, 'month').startOf('month'), moment().subtract(2, 'month').endOf('month')],
'This Year': [moment().startOf('year'), moment().endOf('year')],
'Last Year': [moment().subtract(1, 'year').startOf('year'), moment().subtract(1, 'year').endOf('year')],
-
+
},
locale: {
format: dtf.dformat(),
@@ -68,15 +68,15 @@ var dtf = require("./dateformat.js");
$("#group1").select2();
$("#group2").select2();
$("#group3").select2();
- $('#group1').on("select2:select select2:unselect", function(e) {
+ $('#group1').on("select2:select select2:unselect", function(e) {
group1 = e.params.data.id;
getReport();
});
- $('#group2').on("select2:select select2:unselect", function(e) {
+ $('#group2').on("select2:select select2:unselect", function(e) {
group2 = e.params.data.id;
getReport();
});
- $('#group3').on("select2:select select2:unselect", function(e) {
+ $('#group3').on("select2:select select2:unselect", function(e) {
group3 = e.params.data.id;
getReport();
});
@@ -96,15 +96,15 @@ var dtf = require("./dateformat.js");
);
return $state;
},
- ajax: {
+ ajax: {
tags: true,
url: OC.generateUrl('/apps/timetracker/ajax/projects'),
-
+
dataType: 'json',
delay: 250,
-
+
processResults: function (data, page) { //json parse
- return {
+ return {
results: $.map(data.Projects,function(val, i){
return { id: val.id, text:val.name, color: val.color};
}),
@@ -114,13 +114,13 @@ var dtf = require("./dateformat.js");
};
},
cache: false,
-
+
},
});
- $('#filter-project').on("select2:select select2:unselect", function(e) {
-
-
+ $('#filter-project').on("select2:select select2:unselect", function(e) {
+
+
filterProjectId = ($(e.target).val() != null)? $(e.target).val() : "";
getReport();
});
@@ -133,14 +133,14 @@ var dtf = require("./dateformat.js");
escapeMarkup : function(markup) { return markup; },
placeholder: "Select client",
allowClear: true,
- ajax: {
+ ajax: {
tags: true,
url: OC.generateUrl('/apps/timetracker/ajax/clients'),
-
+
dataType: 'json',
delay: 250,
processResults: function (data, page) { //json parse
- return {
+ return {
results: $.map(data.Clients,function(val, i){
return { id: val.id, text:val.name};
}),
@@ -150,14 +150,14 @@ var dtf = require("./dateformat.js");
};
},
cache: false,
-
+
},
});
-
- $('#filter-client').on("select2:select select2:unselect", function(e) {
-
-
+
+ $('#filter-client').on("select2:select select2:unselect", function(e) {
+
+
filterClientId = ($(e.target).val() != null)? $(e.target).val() : "";
getReport();
});
@@ -165,8 +165,8 @@ var dtf = require("./dateformat.js");
$('input.select2-input').attr('autocomplete', "xxxxxxxxxxx");
-
-
+
+
function getReport(){
var baseUrl = OC.generateUrl('/apps/timetracker/ajax/report?name=&from='+start.unix()+'&to='+end.unix()+'&group1='+group1+'&group2='+group2+'&timegroup='+group3+'&filterProjectId='+filterProjectId+'&filterClientId='+filterClientId);
function pad(n, width, z) {
@@ -203,6 +203,10 @@ var dtf = require("./dateformat.js");
var nullCheckAccessor = function(value, data, type, params, column){
return value ? value : '';
}
+
+ var money = function(value, data, type, params, component) {
+ return value / 100;
+ }
var table = new Tabulator("#report", {
ajaxURL:baseUrl,
layout:"fitColumns",
@@ -229,6 +233,7 @@ var dtf = require("./dateformat.js");
return moment.unix(t).format(dtf.dtformat());
}
}},
+ {title:"Cost", field:"cost", mutator:money, accessorDownload: nullCheckAccessor, widthGrow:1, bottomCalc: "sum", formatter: "money", bottomCalcFormatter: "money"}, //column will be allocated 1/5 of the remaining space
{title:"Total Duration", field:"totalDuration",accessorDownload:totalDurationAccessor,formatter:function(cell, formatterParams, onRendered){
//cell - the cell component
//formatterParams - parameters set for the column
@@ -238,7 +243,7 @@ var dtf = require("./dateformat.js");
var m = Math.floor( (duration/60) % 60 );
var h = Math.floor( (duration/(60*60)));
return pad(h,2) + ':' + pad(m,2) + ':' + pad(s,2);
-
+
},bottomCalc:"sum", bottomCalcParams:{
precision:1,
},bottomCalcFormatter:function(cell, formatterParams, onRendered){
@@ -263,11 +268,11 @@ var dtf = require("./dateformat.js");
var time = cell.getRow().getData().time;
var duration = cell.getRow().getData().totalDuration;
return moment.unix(parseInt(time) + parseInt(duration)).format(dtf.dtformat());
-
+
}},
],
ajaxResponse:function(url, params, response){
-
+
return response.items; //return the tableData property of a response json object
},
});
diff --git a/js/src/timer.js b/js/src/timer.js
index 094cd4c..d4c3ffe 100644
--- a/js/src/timer.js
+++ b/js/src/timer.js
@@ -13,10 +13,10 @@ require('../../css/style.css');
var dtf = require("./dateformat.js");
(
-
+
function() {
-
+
$.ajaxSetup({
headers: { 'RequestToken': OC.requestToken }
});
@@ -70,7 +70,7 @@ function() {
'`': '`',
'=': '='
};
-
+
function escapeHtml (string) {
return String(string).replace(/[&<>"'`=\/]/g, function (s) {
return entityMap[s];
@@ -116,10 +116,10 @@ function() {
validateManualEntryFields();
});
-
+
$("#dialog-manual-entry").dialog({
autoOpen: false,
- buttons :
+ buttons :
[ {
id: 'confirm-button',
text: "Confirm",
@@ -183,7 +183,7 @@ function() {
alert( "error" );
})
.always(function() {
-
+
});
}
@@ -223,7 +223,7 @@ function() {
//contentType: "application/json; charset=utf-8"
});
$.getJSON( baseUrl, function( data ) {
-
+
if (data.running.length > 0){
localStorage.setItem('isTimerStarted', true);
localStorage.setItem('timerStartTime', data.running[0].start);
@@ -246,16 +246,18 @@ function() {
$('#timer').html(secondsToTimer(0));
$('#start-tracking > span').addClass("play-button").removeClass("stop-button");
}
-
+
var days = [];
$.each( data.days, function( dayName, dayMap ) {
-
+
var dayItems = [];
$.each(dayMap, function (dayItemName, workItem){
var children = [];
-
+
$.each(workItem.children, function (ckey, child){
+ var cost = ''
+ if (child.cost !== 0) { var cost = (child.cost / 100).toFixed(2) }
children.push(
"