From ff08b8261159639b516e55e49ad51c1be3ebd73c Mon Sep 17 00:00:00 2001 From: franc Date: Tue, 13 May 2025 14:44:18 +0200 Subject: [PATCH 01/20] 1.) Add an eindpoint for deleting recipe with their connections 2.) Add Endpoint for adjust Ingredient Amount --- .../Controller/Recipe/RecipeController.cs | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs b/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs index e19eea3..551e8fa 100644 --- a/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs +++ b/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs @@ -412,7 +412,7 @@ } catch (Exception ex) { - return BadRequest(ex.Message); + return BadRequest(ex.Message); } } @@ -470,5 +470,49 @@ return PartialView("_IngredientsPartial", viewModel); } + + // GET: /Recipe/{recipeId}/AdjustableIngredients + [HttpGet("{recipeId}/AdjustableIngredients")] + public async Task GetAdjustableIngredients(Guid recipeId) + { + var recipe = await _recipeRepository.GetRecipeByIdAsync(recipeId); + if (recipe == null) + { + return NotFound("Recipe not found."); + } + + return PartialView("_AdjustableIngredientsPartial", recipe); + } + + // GET: /Recipe/{recipeId}/Delete + [HttpGet("{recipeId}/Delete")] + public async Task Delete(Guid recipeId) + { + var recipe = await _recipeRepository.GetRecipeByIdAsync(recipeId); + if (recipe == null) + { + return NotFound("Recipe not found."); + } + + return View(recipe); + } + + // POST: /Recipe/{recipeId}/Delete + [HttpPost("{recipeId}/Delete")] + [ValidateAntiForgeryToken] + public async Task DeleteConfirmed(Guid recipeId) + { + try + { + await _recipeRepository.DeleteRecipeAsync(recipeId); + TempData["SuccessMessage"] = "Rezept wurde erfolgreich gelöscht."; + return RedirectToAction("CategoryRecipes", "Recipe"); + } + catch (Exception ex) + { + TempData["ErrorMessage"] = $"Ein Fehler ist aufgetreten: {ex.Message}"; + return RedirectToAction("Details", new { recipeId }); + } + } } } From 1c47b770b421f787d651f400f027e7d4e57cf5dc Mon Sep 17 00:00:00 2001 From: franc Date: Tue, 13 May 2025 14:45:39 +0200 Subject: [PATCH 02/20] Add Method to delete whole recipe with their relations and include Mediafiles to Instruction by getting recipe --- .../Repositories/Recipe/IRecipeRepository.cs | 2 + .../Repositories/Recipe/RecipeRepository.cs | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs b/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs index fdbc876..2e4b375 100644 --- a/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs +++ b/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs @@ -18,5 +18,7 @@ Task CreateRecipeForCategoryAsync(Category category, string name, string description, Difficulty difficulty, int servings, TimeSpan preparationTime, TimeSpan cookingTime); Task> SearchInRecipesAndIngredients(string searchterm); + + Task DeleteRecipeAsync(Guid recipeId); } } diff --git a/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs b/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs index 382bf90..4492d1e 100644 --- a/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs +++ b/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs @@ -38,6 +38,7 @@ .ThenInclude(ri => ri.Unit) .Include(r => r.MediaFiles) .Include(r => r.Instructions) + .ThenInclude(i => i.MediaFiles) .FirstOrDefaultAsync(r => r.Id == recipeId); } @@ -182,5 +183,47 @@ .ThenInclude(ri => ri.Ingredient) .ToListAsync(); } + + public async Task DeleteRecipeAsync(Guid recipeId) + { + var recipe = await GetRecipeByIdAsync(recipeId); + + if (recipe == null) + { + throw new InvalidDataException($"Rezept mit ID {recipeId} nicht gefunden."); + } + + if (recipe.RecipeIngredients != null && recipe.RecipeIngredients.Any()) + { + _context.RecipeIngredients.RemoveRange(recipe.RecipeIngredients); + } + + if (recipe.Instructions != null && recipe.Instructions.Any()) + { + foreach (var instruction in recipe.Instructions) + { + if (instruction.MediaFiles != null && instruction.MediaFiles.Any()) + { + _context.MediaFiles.RemoveRange(instruction.MediaFiles); + } + } + + _context.Instructions.RemoveRange(recipe.Instructions); + } + + if (recipe.MediaFiles != null && recipe.MediaFiles.Any()) + { + _context.MediaFiles.RemoveRange(recipe.MediaFiles); + } + + if (recipe.Favorit != null && recipe.Favorit.Id != Guid.Empty) + { + _context.Remove(recipe.Favorit); + } + + _context.Recipes.Remove(recipe); + + await _context.SaveChangesAsync(); + } } } From a57ebd450757be43f24d1e77c17f745f4f1b7eb3 Mon Sep 17 00:00:00 2001 From: franc Date: Tue, 13 May 2025 14:46:59 +0200 Subject: [PATCH 03/20] Adjust endpoint path for navigation --- Francesco.Recipes.World/Views/Home/Index.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Francesco.Recipes.World/Views/Home/Index.cshtml b/Francesco.Recipes.World/Views/Home/Index.cshtml index af2e530..0c9e60c 100644 --- a/Francesco.Recipes.World/Views/Home/Index.cshtml +++ b/Francesco.Recipes.World/Views/Home/Index.cshtml @@ -60,7 +60,7 @@ @await Html.PartialAsync("_FavoriteButton", recipe) - Details From 325120e5ecfe517002ef6295196e558f0ccfeb6d Mon Sep 17 00:00:00 2001 From: franc Date: Tue, 13 May 2025 14:52:16 +0200 Subject: [PATCH 04/20] Add style to Instructions Grid Layout --- Francesco.Recipes.World/wwwroot/css/site.css | 100 ++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/Francesco.Recipes.World/wwwroot/css/site.css b/Francesco.Recipes.World/wwwroot/css/site.css index a505610..b8d4e75 100644 --- a/Francesco.Recipes.World/wwwroot/css/site.css +++ b/Francesco.Recipes.World/wwwroot/css/site.css @@ -84,4 +84,102 @@ textarea.form-control { border-radius: 4px; cursor: pointer; margin-top: 10px; -} \ No newline at end of file +} + + +/* Recipe Instructions Grid Layout */ +.instructions-grid { + margin-top: 2rem; + margin-bottom: 2rem; +} + +.instruction-row { + display: flex; + justify-content: space-between; + margin-bottom: 2.5rem; + flex-wrap: wrap; +} + +.instruction-card { + width: calc(33.333% - 20px); + margin-bottom: 1.5rem; +} + +@media (max-width: 992px) { + .instruction-card { + width: calc(50% - 15px); + } +} + +@media (max-width: 576px) { + .instruction-card { + width: 100%; + } +} + +.instruction-image { + position: relative; + width: 100%; + aspect-ratio: 1/1; + border: 1px solid #ddd; + border-radius: 4px; + overflow: hidden; + margin-bottom: 0.5rem; + display: flex; + align-items: center; + justify-content: center; + background-color: #f8f9fa; +} + + .instruction-image img { + width: 100%; + height: 100%; + object-fit: cover; + } + +.placeholder-image { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + color: #aaa; +} + + .placeholder-image i { + font-size: 2rem; + } + +.step-number { + position: absolute; + bottom: 5px; + right: 10px; + background-color: rgba(0, 0, 0, 0.5); + color: white; + font-weight: bold; + padding: 3px 8px; + border-radius: 50%; +} + +.instruction-text { + font-size: 0.9rem; + line-height: 1.4; + color: #333; +} + +.recipe-instructions-section h3 { + margin-bottom: 1.5rem; + position: relative; + padding-bottom: 0.5rem; +} + + .recipe-instructions-section h3:after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 50px; + height: 2px; + background-color: #007bff; + } + From aa6f27383f4071f06e08ba17943275feb905d7b5 Mon Sep 17 00:00:00 2001 From: franc Date: Wed, 14 May 2025 11:46:48 +0200 Subject: [PATCH 05/20] Move the Ingredient List stuff to a Partial view --- .../Views/Recipe/Details.cshtml | 204 +++++++++++++++--- 1 file changed, 171 insertions(+), 33 deletions(-) diff --git a/Francesco.Recipes.World/Views/Recipe/Details.cshtml b/Francesco.Recipes.World/Views/Recipe/Details.cshtml index 4ea25c7..e30b936 100644 --- a/Francesco.Recipes.World/Views/Recipe/Details.cshtml +++ b/Francesco.Recipes.World/Views/Recipe/Details.cshtml @@ -6,7 +6,7 @@

