mirror of
https://github.com/grocy/grocy.git
synced 2025-08-20 04:12:59 +00:00
Added a "recipe gallery" view (closes #147)
This commit is contained in:
@@ -341,5 +341,7 @@ return array(
|
||||
'This is useful e.g. for flour in jars - on purchase/consume/inventory you always weigh the whole jar, the amount to be posted is then automatically calculated based on what is in stock and the tare weight defined below' => 'This is useful e.g. for flour in jars - on purchase/consume/inventory you always weigh the whole jar, the amount to be posted is then automatically calculated based on what is in stock and the tare weight defined below',
|
||||
'Tare weight' => 'Tare weight',
|
||||
'Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated' => 'Tare weight handling enabled - please weigh the whole container, the amount to be posted will be automatically calculcated',
|
||||
'You have to select a location' => 'You have to select a location'
|
||||
'You have to select a location' => 'You have to select a location',
|
||||
'List' => 'List',
|
||||
'Gallery' => 'Gallery'
|
||||
);
|
||||
|
@@ -26,12 +26,29 @@
|
||||
$('#recipes-table tbody').removeClass("d-none");
|
||||
recipesTables.columns.adjust().draw();
|
||||
|
||||
var rowSelect = GetUriParam("row");
|
||||
if (typeof rowSelect !== "undefined")
|
||||
if ((typeof tab !== "undefined" && tab === "gallery") || window.localStorage.getItem("recipes_last_tab_id") == "gallery-tab")
|
||||
{
|
||||
recipesTables.row(rowSelect).select();
|
||||
$(".nav-tabs a[href='#gallery']").tab("show");
|
||||
}
|
||||
|
||||
var recipe = GetUriParam("recipe");
|
||||
if (typeof recipe !== "undefined")
|
||||
{
|
||||
$("#recipes-table tr").removeClass("selected");
|
||||
var rowId = "#recipe-row-" + recipe;
|
||||
$(rowId).addClass("selected")
|
||||
|
||||
var cardId = "#recipe-card-" + recipe;
|
||||
$(cardId).addClass("bg-primary").addClass("text-white");
|
||||
$(cardId)[0].scrollIntoView();
|
||||
}
|
||||
|
||||
$("a[data-toggle='tab']").on("shown.bs.tab", function(e)
|
||||
{
|
||||
var tabId = $(e.target).attr("id");
|
||||
window.localStorage.setItem("recipes_last_tab_id", tabId);
|
||||
});
|
||||
|
||||
$("#search").on("keyup", function()
|
||||
{
|
||||
var value = $(this).val();
|
||||
@@ -169,10 +186,17 @@ recipesTables.on('select', function(e, dt, type, indexes)
|
||||
if (type === 'row')
|
||||
{
|
||||
var selectedRecipeId = $(recipesTables.row(indexes[0]).node()).data("recipe-id");
|
||||
window.location.href = U('/recipes?recipe=' + selectedRecipeId.toString() + "&row=" + indexes[0].toString());
|
||||
window.location.href = U('/recipes?recipe=' + selectedRecipeId.toString());
|
||||
}
|
||||
});
|
||||
|
||||
$(".recipe-gallery-item").on("click", function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
window.location.href = U('/recipes?tab=gallery&recipe=' + $(this).data("recipe-id"));
|
||||
});
|
||||
|
||||
$("#selectedRecipeToggleFullscreenButton").on('click', function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
@@ -71,12 +71,12 @@ class DemoDataGeneratorService extends BaseService
|
||||
INSERT INTO shopping_list (product_id, amount) VALUES (20, 1);
|
||||
INSERT INTO shopping_list (product_id, amount) VALUES (17, 1);
|
||||
|
||||
INSERT INTO recipes (name, description) VALUES ('{$localizationService->LocalizeForSqlString('Pizza')}', '{$loremIpsumWithHtmlFormattings}'); --1
|
||||
INSERT INTO recipes (name, description) VALUES ('{$localizationService->LocalizeForSqlString('Spaghetti bolognese')}', '{$loremIpsumWithHtmlFormattings}'); --2
|
||||
INSERT INTO recipes (name, description) VALUES ('{$localizationService->LocalizeForSqlString('Sandwiches')}', '{$loremIpsumWithHtmlFormattings}'); --3
|
||||
INSERT INTO recipes (name, description) VALUES ('{$localizationService->LocalizeForSqlString('Pancakes')}', '{$loremIpsumWithHtmlFormattings}'); --4
|
||||
INSERT INTO recipes (name, description) VALUES ('{$localizationService->LocalizeForSqlString('Chocolate sauce')}', '{$loremIpsumWithHtmlFormattings}'); --5
|
||||
INSERT INTO recipes (name, description) VALUES ('{$localizationService->LocalizeForSqlString('Pancakes')} / {$localizationService->LocalizeForSqlString('Chocolate sauce')}', '{$loremIpsumWithHtmlFormattings}'); --6
|
||||
INSERT INTO recipes (name, description, picture_file_name) VALUES ('{$localizationService->LocalizeForSqlString('Pizza')}', '{$loremIpsumWithHtmlFormattings}', 'pizza.jpg'); --1
|
||||
INSERT INTO recipes (name, description, picture_file_name) VALUES ('{$localizationService->LocalizeForSqlString('Spaghetti bolognese')}', '{$loremIpsumWithHtmlFormattings}', 'spaghetti.jpg'); --2
|
||||
INSERT INTO recipes (name, description, picture_file_name) VALUES ('{$localizationService->LocalizeForSqlString('Sandwiches')}', '{$loremIpsumWithHtmlFormattings}', 'sandwiches.jpg'); --3
|
||||
INSERT INTO recipes (name, description, picture_file_name) VALUES ('{$localizationService->LocalizeForSqlString('Pancakes')}', '{$loremIpsumWithHtmlFormattings}', 'pancakes.jpg'); --4
|
||||
INSERT INTO recipes (name, description, picture_file_name) VALUES ('{$localizationService->LocalizeForSqlString('Chocolate sauce')}', '{$loremIpsumWithHtmlFormattings}', 'chocolate_sauce.jpg'); --5
|
||||
INSERT INTO recipes (name, description, picture_file_name) VALUES ('{$localizationService->LocalizeForSqlString('Pancakes')} / {$localizationService->LocalizeForSqlString('Chocolate sauce')}', '{$loremIpsumWithHtmlFormattings}', 'pancakes_chocolate_sauce.jpg'); --6
|
||||
|
||||
INSERT INTO recipes_pos (recipe_id, product_id, amount, ingredient_group) VALUES (1, 16, 1, '{$localizationService->LocalizeForSqlString('Bottom')}');
|
||||
INSERT INTO recipes_pos (recipe_id, product_id, amount, ingredient_group) VALUES (1, 17, 1, '{$localizationService->LocalizeForSqlString('Topping')}');
|
||||
@@ -225,9 +225,11 @@ class DemoDataGeneratorService extends BaseService
|
||||
// Download demo storage data
|
||||
$productPicturesFolder = GROCY_DATAPATH . '/storage/productpictures';
|
||||
$equipmentManualsFolder = GROCY_DATAPATH . '/storage/equipmentmanuals';
|
||||
$recipePicturesFolder = GROCY_DATAPATH . '/storage/recipepictures';
|
||||
mkdir(GROCY_DATAPATH . '/storage');
|
||||
mkdir(GROCY_DATAPATH . '/storage/productpictures');
|
||||
mkdir(GROCY_DATAPATH . '/storage/equipmentmanuals');
|
||||
mkdir($productPicturesFolder);
|
||||
mkdir($equipmentManualsFolder);
|
||||
mkdir($recipePicturesFolder);
|
||||
$sslOptions = array(
|
||||
'ssl' => array(
|
||||
'verify_peer' => false,
|
||||
@@ -240,6 +242,12 @@ class DemoDataGeneratorService extends BaseService
|
||||
file_put_contents("$productPicturesFolder/paprika.jpg", file_get_contents('https://releases.grocy.info/demoresources/paprika.jpg', false, stream_context_create($sslOptions)));
|
||||
file_put_contents("$productPicturesFolder/tomato.jpg", file_get_contents('https://releases.grocy.info/demoresources/tomato.jpg', false, stream_context_create($sslOptions)));
|
||||
file_put_contents("$equipmentManualsFolder/loremipsum.pdf", file_get_contents('https://releases.grocy.info/demoresources/loremipsum.pdf', false, stream_context_create($sslOptions)));
|
||||
file_put_contents("$recipePicturesFolder/pizza.jpg", file_get_contents('https://releases.grocy.info/demoresources/pizza.jpg', false, stream_context_create($sslOptions)));
|
||||
file_put_contents("$recipePicturesFolder/sandwiches.jpg", file_get_contents('https://releases.grocy.info/demoresources/sandwiches.jpg', false, stream_context_create($sslOptions)));
|
||||
file_put_contents("$recipePicturesFolder/pancakes.jpg", file_get_contents('https://releases.grocy.info/demoresources/pancakes.jpg', false, stream_context_create($sslOptions)));
|
||||
file_put_contents("$recipePicturesFolder/spaghetti.jpg", file_get_contents('https://releases.grocy.info/demoresources/spaghetti.jpg', false, stream_context_create($sslOptions)));
|
||||
file_put_contents("$recipePicturesFolder/chocolate_sauce.jpg", file_get_contents('https://releases.grocy.info/demoresources/chocolate_sauce.jpg', false, stream_context_create($sslOptions)));
|
||||
file_put_contents("$recipePicturesFolder/pancakes_chocolate_sauce.jpg", file_get_contents('https://releases.grocy.info/demoresources/pancakes_chocolate_sauce.jpg', false, stream_context_create($sslOptions)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,35 +18,73 @@
|
||||
<label for="search">{{ $L('Search') }}</label> <i class="fas fa-search"></i>
|
||||
<input type="text" class="form-control" id="search">
|
||||
|
||||
<table id="recipes-table" class="table table-striped dt-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $L('Name') }}</th>
|
||||
<th class="fit-content text-right">{{ $L('Servings') }}</th>
|
||||
<th>{{ $L('Requirements fulfilled') }}</th>
|
||||
<th class="d-none">Hidden status for sorting of "Requirements fulfilled" column</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($recipes as $recipe)
|
||||
<tr data-recipe-id="{{ $recipe->id }}">
|
||||
<td>
|
||||
{{ $recipe->name }}
|
||||
</td>
|
||||
<td class="fit-content text-right">
|
||||
{{ $recipe->desired_servings }}
|
||||
</td>
|
||||
<td>
|
||||
@if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif
|
||||
<span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled == 1){{ $L('Enough in stock') }}@elseif(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1){{ $L('Not enough in stock, #1 ingredients missing but already on the shopping list', FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->missing_products_count) }}@else{{ $L('Not enough in stock, #1 ingredients missing', FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->missing_products_count) }}@endif</span>
|
||||
</td>
|
||||
<td class="d-none">
|
||||
{{ FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->missing_products_count }}
|
||||
</td>
|
||||
</tr>
|
||||
<ul class="nav nav-tabs mt-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="list-tab" data-toggle="tab" href="#list">{{ $L('List') }}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="gallery-tab" data-toggle="tab" href="#gallery">{{ $L('Gallery') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div class="tab-pane show active" id="list">
|
||||
<table id="recipes-table" class="table table-striped dt-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $L('Name') }}</th>
|
||||
<th class="fit-content text-right">{{ $L('Servings') }}</th>
|
||||
<th>{{ $L('Requirements fulfilled') }}</th>
|
||||
<th class="d-none">Hidden status for sorting of "Requirements fulfilled" column</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="d-none">
|
||||
@foreach($recipes as $recipe)
|
||||
<tr id="recipe-row-{{ $recipe->id }}" data-recipe-id="{{ $recipe->id }}">
|
||||
<td>
|
||||
{{ $recipe->name }}
|
||||
</td>
|
||||
<td class="fit-content text-right">
|
||||
{{ $recipe->desired_servings }}
|
||||
</td>
|
||||
<td>
|
||||
@if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif
|
||||
<span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled == 1){{ $L('Enough in stock') }}@elseif(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1){{ $L('Not enough in stock, #1 ingredients missing but already on the shopping list', FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->missing_products_count) }}@else{{ $L('Not enough in stock, #1 ingredients missing', FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->missing_products_count) }}@endif</span>
|
||||
</td>
|
||||
<td class="d-none">
|
||||
{{ FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->missing_products_count }}
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane show" id="gallery">
|
||||
<div class="row no-gutters">
|
||||
@foreach($recipes as $recipe)
|
||||
<div class="col-6">
|
||||
<a class="discrete-link recipe-gallery-item" data-recipe-id="{{ $recipe->id }}" href="#">
|
||||
<div id="recipe-card-{{ $recipe->id }}" class="card border-white mb-0">
|
||||
@if(!empty($recipe->picture_file_name))
|
||||
<img src="{{ $U('/api/files/recipepictures/' . base64_encode($recipe->picture_file_name)) }}" class="img-fluid">
|
||||
@endif
|
||||
<div class="card-body text-center">
|
||||
<h5 class="card-title mb-1">{{ $recipe->name }}</h5>
|
||||
<p class="card-text">
|
||||
@if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled == 1)<i class="fas fa-check text-success"></i>@elseif(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1)<i class="fas fa-exclamation text-warning"></i>@else<i class="fas fa-times text-danger"></i>@endif
|
||||
<span class="timeago-contextual">@if(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled == 1){{ $L('Enough in stock') }}@elseif(FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->need_fulfilled_with_shopping_list == 1){{ $L('Not enough in stock, #1 ingredients missing but already on the shopping list', FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->missing_products_count) }}@else{{ $L('Not enough in stock, #1 ingredients missing', FindObjectInArrayByPropertyValue($recipesSumFulfillment, 'recipe_id', $recipe->id)->missing_products_count) }}@endif</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($selectedRecipe !== null)
|
||||
@@ -88,7 +126,7 @@
|
||||
<span class="small text-muted">{{ $L('Based on the prices of the last purchase per product') }}</span>
|
||||
</label>
|
||||
<p class="font-weight-bold font-italic">
|
||||
<span class="locale-number-format" data-format="currency">{{ $totalRecipeCosts }}</span> {{ GROCY_CURRENCY }}
|
||||
<span class="locale-number-format" data-format="currency">{{ $totalRecipeCosts }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -101,7 +139,7 @@
|
||||
</div>
|
||||
|
||||
@if(!empty($selectedRecipeSubRecipe->picture_file_name))
|
||||
<p><img src="{{ $U('/api/files/recipepictures/' . base64_encode($selectedRecipeSubRecipe->picture_file_name)) }}" class="img-fluid"></p>
|
||||
<p class="w-75 mx-auto"><img src="{{ $U('/api/files/recipepictures/' . base64_encode($selectedRecipeSubRecipe->picture_file_name)) }}" class="img-fluid img-thumbnail"></p>
|
||||
@endif
|
||||
|
||||
@php $selectedRecipeSubRecipePositionsFiltered = FindAllObjectsInArrayByPropertyValue($selectedRecipeSubRecipesPositions, 'recipe_id', $selectedRecipeSubRecipe->id); @endphp
|
||||
@@ -137,7 +175,7 @@
|
||||
|
||||
<!-- Selected recipe -->
|
||||
@if(!empty($selectedRecipe->picture_file_name))
|
||||
<p><img src="{{ $U('/api/files/recipepictures/' . base64_encode($selectedRecipe->picture_file_name)) }}" class="img-fluid"></p>
|
||||
<p class="w-75 mx-auto"><img src="{{ $U('/api/files/recipepictures/' . base64_encode($selectedRecipe->picture_file_name)) }}" class="img-fluid img-thumbnail"></p>
|
||||
@endif
|
||||
|
||||
@if($selectedRecipePositions->count() > 0)
|
||||
|
Reference in New Issue
Block a user