From aef646e9dfe06389e140d3f8c4296dabfb3c2c60 Mon Sep 17 00:00:00 2001 From: Bernd Bestel Date: Sun, 23 Jan 2022 17:29:25 +0100 Subject: [PATCH] Highlight chores/tasks/batteries due today in a separate color + status filter (closes #1740) --- changelog/66_UNRELEASED_xxxx-xx-xx.md | 1 + controllers/BatteriesController.php | 25 ++++++++++++-- controllers/ChoresController.php | 25 ++++++++++++-- controllers/TasksController.php | 20 ++++++++++-- localization/strings.pot | 12 +++++++ public/viewjs/batteriesoverview.js | 26 ++++++++++----- public/viewjs/choresoverview.js | 26 ++++++++++----- public/viewjs/tasks.js | 28 +++++++++++----- services/DemoDataGeneratorService.php | 17 +++++----- views/batteriesoverview.blade.php | 43 +++++++++--------------- views/choresoverview.blade.php | 47 +++++++++++---------------- views/stockoverview.blade.php | 12 +++---- views/tasks.blade.php | 39 ++++++++++------------ 13 files changed, 198 insertions(+), 123 deletions(-) diff --git a/changelog/66_UNRELEASED_xxxx-xx-xx.md b/changelog/66_UNRELEASED_xxxx-xx-xx.md index 3791db24..9de369d9 100644 --- a/changelog/66_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/66_UNRELEASED_xxxx-xx-xx.md @@ -1,4 +1,5 @@ - Stock entry labels get now also printed on inventory (only when adding products, same option "Stock entry label" like on the purchase page) +- Added a separate status filter and table row highlighting (blue) on the chores, tasks and batteries overview pages for items due today - Optimized relative time display (also fixed a phrasing problem for some languages, e.g. Hungarian) (thanks @Tallyrald) - When using LDAP authentication, the configured `LDAP_UID_ATTR` is now used to compare if the user already exists instead of the username entered on the login page (that prevents creating multiple users if you entere the username in different notations) (thanks @FloSet) - When using reverse proxy authentication (`ReverseProxyAuthMiddleware`), it's now also possible to pass the username in an environment variable instead of an HTTP header (new `config.php` option `REVERSE_PROXY_AUTH_USE_ENV`) (thanks @Forceu) diff --git a/controllers/BatteriesController.php b/controllers/BatteriesController.php index 95d7441b..4593a1c4 100644 --- a/controllers/BatteriesController.php +++ b/controllers/BatteriesController.php @@ -80,9 +80,30 @@ class BatteriesController extends BaseController $usersService = $this->getUsersService(); $nextXDays = $usersService->GetUserSettings(GROCY_USER_ID)['batteries_due_soon_days']; + $batteries = $this->getDatabase()->batteries()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'); + $currentBatteries = $this->getBatteriesService()->GetCurrent(); + foreach ($currentBatteries as $currentBattery) + { + if (FindObjectInArrayByPropertyValue($batteries, 'id', $currentBattery->battery_id)->charge_interval_days > 0) + { + if ($currentBattery->next_estimated_charge_time < date('Y-m-d H:i:s')) + { + $currentBattery->due_type = 'overdue'; + } + elseif ($currentBattery->next_estimated_charge_time <= date('Y-m-d 23:59:59')) + { + $currentBattery->due_type = 'duetoday'; + } + elseif ($currentBattery->next_estimated_charge_time <= date('Y-m-d H:i:s', strtotime('+' . $nextXDays . ' days'))) + { + $currentBattery->due_type = 'duesoon'; + } + } + } + return $this->renderPage($response, 'batteriesoverview', [ - 'batteries' => $this->getDatabase()->batteries()->where('active = 1')->orderBy('name', 'COLLATE NOCASE'), - 'current' => $this->getBatteriesService()->GetCurrent(), + 'batteries' => $batteries, + 'current' => $currentBatteries, 'nextXDays' => $nextXDays, 'userfields' => $this->getUserfieldsService()->GetFields('batteries'), 'userfieldValues' => $this->getUserfieldsService()->GetAllValues('batteries') diff --git a/controllers/ChoresController.php b/controllers/ChoresController.php index d0f13a25..71ff9f50 100644 --- a/controllers/ChoresController.php +++ b/controllers/ChoresController.php @@ -94,9 +94,30 @@ class ChoresController extends BaseController $usersService = $this->getUsersService(); $nextXDays = $usersService->GetUserSettings(GROCY_USER_ID)['chores_due_soon_days']; + $chores = $this->getDatabase()->chores()->orderBy('name', 'COLLATE NOCASE'); + $currentChores = $this->getChoresService()->GetCurrent(); + foreach ($currentChores as $currentChore) + { + if (FindObjectInArrayByPropertyValue($chores, 'id', $currentChore->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY) + { + if ($currentChore->next_estimated_execution_time < date('Y-m-d H:i:s')) + { + $currentChore->due_type = 'overdue'; + } + elseif ($currentChore->next_estimated_execution_time <= date('Y-m-d 23:59:59')) + { + $currentChore->due_type = 'duetoday'; + } + elseif ($currentChore->next_estimated_execution_time <= date('Y-m-d H:i:s', strtotime('+' . $nextXDays . ' days'))) + { + $currentChore->due_type = 'duesoon'; + } + } + } + return $this->renderPage($response, 'choresoverview', [ - 'chores' => $this->getDatabase()->chores()->orderBy('name', 'COLLATE NOCASE'), - 'currentChores' => $this->getChoresService()->GetCurrent(), + 'chores' => $chores, + 'currentChores' => $currentChores, 'nextXDays' => $nextXDays, 'userfields' => $this->getUserfieldsService()->GetFields('chores'), 'userfieldValues' => $this->getUserfieldsService()->GetAllValues('chores'), diff --git a/controllers/TasksController.php b/controllers/TasksController.php index 08eeee4d..63a8d039 100644 --- a/controllers/TasksController.php +++ b/controllers/TasksController.php @@ -6,6 +6,9 @@ class TasksController extends BaseController { public function Overview(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { + $usersService = $this->getUsersService(); + $nextXDays = $usersService->GetUserSettings(GROCY_USER_ID)['tasks_due_soon_days']; + if (isset($request->getQueryParams()['include_done'])) { $tasks = $this->getDatabase()->tasks()->orderBy('name', 'COLLATE NOCASE'); @@ -15,8 +18,21 @@ class TasksController extends BaseController $tasks = $this->getTasksService()->GetCurrent(); } - $usersService = $this->getUsersService(); - $nextXDays = $usersService->GetUserSettings(GROCY_USER_ID)['tasks_due_soon_days']; + foreach ($tasks as $task) + { + if ($task->due_date < date('Y-m-d 23:59:59', strtotime('-1 days'))) + { + $task->due_type = 'overdue'; + } + elseif ($task->due_date <= date('Y-m-d 23:59:59')) + { + $task->due_type = 'duetoday'; + } + elseif ($task->due_date <= date('Y-m-d 23:59:59', strtotime('+' . $nextXDays . ' days'))) + { + $task->due_type = 'duesoon'; + } + } return $this->renderPage($response, 'tasks', [ 'tasks' => $tasks, diff --git a/localization/strings.pot b/localization/strings.pot index 287f327d..5266b3d3 100644 --- a/localization/strings.pot +++ b/localization/strings.pot @@ -2259,3 +2259,15 @@ msgstr "" msgid "After merging, all occurences of this chore will be replaced by the kept chore (means this chore will not exist anymore)" msgstr "" + +msgid "Due today" +msgstr "" + +msgid "%s chore is due to be done today" +msgstr "" + +msgid "%s task is due to be done today" +msgstr "" + +msgid "%s battery is due to be charged today" +msgstr "" diff --git a/public/viewjs/batteriesoverview.js b/public/viewjs/batteriesoverview.js index 085c04ef..d5c7b546 100644 --- a/public/viewjs/batteriesoverview.js +++ b/public/viewjs/batteriesoverview.js @@ -71,7 +71,7 @@ $(document).on('click', '.track-charge-cycle-button', function(e) function(result) { var batteryRow = $('#battery-' + batteryId + '-row'); - var nextXDaysThreshold = moment().add($("#info-due-batteries").data("next-x-days"), "days"); + var nextXDaysThreshold = moment().add($("#info-due-soon-batteries").data("next-x-days"), "days"); var now = moment(); var nextExecutionTime = moment(result.next_estimated_charge_time); @@ -139,28 +139,38 @@ $(document).on('click', '.battery-grocycode-label-print', function(e) function RefreshStatistics() { - var nextXDays = $("#info-due-batteries").data("next-x-days"); + var nextXDays = $("#info-due-soon-batteries").data("next-x-days"); Grocy.Api.Get('batteries', function(result) { - var dueCount = 0; + var dueTodayCount = 0; + var dueSoonCount = 0; var overdueCount = 0; - var now = moment(); + var overdueThreshold = moment(); var nextXDaysThreshold = moment().add(nextXDays, "days"); + var todayThreshold = moment().endOf("day"); + result.forEach(element => { var date = moment(element.next_estimated_charge_time); - if (date.isBefore(now)) + + if (date.isBefore(overdueThreshold)) { overdueCount++; } - else if (date.isBefore(nextXDaysThreshold)) + else if (date.isSameOrBefore(todayThreshold)) { - dueCount++; + dueTodayCount++; + dueSoonCount++; + } + else if (date.isSameOrBefore(nextXDaysThreshold)) + { + dueSoonCount++; } }); - $("#info-due-batteries").html('' + dueCount + ' ' + __n(dueCount, '%s battery is due to be charged', '%s batteries are due to be charged') + ' ' + __n(nextXDays, 'within the next day', 'within the next %s days')); + $("#info-due-today-batteries").html('' + dueTodayCount + ' ' + __n(dueTodayCount, '%s battery is due to be charged today', '%s batteries are due to be charged today')); + $("#info-due-soon-batteries").html('' + dueSoonCount + ' ' + __n(dueSoonCount, '%s battery is due to be charged', '%s batteries are due to be charged') + ' ' + __n(nextXDays, 'within the next day', 'within the next %s days')); $("#info-overdue-batteries").html('' + overdueCount + ' ' + __n(overdueCount, '%s battery is overdue to be charged', '%s batteries are overdue to be charged')); }, function(xhr) diff --git a/public/viewjs/choresoverview.js b/public/viewjs/choresoverview.js index 2a8ea6d2..b327edcf 100644 --- a/public/viewjs/choresoverview.js +++ b/public/viewjs/choresoverview.js @@ -109,7 +109,7 @@ $(document).on('click', '.track-chore-button', function(e) function(result) { var choreRow = $('#chore-' + choreId + '-row'); - var nextXDaysThreshold = moment().add($("#info-due-chores").data("next-x-days"), "days"); + var nextXDaysThreshold = moment().add($("#info-due-soon-chores").data("next-x-days"), "days"); var now = moment(); var nextExecutionTime = moment(result.next_estimated_execution_time); @@ -202,25 +202,34 @@ $(document).on('click', '.chore-grocycode-label-print', function(e) function RefreshStatistics() { - var nextXDays = $("#info-due-chores").data("next-x-days"); + var nextXDays = $("#info-due-soon-chores").data("next-x-days"); Grocy.Api.Get('chores', function(result) { - var dueCount = 0; + var dueTodayCount = 0; + var dueSoonCount = 0; var overdueCount = 0; var assignedToMeCount = 0; - var now = moment(); + var overdueThreshold = moment(); var nextXDaysThreshold = moment().add(nextXDays, "days"); + var todayThreshold = moment().endOf("day"); + result.forEach(element => { var date = moment(element.next_estimated_execution_time); - if (date.isBefore(now)) + + if (date.isBefore(overdueThreshold)) { overdueCount++; } - else if (date.isBefore(nextXDaysThreshold)) + else if (date.isSameOrBefore(todayThreshold)) { - dueCount++; + dueTodayCount++; + dueSoonCount++; + } + else if (date.isSameOrBefore(nextXDaysThreshold)) + { + dueSoonCount++; } if (parseInt(element.next_execution_assigned_to_user_id) == Grocy.UserId) @@ -229,7 +238,8 @@ function RefreshStatistics() } }); - $("#info-due-chores").html('' + dueCount + ' ' + __n(dueCount, '%s chore is due to be done', '%s chores are due to be done') + ' ' + __n(nextXDays, 'within the next day', 'within the next %s days')); + $("#info-due-today-chores").html('' + dueTodayCount + ' ' + __n(dueTodayCount, '%s chore is due to be done today', '%s chores are due to be done today')); + $("#info-due-soon-chores").html('' + dueSoonCount + ' ' + __n(dueSoonCount, '%s chore is due to be done', '%s chores are due to be done') + ' ' + __n(nextXDays, 'within the next day', 'within the next %s days')); $("#info-overdue-chores").html('' + overdueCount + ' ' + __n(overdueCount, '%s chore is overdue to be done', '%s chores are overdue to be done')); $("#info-assigned-to-me-chores").html('' + assignedToMeCount + ' ' + __n(assignedToMeCount, '%s chore is assigned to me', '%s chores are assigned to me')); }, diff --git a/public/viewjs/tasks.js b/public/viewjs/tasks.js index fcdde80e..c020b4c6 100644 --- a/public/viewjs/tasks.js +++ b/public/viewjs/tasks.js @@ -183,28 +183,38 @@ if (GetUriParam('include_done')) function RefreshStatistics() { - var nextXDays = $("#info-due-tasks").data("next-x-days"); + var nextXDays = $("#info-due-soon-tasks").data("next-x-days"); Grocy.Api.Get('tasks', function(result) { - var dueCount = 0; + var dueTodayCount = 0; + var dueSoonCount = 0; var overdueCount = 0; - var now = moment(); - var nextXDaysThreshold = moment().add(nextXDays, "days"); + var overdueThreshold = moment().subtract(1, "days").endOf("day"); + var nextXDaysThreshold = moment().endOf("day").add(nextXDays, "days"); + var todayThreshold = moment().endOf("day"); + result.forEach(element => { - var date = moment(element.due_date); - if (date.isBefore(now)) + var date = moment(element.due_date + " 23:59:59").endOf("day"); + + if (date.isSameOrBefore(overdueThreshold)) { overdueCount++; } - else if (date.isBefore(nextXDaysThreshold)) + else if (date.isSameOrBefore(todayThreshold)) { - dueCount++; + dueTodayCount++; + dueSoonCount++; + } + else if (date.isSameOrBefore(nextXDaysThreshold)) + { + dueSoonCount++; } }); - $("#info-due-tasks").html('' + dueCount + ' ' + __n(dueCount, '%s task is due to be done', '%s tasks are due to be done') + ' ' + __n(nextXDays, 'within the next day', 'within the next %s days')); + $("#info-due-today-tasks").html('' + dueTodayCount + ' ' + __n(dueTodayCount, '%s task is due to be done today', '%s tasks are due to be done today')); + $("#info-due-soon-tasks").html('' + dueSoonCount + ' ' + __n(dueSoonCount, '%s task is due to be done', '%s tasks are due to be done') + ' ' + __n(nextXDays, 'within the next day', 'within the next %s days')); $("#info-overdue-tasks").html('' + overdueCount + ' ' + __n(overdueCount, '%s task is overdue to be done', '%s tasks are overdue to be done')); }, function(xhr) diff --git a/services/DemoDataGeneratorService.php b/services/DemoDataGeneratorService.php index 80c26966..ec4a914d 100644 --- a/services/DemoDataGeneratorService.php +++ b/services/DemoDataGeneratorService.php @@ -169,7 +169,7 @@ class DemoDataGeneratorService extends BaseService INSERT INTO chores (name, period_type) VALUES ('{$this->__t_sql('The thing which happens daily')}', 'daily'); --5 INSERT INTO chores (name, period_type, period_config) VALUES ('{$this->__t_sql('The thing which happens on Mondays and Wednesdays')}', 'weekly', 'monday,wednesday'); --6 - INSERT INTO batteries (name, description, used_in) VALUES ('{$this->__t_sql('Battery')}1', '{$this->__t_sql('Warranty ends')} 2023', '{$this->__t_sql('TV remote control')}'); --1 + INSERT INTO batteries (name, description, used_in, charge_interval_days) VALUES ('{$this->__t_sql('Battery')}1', '{$this->__t_sql('Warranty ends')} 2023', '{$this->__t_sql('TV remote control')}', 180); --1 INSERT INTO batteries (name, description, used_in) VALUES ('{$this->__t_sql('Battery')}2', '{$this->__t_sql('Warranty ends')} 2022', '{$this->__t_sql('Alarm clock')}'); --2 INSERT INTO batteries (name, description, used_in, charge_interval_days) VALUES ('{$this->__t_sql('Battery')}3', '{$this->__t_sql('Warranty ends')} 2022', '{$this->__t_sql('Heat remote control')}', 60); --3 INSERT INTO batteries (name, description, used_in, charge_interval_days) VALUES ('{$this->__t_sql('Battery')}4', '{$this->__t_sql('Warranty ends')} 2028', '{$this->__t_sql('Heat remote control')}', 60); --4 @@ -178,7 +178,7 @@ class DemoDataGeneratorService extends BaseService INSERT INTO task_categories (name) VALUES ('{$this->__t_sql('Life')}'); --2 INSERT INTO task_categories (name) VALUES ('{$this->__t_sql('Projects')}'); --3 - INSERT INTO tasks (name, category_id, due_date, assigned_to_user_id) VALUES ('{$this->__t_sql('Repair the garage door')}', 1, date(datetime('now', 'localtime'), '+14 day'), 1); + INSERT INTO tasks (name, category_id, due_date, assigned_to_user_id) VALUES ('{$this->__t_sql('Repair the garage door')}', 1, date(datetime('now', 'localtime')), 1); INSERT INTO tasks (name, category_id, due_date, assigned_to_user_id) VALUES ('{$this->__t_sql('Fork and improve grocy')}', 3, date(datetime('now', 'localtime'), '+30 day'), 1); INSERT INTO tasks (name, category_id, due_date, assigned_to_user_id) VALUES ('{$this->__t_sql('Task')}1', 2, date(datetime('now', 'localtime'), '-1 day'), 1); INSERT INTO tasks (name, category_id, due_date, assigned_to_user_id) VALUES ('{$this->__t_sql('Task')}2', 2, date(datetime('now', 'localtime'), '-1 day'), 1); @@ -293,18 +293,19 @@ class DemoDataGeneratorService extends BaseService $choresService->TrackChore(1, date('Y-m-d H:i:s', strtotime('-5 days'))); $choresService->TrackChore(1, date('Y-m-d H:i:s', strtotime('-10 days'))); $choresService->TrackChore(1, date('Y-m-d H:i:s', strtotime('-15 days'))); - $choresService->TrackChore(2, date('Y-m-d H:i:s', strtotime('-10 days'))); - $choresService->TrackChore(2, date('Y-m-d H:i:s', strtotime('-20 days'))); + $choresService->TrackChore(2, date('Y-m-d 23:59:59', strtotime('-7 days'))); + $choresService->TrackChore(2, date('Y-m-d H:i:s', strtotime('-14 days'))); + $choresService->TrackChore(2, date('Y-m-d H:i:s', strtotime('-21 days'))); $choresService->TrackChore(3, date('Y-m-d H:i:s', strtotime('-17 days'))); $choresService->TrackChore(4, date('Y-m-d H:i:s', strtotime('-10 days'))); $choresService->TrackChore(5, date('Y-m-d H:i:s', strtotime('+0 days'))); $choresService->TrackChore(6, date('Y-m-d H:i:s', strtotime('-10 days'))); $batteriesService = new BatteriesService(); - $batteriesService->TrackChargeCycle(1, date('Y-m-d H:i:s', strtotime('-200 days'))); - $batteriesService->TrackChargeCycle(1, date('Y-m-d H:i:s', strtotime('-150 days'))); - $batteriesService->TrackChargeCycle(1, date('Y-m-d H:i:s', strtotime('-100 days'))); - $batteriesService->TrackChargeCycle(1, date('Y-m-d H:i:s', strtotime('-50 days'))); + $batteriesService->TrackChargeCycle(1, date('Y-m-d H:i:s', strtotime('-720 days'))); + $batteriesService->TrackChargeCycle(1, date('Y-m-d H:i:s', strtotime('-540 days'))); + $batteriesService->TrackChargeCycle(1, date('Y-m-d H:i:s', strtotime('-360 days'))); + $batteriesService->TrackChargeCycle(1, date('Y-m-d 23:59:59', strtotime('-180 days'))); $batteriesService->TrackChargeCycle(2, date('Y-m-d H:i:s', strtotime('-200 days'))); $batteriesService->TrackChargeCycle(2, date('Y-m-d H:i:s', strtotime('-150 days'))); $batteriesService->TrackChargeCycle(2, date('Y-m-d H:i:s', strtotime('-100 days'))); diff --git a/views/batteriesoverview.blade.php b/views/batteriesoverview.blade.php index 9ee95562..2c04ffe4 100644 --- a/views/batteriesoverview.blade.php +++ b/views/batteriesoverview.blade.php @@ -29,13 +29,16 @@
-
+ class="error-message status-filter-message responsive-button mr-2">
+
+
@@ -109,9 +113,7 @@ @foreach($current as $currentBatteryEntry) + class="@if($currentBatteryEntry->due_type == 'overdue') table-danger @elseif($currentBatteryEntry->due_type == 'duetoday') table-info @elseif($currentBatteryEntry->due_type == 'duesoon') table-warning @endif"> - "@if(FindObjectInArrayByPropertyValue($batteries, 'id', $currentBatteryEntry->battery_id)->charge_interval_days > 0 && $currentBatteryEntry->next_estimated_charge_time < date('Y-m-d - H:i:s')) - overdue - @elseif(FindObjectInArrayByPropertyValue($batteries, 'id' - , - $currentBatteryEntry->battery_id)->charge_interval_days > 0 && $currentBatteryEntry->next_estimated_charge_time < date('Y-m-d - H:i:s', - strtotime('+' - . - $nextXDays - . ' days' - ))) - duesoon - @endif - - @include('components.userfields_tbody', - array( 'userfields'=> $userfields, - 'userfieldValues' => FindAllObjectsInArrayByPropertyValue($userfieldValues, 'object_id', $currentBatteryEntry->battery_id) - )) + {{ $currentBatteryEntry->due_type }} + @if($currentBatteryEntry->due_type == 'duetoday') + duesoon + @endif @endforeach diff --git a/views/choresoverview.blade.php b/views/choresoverview.blade.php index 305a5d38..378a9db5 100644 --- a/views/choresoverview.blade.php +++ b/views/choresoverview.blade.php @@ -29,17 +29,20 @@
-
+
+
@if(GROCY_FEATURE_FLAG_CHORES_ASSIGNMENTS)
+ class="secondary-message user-filter-message responsive-button">
@endif
@@ -132,9 +136,7 @@ @foreach($currentChores as $curentChoreEntry) + class="@if($curentChoreEntry->due_type == 'overdue') table-danger @elseif($curentChoreEntry->due_type == 'duetoday') table-info @elseif($curentChoreEntry->due_type == 'duesoon') table-warning @endif"> - @if(FindObjectInArrayByPropertyValue($chores, 'id', $curentChoreEntry->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY && $curentChoreEntry->next_estimated_execution_time < date('Y-m-d - H:i:s')) - overdue - @elseif(FindObjectInArrayByPropertyValue($chores, 'id' - , - $curentChoreEntry->chore_id)->period_type !== \Grocy\Services\ChoresService::CHORE_PERIOD_TYPE_MANUALLY && $curentChoreEntry->next_estimated_execution_time < date('Y-m-d - H:i:s', - strtotime('+' - . - $nextXDays - . ' days' - ))) - duesoon - @endif - - - @if(!empty($curentChoreEntry->next_execution_assigned_to_user_id)) - xx{{ $curentChoreEntry->next_execution_assigned_to_user_id }}xx + {{ $curentChoreEntry->due_type }} + @if($curentChoreEntry->due_type == 'duetoday') + duesoon + @endif + + + @if(!empty($curentChoreEntry->next_execution_assigned_to_user_id)) + xx{{ $curentChoreEntry->next_execution_assigned_to_user_id }}xx @endif diff --git a/views/stockoverview.blade.php b/views/stockoverview.blade.php index 5e89a8f5..6ad64ef3 100755 --- a/views/stockoverview.blade.php +++ b/views/stockoverview.blade.php @@ -50,16 +50,16 @@