@Model.Name

-
+
@if (Model.MediaFiles.Any() && Model.MediaFiles.First().Data != null) { @@ -20,49 +20,187 @@

Description: @Model.Description

Difficulty: @Model.Difficulty

-

Servings: @Model.Servings

Preparation Time: @Model.PreparationTime

Cooking Time: @Model.CookingTime

-
-

Ingredients

-
-
    - @foreach (var ingredient in Model.RecipeIngredients) - { -
  • - - @ingredient.Ingredient.Name - @ingredient.Quantity @(ingredient.Unit != null ? ingredient.Unit.Symbol : string.Empty) -
  • - } -
- -
-
+ + + + @await Html.PartialAsync("_RecipeInstructionGridPartial", new Francesco.Recipes.World.Models.InstructionViewModel +{ + RecipeId = Model.Id, + Instructions = Model.Instructions.ToList() +}) + +
+ @Html.AntiForgeryToken() + +
@section Scripts { } From bfbfd34624c2401b001db06ae1b624ed99bb361d Mon Sep 17 00:00:00 2001 From: franc Date: Wed, 14 May 2025 11:47:29 +0200 Subject: [PATCH 06/20] Create Grid for show instructions of recipe --- .../_RecipeInstructionGridPartial.cshtml | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml diff --git a/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml b/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml new file mode 100644 index 0000000..2a7b44b --- /dev/null +++ b/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml @@ -0,0 +1,43 @@ +@model Francesco.Recipes.World.Models.InstructionViewModel + +
+

Instructions

+ +
+ @{ + var groupedInstructions = Model.Instructions.OrderBy(i => i.Number).ToList(); + var rows = (int)Math.Ceiling(groupedInstructions.Count / 3.0); + + for (int row = 0; row < rows; row++) + { +
+ @for (int col = 0; col < 3 && (row * 3 + col < groupedInstructions.Count); col++) + { + var instruction = groupedInstructions[row * 3 + col]; +
+
+ @if (instruction.MediaFiles != null && instruction.MediaFiles.Any()) + { + var mediaFile = instruction.MediaFiles.First(); + @if (mediaFile.Data != null && mediaFile.Data.Length > 0) + { + Step @instruction.Number + } + } + + else + { +
+ +
+ } + @instruction.Number +
+

@instruction.Description

+
+ } +
+ } + } +
+
From e470dd11bbc252e3e76b264c93c1be29f33e1f79 Mon Sep 17 00:00:00 2001 From: franc Date: Wed, 14 May 2025 11:57:03 +0200 Subject: [PATCH 07/20] Make a partial view for adjust amount of ingredient --- .../_AdjustableIngredientsPartial.cshtml | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Francesco.Recipes.World/Views/Shared/_AdjustableIngredientsPartial.cshtml diff --git a/Francesco.Recipes.World/Views/Shared/_AdjustableIngredientsPartial.cshtml b/Francesco.Recipes.World/Views/Shared/_AdjustableIngredientsPartial.cshtml new file mode 100644 index 0000000..7855058 --- /dev/null +++ b/Francesco.Recipes.World/Views/Shared/_AdjustableIngredientsPartial.cshtml @@ -0,0 +1,36 @@ +@model Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe + +
+
+

Ingredients

+
+ +
+ + + +
+ (Original: @Model.Servings) +
+
+ +
+
    + @foreach (var ingredient in Model.RecipeIngredients) + { +
  • + + @ingredient.Ingredient.Name - + @ingredient.Quantity + @(ingredient.Unit != null ? ingredient.Unit.Symbol : string.Empty) + +
  • + } +
+ +
+
From 144328423626813657714921799ee43a5bc2671c Mon Sep 17 00:00:00 2001 From: franc Date: Wed, 14 May 2025 12:00:54 +0200 Subject: [PATCH 08/20] Add bootastrap for icon by darkMode and LightMode --- Francesco.Recipes.World/Views/Shared/_Layout.cshtml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Francesco.Recipes.World/Views/Shared/_Layout.cshtml b/Francesco.Recipes.World/Views/Shared/_Layout.cshtml index a06f9bd..8eae480 100644 --- a/Francesco.Recipes.World/Views/Shared/_Layout.cshtml +++ b/Francesco.Recipes.World/Views/Shared/_Layout.cshtml @@ -7,6 +7,8 @@ + +
From da2fd1e61d2f424b0cdeeb3f2842b469af599ab4 Mon Sep 17 00:00:00 2001 From: franc Date: Wed, 14 May 2025 12:01:20 +0200 Subject: [PATCH 09/20] change redirection to index --- Francesco.Recipes.World/Controller/Recipe/RecipeController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs b/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs index 551e8fa..ae4e79f 100644 --- a/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs +++ b/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs @@ -506,7 +506,7 @@ { await _recipeRepository.DeleteRecipeAsync(recipeId); TempData["SuccessMessage"] = "Rezept wurde erfolgreich gelöscht."; - return RedirectToAction("CategoryRecipes", "Recipe"); + return RedirectToAction("Index", "Home"); } catch (Exception ex) { From a5b70532e6ae4c5092aa87e784cb51fb244b130a Mon Sep 17 00:00:00 2001 From: franc Date: Wed, 14 May 2025 12:01:31 +0200 Subject: [PATCH 10/20] rest --- Francesco.Recipes.World/wwwroot/js/site.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Francesco.Recipes.World/wwwroot/js/site.js b/Francesco.Recipes.World/wwwroot/js/site.js index 12bdab8..20666ef 100644 --- a/Francesco.Recipes.World/wwwroot/js/site.js +++ b/Francesco.Recipes.World/wwwroot/js/site.js @@ -189,3 +189,25 @@ async function addIngredient() { } } + + async function addSelectedIngredientsToShoppingList() { + var form = document.getElementById('ingredient-form'); + var formData = new FormData(form); + var selectedIngredientIds = formData.getAll('ingredientIds'); + + var response = await fetch('/ShoppingList/CreateOrAddIngredients', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ recipeId: '@Model.Id', ingredientIds: selectedIngredientIds }) + }); + + if (response.ok) { + var result = await response.json(); + localStorage.setItem('shoppingListId', result.shoppingListId); + alert('Shopping list updated.'); + } else { + alert('Failed to update shopping list.'); + } +} \ No newline at end of file From 3fe194886740e51e5de68e9a1d4a5fabb4ffa725 Mon Sep 17 00:00:00 2001 From: franc Date: Wed, 14 May 2025 12:01:59 +0200 Subject: [PATCH 11/20] remove Service Folder --- Francesco.Recipes.World/Francesco.Recipes.World.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/Francesco.Recipes.World/Francesco.Recipes.World.csproj b/Francesco.Recipes.World/Francesco.Recipes.World.csproj index 84f6487..e28537c 100644 --- a/Francesco.Recipes.World/Francesco.Recipes.World.csproj +++ b/Francesco.Recipes.World/Francesco.Recipes.World.csproj @@ -49,7 +49,6 @@ - From 8d9eb4b364e10c336a60d49e45eb4f87194e0831 Mon Sep 17 00:00:00 2001 From: Francesco Lorenzo D'Amico Date: Tue, 20 May 2025 14:22:28 +0200 Subject: [PATCH 12/20] Everywhere an delete operation is name from HttpPost to HttpDelete --- .../Controller/Recipe/RecipeController.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs b/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs index ae4e79f..9d31328 100644 --- a/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs +++ b/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs @@ -305,8 +305,8 @@ return View(); } - // POST: /Recipe/{recipeId}/RemoveIngredient/{ingredientId} - [HttpPost("{recipeId}/RemoveIngredient/{ingredientId}")] + // DELETE: /Recipe/{recipeId}/RemoveIngredient/{ingredientId} + [HttpDelete("{recipeId}/RemoveIngredient/{ingredientId}")] [ValidateAntiForgeryToken] public async Task RemoveIngredientConfirmed(Guid recipeId, Guid ingredientId) { @@ -338,8 +338,8 @@ return View(); } - // POST: /Recipe/{recipeId}/RemoveInstruction/{instructionId} - [HttpPost("{recipeId}/RemoveInstruction/{instructionId}")] + // DELETE: /Recipe/{recipeId}/RemoveInstruction/{instructionId} + [HttpDelete("{recipeId}/RemoveInstruction/{instructionId}")] [ValidateAntiForgeryToken] public async Task RemoveInstructionConfirmed(Guid recipeId, Guid instructionId) { @@ -498,19 +498,20 @@ } // POST: /Recipe/{recipeId}/Delete - [HttpPost("{recipeId}/Delete")] + [HttpDelete("{recipeId}/Delete")] [ValidateAntiForgeryToken] public async Task DeleteConfirmed(Guid recipeId) { - try + var deleted = await _recipeRepository.DeleteRecipeAsync(recipeId); + + if (deleted) { - await _recipeRepository.DeleteRecipeAsync(recipeId); TempData["SuccessMessage"] = "Rezept wurde erfolgreich gelöscht."; return RedirectToAction("Index", "Home"); } - catch (Exception ex) + else { - TempData["ErrorMessage"] = $"Ein Fehler ist aufgetreten: {ex.Message}"; + TempData["ErrorMessage"] = "Rezept nicht gefunden oder konnte nicht gelöscht werden."; return RedirectToAction("Details", new { recipeId }); } } From 0c3b7380c5e9dd9c1c9aa009bf9586d211664c07 Mon Sep 17 00:00:00 2001 From: Francesco Lorenzo D'Amico Date: Tue, 20 May 2025 14:23:46 +0200 Subject: [PATCH 13/20] Refactoring some Repo methods to not throw exception --- .../Repositories/Recipe/IRecipeRepository.cs | 2 +- .../Repositories/Recipe/RecipeRepository.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs b/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs index 2e4b375..5afb2f0 100644 --- a/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs +++ b/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs @@ -19,6 +19,6 @@ Task> SearchInRecipesAndIngredients(string searchterm); - Task DeleteRecipeAsync(Guid recipeId); + Task DeleteRecipeAsync(Guid recipeId); } } diff --git a/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs b/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs index 4492d1e..73c76a7 100644 --- a/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs +++ b/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs @@ -184,13 +184,13 @@ .ToListAsync(); } - public async Task DeleteRecipeAsync(Guid recipeId) + public async Task DeleteRecipeAsync(Guid recipeId) { var recipe = await GetRecipeByIdAsync(recipeId); if (recipe == null) { - throw new InvalidDataException($"Rezept mit ID {recipeId} nicht gefunden."); + return false; } if (recipe.RecipeIngredients != null && recipe.RecipeIngredients.Any()) @@ -224,6 +224,7 @@ _context.Recipes.Remove(recipe); await _context.SaveChangesAsync(); + return true; } } } From 06ef8927a7a6160067d66f4fba14ca26699e5942 Mon Sep 17 00:00:00 2001 From: Francesco Lorenzo D'Amico Date: Tue, 20 May 2025 14:25:19 +0200 Subject: [PATCH 14/20] Use one way of loading for avoid inconsistency --- Francesco.Recipes.World/Views/Recipe/Details.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Francesco.Recipes.World/Views/Recipe/Details.cshtml b/Francesco.Recipes.World/Views/Recipe/Details.cshtml index e30b936..777ebce 100644 --- a/Francesco.Recipes.World/Views/Recipe/Details.cshtml +++ b/Francesco.Recipes.World/Views/Recipe/Details.cshtml @@ -24,7 +24,7 @@

