Use absolute URLs everywhere, this should fix #3

This commit is contained in:
Bernd Bestel 2018-04-18 19:03:39 +02:00
parent 3d1c6fc5f0
commit 607a90cccc
No known key found for this signature in database
GPG Key ID: 71BD34C0D4891300
36 changed files with 207 additions and 159 deletions

View File

@ -4,11 +4,11 @@ use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response; use \Psr\Http\Message\ResponseInterface as Response;
use \Grocy\Middleware\SessionAuthMiddleware; use \Grocy\Middleware\SessionAuthMiddleware;
use \Grocy\Helpers\UrlManager;
use \Grocy\Services\ApplicationService; use \Grocy\Services\ApplicationService;
require_once __DIR__ . '/vendor/autoload.php'; require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/data/config.php'; require_once __DIR__ . '/data/config.php';
require_once __DIR__ . '/extensions.php';
// Setup base application // Setup base application
if (PHP_SAPI !== 'cli') if (PHP_SAPI !== 'cli')
@ -21,6 +21,10 @@ if (PHP_SAPI !== 'cli')
'view' => function($container) 'view' => function($container)
{ {
return new \Slim\Views\Blade(__DIR__ . '/views', __DIR__ . '/data/viewcache'); return new \Slim\Views\Blade(__DIR__ . '/views', __DIR__ . '/data/viewcache');
},
'UrlManager' => function($container)
{
return new UrlManager(BASE_URL);
} }
]); ]);

View File

@ -9,7 +9,11 @@
"psr-4": { "psr-4": {
"Grocy\\Services\\": "services/", "Grocy\\Services\\": "services/",
"Grocy\\Controllers\\": "controllers/", "Grocy\\Controllers\\": "controllers/",
"Grocy\\Middleware\\": "middleware/" "Grocy\\Middleware\\": "middleware/",
} "Grocy\\Helpers\\": "helpers/"
},
"files": [
"helpers/extensions.php"
]
} }
} }

View File

@ -1,7 +1,17 @@
<?php <?php
define('MODE', 'production'); # Either "production" or "dev" # Login credentials
define('CULTURE', 'en');
define('HTTP_USER', 'admin'); define('HTTP_USER', 'admin');
define('HTTP_PASSWORD', 'admin'); define('HTTP_PASSWORD', 'admin');
# Either "production" or "dev"
define('MODE', 'production');
# Either "en" or "de" or the filename (without extension) of
# one of the other available localization files in the "/localization" directory
define('CULTURE', 'en');
# The base url of your installation,
# should be just "/" when running directly under the root of a (sub)domain
# or for example "https:/example.com/grocy" when using a subdirectory
define('BASE_URL', '/');

View File

@ -9,8 +9,6 @@ use \Grocy\Services\LocalizationService;
class BaseController class BaseController
{ {
public function __construct(\Slim\Container $container) { public function __construct(\Slim\Container $container) {
$this->AppContainer = $container;
$databaseService = new DatabaseService(); $databaseService = new DatabaseService();
$this->Database = $databaseService->GetDbConnection(); $this->Database = $databaseService->GetDbConnection();
@ -23,6 +21,12 @@ class BaseController
{ {
return $localizationService->Localize($text, ...$placeholderValues); return $localizationService->Localize($text, ...$placeholderValues);
}); });
$container->view->set('U', function($relativePath) use($container)
{
return $container->UrlManager->ConstructUrl($relativePath);
});
$this->AppContainer = $container;
} }
protected $AppContainer; protected $AppContainer;

View File

@ -27,16 +27,16 @@ class LoginController extends BaseController
$sessionKey = $this->SessionService->CreateSession(); $sessionKey = $this->SessionService->CreateSession();
setcookie('grocy_session', $sessionKey, time() + 31536000); // Cookie expires in 1 year, but session validity is up to SessionService setcookie('grocy_session', $sessionKey, time() + 31536000); // Cookie expires in 1 year, but session validity is up to SessionService
return $response->withRedirect('/'); return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/'));
} }
else else
{ {
return $response->withRedirect('/login?invalid=true'); return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/login?invalid=true'));
} }
} }
else else
{ {
return $response->withRedirect('/login?invalid=true'); return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/login?invalid=true'));
} }
} }
@ -48,7 +48,7 @@ class LoginController extends BaseController
public function Logout(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) public function Logout(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
{ {
$this->SessionService->RemoveSession($_COOKIE['grocy_session']); $this->SessionService->RemoveSession($_COOKIE['grocy_session']);
return $response->withRedirect('/'); return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/'));
} }
public function Root(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args) public function Root(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
@ -64,6 +64,6 @@ class LoginController extends BaseController
$demoDataGeneratorService->PopulateDemoData(); $demoDataGeneratorService->PopulateDemoData();
} }
return $response->withRedirect('/stockoverview'); return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/stockoverview'));
} }
} }

17
helpers/UrlManager.php Normal file
View File

@ -0,0 +1,17 @@
<?php
namespace Grocy\Helpers;
class UrlManager
{
public function __construct(string $basePath) {
$this->BasePath = $basePath;
}
protected $BasePath;
public function ConstructUrl($relativePath)
{
return rtrim($this->BasePath, '/') . $relativePath;
}
}

View File

@ -5,8 +5,8 @@ namespace Grocy\Middleware;
class BaseMiddleware class BaseMiddleware
{ {
public function __construct(\Slim\Container $container) { public function __construct(\Slim\Container $container) {
$this->container = $container; $this->AppContainer = $container;
} }
protected $container; protected $AppContainer;
} }

View File

