Continue working on tasks feature

This commit is contained in:
Bernd Bestel 2018-09-23 09:22:54 +02:00
parent 6fe0100927
commit f85a67a1ff
No known key found for this signature in database
GPG Key ID: 71BD34C0D4891300
17 changed files with 370 additions and 71 deletions

View File

@ -18,13 +18,14 @@ class TasksController extends BaseController
{ {
return $this->AppContainer->view->render($response, 'tasks', [ return $this->AppContainer->view->render($response, 'tasks', [
'tasks' => $this->Database->tasks()->orderBy('name'), 'tasks' => $this->Database->tasks()->orderBy('name'),
'nextXDays' => 5 'nextXDays' => 5,
'taskCategories' => $this->Database->task_categories()->orderBy('name')
]); ]);
} }
public function TaskEditForm(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) public function TaskEditForm(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{ {
if ($args['taskdId'] == 'new') if ($args['taskId'] == 'new')
{ {
return $this->AppContainer->view->render($response, 'taskform', [ return $this->AppContainer->view->render($response, 'taskform', [
'mode' => 'create', 'mode' => 'create',
@ -40,4 +41,28 @@ class TasksController extends BaseController
]); ]);
} }
} }
public function TaskCategoriesList(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{
return $this->AppContainer->view->render($response, 'taskcategories', [
'taskCategories' => $this->Database->task_categories()->orderBy('name')
]);
}
public function TaskCategoryEditForm(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{
if ($args['categoryId'] == 'new')
{
return $this->AppContainer->view->render($response, 'taskcategoryform', [
'mode' => 'create'
]);
}
else
{
return $this->AppContainer->view->render($response, 'taskcategoryform', [
'category' => $this->Database->task_categories($args['categoryId']),
'mode' => 'edit'
]);
}
}
} }

View File

@ -1316,7 +1316,8 @@
"shopping_list", "shopping_list",
"recipes", "recipes",
"recipes_pos", "recipes_pos",
"tasks" "tasks",
"task_categories"
] ]
}, },
"StockTransactionType": { "StockTransactionType": {

View File

@ -2,9 +2,9 @@ CREATE TABLE tasks (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE, id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
name TEXT NOT NULL UNIQUE, name TEXT NOT NULL UNIQUE,
description TEXT, description TEXT,
due DATETIME, due_date DATETIME,
started TINYINT NOT NULL DEFAULT 0 CHECK(started IN (0, 1)),
done TINYINT NOT NULL DEFAULT 0 CHECK(done IN (0, 1)), done TINYINT NOT NULL DEFAULT 0 CHECK(done IN (0, 1)),
done_date DATETIME,
category_id INTEGER, category_id INTEGER,
row_created_timestamp DATETIME DEFAULT (datetime('now', 'localtime')) row_created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
); );
@ -20,5 +20,5 @@ CREATE VIEW tasks_current
AS AS
SELECT * SELECT *
FROM tasks FROM tasks
WHERE due IS NULL WHERE due_date IS NULL
OR (due IS NOT NULL AND due > datetime('now', 'localtime')); OR (due_date IS NOT NULL AND due_date > datetime('now', 'localtime'));

View File

@ -16,6 +16,8 @@
"datatables.net-responsive-bs4": "^2.2.3", "datatables.net-responsive-bs4": "^2.2.3",
"datatables.net-select": "^1.2.7", "datatables.net-select": "^1.2.7",
"datatables.net-select-bs4": "^1.2.7", "datatables.net-select-bs4": "^1.2.7",
"datatables.net-rowgroup": "^1.0.4",
"datatables.net-rowgroup-bs4": "^1.0.4",
"jquery": "^3.3.1", "jquery": "^3.3.1",
"jquery-serializejson": "^2.8.1", "jquery-serializejson": "^2.8.1",
"jquery-ui-dist": "^1.12.1", "jquery-ui-dist": "^1.12.1",

View File

@ -0,0 +1,62 @@
var categoriesTable = $('#task-categories-table').DataTable({
'paginate': false,
'order': [[1, 'asc']],
'columnDefs': [
{ 'orderable': false, 'targets': 0 }
],
'language': JSON.parse(L('datatables_localization')),
'scrollY': false,
'colReorder': true,
'stateSave': true,
'stateSaveParams': function(settings, data)
{
data.search.search = "";
}
});
$("#search").on("keyup", function()
{
var value = $(this).val();
if (value === "all")
{
value = "";
}
categoriesTable.search(value).draw();
});
$(document).on('click', '.task-category-delete-button', function (e)
{
var objectName = $(e.currentTarget).attr('data-category-name');
var objectId = $(e.currentTarget).attr('data-category-id');
bootbox.confirm({
message: L('Are you sure to delete task category "#1"?', objectName),
buttons: {
confirm: {
label: L('Yes'),
className: 'btn-success'
},
cancel: {
label: L('No'),
className: 'btn-danger'
}
},
callback: function(result)
{
if (result === true)
{
Grocy.Api.Get('delete-object/task_categories/' + objectId,
function(result)
{
window.location.href = U('/taskcategories');
},
function(xhr)
{
console.error(xhr);
}
);
}
}
});
});

View File

@ -0,0 +1,55 @@
$('#save-task-category-button').on('click', function(e)
{
e.preventDefault();
if (Grocy.EditMode === 'create')
{
Grocy.Api.Post('add-object/task_categories', $('#task-category-form').serializeJSON(),
function(result)
{
window.location.href = U('/taskcategories');
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
}
else
{
Grocy.Api.Post('edit-object/task_categories/' + Grocy.EditObjectId, $('#task-category-form').serializeJSON(),
function(result)
{
window.location.href = U('/taskcategories');
},
function(xhr)
{
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
}
);
}
});
$('#task-category-form input').keyup(function (event)
{
Grocy.FrontendHelpers.ValidateForm('task-category-form');
});
$('#task-category-form input').keydown(function (event)
{
if (event.keyCode === 13) //Enter
{
if (document.getElementById('task-category-form').checkValidity() === false) //There is at least one validation error
{
event.preventDefault();
return false;
}
else
{
$('#save-task-category-button').click();
}
}
});
$('#name').focus();
Grocy.FrontendHelpers.ValidateForm('task-category-form');

View File

@ -2,7 +2,8 @@
'paginate': false, 'paginate': false,
'order': [[2, 'desc']], 'order': [[2, 'desc']],
'columnDefs': [ 'columnDefs': [
{ 'orderable': false, 'targets': 0 } { 'orderable': false, 'targets': 0 },
{ 'visible': false, 'targets': 3 }
], ],
'language': JSON.parse(L('datatables_localization')), 'language': JSON.parse(L('datatables_localization')),
'scrollY': false, 'scrollY': false,
@ -11,6 +12,9 @@
'stateSaveParams': function(settings, data) 'stateSaveParams': function(settings, data)
{ {
data.search.search = ""; data.search.search = "";
},
'rowGroup': {
dataSrc: 3
} }
}); });
@ -28,48 +32,13 @@ $("#search").on("keyup", function()
$(document).on('click', '.do-task-button', function(e) $(document).on('click', '.do-task-button', function(e)
{ {
var taskId = $(e.currentTarget).attr('data-task-id'); var taskId = $(e.currentTarget).attr('data-task-id');
var taskName = $(e.currentTarget).attr('data-task-name');
var doneTime = moment().format('YYYY-MM-DD HH:mm:ss'); var doneTime = moment().format('YYYY-MM-DD HH:mm:ss');
Grocy.Api.Get('tasks/track-task-execution/' + taskId + '?tracked_time=' + trackedTime, Grocy.Api.Get('tasks/mark-task-done/' + taskId + '?tracked_time=' + doneTime,
function() function()
{ {
Grocy.Api.Get('tasks/get-task-details/' + taskId, toastr.success(L('Marked task #1 as completed on #2', taskName, doneTime));
function(result)
{
var taskRow = $('#task-' + taskId + '-row');
var nextXDaysThreshold = moment().add($("#info-due-tasks").data("next-x-days"), "days");
var now = moment();
var nextExecutionTime = moment(result.next_estimated_execution_time);
taskRow.removeClass("table-warning");
taskRow.removeClass("table-danger");
if (nextExecutionTime.isBefore(now))
{
taskRow.addClass("table-danger");
}
else if (nextExecutionTime.isBefore(nextXDaysThreshold))
{
taskRow.addClass("table-warning");
}
$('#task-' + taskId + '-last-tracked-time').parent().effect('highlight', { }, 500);
$('#task-' + taskId + '-last-tracked-time').fadeOut(500, function()
{
$(this).text(trackedTime).fadeIn(500);
});
$('#task-' + taskId + '-last-tracked-time-timeago').attr('datetime', trackedTime);
if (result.task.period_type == "dynamic-regular")
{
$('#task-' + taskId + '-next-execution-time').parent().effect('highlight', { }, 500);
$('#task-' + taskId + '-next-execution-time').fadeOut(500, function()
{
$(this).text(result.next_estimated_execution_time).fadeIn(500);
});
$('#task-' + taskId + '-next-execution-time-timeago').attr('datetime', result.next_estimated_execution_time);
}
toastr.success(L('Tracked execution of task #1 on #2', taskName, trackedTime));
RefreshContextualTimeago(); RefreshContextualTimeago();
RefreshStatistics(); RefreshStatistics();
}, },
@ -78,12 +47,45 @@ $(document).on('click', '.do-task-button', function(e)
console.error(xhr); console.error(xhr);
} }
); );
});
$(document).on('click', '.delete-task-button', function (e)
{
var objectName = $(e.currentTarget).attr('data-task-name');
var objectId = $(e.currentTarget).attr('data-task-id');
bootbox.confirm({
message: L('Are you sure to delete task "#1"?', objectName),
buttons: {
confirm: {
label: L('Yes'),
className: 'btn-success'
},
cancel: {
label: L('No'),
className: 'btn-danger'
}
},
callback: function(result)
{
if (result === true)
{
Grocy.Api.Get('delete-object/tasks/' + objectId,
function(result)
{
$('#task-' + objectId + '-row').fadeOut(500, function ()
{
$(this).remove();
});
}, },
function(xhr) function(xhr)
{ {
console.error(xhr); console.error(xhr);
} }
); );
}
}
});
}); });
function RefreshStatistics() function RefreshStatistics()
@ -97,7 +99,7 @@ function RefreshStatistics()
var now = moment(); var now = moment();
var nextXDaysThreshold = moment().add(nextXDays, "days"); var nextXDaysThreshold = moment().add(nextXDays, "days");
result.forEach(element => { result.forEach(element => {
var date = moment(element.due); var date = moment(element.due_date);
if (date.isBefore(now)) if (date.isBefore(now))
{ {
overdueCount++; overdueCount++;

View File

@ -56,6 +56,8 @@ $app->group('', function()
// Task routes // Task routes
$this->get('/tasks', '\Grocy\Controllers\TasksController:Overview'); $this->get('/tasks', '\Grocy\Controllers\TasksController:Overview');
$this->get('/task/{taskId}', '\Grocy\Controllers\TasksController:TaskEditForm'); $this->get('/task/{taskId}', '\Grocy\Controllers\TasksController:TaskEditForm');
$this->get('/taskcategories', '\Grocy\Controllers\TasksController:TaskCategoriesList');
$this->get('/taskcategory/{categoryId}', '\Grocy\Controllers\TasksController:TaskCategoryEditForm');
// OpenAPI routes // OpenAPI routes
$this->get('/api', '\Grocy\Controllers\OpenApiController:DocumentationUi'); $this->get('/api', '\Grocy\Controllers\OpenApiController:DocumentationUi');

View File

@ -91,7 +91,12 @@ class DemoDataGeneratorService extends BaseService
INSERT INTO task_categories (name) VALUES ('{$localizationService->Localize('Life')}'); --2 INSERT INTO task_categories (name) VALUES ('{$localizationService->Localize('Life')}'); --2
INSERT INTO task_categories (name) VALUES ('{$localizationService->Localize('Projects')}'); --3 INSERT INTO task_categories (name) VALUES ('{$localizationService->Localize('Projects')}'); --3
INSERT INTO tasks (name, category_id, due) VALUES ('{$localizationService->Localize('Repair the garage door')}', 1, date(datetime('now', 'localtime'), '+14 day')); INSERT INTO tasks (name, category_id, due_date) VALUES ('{$localizationService->Localize('Repair the garage door')}', 1, date(datetime('now', 'localtime'), '+14 day'));
INSERT INTO tasks (name, category_id, due_date) VALUES ('{$localizationService->Localize('Task2')}', 1, date(datetime('now', 'localtime'), '+14 day'));
INSERT INTO tasks (name, category_id, due_date) VALUES ('{$localizationService->Localize('Task3')}', 2, date(datetime('now', 'localtime'), '-1 day'));
INSERT INTO tasks (name, category_id, due_date) VALUES ('{$localizationService->Localize('Task4')}', 2, date(datetime('now', 'localtime'), '-1 day'));
INSERT INTO tasks (name, due_date) VALUES ('{$localizationService->Localize('Task5')}', date(datetime('now', 'localtime'), '+3 day'));
INSERT INTO tasks (name, due_date) VALUES ('{$localizationService->Localize('Task6')}', date(datetime('now', 'localtime'), '+4 day'));
INSERT INTO migrations (migration) VALUES (-1); INSERT INTO migrations (migration) VALUES (-1);
"; ";

View File

@ -158,6 +158,12 @@
<span class="nav-link-text">{{ $L('Batteries') }}</span> <span class="nav-link-text">{{ $L('Batteries') }}</span>
</a> </a>
</li> </li>
<li data-nav-for-page="taskcategories" data-sub-menu-of="#top-nav-manager-master-data">
<a class="nav-link discrete-link" href="{{ $U('/taskcategories') }}">
<i class="fas fa-project-diagram "></i>
<span class="nav-link-text">{{ $L('Task categories') }}</span>
</a>
</li>
</ul> </ul>
</li> </li>
</ul> </ul>

View File

@ -43,7 +43,7 @@
<th>{{ $L('Product') }}</th> <th>{{ $L('Product') }}</th>
<th>{{ $L('Amount') }}</th> <th>{{ $L('Amount') }}</th>
<th>{{ $L('Next best before date') }}</th> <th>{{ $L('Next best before date') }}</th>
<th class="hidden">Hidden location</th> <th class="d-none">Hidden location</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -75,7 +75,7 @@
<span id="product-{{ $currentStockEntry->product_id }}-next-best-before-date">{{ $currentStockEntry->best_before_date }}</span> <span id="product-{{ $currentStockEntry->product_id }}-next-best-before-date">{{ $currentStockEntry->best_before_date }}</span>
<time id="product-{{ $currentStockEntry->product_id }}-next-best-before-date-timeago" class="timeago timeago-contextual" datetime="{{ $currentStockEntry->best_before_date }}"></time> <time id="product-{{ $currentStockEntry->product_id }}-next-best-before-date-timeago" class="timeago timeago-contextual" datetime="{{ $currentStockEntry->best_before_date }}"></time>
</td> </td>
<td class="hidden"> <td class="d-none">
{{ FindObjectInArrayByPropertyValue($locations, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->location_id)->name }} {{ FindObjectInArrayByPropertyValue($locations, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->location_id)->name }}
</td> </td>
</tr> </tr>

View File

@ -0,0 +1,59 @@
@extends('layout.default')
@section('title', $L('Task categories'))
@section('activeNav', 'taskcategories')
@section('viewJsName', 'taskcategories')
@section('content')
<div class="row">
<div class="col">
<h1>
@yield('title')
<a class="btn btn-outline-dark" href="{{ $U('/taskcategory/new') }}">
<i class="fas fa-plus"></i>&nbsp;{{ $L('Add') }}
</a>
</h1>
</div>
</div>
<div class="row mt-3">
<div class="col-xs-12 col-md-6 col-xl-3">
<label for="search">{{ $L('Search') }}</label> <i class="fas fa-search"></i>
<input type="text" class="form-control" id="search">
</div>
</div>
<div class="row">
<div class="col">
<table id="taskcategories-table" class="table table-sm table-striped dt-responsive">
<thead>
<tr>
<th>#</th>
<th>{{ $L('Name') }}</th>
<th>{{ $L('Description') }}</th>
</tr>
</thead>
<tbody>
@foreach($taskCategories as $taskCategory)
<tr>
<td class="fit-content">
<a class="btn btn-info btn-sm" href="{{ $U('/taskcategory/') }}{{ $taskCategory->id }}">
<i class="fas fa-edit"></i>
</a>
<a class="btn btn-danger btn-sm task-category-delete-button" href="#" data-category-id="{{ $taskCategory->id }}" data-category-name="{{ $taskCategory->name }}">
<i class="fas fa-trash"></i>
</a>
</td>
<td>
{{ $taskCategory->name }}
</td>
<td>
{{ $taskCategory->description }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@stop

View File

@ -0,0 +1,40 @@
@extends('layout.default')
@if($mode == 'edit')
@section('title', $L('Edit task category'))
@else
@section('title', $L('Create task category'))
@endif
@section('viewJsName', 'taskcategoryform')
@section('content')
<div class="row">
<div class="col-lg-6 col-xs-12">
<h1>@yield('title')</h1>
<script>Grocy.EditMode = '{{ $mode }}';</script>
@if($mode == 'edit')
<script>Grocy.EditObjectId = {{ $category->id }};</script>
@endif
<form id="task-category-form" novalidate>
<div class="form-group">
<label for="name">{{ $L('Name') }}</label>
<input type="text" class="form-control" required id="name" name="name" value="@if($mode == 'edit'){{ $category->name }}@endif">
<div class="invalid-feedback">{{ $L('A name is required') }}</div>
</div>
<div class="form-group">
<label for="description">{{ $L('Description') }}</label>
<textarea class="form-control" rows="2" id="description" name="description">@if($mode == 'edit'){{ $category->description }}@endif</textarea>
</div>
<button id="save-task-category-button" type="submit" class="btn btn-success">{{ $L('Save') }}</button>
</form>
</div>
</div>
@stop

View File

@ -32,14 +32,22 @@
<textarea class="form-control" rows="2" id="description" name="description">@if($mode == 'edit'){{ $task->description }}@endif</textarea> <textarea class="form-control" rows="2" id="description" name="description">@if($mode == 'edit'){{ $task->description }}@endif</textarea>
</div> </div>
@php
$initialDueDate = null;
if ($mode == 'edit')
{
$initialDueDate = date('Y-m-d', strtotime($task->due_date));
}
@endphp
@include('components.datetimepicker', array( @include('components.datetimepicker', array(
'id' => 'due', 'id' => 'due_date',
'label' => 'Due', 'label' => 'Due',
'format' => 'YYYY-MM-DD', 'format' => 'YYYY-MM-DD',
'initWithNow' => false, 'initWithNow' => false,
'initialValue' => $initialDueDate,
'limitEndToNow' => false, 'limitEndToNow' => false,
'limitStartToNow' => false, 'limitStartToNow' => false,
'invalidFeedback' => $L('A due dat is required'), 'invalidFeedback' => $L('A due date is required'),
'nextInputSelector' => '', 'nextInputSelector' => '',
'additionalCssClasses' => 'date-only-datetimepicker', 'additionalCssClasses' => 'date-only-datetimepicker',
'isRequired' => false 'isRequired' => false

View File

@ -6,12 +6,23 @@
@push('pageScripts') @push('pageScripts')
<script src="{{ $U('/node_modules/jquery-ui-dist/jquery-ui.min.js?v=', true) }}{{ $version }}"></script> <script src="{{ $U('/node_modules/jquery-ui-dist/jquery-ui.min.js?v=', true) }}{{ $version }}"></script>
<script src="{{ $U('/node_modules/datatables.net-rowgroup/js/dataTables.rowGroup.min.js?v=', true) }}{{ $version }}"></script>
<script src="{{ $U('/node_modules/datatables.net-rowgroup-bs4/js/rowGroup.bootstrap4.min.js?v=', true) }}{{ $version }}"></script>
@endpush
@push('pageStyles')
<link href="{{ $U('/node_modules/datatables.net-rowgroup-bs4/css/rowGroup.bootstrap4.min.css?v=', true) }}{{ $version }}" rel="stylesheet">
@endpush @endpush
@section('content') @section('content')
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h1>@yield('title')</h1> <h1>
@yield('title')
<a class="btn btn-outline-dark responsive-button" href="{{ $U('/task/new') }}">
<i class="fas fa-plus"></i> {{ $L('Add') }}
</a>
</h1>
<p id="info-due-tasks" data-next-x-days="{{ $nextXDays }}" class="btn btn-lg btn-warning no-real-button responsive-button mr-2"></p> <p id="info-due-tasks" data-next-x-days="{{ $nextXDays }}" class="btn btn-lg btn-warning no-real-button responsive-button mr-2"></p>
<p id="info-overdue-tasks" class="btn btn-lg btn-danger no-real-button responsive-button"></p> <p id="info-overdue-tasks" class="btn btn-lg btn-danger no-real-button responsive-button"></p>
</div> </div>
@ -32,19 +43,22 @@
<th>#</th> <th>#</th>
<th>{{ $L('Aufgabe') }}</th> <th>{{ $L('Aufgabe') }}</th>
<th>{{ $L('Due') }}</th> <th>{{ $L('Due') }}</th>
<th class="d-none">Hidden category</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach($tasks as $task) @foreach($tasks as $task)
<tr id="task-{{ $task->id }}-row" class="@if($task->due < date('Y-m-d H:i:s')) table-danger @elseif($task->due < date('Y-m-d H:i:s', strtotime("+$nextXDays days"))) table-warning @endif"> <tr id="task-{{ $task->id }}-row" class="@if($task->due_date < date('Y-m-d')) table-danger @elseif($task->due_date < date('Y-m-d', strtotime("+$nextXDays days"))) table-warning @endif">
<td class="fit-content"> <td class="fit-content">
<a class="btn btn-success btn-sm do-task-button" href="#" data-toggle="tooltip" title="{{ $L('Mark task "#1" as completed', $task->name) }}" <a class="btn btn-success btn-sm do-task-button" href="#" data-toggle="tooltip" title="{{ $L('Mark task "#1" as completed', $task->name) }}"
data-task-id="{{ $task->id }}"> data-task-id="{{ $task->id }}"
data-task-name="{{ $task->name }}">
<i class="fas fa-check"></i> <i class="fas fa-check"></i>
</a> </a>
<a class="btn btn-success btn-sm start-task-button" href="#" data-toggle="tooltip" title="{{ $L('Start task "#1"', $task->name) }}" <a class="btn btn-sm btn-danger delete-task-button" href="#"
data-task-id="{{ $task->id }}"> data-task-id="{{ $task->id }}"
<i class="fas fa-play"></i> data-task-name="{{ $task->name }}">
<i class="fas fa-trash"></i>
</a> </a>
<a class="btn btn-info btn-sm" href="{{ $U('/task/') }}{{ $task->id }}"> <a class="btn btn-info btn-sm" href="{{ $U('/task/') }}{{ $task->id }}">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
@ -54,8 +68,11 @@
{{ $task->name }} {{ $task->name }}
</td> </td>
<td> <td>
<span>{{ $task->due }}</span> <span>{{ $task->due_date }}</span>
<time class="timeago timeago-contextual" datetime="{{ $task->due }}"></time> <time class="timeago timeago-contextual" datetime="{{ $task->due_date }}"></time>
</td>
<td class="d-none">
@if($task->category_id !== null) <span>{{ FindObjectInArrayByPropertyValue($taskCategories, 'id', $task->category_id)->name }}</span> @else <span class="font-italic font-weight-light">{{ $L('Uncategorized') }}</span>@endif
</td> </td>
</tr> </tr>
@endforeach @endforeach

View File

@ -10,7 +10,7 @@
version "5.3.1" version "5.3.1"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.3.1.tgz#5466b8f31c1f493a96754c1426c25796d0633dd9" resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.3.1.tgz#5466b8f31c1f493a96754c1426c25796d0633dd9"
"TagManager@https://github.com/max-favilli/tagmanager.git#3.0.2": "TagManager@https://github.com/max-favilli/tagmanager.git#3.0.2", "tagmanager@https://github.com/max-favilli/tagmanager.git#3.0.2":
version "3.0.1" version "3.0.1"
resolved "https://github.com/max-favilli/tagmanager.git#df9eb9935c8585a392dfc00602f890caf233fa94" resolved "https://github.com/max-favilli/tagmanager.git#df9eb9935c8585a392dfc00602f890caf233fa94"
dependencies: dependencies:
@ -107,6 +107,21 @@ datatables.net-responsive@2.2.3, datatables.net-responsive@^2.2.3:
datatables.net "^1.10.15" datatables.net "^1.10.15"
jquery ">=1.7" jquery ">=1.7"
datatables.net-rowgroup-bs4@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/datatables.net-rowgroup-bs4/-/datatables.net-rowgroup-bs4-1.0.4.tgz#602f056f9a60bab1b3ac3a36088636f40156b05a"
dependencies:
datatables.net-bs4 "^1.10.15"
datatables.net-rowgroup "1.0.4"
jquery ">=1.7"
datatables.net-rowgroup@1.0.4, datatables.net-rowgroup@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/datatables.net-rowgroup/-/datatables.net-rowgroup-1.0.4.tgz#2caf979f28747be7d9ab66725b639b73099d8eb0"
dependencies:
datatables.net "^1.10.15"
jquery ">=1.7"
datatables.net-select-bs4@^1.2.7: datatables.net-select-bs4@^1.2.7:
version "1.2.7" version "1.2.7"
resolved "https://registry.yarnpkg.com/datatables.net-select-bs4/-/datatables.net-select-bs4-1.2.7.tgz#5e4ddd8feb412e974b54a15e81b2bb29840ba55b" resolved "https://registry.yarnpkg.com/datatables.net-select-bs4/-/datatables.net-select-bs4-1.2.7.tgz#5e4ddd8feb412e974b54a15e81b2bb29840ba55b"