Cooking Time: @Model.CookingTime

- + @await Html.PartialAsync("_AdjustableIngredientsPartial", Model) @await Html.PartialAsync("_RecipeInstructionGridPartial", new Francesco.Recipes.World.Models.InstructionViewModel { From f89cf27be46b57ced2bd8fde7e1b80c55c1da224 Mon Sep 17 00:00:00 2001 From: Francesco Lorenzo D'Amico Date: Tue, 20 May 2025 14:26:14 +0200 Subject: [PATCH 15/20] Add LIst instead of use an 2 dimensional array --- .../Views/Shared/_RecipeInstructionGridPartial.cshtml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml b/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml index 2a7b44b..ed4f682 100644 --- a/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml +++ b/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml @@ -6,12 +6,16 @@
@{ var groupedInstructions = Model.Instructions.OrderBy(i => i.Number).ToList(); - var rows = (int)Math.Ceiling(groupedInstructions.Count / 3.0); + var instructionRows = groupedInstructions + .Select((instruction, index) => new { Instruction = instruction, Index = index }) + .GroupBy(x => x.Index / 3) + .Select(g => g.Select(x => x.Instruction).ToList()) + .ToList(); - for (int row = 0; row < rows; row++) + foreach (var row in instructionRows) {
- @for (int col = 0; col < 3 && (row * 3 + col < groupedInstructions.Count); col++) + @for (var instruction in row) { var instruction = groupedInstructions[row * 3 + col];
From 924b896a9a168811183d73678a8948408f3e007a Mon Sep 17 00:00:00 2001 From: Francesco Lorenzo D'Amico Date: Tue, 20 May 2025 14:26:42 +0200 Subject: [PATCH 16/20] Remove empty line --- Francesco.Recipes.World/Views/Shared/_Layout.cshtml | 1 - 1 file changed, 1 deletion(-) diff --git a/Francesco.Recipes.World/Views/Shared/_Layout.cshtml b/Francesco.Recipes.World/Views/Shared/_Layout.cshtml index 8eae480..55fbcc0 100644 --- a/Francesco.Recipes.World/Views/Shared/_Layout.cshtml +++ b/Francesco.Recipes.World/Views/Shared/_Layout.cshtml @@ -8,7 +8,6 @@ -
From f2bb057a9bb54b43ce944f8da8c4e75f5ea23be6 Mon Sep 17 00:00:00 2001 From: Francesco Lorenzo D'Amico Date: Tue, 27 May 2025 13:47:13 +0200 Subject: [PATCH 17/20] Change from HttpPost to HttpDelete --- Francesco.Recipes.World/Controller/Recipe/RecipeController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs b/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs index 9d31328..46177ee 100644 --- a/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs +++ b/Francesco.Recipes.World/Controller/Recipe/RecipeController.cs @@ -497,7 +497,7 @@ return View(recipe); } - // POST: /Recipe/{recipeId}/Delete + // DELETE: /Recipe/{recipeId}/Delete [HttpDelete("{recipeId}/Delete")] [ValidateAntiForgeryToken] public async Task DeleteConfirmed(Guid recipeId) From 5c9f6c812069c9af55567171bebc764e39bd5be1 Mon Sep 17 00:00:00 2001 From: Francesco Lorenzo D'Amico Date: Tue, 27 May 2025 13:47:51 +0200 Subject: [PATCH 18/20] add comment for a specification of this code --- ...4459_CreateNewTableRecipeIngredientShoppinglist.Designer.cs | 3 --- Francesco.Recipes.World/Views/Recipe/Details.cshtml | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Francesco.Recipes.World/Migrations/20250324124459_CreateNewTableRecipeIngredientShoppinglist.Designer.cs b/Francesco.Recipes.World/Migrations/20250324124459_CreateNewTableRecipeIngredientShoppinglist.Designer.cs index 6807402..e98a564 100644 --- a/Francesco.Recipes.World/Migrations/20250324124459_CreateNewTableRecipeIngredientShoppinglist.Designer.cs +++ b/Francesco.Recipes.World/Migrations/20250324124459_CreateNewTableRecipeIngredientShoppinglist.Designer.cs @@ -1,11 +1,8 @@ // -using System; using Francesco.Recipes.World.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; #nullable disable diff --git a/Francesco.Recipes.World/Views/Recipe/Details.cshtml b/Francesco.Recipes.World/Views/Recipe/Details.cshtml index 777ebce..545b9f3 100644 --- a/Francesco.Recipes.World/Views/Recipe/Details.cshtml +++ b/Francesco.Recipes.World/Views/Recipe/Details.cshtml @@ -123,6 +123,8 @@ finalQuantity = Math.round(adjustedQuantity); } } + // Convert small unit "msp" (pinch) to teaspoons (TL) for a better estimate + // 4 pinches = about 1 TL → show that as a note, rounded to 1 decimal else if (specialUnits.smallUnits.includes(unitSymbol)) { if (adjustedQuantity > 3) { finalQuantity = Math.round(adjustedQuantity / 4 * 10) / 10; From 907e19e3983681d282cc67dc31e2eef1a6ec6417 Mon Sep 17 00:00:00 2001 From: Francesco Lorenzo D'Amico Date: Tue, 27 May 2025 13:48:39 +0200 Subject: [PATCH 19/20] Make the code more readable and easier with some style --- .../_RecipeInstructionGridPartial.cshtml | 52 ++++++---------- Francesco.Recipes.World/wwwroot/css/site.css | 59 ++++++++++--------- 2 files changed, 48 insertions(+), 63 deletions(-) diff --git a/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml b/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml index ed4f682..c814599 100644 --- a/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml +++ b/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml @@ -4,44 +4,28 @@

Instructions

- @{ - var groupedInstructions = Model.Instructions.OrderBy(i => i.Number).ToList(); - var instructionRows = groupedInstructions - .Select((instruction, index) => new { Instruction = instruction, Index = index }) - .GroupBy(x => x.Index / 3) - .Select(g => g.Select(x => x.Instruction).ToList()) - .ToList(); - - foreach (var row in instructionRows) - { -
- @for (var instruction in row) + @foreach (var instruction in Model.Instructions.OrderBy(i => i.Number)) + { +
+
+ @if (instruction.MediaFiles != null && instruction.MediaFiles.Any()) { - var instruction = groupedInstructions[row * 3 + col]; -
-
- @if (instruction.MediaFiles != null && instruction.MediaFiles.Any()) - { - var mediaFile = instruction.MediaFiles.First(); - @if (mediaFile.Data != null && mediaFile.Data.Length > 0) - { - Step @instruction.Number - } - } - - else - { -
- -
- } - @instruction.Number -
-

@instruction.Description

+ var mediaFile = instruction.MediaFiles.First(); + if (mediaFile.Data != null && mediaFile.Data.Length > 0) + { + Step @instruction.Number + } + } + else + { +
+
} + @instruction.Number
- } +

@instruction.Description

+
}
diff --git a/Francesco.Recipes.World/wwwroot/css/site.css b/Francesco.Recipes.World/wwwroot/css/site.css index b8d4e75..c895bb3 100644 --- a/Francesco.Recipes.World/wwwroot/css/site.css +++ b/Francesco.Recipes.World/wwwroot/css/site.css @@ -89,38 +89,36 @@ textarea.form-control { /* Recipe Instructions Grid Layout */ .instructions-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; margin-top: 2rem; margin-bottom: 2rem; } -.instruction-row { - display: flex; - justify-content: space-between; - margin-bottom: 2.5rem; - flex-wrap: wrap; -} - -.instruction-card { - width: calc(33.333% - 20px); - margin-bottom: 1.5rem; -} - @media (max-width: 992px) { - .instruction-card { - width: calc(50% - 15px); + .instructions-grid { + grid-template-columns: repeat(2, 1fr); } } @media (max-width: 576px) { - .instruction-card { - width: 100%; + .instructions-grid { + grid-template-columns: 1fr; } } +.instruction-card { + border: 1px solid #ccc; + padding: 1rem; + border-radius: 8px; + background-color: white; +} + .instruction-image { position: relative; width: 100%; - aspect-ratio: 1/1; + aspect-ratio: 1 / 1; border: 1px solid #ddd; border-radius: 4px; overflow: hidden; @@ -129,36 +127,38 @@ textarea.form-control { align-items: center; justify-content: center; background-color: #f8f9fa; + text-align: center; } .instruction-image img { width: 100%; height: 100%; object-fit: cover; + border-radius: 4px; } .placeholder-image { - display: flex; - align-items: center; - justify-content: center; width: 100%; height: 100%; - color: #aaa; + display: flex; + justify-content: center; + align-items: center; + font-size: 2rem; + color: #999; + background-color: #f0f0f0; + border-radius: 4px; } - .placeholder-image i { - font-size: 2rem; - } - .step-number { position: absolute; - bottom: 5px; - right: 10px; - background-color: rgba(0, 0, 0, 0.5); + bottom: 8px; + right: 8px; + background: rgba(0, 0, 0, 0.6); color: white; - font-weight: bold; padding: 3px 8px; border-radius: 50%; + font-size: 0.875rem; + font-weight: bold; } .instruction-text { @@ -183,3 +183,4 @@ textarea.form-control { background-color: #007bff; } + From c503e008a7653473fb7b3e3267b8e1a1f05d91bd Mon Sep 17 00:00:00 2001 From: Francesco Lorenzo D'Amico Date: Tue, 27 May 2025 14:46:59 +0200 Subject: [PATCH 20/20] - remove blank line --- .../Views/Shared/_RecipeInstructionGridPartial.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml b/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml index c814599..c9b7df8 100644 --- a/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml +++ b/Francesco.Recipes.World/Views/Shared/_RecipeInstructionGridPartial.cshtml @@ -28,4 +28,4 @@
}
-
+
\ No newline at end of file