@ -20,7 +20,7 @@ class SessionAuthMiddleware extends BaseMiddleware
$sessionService = new SessionService(); $sessionService = new SessionService();
if ((!isset($_COOKIE['grocy_session']) || !$sessionService->IsValidSession($_COOKIE['grocy_session'])) && $routeName !== 'login') if ((!isset($_COOKIE['grocy_session']) || !$sessionService->IsValidSession($_COOKIE['grocy_session'])) && $routeName !== 'login')
{ {
$response = $response->withRedirect('/login'); $response = $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/login'));
} }
else else
{ {

View File

@ -14,6 +14,11 @@
return localizedText; return localizedText;
} }
U = function(relativePath)
{
return Grocy.BaseUrl.replace(/\/$/, '') + relativePath;
}
if (!Grocy.ActiveNav.isEmpty()) if (!Grocy.ActiveNav.isEmpty())
{ {
var menuItem = $('.nav').find("[data-nav-for-page='" + Grocy.ActiveNav + "']"); var menuItem = $('.nav').find("[data-nav-for-page='" + Grocy.ActiveNav + "']");
@ -23,9 +28,11 @@ if (!Grocy.ActiveNav.isEmpty())
$.timeago.settings.allowFuture = true; $.timeago.settings.allowFuture = true;
$('time.timeago').timeago(); $('time.timeago').timeago();
Grocy.FetchJson = function(url, success, error) Grocy.Api = { };
Grocy.Api.Get = function(apiFunction, success, error)
{ {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
var url = U('/api/' + apiFunction);
xhr.onreadystatechange = function() xhr.onreadystatechange = function()
{ {
@ -52,9 +59,10 @@ Grocy.FetchJson = function(url, success, error)
xhr.send(); xhr.send();
}; };
Grocy.PostJson = function(url, jsonData, success, error) Grocy.Api.Post = function(apiFunction, jsonData, success, error)
{ {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
var url = U('/api/' + apiFunction);
xhr.onreadystatechange = function() xhr.onreadystatechange = function()
{ {

View File

@ -1,7 +1,7 @@
$(document).on('click', '.battery-delete-button', function(e) $(document).on('click', '.battery-delete-button', function(e)
{ {
bootbox.confirm({ bootbox.confirm({
message: 'Delete battery <strong>' + $(e.target).attr('data-battery-name') + '</strong>?', message: 'Delete battery <strong>' + $(e.currentTarget).attr('data-battery-name') + '</strong>?',
buttons: { buttons: {
confirm: { confirm: {
label: 'Yes', label: 'Yes',
@ -16,10 +16,10 @@
{ {
if (result === true) if (result === true)
{ {
Grocy.FetchJson('/api/delete-object/batteries/' + $(e.target).attr('data-battery-id'), Grocy.Api.Get('delete-object/batteries/' + $(e.currentTarget).attr('data-battery-id'),
function(result) function(result)
{ {
window.location.href = '/batteries'; window.location.href = U('/batteries');
}, },
function(xhr) function(xhr)
{ {

View File

@ -4,10 +4,10 @@
if (Grocy.EditMode === 'create') if (Grocy.EditMode === 'create')
{ {
Grocy.PostJson('/api/add-object/batteries', $('#battery-form').serializeJSON(), Grocy.Api.Post('add-object/batteries', $('#battery-form').serializeJSON(),
function(result) function(result)
{ {
window.location.href = '/batteries'; window.location.href = U('/batteries');
}, },
function(xhr) function(xhr)
{ {
@ -17,10 +17,10 @@
} }
else else
{ {
Grocy.PostJson('/api/edit-object/batteries/' + Grocy.EditObjectId, $('#battery-form').serializeJSON(), Grocy.Api.Post('edit-object/batteries/' + Grocy.EditObjectId, $('#battery-form').serializeJSON(),
function(result) function(result)
{ {
window.location.href = '/batteries'; window.location.href = U('/batteries');
}, },
function(xhr) function(xhr)
{ {

View File

@ -4,10 +4,10 @@
var jsonForm = $('#batterytracking-form').serializeJSON(); var jsonForm = $('#batterytracking-form').serializeJSON();
Grocy.FetchJson('/api/batteries/get-battery-details/' + jsonForm.battery_id, Grocy.Api.Get('batteries/get-battery-details/' + jsonForm.battery_id,
function (batteryDetails) function (batteryDetails)
{ {
Grocy.FetchJson('/api/batteries/track-charge-cycle/' + jsonForm.battery_id + '?tracked_time=' + $('#tracked_time').val(), Grocy.Api.Get('batteries/track-charge-cycle/' + jsonForm.battery_id + '?tracked_time=' + $('#tracked_time').val(),
function(result) function(result)
{ {
toastr.success('Tracked charge cylce of battery ' + batteryDetails.battery.name + ' on ' + $('#tracked_time').val()); toastr.success('Tracked charge cylce of battery ' + batteryDetails.battery.name + ' on ' + $('#tracked_time').val());

View File

@ -2,7 +2,7 @@ Grocy.Components.BatteryCard = { };
Grocy.Components.BatteryCard.Refresh = function(batteryId) Grocy.Components.BatteryCard.Refresh = function(batteryId)
{ {
Grocy.FetchJson('/api/batteries/get-battery-details/' + batteryId, Grocy.Api.Get('batteries/get-battery-details/' + batteryId,
function(batteryDetails) function(batteryDetails)
{ {
$('#batterycard-battery-name').text(batteryDetails.battery.name); $('#batterycard-battery-name').text(batteryDetails.battery.name);

View File

@ -2,7 +2,7 @@ Grocy.Components.HabitCard = { };
Grocy.Components.HabitCard.Refresh = function (habitId) Grocy.Components.HabitCard.Refresh = function (habitId)
{ {
Grocy.FetchJson('/api/habits/get-habit-details/' + habitId, Grocy.Api.Get('habits/get-habit-details/' + habitId,
function(habitDetails) function(habitDetails)
{ {
$('#habitcard-habit-name').text(habitDetails.habit.name); $('#habitcard-habit-name').text(habitDetails.habit.name);

View File

@ -2,7 +2,7 @@ Grocy.Components.ProductCard = { };
Grocy.Components.ProductCard.Refresh = function(productId) Grocy.Components.ProductCard.Refresh = function(productId)
{ {
Grocy.FetchJson('/api/stock/get-product-details/' + productId, Grocy.Api.Get('stock/get-product-details/' + productId,
function(productDetails) function(productDetails)
{ {
$('#productcard-product-name').text(productDetails.product.name); $('#productcard-product-name').text(productDetails.product.name);

View File

@ -10,10 +10,10 @@
spoiled = 1; spoiled = 1;
} }
Grocy.FetchJson('/api/stock/get-product-details/' + jsonForm.product_id, Grocy.Api.Get('stock/get-product-details/' + jsonForm.product_id,
function (productDetails) function (productDetails)
{ {
Grocy.FetchJson('/api/stock/consume-product/' + jsonForm.product_id + '/' + jsonForm.amount + '?spoiled=' + spoiled, Grocy.Api.Get('stock/consume-product/' + jsonForm.product_id + '/' + jsonForm.amount + '?spoiled=' + spoiled,
function(result) function(result)
{ {
toastr.success('Removed ' + jsonForm.amount + ' ' + productDetails.quantity_unit_stock.name + ' of ' + productDetails.product.name + ' from stock'); toastr.success('Removed ' + jsonForm.amount + ' ' + productDetails.quantity_unit_stock.name + ' of ' + productDetails.product.name + ' from stock');
@ -46,7 +46,7 @@ $('#product_id').on('change', function(e)
{ {
Grocy.Components.ProductCard.Refresh(productId); Grocy.Components.ProductCard.Refresh(productId);
Grocy.FetchJson('/api/stock/get-product-details/' + productId, Grocy.Api.Get('stock/get-product-details/' + productId,
function (productDetails) function (productDetails)
{ {
$('#amount').attr('max', productDetails.stock_amount); $('#amount').attr('max', productDetails.stock_amount);

View File

@ -4,10 +4,10 @@
if (Grocy.EditMode === 'create') if (Grocy.EditMode === 'create')
{ {
Grocy.PostJson('/api/add-object/habits', $('#habit-form').serializeJSON(), Grocy.Api.Post('add-object/habits', $('#habit-form').serializeJSON(),
function(result) function(result)
{ {
window.location.href = '/habits'; window.location.href = U('/habits');
}, },
function(xhr) function(xhr)
{ {
@ -17,10 +17,10 @@
} }
else else
{ {
Grocy.PostJson('/api/edit-object/habits/' + Grocy.EditObjectId, $('#habit-form').serializeJSON(), Grocy.Api.Post('edit-object/habits/' + Grocy.EditObjectId, $('#habit-form').serializeJSON(),
function(result) function(result)
{ {
window.location.href = '/habits'; window.location.href = U('/habits');
}, },
function(xhr) function(xhr)
{ {

View File

@ -1,7 +1,7 @@
$(document).on('click', '.habit-delete-button', function(e) $(document).on('click', '.habit-delete-button', function(e)
{ {
bootbox.confirm({ bootbox.confirm({
message: 'Delete habit <strong>' + $(e.target).attr('data-habit-name') + '</strong>?', message: 'Delete habit <strong>' + $(e.currentTarget).attr('data-habit-name') + '</strong>?',
buttons: { buttons: {
confirm: { confirm: {
label: 'Yes', label: 'Yes',
@ -16,10 +16,10 @@
{ {
if (result === true) if (result === true)
{ {
Grocy.FetchJson('/api/delete-object/habits/' + $(e.target).attr('data-habit-id'), Grocy.Api.Get('delete-object/habits/' + $(e.currentTarget).attr('data-habit-id'),
function(result) function(result)
{ {
window.location.href = '/habits'; window.location.href = U('/habits');
}, },
function(xhr) function(xhr)
{ {

View File

@ -4,10 +4,10 @@
var jsonForm = $('#habittracking-form').serializeJSON(); var jsonForm = $('#habittracking-form').serializeJSON();
Grocy.FetchJson('/api/habits/get-habit-details/' + jsonForm.habit_id, Grocy.Api.Get('habits/get-habit-details/' + jsonForm.habit_id,
function (habitDetails) function (habitDetails)
{ {
Grocy.FetchJson('/api/habits/track-habit-execution/' + jsonForm.habit_id + '?tracked_time=' + $('#tracked_time').val(), Grocy.Api.Get('habits/track-habit-execution/' + jsonForm.habit_id + '?tracked_time=' + $('#tracked_time').val(),
function(result) function(result)
{ {
toastr.success('Tracked execution of habit ' + habitDetails.habit.name + ' on ' + $('#tracked_time').val()); toastr.success('Tracked execution of habit ' + habitDetails.habit.name + ' on ' + $('#tracked_time').val());

View File

@ -4,10 +4,10 @@
var jsonForm = $('#inventory-form').serializeJSON(); var jsonForm = $('#inventory-form').serializeJSON();
Grocy.FetchJson('/api/stock/get-product-details/' + jsonForm.product_id, Grocy.Api.Get('stock/get-product-details/' + jsonForm.product_id,
function (productDetails) function (productDetails)
{ {
Grocy.FetchJson('/api/stock/inventory-product/' + jsonForm.product_id + '/' + jsonForm.new_amount + '?bestbeforedate=' + $('#best_before_date').val(), Grocy.Api.Get('stock/inventory-product/' + jsonForm.product_id + '/' + jsonForm.new_amount + '?bestbeforedate=' + $('#best_before_date').val(),
function(result) function(result)
{ {
var addBarcode = GetUriParam('addbarcodetoselection'); var addBarcode = GetUriParam('addbarcodetoselection');
@ -23,7 +23,7 @@
productDetails.product.barcode += ',' + addBarcode; productDetails.product.barcode += ',' + addBarcode;
} }
Grocy.PostJson('/api/edit-object/products/' + productDetails.product.id, productDetails.product, Grocy.Api.Get('edit-object/products/' + productDetails.product.id, productDetails.product,
function (result) { }, function (result) { },
function(xhr) function(xhr)
{ {
@ -36,7 +36,7 @@
if (addBarcode !== undefined) if (addBarcode !== undefined)
{ {
window.location.href = '/inventory'; window.location.href = U('/inventory');
} }
else else
{ {
@ -71,7 +71,7 @@ $('#product_id').on('change', function(e)
{ {
Grocy.Components.ProductCard.Refresh(productId); Grocy.Components.ProductCard.Refresh(productId);
Grocy.FetchJson('/api/stock/get-product-details/' + productId, Grocy.Api.Get('stock/get-product-details/' + productId,
function(productDetails) function(productDetails)
{ {
$('#new_amount').attr('not-equal', productDetails.stock_amount); $('#new_amount').attr('not-equal', productDetails.stock_amount);
@ -124,7 +124,7 @@ $('#product_id_text_input').on('change', function(e)
className: 'btn-success add-new-product-dialog-button', className: 'btn-success add-new-product-dialog-button',
callback: function() callback: function()
{ {
window.location.href = '/product/new?prefillname=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname); window.location.href = U('/product/new?prefillname=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname));
} }
}, },
addbarcode: { addbarcode: {
@ -132,7 +132,7 @@ $('#product_id_text_input').on('change', function(e)
className: 'btn-info add-new-barcode-dialog-button', className: 'btn-info add-new-barcode-dialog-button',
callback: function() callback: function()
{ {
window.location.href = '/inventory?addbarcodetoselection=' + encodeURIComponent(input); window.location.href = U('/inventory?addbarcodetoselection=' + encodeURIComponent(input));
} }
}, },
addnewproductwithbarcode: { addnewproductwithbarcode: {
@ -140,7 +140,7 @@ $('#product_id_text_input').on('change', function(e)
className: 'btn-warning add-new-product-with-barcode-dialog-button', className: 'btn-warning add-new-product-with-barcode-dialog-button',
callback: function() callback: function()
{ {
window.location.href = '/product/new?prefillbarcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname); window.location.href = U('/product/new?prefillbarcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname));
} }
} }
} }
@ -283,7 +283,7 @@ $('#new_amount').on('change', function(e)
if (productId) if (productId)
{ {
Grocy.FetchJson('/api/stock/get-product-details/' + productId, Grocy.Api.Get('stock/get-product-details/' + productId,
function(productDetails) function(productDetails)
{ {
var productStockAmount = productDetails.stock_amount || '0'; var productStockAmount = productDetails.stock_amount || '0';

View File

@ -4,10 +4,10 @@
if (Grocy.EditMode === 'create') if (Grocy.EditMode === 'create')
{ {
Grocy.PostJson('/api/add-object/locations', $('#location-form').serializeJSON(), Grocy.Api.Post('add-object/locations', $('#location-form').serializeJSON(),
function(result) function(result)
{ {
window.location.href = '/locations'; window.location.href = U('/locations');
}, },
function(xhr) function(xhr)
{ {
@ -17,10 +17,10 @@
} }
else else
{ {
Grocy.PostJson('/api/edit-object/locations/' + Grocy.EditObjectId, $('#location-form').serializeJSON(), Grocy.Api.Post('edit-object/locations/' + Grocy.EditObjectId, $('#location-form').serializeJSON(),
function(result) function(result)
{ {
window.location.href = '/locations'; window.location.href = U('/locations');
}, },
function(xhr) function(xhr)
{ {

View File

@ -1,7 +1,7 @@
$(document).on('click', '.location-delete-button', function(e) $(document).on('click', '.location-delete-button', function(e)
{ {
bootbox.confirm({ bootbox.confirm({
message: 'Delete location <strong>' + $(e.target).attr('data-location-name') + '</strong>?', message: 'Delete location <strong>' + $(e.currentTarget).attr('data-location-name') + '</strong>?',
buttons: { buttons: {
confirm: { confirm: {
label: 'Yes', label: 'Yes',
@ -16,10 +16,10 @@
{ {
if (result === true) if (result === true)
{ {
Grocy.FetchJson('/api/delete-object/locations/' + $(e.target).attr('data-location-id'), Grocy.Api.Get('delete-object/locations/' + $(e.currentTarget).attr('data-location-id'),
function(result) function(result)
{ {
window.location.href = '/locations'; window.location.href = U('/locations');
}, },
function(xhr) function(xhr)
{ {

View File

@ -2,7 +2,7 @@
{ {
e.preventDefault(); e.preventDefault();
var redirectDestination = '/products'; var redirectDestination = U('/products');
var returnTo = GetUriParam('returnto'); var returnTo = GetUriParam('returnto');
if (returnTo !== undefined) if (returnTo !== undefined)
{ {
@ -11,7 +11,7 @@
if (Grocy.EditMode === 'create') if (Grocy.EditMode === 'create')
{ {
Grocy.PostJson('/api/add-object/products', $('#product-form').serializeJSON(), Grocy.Api.Post('add-object/products', $('#product-form').serializeJSON(),
function(result) function(result)
{ {
window.location.href = redirectDestination; window.location.href = redirectDestination;
@ -24,7 +24,7 @@
} }
else else
{ {
Grocy.PostJson('/api/edit-object/products/' + Grocy.EditObjectId, $('#product-form').serializeJSON(), Grocy.Api.Post('edit-object/products/' + Grocy.EditObjectId, $('#product-form').serializeJSON(),
function(result) function(result)
{ {
window.location.href = redirectDestination; window.location.href = redirectDestination;
@ -44,7 +44,7 @@ $('#barcode-taginput').tagsManager({
if (Grocy.EditMode === 'edit') if (Grocy.EditMode === 'edit')
{ {
Grocy.FetchJson('/api/get-object/products/' + Grocy.EditObjectId, Grocy.Api.Get('get-object/products/' + Grocy.EditObjectId,
function (product) function (product)
{ {
if (product.barcode !== null && product.barcode.length > 0) if (product.barcode !== null && product.barcode.length > 0)

View File

@ -1,7 +1,7 @@
$(document).on('click', '.product-delete-button', function(e) $(document).on('click', '.product-delete-button', function(e)
{ {
bootbox.confirm({ bootbox.confirm({
message: 'Delete product <strong>' + $(e.target).attr('data-product-name') + '</strong>?', message: 'Delete product <strong>' + $(e.currentTarget).attr('data-product-name') + '</strong>?',
buttons: { buttons: {
confirm: { confirm: {
label: 'Yes', label: 'Yes',
@ -16,10 +16,10 @@
{ {
if (result === true) if (result === true)
{ {
Grocy.FetchJson('/api/delete-object/products/' + $(e.target).attr('data-product-id'), Grocy.Api.Get('delete-object/products/' + $(e.currentTarget).attr('data-product-id'),
function(result) function(result)
{ {
window.location.href = '/products'; window.location.href = U('/products');
}, },
function(xhr) function(xhr)
{ {

View File

@ -4,12 +4,12 @@
var jsonForm = $('#purchase-form').serializeJSON(); var jsonForm = $('#purchase-form').serializeJSON();
Grocy.FetchJson('/api/stock/get-product-details/' + jsonForm.product_id, Grocy.Api.Get('stock/get-product-details/' + jsonForm.product_id,
function (productDetails) function (productDetails)
{ {
var amount = jsonForm.amount * productDetails.product.qu_factor_purchase_to_stock; var amount = jsonForm.amount * productDetails.product.qu_factor_purchase_to_stock;
Grocy.FetchJson('/api/stock/add-product/' + jsonForm.product_id + '/' + amount + '?bestbeforedate=' + $('#best_before_date').val(), Grocy.Api.Get('stock/add-product/' + jsonForm.product_id + '/' + amount + '?bestbeforedate=' + $('#best_before_date').val(),
function(result) function(result)
{ {
var addBarcode = GetUriParam('addbarcodetoselection'); var addBarcode = GetUriParam('addbarcodetoselection');
@ -25,7 +25,7 @@
productDetails.product.barcode += ',' + addBarcode; productDetails.product.barcode += ',' + addBarcode;
} }
Grocy.PostJson('/api/edit-object/products/' + productDetails.product.id, productDetails.product, Grocy.Api.Post('edit-object/products/' + productDetails.product.id, productDetails.product,
function (result) { }, function (result) { },
function(xhr) function(xhr)
{ {
@ -38,7 +38,7 @@
if (addBarcode !== undefined) if (addBarcode !== undefined)
{ {
window.location.href = '/purchase'; window.location.href = U('/purchase');
} }
else else
{ {
@ -72,7 +72,7 @@ $('#product_id').on('change', function(e)
{ {
Grocy.Components.ProductCard.Refresh(productId); Grocy.Components.ProductCard.Refresh(productId);
Grocy.FetchJson('/api/stock/get-product-details/' + productId, Grocy.Api.Get('stock/get-product-details/' + productId,
function(productDetails) function(productDetails)
{ {
$('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name); $('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name);
@ -133,7 +133,7 @@ $('#product_id_text_input').on('change', function(e)
className: 'btn-success add-new-product-dialog-button', className: 'btn-success add-new-product-dialog-button',
callback: function() callback: function()
{ {
window.location.href = '/product/new?prefillname=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname); window.location.href = U('/product/new?prefillname=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname));
} }
}, },
addbarcode: { addbarcode: {
@ -141,7 +141,7 @@ $('#product_id_text_input').on('change', function(e)
className: 'btn-info add-new-barcode-dialog-button', className: 'btn-info add-new-barcode-dialog-button',
callback: function() callback: function()
{ {
window.location.href = '/purchase?addbarcodetoselection=' + encodeURIComponent(input); window.location.href = U('/purchase?addbarcodetoselection=' + encodeURIComponent(input));
} }
}, },
addnewproductwithbarcode: { addnewproductwithbarcode: {
@ -149,7 +149,7 @@ $('#product_id_text_input').on('change', function(e)
className: 'btn-warning add-new-product-with-barcode-dialog-button', className: 'btn-warning add-new-product-with-barcode-dialog-button',
callback: function() callback: function()
{ {
window.location.href = '/product/new?prefillbarcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname); window.location.href = U('/product/new?prefillbarcode=' + encodeURIComponent(input) + '&returnto=' + encodeURIComponent(window.location.pathname));
} }
} }
} }

View File

@ -4,10 +4,10 @@
if (Grocy.EditMode === 'create') if (Grocy.EditMode === 'create')
{ {
Grocy.PostJson('/api/add-object/quantity_units', $('#quantityunit-form').serializeJSON(), Grocy.Api.Post('add-object/quantity_units', $('#quantityunit-form').serializeJSON(),
function(result) function(result)
{ {
window.location.href = '/quantityunits'; window.location.href = U('/quantityunits');
}, },
function(xhr) function(xhr)
{ {
@ -17,10 +17,10 @@
} }
else else
{ {
Grocy.PostJson('/api/edit-object/quantity_units/' + Grocy.EditObjectId, $('#quantityunit-form').serializeJSON(), Grocy.Api.Post('edit-object/quantity_units/' + Grocy.EditObjectId, $('#quantityunit-form').serializeJSON(),
function(result) function(result)
{ {
window.location.href = '/quantityunits'; window.location.href = U('/quantityunits');
}, },
function(xhr) function(xhr)
{ {

View File

@ -1,7 +1,7 @@
$(document).on('click', '.quantityunit-delete-button', function(e) $(document).on('click', '.quantityunit-delete-button', function(e)
{ {
bootbox.confirm({ bootbox.confirm({
message: 'Delete quantity unit <strong>' + $(e.target).attr('data-quantityunit-name') + '</strong>?', message: 'Delete quantity unit <strong>' + $(e.currentTarget).attr('data-quantityunit-name') + '</strong>?',
buttons: { buttons: {
confirm: { confirm: {
label: 'Yes', label: 'Yes',
@ -16,10 +16,10 @@
{ {
if (result === true) if (result === true)
{ {
Grocy.FetchJson('/api/delete-object/quantity_units/' + $(e.target).attr('data-quantityunit-id'), Grocy.Api.Get('delete-object/quantity_units/' + $(e.currentTarget).attr('data-quantityunit-id'),
function(result) function(result)
{ {
window.location.href = '/quantityunits'; window.location.href = U('/quantityunits');
}, },
function(xhr) function(xhr)
{ {

View File

@ -1,9 +1,9 @@
$(document).on('click', '.shoppinglist-delete-button', function(e) $(document).on('click', '.shoppinglist-delete-button', function(e)
{ {
Grocy.FetchJson('/api/delete-object/shopping_list/' + $(e.target).attr('data-shoppinglist-id'), Grocy.Api.Get('delete-object/shopping_list/' + $(e.currentTarget).attr('data-shoppinglist-id'),
function(result) function(result)
{ {
window.location.href = '/shoppinglist'; window.location.href = U('/shoppinglist');
}, },
function(xhr) function(xhr)
{ {
@ -14,10 +14,10 @@
$(document).on('click', '#add-products-below-min-stock-amount', function(e) $(document).on('click', '#add-products-below-min-stock-amount', function(e)
{ {
Grocy.FetchJson('/api/stock/add-missing-products-to-shoppinglist', Grocy.Api.Get('stock/add-missing-products-to-shoppinglist',
function(result) function(result)
{ {
window.location.href = '/shoppinglist'; window.location.href = U('/shoppinglist');
}, },
function(xhr) function(xhr)
{ {

View File

@ -4,10 +4,10 @@
if (Grocy.EditMode === 'create') if (Grocy.EditMode === 'create')
{ {
Grocy.PostJson('/api/add-object/shopping_list', $('#shoppinglist-form').serializeJSON(), Grocy.Api.Post('add-object/shopping_list', $('#shoppinglist-form').serializeJSON(),
function(result) function(result)
{ {
window.location.href = '/shoppinglist'; window.location.href = U('/shoppinglist');
}, },
function(xhr) function(xhr)
{ {
@ -17,10 +17,10 @@
} }
else else
{ {
Grocy.PostJson('/api/edit-object/shopping_list/' + Grocy.EditObjectId, $('#shoppinglist-form').serializeJSON(), Grocy.Api.Post('edit-object/shopping_list/' + Grocy.EditObjectId, $('#shoppinglist-form').serializeJSON(),
function(result) function(result)
{ {
window.location.href = '/shoppinglist'; window.location.href = U('/shoppinglist');
}, },
function(xhr) function(xhr)
{ {
@ -38,7 +38,7 @@ $('#product_id').on('change', function(e)
{ {
Grocy.Components.ProductCard.Refresh(productId); Grocy.Components.ProductCard.Refresh(productId);
Grocy.FetchJson('/api/stock/get-product-details/' + productId, Grocy.Api.Get('stock/get-product-details/' + productId,
function (productDetails) function (productDetails)
{ {
$('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name); $('#amount_qu_unit').text(productDetails.quantity_unit_purchase.name);
@ -49,7 +49,7 @@ $('#product_id').on('change', function(e)
} }
else else
{ {
Grocy.FetchJson('/api/get-objects/shopping_list', Grocy.Api.Get('get-objects/shopping_list',
function (currentShoppingListItems) function (currentShoppingListItems)
{ {
if (currentShoppingListItems.filter(function (e) { return e.product_id === productId; }).length > 0) if (currentShoppingListItems.filter(function (e) { return e.product_id === productId; }).length > 0)

View File

@ -1,5 +1,5 @@
@push('componentScripts') @push('componentScripts')
<script src="/viewjs/components/batterycard.js"></script> <script src="{{ $U('/viewjs/components/batterycard.js') }}?v={{ $version }}"></script>
@endpush @endpush
<div class="main well"> <div class="main well">

View File

@ -1,5 +1,5 @@
@push('componentScripts') @push('componentScripts')
<script src="/viewjs/components/datepicker.js"></script> <script src="{{ $U('/viewjs/components/datepicker.js') }}?v={{ $version }}"></script>
@endpush @endpush
<div class="form-group"> <div class="form-group">

View File

@ -1,5 +1,5 @@
@push('componentScripts') @push('componentScripts')
<script src="/viewjs/components/datetimepicker.js"></script> <script src="{{ $U('/viewjs/components/datetimepicker.js') }}?v={{ $version }}"></script>
@endpush @endpush
<div class="form-group"> <div class="form-group">

View File

@ -1,5 +1,5 @@
@push('componentScripts') @push('componentScripts')
<script src="/viewjs/components/habitcard.js"></script> <script src="{{ $U('/viewjs/components/habitcard.js') }}?v={{ $version }}"></script>
@endpush @endpush
<div class="main well"> <div class="main well">

View File

@ -1,5 +1,5 @@
@push('componentScripts') @push('componentScripts')
<script src="/viewjs/components/productcard.js"></script> <script src="{{ $U('/viewjs/components/productcard.js') }}?v={{ $version }}"></script>
@endpush @endpush
<div class="main well"> <div class="main well">

View File

@ -13,21 +13,22 @@
<title>@yield('title') | grocy</title> <title>@yield('title') | grocy</title>
<link href="/bower_components/bootstrap/dist/css/bootstrap.min.css?v={{ $version }}" rel="stylesheet"> <link href="{{ $U('/bower_components/bootstrap/dist/css/bootstrap.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="/bower_components/font-awesome/css/font-awesome.min.css?v={{ $version }}" rel="stylesheet"> <link href="{{ $U('/bower_components/font-awesome/css/font-awesome.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="/bower_components/bootstrap-datepicker/dist/css/bootstrap-datepicker3.min.css?v={{ $version }}" rel="stylesheet"> <link href="{{ $U('/bower_components/bootstrap-datepicker/dist/css/bootstrap-datepicker3.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="/bower_components/bootstrap-combobox/css/bootstrap-combobox.css?v={{ $version }}" rel="stylesheet"> <link href="{{ $U('/bower_components/bootstrap-combobox/css/bootstrap-combobox.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css?v={{ $version }}" rel="stylesheet"> <link href="{{ $U('/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="/bower_components/datatables.net-responsive-bs/css/responsive.bootstrap.min.css?v={{ $version }}" rel="stylesheet"> <link href="{{ $U('/bower_components/datatables.net-responsive-bs/css/responsive.bootstrap.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="/bower_components/toastr/toastr.min.css?v={{ $version }}" rel="stylesheet"> <link href="{{ $U('/bower_components/toastr/toastr.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="/bower_components/tagmanager/tagmanager.css?v={{ $version }}" rel="stylesheet"> <link href="{{ $U('/bower_components/tagmanager/tagmanager.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css?v={{ $version }}" rel="stylesheet"> <link href="{{ $U('/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="/components_unmanaged/noto-sans-v6-latin/noto-sans-v6-latin.css?v={{ $version }}" rel="stylesheet"> <link href="{{ $U('/components_unmanaged/noto-sans-v6-latin/noto-sans-v6-latin.css?v=') }}{{ $version }}" rel="stylesheet">
<link href="/css/grocy.css?v={{ $version }}" rel="stylesheet"> <link href="{{ $U('/css/grocy.css?v=') }}{{ $version }}" rel="stylesheet">
<script> <script>
var Grocy = { }; var Grocy = { };
Grocy.Components = { }; Grocy.Components = { };
Grocy.BaseUrl = '{{ $U('/') }}';
Grocy.LocalizationStrings = {!! json_encode($localizationStrings) !!}; Grocy.LocalizationStrings = {!! json_encode($localizationStrings) !!};
Grocy.ActiveNav = '@yield('activeNav', '')'; Grocy.ActiveNav = '@yield('activeNav', '')';
</script> </script>
@ -42,13 +43,13 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" href="/">grocy</a> <a class="navbar-brand" href="{{ $U('/') }}">grocy</a>
</div> </div>
<div id="navbar" class="navbar-collapse collapse"> <div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li> <li>
<a class="discrete-link logout-button" href="/logout"><i class="fa fa-sign-out fa-fw"></i>&nbsp;{{ $L('Logout') }}</a> <a class="discrete-link logout-button" href="{{ $U('/logout') }}"><i class="fa fa-sign-out fa-fw"></i>&nbsp;{{ $L('Logout') }}</a>
</li> </li>
</ul> </ul>
</div> </div>
@ -57,60 +58,60 @@
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li data-nav-for-page="stockoverview"> <li data-nav-for-page="stockoverview">
<a class="discrete-link" href="/stockoverview"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Stock overview') }}</a> <a class="discrete-link" href="{{ $U('/stockoverview') }}"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Stock overview') }}</a>
</li> </li>
<li data-nav-for-page="habitsoverview"> <li data-nav-for-page="habitsoverview">
<a class="discrete-link" href="/habitsoverview"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Habits overview') }}</a> <a class="discrete-link" href="{{ $U('/habitsoverview') }}"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Habits overview') }}</a>
</li> </li>
<li data-nav-for-page="batteriesoverview"> <li data-nav-for-page="batteriesoverview">
<a class="discrete-link" href="/batteriesoverview"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Batteries overview') }}</a> <a class="discrete-link" href="{{ $U('/batteriesoverview') }}"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Batteries overview') }}</a>
</li> </li>
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li class="disabled"><a href="#"><strong>{{ $L('Record data') }}</strong></a></li> <li class="disabled"><a href="#"><strong>{{ $L('Record data') }}</strong></a></li>
<li data-nav-for-page="purchase"> <li data-nav-for-page="purchase">
<a class="discrete-link" href="/purchase"><i class="fa fa-shopping-cart fa-fw"></i>&nbsp;{{ $L('Purchase') }}</a> <a class="discrete-link" href="{{ $U('/purchase') }}"><i class="fa fa-shopping-cart fa-fw"></i>&nbsp;{{ $L('Purchase') }}</a>
</li> </li>
<li data-nav-for-page="consume"> <li data-nav-for-page="consume">
<a class="discrete-link" href="/consume"><i class="fa fa-cutlery fa-fw"></i>&nbsp;{{ $L('Consume') }}</a> <a class="discrete-link" href="{{ $U('/consume') }}"><i class="fa fa-cutlery fa-fw"></i>&nbsp;{{ $L('Consume') }}</a>
</li> </li>
<li data-nav-for-page="shoppinglist"> <li data-nav-for-page="shoppinglist">
<a class="discrete-link" href="/shoppinglist"><i class="fa fa-shopping-bag fa-fw"></i>&nbsp;{{ $L('Shopping list') }}</a> <a class="discrete-link" href="{{ $U('/shoppinglist') }}"><i class="fa fa-shopping-bag fa-fw"></i>&nbsp;{{ $L('Shopping list') }}</a>
</li> </li>
<li data-nav-for-page="inventory"> <li data-nav-for-page="inventory">
<a class="discrete-link" href="/inventory"><i class="fa fa-list fa-fw"></i>&nbsp;{{ $L('Inventory') }}</a> <a class="discrete-link" href="{{ $U('/inventory') }}"><i class="fa fa-list fa-fw"></i>&nbsp;{{ $L('Inventory') }}</a>
</li> </li>
<li data-nav-for-page="habittracking"> <li data-nav-for-page="habittracking">
<a class="discrete-link" href="/habittracking"><i class="fa fa-play fa-fw"></i>&nbsp;{{ $L('Habit tracking') }}</a> <a class="discrete-link" href="{{ $U('/habittracking') }}"><i class="fa fa-play fa-fw"></i>&nbsp;{{ $L('Habit tracking') }}</a>
</li> </li>
<li data-nav-for-page="batterytracking"> <li data-nav-for-page="batterytracking">
<a class="discrete-link" href="/batterytracking"><i class="fa fa-fire fa-fw"></i>&nbsp;{{ $L('Battery tracking') }}</a> <a class="discrete-link" href="{{ $U('/batterytracking') }}"><i class="fa fa-fire fa-fw"></i>&nbsp;{{ $L('Battery tracking') }}</a>
</li> </li>
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li class="disabled"><a href="#"><strong>{{ $L('Manage master data') }}</strong></a></li> <li class="disabled"><a href="#"><strong>{{ $L('Manage master data') }}</strong></a></li>
<li data-nav-for-page="products"> <li data-nav-for-page="products">
<a class="discrete-link" href="/products"><i class="fa fa-product-hunt fa-fw"></i>&nbsp;{{ $L('Products') }}</a> <a class="discrete-link" href="{{ $U('/products') }}"><i class="fa fa-product-hunt fa-fw"></i>&nbsp;{{ $L('Products') }}</a>
</li> </li>
<li data-nav-for-page="locations"> <li data-nav-for-page="locations">
<a class="discrete-link" href="/locations"><i class="fa fa-map-marker fa-fw"></i>&nbsp;{{ $L('Locations') }}</a> <a class="discrete-link" href="{{ $U('/locations') }}"><i class="fa fa-map-marker fa-fw"></i>&nbsp;{{ $L('Locations') }}</a>
</li> </li>
<li data-nav-for-page="quantityunits"> <li data-nav-for-page="quantityunits">
<a class="discrete-link" href="/quantityunits"><i class="fa fa-balance-scale fa-fw"></i>&nbsp;{{ $L('Quantity units') }}</a> <a class="discrete-link" href="{{ $U('/quantityunits') }}"><i class="fa fa-balance-scale fa-fw"></i>&nbsp;{{ $L('Quantity units') }}</a>
</li> </li>
<li data-nav-for-page="habits"> <li data-nav-for-page="habits">
<a class="discrete-link" href="/habits"><i class="fa fa-refresh fa-fw"></i>&nbsp;{{ $L('Habits') }}</a> <a class="discrete-link" href="{{ $U('/habits') }}"><i class="fa fa-refresh fa-fw"></i>&nbsp;{{ $L('Habits') }}</a>
</li> </li>
<li data-nav-for-page="batteries"> <li data-nav-for-page="batteries">
<a class="discrete-link" href="/batteries"><i class="fa fa-battery-three-quarters fa-fw"></i>&nbsp;{{ $L('Batteries') }}</a> <a class="discrete-link" href="{{ $U('/batteries') }}"><i class="fa fa-battery-three-quarters fa-fw"></i>&nbsp;{{ $L('Batteries') }}</a>
</li> </li>
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li> <li>
<a class="discrete-link logout-button" href="/logout"><i class="fa fa-sign-out fa-fw"></i>&nbsp;{{ $L('Logout') }}</a> <a class="discrete-link logout-button" href="{{ $U('/logout') }}"><i class="fa fa-sign-out fa-fw"></i>&nbsp;{{ $L('Logout') }}</a>
</li> </li>
</ul> </ul>
@ -140,54 +141,54 @@
<ul class="nav nav-sidebar"> <ul class="nav nav-sidebar">
<li data-nav-for-page="stockoverview"> <li data-nav-for-page="stockoverview">
<a class="discrete-link" href="/stockoverview"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Stock overview') }}</a> <a class="discrete-link" href="{{ $U('/stockoverview') }}"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Stock overview') }}</a>
</li> </li>
<li data-nav-for-page="habitsoverview"> <li data-nav-for-page="habitsoverview">
<a class="discrete-link" href="/habitsoverview"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Habits overview') }}</a> <a class="discrete-link" href="{{ $U('/habitsoverview') }}"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Habits overview') }}</a>
</li> </li>
<li data-nav-for-page="batteriesoverview"> <li data-nav-for-page="batteriesoverview">
<a class="discrete-link" href="/batteriesoverview"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Batteries overview') }}</a> <a class="discrete-link" href="{{ $U('/batteriesoverview') }}"><i class="fa fa-tachometer fa-fw"></i>&nbsp;{{ $L('Batteries overview') }}</a>
</li> </li>
</ul> </ul>
<ul class="nav nav-sidebar"> <ul class="nav nav-sidebar">
<li class="disabled"><a href="#"><strong>{{ $L('Record data') }}</strong></a></li> <li class="disabled"><a href="#"><strong>{{ $L('Record data') }}</strong></a></li>
<li data-nav-for-page="purchase"> <li data-nav-for-page="purchase">
<a class="discrete-link" href="/purchase"><i class="fa fa-shopping-cart fa-fw"></i>&nbsp;{{ $L('Purchase') }}</a> <a class="discrete-link" href="{{ $U('/purchase') }}"><i class="fa fa-shopping-cart fa-fw"></i>&nbsp;{{ $L('Purchase') }}</a>
</li> </li>
<li data-nav-for-page="consume"> <li data-nav-for-page="consume">
<a class="discrete-link" href="/consume"><i class="fa fa-cutlery fa-fw"></i>&nbsp;{{ $L('Consume') }}</a> <a class="discrete-link" href="{{ $U('/consume') }}"><i class="fa fa-cutlery fa-fw"></i>&nbsp;{{ $L('Consume') }}</a>
</li> </li>
<li data-nav-for-page="shoppinglist"> <li data-nav-for-page="shoppinglist">
<a class="discrete-link" href="/shoppinglist"><i class="fa fa-shopping-bag fa-fw"></i>&nbsp;{{ $L('Shopping list') }}</a> <a class="discrete-link" href="{{ $U('/shoppinglist') }}"><i class="fa fa-shopping-bag fa-fw"></i>&nbsp;{{ $L('Shopping list') }}</a>
</li> </li>
<li data-nav-for-page="inventory"> <li data-nav-for-page="inventory">
<a class="discrete-link" href="/inventory"><i class="fa fa-list fa-fw"></i>&nbsp;{{ $L('Inventory') }}</a> <a class="discrete-link" href="{{ $U('/inventory') }}"><i class="fa fa-list fa-fw"></i>&nbsp;{{ $L('Inventory') }}</a>
</li> </li>
<li data-nav-for-page="habittracking"> <li data-nav-for-page="habittracking">
<a class="discrete-link" href="/habittracking"><i class="fa fa-play fa-fw"></i>&nbsp;{{ $L('Habit tracking') }}</a> <a class="discrete-link" href="{{ $U('/habittracking') }}"><i class="fa fa-play fa-fw"></i>&nbsp;{{ $L('Habit tracking') }}</a>
</li> </li>
<li data-nav-for-page="batterytracking"> <li data-nav-for-page="batterytracking">
<a class="discrete-link" href="/batterytracking"><i class="fa fa-fire fa-fw"></i>&nbsp;{{ $L('Battery tracking') }}</a> <a class="discrete-link" href="{{ $U('/batterytracking') }}"><i class="fa fa-fire fa-fw"></i>&nbsp;{{ $L('Battery tracking') }}</a>
</li> </li>
</ul> </ul>
<ul class="nav nav-sidebar"> <ul class="nav nav-sidebar">
<li class="disabled"><a href="#"><strong>{{ $L('Manage master data') }}</strong></a></li> <li class="disabled"><a href="#"><strong>{{ $L('Manage master data') }}</strong></a></li>
<li data-nav-for-page="products"> <li data-nav-for-page="products">
<a class="discrete-link" href="/products"><i class="fa fa-product-hunt fa-fw"></i>&nbsp;{{ $L('Products') }}</a> <a class="discrete-link" href="{{ $U('/products') }}"><i class="fa fa-product-hunt fa-fw"></i>&nbsp;{{ $L('Products') }}</a>
</li> </li>
<li data-nav-for-page="locations"> <li data-nav-for-page="locations">
<a class="discrete-link" href="/locations"><i class="fa fa-map-marker fa-fw"></i>&nbsp;{{ $L('Locations') }}</a> <a class="discrete-link" href="{{ $U('/locations') }}"><i class="fa fa-map-marker fa-fw"></i>&nbsp;{{ $L('Locations') }}</a>
</li> </li>
<li data-nav-for-page="quantityunits"> <li data-nav-for-page="quantityunits">
<a class="discrete-link" href="/quantityunits"><i class="fa fa-balance-scale fa-fw"></i>&nbsp;{{ $L('Quantity units') }}</a> <a class="discrete-link" href="{{ $U('/quantityunits') }}"><i class="fa fa-balance-scale fa-fw"></i>&nbsp;{{ $L('Quantity units') }}</a>
</li> </li>
<li data-nav-for-page="habits"> <li data-nav-for-page="habits">
<a class="discrete-link" href="/habits"><i class="fa fa-refresh fa-fw"></i>&nbsp;{{ $L('Habits') }}</a> <a class="discrete-link" href="{{ $U('/habits') }}"><i class="fa fa-refresh fa-fw"></i>&nbsp;{{ $L('Habits') }}</a>
</li> </li>
<li data-nav-for-page="batteries"> <li data-nav-for-page="batteries">
<a class="discrete-link" href="/batteries"><i class="fa fa-battery-three-quarters fa-fw"></i>&nbsp;{{ $L('Batteries') }}</a> <a class="discrete-link" href="{{ $U('/batteries') }}"><i class="fa fa-battery-three-quarters fa-fw"></i>&nbsp;{{ $L('Batteries') }}</a>
</li> </li>
</ul> </ul>
@ -213,29 +214,29 @@
</div> </div>
</div> </div>
<script src="/bower_components/jquery/dist/jquery.min.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/jquery/dist/jquery.min.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/bootstrap/dist/js/bootstrap.min.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/bootbox/bootbox.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/bootbox/bootbox.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/jquery.serializeJSON/jquery.serializejson.min.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/jquery.serializeJSON/jquery.serializejson.min.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js?v=') }}{{ $version }}"></script>
@if(!empty($L('bootstrap_datepicker_locale')))<script src="/bower_components/bootstrap-datepicker/dist/locales/bootstrap-datepicker.{{ $L('bootstrap_datepicker_locale') }}.min.js?v={{ $version }}"></script>@endif @if(!empty($L('bootstrap_datepicker_locale')))<script src="{{ $U('/bower_components') }}/bootstrap-datepicker/dist/locales/bootstrap-datepicker.{{ $L('bootstrap_datepicker_locale') }}.min.js?v={{ $version }}"></script>@endif
<script src="/bower_components/moment/min/moment.min.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/moment/min/moment.min.js?v=') }}{{ $version }}"></script>
@if(!empty($L('moment_locale')))<script src="/bower_components/moment/locale/{{ $L('moment_locale') }}.js?v={{ $version }}"></script>@endif @if(!empty($L('moment_locale')))<script src="{{ $U('/bower_components') }}/moment/locale/{{ $L('moment_locale') }}.js?v={{ $version }}"></script>@endif
<script src="/bower_components/bootstrap-validator/dist/validator.min.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/bootstrap-validator/dist/validator.min.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/bootstrap-combobox/js/bootstrap-combobox.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/bootstrap-combobox/js/bootstrap-combobox.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/datatables.net/js/jquery.dataTables.min.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/datatables.net/js/jquery.dataTables.min.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/datatables.net-responsive/js/dataTables.responsive.min.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/datatables.net-responsive/js/dataTables.responsive.min.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/datatables.net-responsive-bs/js/responsive.bootstrap.min.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/datatables.net-responsive-bs/js/responsive.bootstrap.min.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/jquery-timeago/jquery.timeago.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/jquery-timeago/jquery.timeago.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/jquery-timeago/locales/jquery.timeago.{{ $L('timeago_locale') }}.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components') }}/jquery-timeago/locales/jquery.timeago.{{ $L('timeago_locale') }}.js?v={{ $version }}"></script>
<script src="/bower_components/toastr/toastr.min.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/toastr/toastr.min.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/tagmanager/tagmanager.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/tagmanager/tagmanager.js?v=') }}{{ $version }}"></script>
<script src="/bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js?v={{ $version }}"></script> <script src="{{ $U('/bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js?v=') }}{{ $version }}"></script>
<script src="/js/extensions.js?v={{ $version }}"></script> <script src="{{ $U('/js/extensions.js?v=') }}{{ $version }}"></script>
<script src="/js/grocy.js?v={{ $version }}"></script> <script src="{{ $U('/js/grocy.js?v=') }}{{ $version }}"></script>
<script src="/viewjs/@yield('viewJsName').js"></script> <script src="{{ $U('/viewjs') }}/@yield('viewJsName').js?v={{ $version }}"></script>
@stack('componentScripts') @stack('componentScripts')
@if(file_exists(__DIR__ . '/../../data/add_before_end_body.html')) @if(file_exists(__DIR__ . '/../../data/add_before_end_body.html'))