mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 09:39:57 +00:00
Added possibility to track who did a habit (this implements and closes #21)
This commit is contained in:
parent
bcbdf58376
commit
249b01d7a8
1
app.php
1
app.php
@ -9,6 +9,7 @@ use \Grocy\Controllers\LoginController;
|
||||
if (file_exists(__DIR__ . '/embedded.txt'))
|
||||
{
|
||||
define('GROCY_DATAPATH', file_get_contents(__DIR__ . '/embedded.txt'));
|
||||
define('GROCY_USER_ID', 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -22,9 +22,15 @@ class HabitsApiController extends BaseApiController
|
||||
$trackedTime = $request->getQueryParams()['tracked_time'];
|
||||
}
|
||||
|
||||
$doneBy = GROCY_USER_ID;
|
||||
if (isset($request->getQueryParams()['done_by']) && !empty($request->getQueryParams()['done_by']))
|
||||
{
|
||||
$doneBy = $request->getQueryParams()['done_by'];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$this->HabitsService->TrackHabit($args['habitId'], $trackedTime);
|
||||
$this->HabitsService->TrackHabit($args['habitId'], $trackedTime, $doneBy);
|
||||
return $this->VoidApiActionResponse($response);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
|
@ -38,7 +38,8 @@ class HabitsController extends BaseController
|
||||
public function TrackHabitExecution(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
return $this->AppContainer->view->render($response, 'habittracking', [
|
||||
'habits' => $this->Database->habits()->orderBy('name')
|
||||
'habits' => $this->Database->habits()->orderBy('name'),
|
||||
'users' => $this->Database->users()->orderBy('username')
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ class UsersApiController extends BaseApiController
|
||||
try
|
||||
{
|
||||
$this->UsersService->CreateUser($requestBody['username'], $requestBody['first_name'], $requestBody['last_name'], $requestBody['password']);
|
||||
return $this->ApiResponse(array('success' => $success));
|
||||
return $this->ApiResponse(array('success' => true));
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
@ -33,7 +33,7 @@ class UsersApiController extends BaseApiController
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->UsersService->DeleteUser($args['userId']);
|
||||
$success = $this->UsersService->DeleteUser($args['userId']);
|
||||
return $this->ApiResponse(array('success' => $success));
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
@ -49,7 +49,7 @@ class UsersApiController extends BaseApiController
|
||||
try
|
||||
{
|
||||
$this->UsersService->EditUser($args['userId'], $requestBody['username'], $requestBody['first_name'], $requestBody['last_name'], $requestBody['password']);
|
||||
return $this->ApiResponse(array('success' => $success));
|
||||
return $this->ApiResponse(array('success' => true));
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
|
@ -144,3 +144,27 @@ function Setting(string $name, $value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function GetUserDisplayName($user)
|
||||
{
|
||||
$displayName = '';
|
||||
|
||||
if (empty($user->first_name) && !empty($user->last_name))
|
||||
{
|
||||
$displayName = $user->last_name;
|
||||
}
|
||||
elseif (empty($user->last_name) && !empty($user->first_name))
|
||||
{
|
||||
$displayName = $user->first_name;
|
||||
}
|
||||
elseif (!empty($user->last_name) && !empty($user->first_name))
|
||||
{
|
||||
$displayName = $user->first_name . ' ' . $user->last_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
$displayName = $user->username;
|
||||
}
|
||||
|
||||
return $displayName;
|
||||
}
|
||||
|
@ -180,6 +180,7 @@ return array(
|
||||
'Confirm password' => 'Passwort bestätigen',
|
||||
'Passwords do not match' => 'Passwörter stimmen nicht überein',
|
||||
'Change password' => 'Passwort ändern',
|
||||
'Done by' => 'Ausgeführt von',
|
||||
|
||||
//Constants
|
||||
'manually' => 'Manuell',
|
||||
|
2
migrations/0028.sql
Normal file
2
migrations/0028.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE habits_log
|
||||
ADD done_by_user_id
|
@ -1,6 +1,6 @@
|
||||
Grocy.Components.ProductPicker = { };
|
||||
|
||||
Grocy.Components.ProductPicker.GetPicker = function ()
|
||||
Grocy.Components.ProductPicker.GetPicker = function()
|
||||
{
|
||||
return $('#product_id');
|
||||
}
|
||||
@ -43,7 +43,7 @@ Grocy.Components.ProductPicker.HideCustomError = function()
|
||||
$("#custom-productpicker-error").addClass("d-none");
|
||||
}
|
||||
|
||||
$('.combobox').combobox({
|
||||
$('.product-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4'
|
||||
});
|
||||
|
62
public/viewjs/components/userpicker.js
Normal file
62
public/viewjs/components/userpicker.js
Normal file
@ -0,0 +1,62 @@
|
||||
Grocy.Components.UserPicker = { };
|
||||
|
||||
Grocy.Components.UserPicker.GetPicker = function()
|
||||
{
|
||||
return $('#user_id');
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.GetInputElement = function()
|
||||
{
|
||||
return $('#user_id_text_input');
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.GetValue = function()
|
||||
{
|
||||
return $('#user_id').val();
|
||||
}
|
||||
|
||||
Grocy.Components.UserPicker.SetValue = function(value)
|
||||
{
|
||||
Grocy.Components.UserPicker.GetInputElement().val(value);
|
||||
Grocy.Components.UserPicker.GetInputElement().trigger('change');
|
||||
}
|
||||
|
||||
$('.user-combobox').combobox({
|
||||
appendId: '_text_input',
|
||||
bsVersion: '4'
|
||||
});
|
||||
|
||||
var prefillUser = Grocy.Components.UserPicker.GetPicker().parent().data('prefill-by-username').toString();
|
||||
if (typeof prefillUser !== "undefined")
|
||||
{
|
||||
var possibleOptionElement = $("#user_id option[data-additional-searchdata*='" + prefillUser + "']").first();
|
||||
if (possibleOptionElement.length === 0)
|
||||
{
|
||||
possibleOptionElement = $("#user_id option:contains('" + prefillUser + "')").first();
|
||||
}
|
||||
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
$('#user_id').val(possibleOptionElement.val());
|
||||
$('#user_id').data('combobox').refresh();
|
||||
$('#user_id').trigger('change');
|
||||
|
||||
var nextInputElement = $(Grocy.Components.UserPicker.GetPicker().parent().data('next-input-selector').toString());
|
||||
nextInputElement.focus();
|
||||
}
|
||||
}
|
||||
|
||||
var prefillUserId = Grocy.Components.UserPicker.GetPicker().parent().data('prefill-by-user-id').toString();
|
||||
if (typeof prefillUserId !== "undefined")
|
||||
{
|
||||
var possibleOptionElement = $("#user_id option[value='" + prefillUserId + "']").first();
|
||||
if (possibleOptionElement.length > 0)
|
||||
{
|
||||
$('#user_id').val(possibleOptionElement.val());
|
||||
$('#user_id').data('combobox').refresh();
|
||||
$('#user_id').trigger('change');
|
||||
|
||||
var nextInputElement = $(Grocy.Components.UserPicker.GetPicker().parent().data('next-input-selector').toString());
|
||||
nextInputElement.focus();
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
Grocy.Api.Get('habits/get-habit-details/' + jsonForm.habit_id,
|
||||
function (habitDetails)
|
||||
{
|
||||
Grocy.Api.Get('habits/track-habit-execution/' + jsonForm.habit_id + '?tracked_time=' + Grocy.Components.DateTimePicker.GetValue(),
|
||||
Grocy.Api.Get('habits/track-habit-execution/' + jsonForm.habit_id + '?tracked_time=' + Grocy.Components.DateTimePicker.GetValue() + "&done_by=" + Grocy.Components.UserPicker.GetValue(),
|
||||
function(result)
|
||||
{
|
||||
toastr.success(L('Tracked execution of habit #1 on #2', habitDetails.habit.name, Grocy.Components.DateTimePicker.GetValue()));
|
||||
|
@ -52,16 +52,23 @@ class HabitsService extends BaseService
|
||||
);
|
||||
}
|
||||
|
||||
public function TrackHabit(int $habitId, string $trackedTime)
|
||||
public function TrackHabit(int $habitId, string $trackedTime, $doneBy = GROCY_USER_ID)
|
||||
{
|
||||
if (!$this->HabitExists($habitId))
|
||||
{
|
||||
throw new \Exception('Habit does not exist');
|
||||
}
|
||||
|
||||
$userRow = $this->Database->users()->where('id = :1', $doneBy)->fetch();
|
||||
if ($userRow === null)
|
||||
{
|
||||
throw new \Exception('User does not exist');
|
||||
}
|
||||
|
||||
$logRow = $this->Database->habits_log()->createRow(array(
|
||||
'habit_id' => $habitId,
|
||||
'tracked_time' => $trackedTime
|
||||
'tracked_time' => $trackedTime,
|
||||
'done_by_user_id' => $doneBy
|
||||
));
|
||||
$logRow->save();
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
<div class="form-group" data-next-input-selector="{{ $nextInputSelector }}" data-disallow-add-product-workflows="{{ BoolToString($disallowAddProductWorkflows) }}" data-prefill-by-name="{{ $prefillByName }}">
|
||||
<label for="product_id">{{ $L('Product') }} <i class="fas fa-barcode"></i><span id="barcode-lookup-disabled-hint" class="small text-muted d-none"> {{ $L('Barcode lookup is disabled') }}</span></label>
|
||||
<select class="form-control combobox" id="product_id" name="product_id" required>
|
||||
<select class="form-control product-combobox" id="product_id" name="product_id" required>
|
||||
<option value=""></option>
|
||||
@foreach($products as $product)
|
||||
<option data-additional-searchdata="{{ $product->barcode }}" value="{{ $product->id }}">{{ $product->name }}</option>
|
||||
|
16
views/components/userpicker.blade.php
Normal file
16
views/components/userpicker.blade.php
Normal file
@ -0,0 +1,16 @@
|
||||
@push('componentScripts')
|
||||
<script src="{{ $U('/viewjs/components/userpicker.js', true) }}?v={{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@php if(empty($prefillByUsername)) { $prefillByUsername = ''; } @endphp
|
||||
@php if(empty($prefillByUserId)) { $prefillByUserId = ''; } @endphp
|
||||
|
||||
<div class="form-group" data-next-input-selector="{{ $nextInputSelector }}" data-prefill-by-username="{{ $prefillByUsername }}" data-prefill-by-user-id="{{ $prefillByUserId }}">
|
||||
<label for="user_id">{{ $L($label) }}</label>
|
||||
<select class="form-control user-combobox" id="user_id" name="user_id">
|
||||
<option value=""></option>
|
||||
@foreach($users as $user)
|
||||
<option data-additional-searchdata="{{ $user->username }}" value="{{ $user->id }}">{{ GetUserDisplayName($user) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
@ -32,6 +32,13 @@
|
||||
'invalidFeedback' => $L('This can only be before now')
|
||||
))
|
||||
|
||||
@include('components.userpicker', array(
|
||||
'label' => 'Done by',
|
||||
'users' => $users,
|
||||
'nextInputSelector' => '#user_id',
|
||||
'prefillByUserId' => GROCY_USER_ID
|
||||
))
|
||||
|
||||
<button id="save-habittracking-button" type="submit" class="btn btn-success">{{ $L('OK') }}</button>
|
||||
|
||||
</form>
|
||||
|
@ -180,10 +180,8 @@
|
||||
<a class="nav-link dropdown-toggle discrete-link" href="#" data-toggle="dropdown"><i class="fas fa-wrench"></i> <span class="d-inline d-lg-none">{{ $L('Settings') }}</span></a>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
@if($isEmbeddedInstallation === false)
|
||||
<a class="dropdown-item discrete-link" href="{{ $U('/users') }}"><i class="fas fa-users"></i> {{ $L('Manage users') }}</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
@endif
|
||||
<a class="dropdown-item discrete-link" href="{{ $U('/manageapikeys') }}"><i class="fas fa-handshake"></i> {{ $L('Manage API keys') }}</a>
|
||||
<a class="dropdown-item discrete-link" target="_blank" href="{{ $U('/api') }}"><i class="fas fa-book"></i> {{ $L('REST API & data model documentation') }}</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
|
@ -41,7 +41,7 @@
|
||||
<a class="btn btn-info btn-sm" href="{{ $U('/user/') }}{{ $user->id }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger btn-sm user-delete-button @if($user->id === GROCY_USER_ID){{ disabled }}@endif" href="#" data-user-id="{{ $user->id }}" data-user-username="{{ $user->username }}">
|
||||
<a class="btn btn-danger btn-sm user-delete-button @if($user->id == GROCY_USER_ID) disabled @endif" href="#" data-user-id="{{ $user->id }}" data-user-username="{{ $user->username }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
|
Loading…
x
Reference in New Issue
Block a user