diff --git a/controllers/RecipesController.php b/controllers/RecipesController.php index 8e0709f1..5e2a7540 100644 --- a/controllers/RecipesController.php +++ b/controllers/RecipesController.php @@ -17,27 +17,41 @@ class RecipesController extends BaseController $recipesResolved = $this->getRecipesService()->GetRecipesResolved(); $selectedRecipe = null; - $selectedRecipePositionsResolved = null; + if (isset($request->getQueryParams()['recipe'])) { $selectedRecipe = $this->getDatabase()->recipes($request->getQueryParams()['recipe']); - $selectedRecipePositionsResolved = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id = :1 AND is_nested_recipe_pos = 0', $request->getQueryParams()['recipe'])->orderBy('ingredient_group', 'ASC', 'product_group', 'ASC'); } else { foreach ($recipes as $recipe) { $selectedRecipe = $recipe; - $selectedRecipePositionsResolved = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id = :1 AND is_nested_recipe_pos = 0', $recipe->id)->orderBy('ingredient_group', 'ASC', 'product_group', 'ASC'); - break; + break; } } + $selectedRecipePositionsResolved = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id = :1 AND is_nested_recipe_pos = 0', $selectedRecipe->id)->orderBy('ingredient_group', 'ASC', 'product_group', 'ASC'); + + $renderArray = [ + 'recipes' => $recipes, + 'recipesResolved' => $recipesResolved, + 'recipePositionsResolved' => $this->getDatabase()->recipes_pos_resolved()->where('recipe_type', RecipesService::RECIPE_TYPE_NORMAL), + 'selectedRecipe' => $selectedRecipe, + 'selectedRecipePositionsResolved' => $selectedRecipePositionsResolved, + 'products' => $this->getDatabase()->products(), + 'quantityUnits' => $this->getDatabase()->quantity_units(), + 'userfields' => $this->getUserfieldsService()->GetFields('recipes'), + 'userfieldValues' => $this->getUserfieldsService()->GetAllValues('recipes'), + 'quantityUnitConversionsResolved' => $this->getDatabase()->quantity_unit_conversions_resolved(), + 'selectedRecipeTotalCosts' => FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $selectedRecipe->id)->costs, + 'selectedRecipeTotalCalories' => FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $selectedRecipe->id)->calories + ]; + if ($selectedRecipe) { $selectedRecipeSubRecipes = $this->getDatabase()->recipes()->where('id IN (SELECT includes_recipe_id FROM recipes_nestings_resolved WHERE recipe_id = :1 AND includes_recipe_id != :1)', $selectedRecipe->id)->orderBy('name')->fetchAll(); - $selectedRecipeSubRecipesPositions = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id = :1', $selectedRecipe->id)->orderBy('ingredient_group', 'ASC', 'product_group', 'ASC')->fetchAll(); - + $includedRecipeIdsAbsolute = array(); $includedRecipeIdsAbsolute[] = $selectedRecipe->id; foreach($selectedRecipeSubRecipes as $subRecipe) @@ -45,38 +59,15 @@ class RecipesController extends BaseController $includedRecipeIdsAbsolute[] = $subRecipe->id; } - $renderArray = [ - 'recipes' => $recipes, - 'recipesResolved' => $recipesResolved, - 'recipePositionsResolved' => $this->getDatabase()->recipes_pos_resolved()->where('recipe_type', RecipesService::RECIPE_TYPE_NORMAL), - 'selectedRecipe' => $selectedRecipe, - 'selectedRecipePositionsResolved' => $selectedRecipePositionsResolved, - 'products' => $this->getDatabase()->products(), - 'quantityUnits' => $this->getDatabase()->quantity_units(), - 'selectedRecipeSubRecipes' => $selectedRecipeSubRecipes, - 'selectedRecipeSubRecipesPositions' => $selectedRecipeSubRecipesPositions, - 'includedRecipeIdsAbsolute' => $includedRecipeIdsAbsolute, - 'selectedRecipeTotalCosts' => FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $selectedRecipe->id)->costs, - 'selectedRecipeTotalCalories' => FindObjectInArrayByPropertyValue($recipesResolved, 'recipe_id', $selectedRecipe->id)->calories, - 'userfields' => $this->getUserfieldsService()->GetFields('recipes'), - 'userfieldValues' => $this->getUserfieldsService()->GetAllValues('recipes'), - 'quantityUnitConversionsResolved' => $this->getDatabase()->quantity_unit_conversions_resolved() - ]; - } - else - { - $renderArray = [ - 'recipes' => $recipes, - 'recipesResolved' => $recipesResolved, - 'recipePositionsResolved' => $this->getDatabase()->recipes_pos_resolved()->where('recipe_type', RecipesService::RECIPE_TYPE_NORMAL), - 'selectedRecipe' => $selectedRecipe, - 'selectedRecipePositionsResolved' => $selectedRecipePositionsResolved, - 'products' => $this->getDatabase()->products(), - 'quantityUnits' => $this->getDatabase()->quantity_units(), - 'userfields' => $this->getUserfieldsService()->GetFields('recipes'), - 'userfieldValues' => $this->getUserfieldsService()->GetAllValues('recipes'), - 'quantityUnitConversionsResolved' => $this->getDatabase()->quantity_unit_conversions_resolved() - ]; + $allRecipePositions = array(); + foreach($includedRecipeIdsAbsolute as $id) + { + $allRecipePositions[$id] = $this->getDatabase()->recipes_pos_resolved()->where('recipe_id = :1 AND is_nested_recipe_pos = 0', $id)->orderBy('ingredient_group', 'ASC', 'product_group', 'ASC'); + } + + $renderArray['selectedRecipeSubRecipes'] = $selectedRecipeSubRecipes; + $renderArray['includedRecipeIdsAbsolute'] = $includedRecipeIdsAbsolute; + $renderArray['allRecipePositions'] = $allRecipePositions; } return $this->renderPage($response, 'recipes', $renderArray); @@ -85,20 +76,11 @@ class RecipesController extends BaseController public function RecipeEditForm(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { $recipeId = $args['recipeId']; - if ($recipeId == 'new') - { - $newRecipe = $this->getDatabase()->recipes()->where('type', RecipesService::RECIPE_TYPE_NORMAL)->createRow(array( - 'name' => $this->getLocalizationService()->__t('New recipe') - )); - $newRecipe->save(); - - $recipeId = $this->getDatabase()->lastInsertId(); - } return $this->renderPage($response, 'recipeform', [ 'recipe' => $this->getDatabase()->recipes($recipeId), 'recipePositions' => $this->getDatabase()->recipes_pos()->where('recipe_id', $recipeId), - 'mode' => 'edit', + 'mode' => $recipeId == 'new' ? "create" : "edit", 'products' => $this->getDatabase()->products()->orderBy('name'), 'quantityunits' => $this->getDatabase()->quantity_units(), 'recipePositionsResolved' => $this->getRecipesService()->GetRecipesPosResolved(), diff --git a/public/css/grocy.css b/public/css/grocy.css index 2c6f0e2e..295aea0b 100644 --- a/public/css/grocy.css +++ b/public/css/grocy.css @@ -31,22 +31,24 @@ a.discrete-link:focus { text-decoration: none !important; } -.card { - border: 2px solid; - border-color: #d6d6d6; - border-radius: 0; +.grocy-card .card-header { + background-color: inherit; } -.card-header { - background-color: #e5e5e5; +.grocy-card .card-title { + color: #495157; } -.card-header:first-child { - border-radius: 0; +.grocy-card .card-icons a { + font-size: 18px; + color: #495157; + padding: 2px 4px; + text-decoration: none; + text-align: center; } -.card-body { - flex-grow: 0; +.grocy-card .card-icons i { + width: 18px; } .content-text .invalid-feedback { @@ -86,6 +88,11 @@ a.discrete-link:focus { display: none; } +.fullscreen.card .card-img-top { + width: 50%; + margin: 0 auto; +} + .form-check-input.is-valid ~ .form-check-label, .was-validated .form-check-input:valid ~ .form-check-label { color: inherit; @@ -505,21 +512,72 @@ canvas.drawingBuffer { height: 1.25rem; } -.recipe-card-name { - font-size: 16px; - text-align: center; - width: 100%; -} - -.recipe-expand { - right: 1.25rem; - top: .75rem; -} - .recipe-servings-input { width: 125px; } .fc-event{ cursor: pointer; -} \ No newline at end of file +} + +.grocy-tabs .nav-link { + color: #767676; + text-transform: uppercase; +} + + .grocy-tabs .nav-link { + border: 2px solid transparent; + transition: border-bottom-color ease-in 0.1s; +} + +.grocy-tabs .nav-link.active { + border: 2px solid transparent; + border-bottom-color: #0b024c !important; +} + +.grocy-tabs .nav-link:hover { + border: 2px solid transparent; + border-bottom-color: #0b024c7d; +} + +.grocy-tabs .nav-item { + margin-bottom: -2px; +} + +.grocy-tabs.card-header-tabs { + margin-bottom: -.65rem; +} + +.grocy-tabs.tab-content > .active { + display: flex; + flex-direction: column; +} + +.recipe-card img { + height: 14rem; + width: 100%; + object-fit: cover; +} + +@media print { + .grocy-tabs.print.tab-content > .tab-pane { + display: flex !important; + flex-direction: column !important; + opacity: 1 !important; + visibility: visible !important; + overflow: visible !important; + width: auto !important; + height: auto !important; + } + + .grocy-tabs.break > .tab-pane { + page-break-after: always; + } + + .print-view { + width: 100%; + max-width: 100%; + flex-basis: 100%; + } +} + diff --git a/public/viewjs/recipeform.js b/public/viewjs/recipeform.js index ae2d4e33..e9da2674 100644 --- a/public/viewjs/recipeform.js +++ b/public/viewjs/recipeform.js @@ -1,4 +1,30 @@ -$('#save-recipe-button').on('click', function(e) +function saveRecipePicture(result, location) +{ + $recipeId = Grocy.EditObjectId || result.created_object_id; + Grocy.Components.UserfieldsForm.Save(() => + { + if (jsonData.hasOwnProperty("picture_file_name") && !Grocy.DeleteRecipePictureOnSave) + { + Grocy.Api.UploadFile($("#recipe-picture")[0].files[0], 'recipepictures', jsonData.picture_file_name, + (result) => + { + window.location.href = U(location + $recipeId); + }, + (xhr) => + { + Grocy.FrontendHelpers.EndUiBusy("recipe-form"); + Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response) + } + ); + } + else + { + window.location.href = U(location + $recipeId); + } + }); +} + +$('.save-recipe').on('click', function(e) { e.preventDefault(); @@ -11,6 +37,15 @@ jsonData.picture_file_name = someRandomStuff + $("#recipe-picture")[0].files[0].name; } + const location = $(e.currentTarget).attr('data-location') == 'return' ? '/recipes?recipe=' : '/recipe/'; + + if(Grocy.EditMode == 'create') { + console.log(jsonData); + Grocy.Api.Post('objects/recipes', jsonData, + (result) => saveRecipePicture(result, location)); + return; + } + if (Grocy.DeleteRecipePictureOnSave) { jsonData.picture_file_name = null; @@ -29,30 +64,7 @@ } Grocy.Api.Put('objects/recipes/' + Grocy.EditObjectId, jsonData, - function(result) - { - Grocy.Components.UserfieldsForm.Save(function() - { - if (jsonData.hasOwnProperty("picture_file_name") && !Grocy.DeleteRecipePictureOnSave) - { - Grocy.Api.UploadFile($("#recipe-picture")[0].files[0], 'recipepictures', jsonData.picture_file_name, - function (result) - { - window.location.href = U('/recipes?recipe=' + Grocy.EditObjectId); - }, - function (xhr) - { - Grocy.FrontendHelpers.EndUiBusy("recipe-form"); - Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response) - } - ); - } - else - { - window.location.href = U('/recipes?recipe=' + Grocy.EditObjectId); - } - }); - }, + (result) => saveRecipePicture(result, location), function(xhr) { Grocy.FrontendHelpers.EndUiBusy("recipe-form"); @@ -354,7 +366,7 @@ $(window).on("message", function(e) Grocy.Api.Put('objects/recipes/' + Grocy.EditObjectId, $('#recipe-form').serializeJSON(), function(result) { - window.location.href = U('/recipe/' + Grocy.EditObjectId); + // window.location.href = U('/recipe/' + Grocy.EditObjectId); }, function(xhr) { @@ -364,35 +376,35 @@ $(window).on("message", function(e) } }); -Grocy.Components.RecipePicker.GetPicker().on('change', function (e) -{ - var value = Grocy.Components.RecipePicker.GetValue(); - if (value.toString().isEmpty()) - { - return; - } +// Grocy.Components.RecipePicker.GetPicker().on('change', function (e) +// { +// var value = Grocy.Components.RecipePicker.GetValue(); +// if (value.toString().isEmpty()) +// { +// return; +// } - Grocy.Api.Get('objects/recipes/' + value, - function(recipe) - { - $("#includes_servings").val(recipe.servings); - }, - function(xhr) - { - console.error(xhr); - } - ); -}); +// Grocy.Api.Get('objects/recipes/' + value, +// function(recipe) +// { +// $("#includes_servings").val(recipe.servings); +// }, +// function(xhr) +// { +// console.error(xhr); +// } +// ); +// }); -Grocy.Components.ProductPicker.GetPicker().on('change', function(e) -{ - // Just save the current recipe on every change of the product picker as a workflow could be started which leaves the page... - Grocy.Api.Put('objects/recipes/' + Grocy.EditObjectId, $('#recipe-form').serializeJSON(), function () { }, function () { }); -}); +// Grocy.Components.ProductPicker.GetPicker().on('change', function(e) +// { +// // Just save the current recipe on every change of the product picker as a workflow could be started which leaves the page... +// Grocy.Api.Put('objects/recipes/' + Grocy.EditObjectId, $('#recipe-form').serializeJSON(), function () { }, function () { }); +// }); // As the /recipe/new route immediately creates a new recipe on load, // always replace the current location by the created recipes edit page location -if (window.location.pathname.toLowerCase() === "/recipe/new") -{ - window.history.replaceState(null, null, U("/recipe/" + Grocy.EditObjectId)); -} +// if (window.location.pathname.toLowerCase() === "/recipe/new") +// { +// window.history.replaceState(null, null, U("/recipe/" + Grocy.EditObjectId)); +// } diff --git a/public/viewjs/recipes.js b/public/viewjs/recipes.js index 6898b004..32a9c2cb 100644 --- a/public/viewjs/recipes.js +++ b/public/viewjs/recipes.js @@ -1,6 +1,8 @@ var recipesTables = $('#recipes-table').DataTable({ - 'order': [[0, 'asc']], + 'order': [[1, 'asc']], 'columnDefs': [ + { 'orderable': false, 'targets': 0 }, + { 'searchable': false, "targets": 0 }, { 'orderData': 2, 'targets': 1 } ], 'select': 'single', @@ -24,18 +26,14 @@ if (typeof recipe !== "undefined") var rowId = "#recipe-row-" + recipe; $(rowId).addClass("selected") - var cardId = "#recipe-card-" + recipe; - $(cardId).addClass("bg-primary").addClass("text-white"); + var cardId = "#RecipeGalleryCard-" + recipe; + $(cardId).addClass("border-primary"); if ($(window).width() < 768) { // Scroll to recipe card on mobile $("#selectedRecipeCard")[0].scrollIntoView(); } - else - { - $(cardId)[0].scrollIntoView(); - } } if (GetUriParam("search") !== undefined) @@ -59,8 +57,10 @@ $("#search").on("keyup", Delay(function() recipesTables.search(value).draw(); - $(".recipe-gallery-item-container").removeClass("d-none"); - $(".recipe-gallery-item-container .card-title:not(:contains_case_insensitive(" + value + "))").parent().parent().parent().parent().addClass("d-none"); + $(".recipe-gallery-item").removeClass("d-none"); + console.log( $(".recipe-gallery-item .card-title:not(:contains_case_insensitive(" + value + "))")); + + $(".recipe-gallery-item .card-title:not(:contains_case_insensitive(" + value + "))").parent().parent().parent().addClass("d-none"); }, 200)); $("#status-filter").on("change", function() @@ -71,15 +71,13 @@ $("#status-filter").on("change", function() value = ""; } - // Transfer CSS classes of selected element to dropdown element (for background) - $(this).attr("class", $("#" + $(this).attr("id") + " option[value='" + value + "']").attr("class") + " form-control"); - - recipesTables.column(4).search(value).draw(); + recipesTables.column(5).search(value).draw(); }); - -$("#selectedRecipeDeleteButton").on('click', function(e) +$(".recipe-delete").on('click', function(e) { + e.preventDefault(); + var objectName = $(e.currentTarget).attr('data-recipe-name'); var objectId = $(e.currentTarget).attr('data-recipe-id'); @@ -115,7 +113,7 @@ $("#selectedRecipeDeleteButton").on('click', function(e) }); }); -$(document).on('click', '.recipe-order-missing-button', function(e) +$(document).on('click', '.recipe-shopping-list', function(e) { var objectName = $(e.currentTarget).attr('data-recipe-name'); var objectId = $(e.currentTarget).attr('data-recipe-id'); @@ -161,7 +159,7 @@ $(document).on('click', '.recipe-order-missing-button', function(e) }); }); -$("#selectedRecipeConsumeButton").on('click', function(e) +$(".recipe-consume").on('click', function(e) { var objectName = $(e.currentTarget).attr('data-recipe-name'); var objectId = $(e.currentTarget).attr('data-recipe-id'); @@ -223,7 +221,7 @@ $(".recipe-gallery-item").on("click", function(e) window.location.href = U('/recipes?tab=gallery&recipe=' + $(this).data("recipe-id")); }); -$("#selectedRecipeToggleFullscreenButton").on('click', function(e) +$(".recipe-fullscreen").on('click', function(e) { e.preventDefault(); @@ -242,6 +240,19 @@ $("#selectedRecipeToggleFullscreenButton").on('click', function(e) } }); +$(".recipe-print").on('click', function(e) +{ + e.preventDefault(); + + $("#selectedRecipeCard").removeClass("fullscreen"); + $("body").removeClass("fullscreen-card"); + $("#selectedRecipeCard .card-header").removeClass("fixed-top"); + $("#selectedRecipeCard .card-body").removeClass("mt-5"); + + window.history.replaceState(null, null, " "); + window.print(); +}); + $('#servings-scale').keyup(function(event) { var data = { }; diff --git a/views/recipeform.blade.php b/views/recipeform.blade.php index b4ad48a4..b28653e2 100644 --- a/views/recipeform.blade.php +++ b/views/recipeform.blade.php @@ -44,7 +44,7 @@
- +
{{ $__t('A name is required') }}
@@ -96,7 +96,7 @@ 'products' => $products, 'isRequired' => false, 'label' => 'Produces product', - 'prefillById' => $recipe->product_id, + 'prefillById' => $mode == 'edit' ? $recipe->product_id : '', 'hint' => $__t('When a product is selected, one unit (per serving in purchase quantity unit) will be added to stock on consuming this recipe') )) @@ -110,12 +110,15 @@ - + {{ $__t('Save & Continue to add Ingredients and Included Recipes') }} + + + -
+
@if(!empty($recipe->picture_file_name)) diff --git a/views/recipes.blade.php b/views/recipes.blade.php index 5d1e08ec..b5b070e3 100644 --- a/views/recipes.blade.php +++ b/views/recipes.blade.php @@ -12,7 +12,7 @@
-
+

@yield('title')

@@ -50,7 +50,7 @@
-