Merge branch 'feature/Implement_All_Repositories' into 'develop'
Repository Stuff See merge request francesco.damico/francescos.recipes.world!5
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
namespace Francesco.Recipes.World.Controller.Category
|
||||
{
|
||||
using Francesco.Recipes.World.Repositories.Category;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
public class CategoryController : Controller
|
||||
{
|
||||
private readonly ICategoryRepository _categoryRepository;
|
||||
|
||||
public CategoryController(ICategoryRepository categoryRepository)
|
||||
{
|
||||
_categoryRepository = categoryRepository;
|
||||
}
|
||||
|
||||
// GET: /Category
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var categories = await _categoryRepository.GetAllCategoriesAsync();
|
||||
return View(categories);
|
||||
}
|
||||
|
||||
// GET: /Category/{id}
|
||||
[HttpGet("{id:guid}")]
|
||||
public async Task<IActionResult> Details(Guid id)
|
||||
{
|
||||
var category = await _categoryRepository.GetCategoryByIdAsync(id);
|
||||
return View(category);
|
||||
}
|
||||
|
||||
// GET: /Category/{id}/recipes
|
||||
[HttpGet("{id:guid}/recipes")]
|
||||
public async Task<IActionResult> GetRecipesByCategory(Guid id)
|
||||
{
|
||||
var recipes = await _categoryRepository.GetRecipesByCategoryAsync(id);
|
||||
return Ok(recipes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Francesco.Recipes.World.Controller.Ingredient
|
||||
{
|
||||
public class IngredientController
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Francesco.Recipes.World.Controller.Instruction
|
||||
{
|
||||
public class InstructionController
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
namespace Francesco.Recipes.World.Controller.MediaFile
|
||||
{
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Francesco.Recipes.World.Repositories.MediaFile;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
[Route("Category/{categoryId}/Recipe")]
|
||||
public class MediaFileController : Controller
|
||||
{
|
||||
private readonly IMediaFileRepository _mediaFileRepository;
|
||||
private readonly FrancescosRecipesWorldDbContext _context;
|
||||
|
||||
public MediaFileController(IMediaFileRepository mediaFileRepository, FrancescosRecipesWorldDbContext context)
|
||||
{
|
||||
_mediaFileRepository = mediaFileRepository;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// POST: /UploadImage
|
||||
[HttpPost("UploadImage")]
|
||||
[AutoValidateAntiforgeryToken]
|
||||
public async Task<IActionResult> UploadImage(Guid recipeId, IFormFile? mediaFile)
|
||||
{
|
||||
if (mediaFile is null)
|
||||
{
|
||||
return BadRequest("Photo is required.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _mediaFileRepository.UploadRecipeMediaAsync(recipeId, mediaFile);
|
||||
return Ok("Image uploaded successfully.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, $"Internal server error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// GET: /UploadImage
|
||||
[HttpGet("UploadImage")]
|
||||
public IActionResult UploadImageView()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost("ReplaceInstructionImage")]
|
||||
[AutoValidateAntiforgeryToken]
|
||||
public async Task<IActionResult> ReplaceInstructionImage(Guid instructionId, Guid mediaFileIdToReplace, IFormFile? newPhoto)
|
||||
{
|
||||
if (newPhoto is null)
|
||||
{
|
||||
return BadRequest("Photo is required.");
|
||||
}
|
||||
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await newPhoto.CopyToAsync(memoryStream);
|
||||
|
||||
var newMediaData = memoryStream.ToArray();
|
||||
|
||||
try
|
||||
{
|
||||
await _mediaFileRepository.ReplaceInstructionImageAsync(instructionId, mediaFileIdToReplace, newPhoto.FileName, newPhoto.ContentType, newMediaData);
|
||||
return Ok("Image replaced successfully.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, $"Internal server error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
namespace Francesco.Recipes.World.Controller.Recipe
|
||||
{
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
using Francesco.Recipes.World.Repositories.Category;
|
||||
using Francesco.Recipes.World.Repositories.Favorit;
|
||||
using Francesco.Recipes.World.Repositories.Ingredient;
|
||||
using Francesco.Recipes.World.Repositories.Instruction;
|
||||
using Francesco.Recipes.World.Repositories.MediaFile;
|
||||
using Francesco.Recipes.World.Repositories.Recipe;
|
||||
using Francesco.Recipes.World.Repositories.Unit;
|
||||
using Francesco.Recipes.World.Views.Category;
|
||||
using Francesco.Recipes.World.Views.Recipe;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
public class RecipeController : Controller
|
||||
{
|
||||
private readonly IRecipeRepository _recipeRepository;
|
||||
private readonly IUnitRepository _unitRepository;
|
||||
private readonly ICategoryRepository _categoryRepository;
|
||||
private readonly IIngredientRepository _ingredientRepository;
|
||||
private readonly IMediaFileRepository _mediaFileRepository;
|
||||
private readonly IInstructionRepository _instructionRepository;
|
||||
private readonly IFavoriteRepository _favoriteRepository;
|
||||
|
||||
public IReadOnlyCollection<Recipe> Recipes { get; set; }
|
||||
|
||||
public RecipeController(
|
||||
IRecipeRepository recipeRepository,
|
||||
IUnitRepository unitRepository,
|
||||
ICategoryRepository categoryRepository,
|
||||
IIngredientRepository ingredientRepository,
|
||||
IMediaFileRepository mediaFileRepository,
|
||||
IInstructionRepository instructionRepository,
|
||||
IFavoriteRepository favoriteRepository)
|
||||
{
|
||||
_recipeRepository = recipeRepository;
|
||||
_unitRepository = unitRepository;
|
||||
_categoryRepository = categoryRepository;
|
||||
_ingredientRepository = ingredientRepository;
|
||||
Recipes = new List<Recipe>();
|
||||
_mediaFileRepository = mediaFileRepository;
|
||||
_instructionRepository = instructionRepository;
|
||||
_favoriteRepository = favoriteRepository;
|
||||
}
|
||||
|
||||
// GET: /Recipe/{recipeId}/AddOrCreateIngredient
|
||||
[HttpGet("{recipeId}/AddOrCreateIngredient")]
|
||||
public async Task<IActionResult> AddOrCreateIngredient(Guid recipeId)
|
||||
{
|
||||
var units = await _unitRepository.GetAllUnitsAsync();
|
||||
ViewBag.Units = new SelectList(units, "Id", "Name");
|
||||
ViewBag.RecipeId = recipeId;
|
||||
return View();
|
||||
}
|
||||
|
||||
// GET: /Recipe/Details/{recipeId}
|
||||
[HttpGet("Details/{recipeId}")]
|
||||
public async Task<IActionResult> Details(Guid recipeId)
|
||||
{
|
||||
var recipe = await _recipeRepository.GetRecipeByIdAsync(recipeId);
|
||||
if (recipe == null)
|
||||
{
|
||||
return NotFound("Recipe not found.");
|
||||
}
|
||||
|
||||
return View(recipe);
|
||||
}
|
||||
|
||||
// GET: /Recipe/CategoryRecipes
|
||||
[HttpGet("CategoryRecipes")]
|
||||
public async Task<IActionResult> CategoryRecipes()
|
||||
{
|
||||
var categories = await _categoryRepository.GetAllCategoriesWithRecipesAsync();
|
||||
|
||||
var viewModel = categories.Select(c => new CategoryRecipesViewModel
|
||||
{
|
||||
Category = c,
|
||||
Recipes = c.Recipes,
|
||||
}).ToList();
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
// POST: /Recipe/{recipeId}/AddOrCreateIngredient
|
||||
[HttpPost("{recipeId}/AddOrCreateIngredient")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> AddOrCreateIngredient(Guid recipeId, string ingredientName, int quantity, Guid unitId)
|
||||
{
|
||||
if (quantity <= 0)
|
||||
{
|
||||
ModelState.AddModelError(nameof(quantity), "Die Menge muss größer als 0 sein.");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
var units = await _unitRepository.GetAllUnitsAsync();
|
||||
ViewBag.Units = new SelectList(units, "Id", "Name");
|
||||
return View();
|
||||
}
|
||||
|
||||
await _recipeRepository.AddOrCreateIngredientToRecipeAsync(recipeId, ingredientName, quantity, unitId);
|
||||
return RedirectToAction("Details", new { id = recipeId });
|
||||
}
|
||||
|
||||
// GET: /Recipe/Create/{categoryId}
|
||||
[HttpGet("Create/{categoryId}")]
|
||||
public async Task<IActionResult> Create(Guid categoryId)
|
||||
{
|
||||
var category = await _categoryRepository.GetCategoryByIdAsync(categoryId);
|
||||
if (category == null)
|
||||
{
|
||||
return NotFound($"Kategorie mit ID {categoryId} wurde nicht gefunden.");
|
||||
}
|
||||
|
||||
ViewBag.CategoryName = category.Name;
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: /Recipe/Create/{categoryId}
|
||||
[HttpPost("Create/{categoryId}")]
|
||||
[AutoValidateAntiforgeryToken]
|
||||
public async Task<IActionResult> Create(Guid categoryId, string name, string description, Difficulty difficulty, int servings, TimeSpan preparationTime, TimeSpan cookingTime, IFormFile? photo)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
ModelState.AddModelError(nameof(name), "Name darf nicht leer sein.");
|
||||
}
|
||||
|
||||
if (servings <= 0)
|
||||
{
|
||||
ModelState.AddModelError(nameof(servings), "Anzahl der Portionen muss größer als 0 sein.");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
var category = await _categoryRepository.GetCategoryByIdAsync(categoryId);
|
||||
if (category == null)
|
||||
{
|
||||
return NotFound($"Kategorie mit ID {categoryId} wurde nicht gefunden.");
|
||||
}
|
||||
|
||||
ViewBag.CategoryName = category.Name;
|
||||
return View();
|
||||
}
|
||||
|
||||
var categoryEntity = await _categoryRepository.GetCategoryByIdAsync(categoryId);
|
||||
if (categoryEntity == null)
|
||||
{
|
||||
return NotFound($"Kategorie mit ID {categoryId} wurde nicht gefunden.");
|
||||
}
|
||||
|
||||
await _recipeRepository.CreateRecipeForCategoryAsync(categoryEntity, name, description, difficulty, servings, preparationTime, cookingTime);
|
||||
var recipe = await _recipeRepository.CreateRecipeForCategoryAsync(categoryEntity, name, description, difficulty, servings, preparationTime, cookingTime);
|
||||
if (photo != null)
|
||||
{
|
||||
await _mediaFileRepository.UploadRecipeMediaAsync(recipe.Id, photo);
|
||||
}
|
||||
|
||||
return RedirectToAction("Details", "Category", new { id = categoryId });
|
||||
}
|
||||
|
||||
// GET: /Recipe/{recipeId}/RemoveIngredient/{ingredientId}
|
||||
[HttpGet("{recipeId}/RemoveIngredient/{ingredientId}")]
|
||||
public async Task<IActionResult> RemoveIngredient(Guid recipeId, Guid ingredientId)
|
||||
{
|
||||
var recipe = await _recipeRepository.GetRecipeAsync(recipeId);
|
||||
var ingredient = await _ingredientRepository.GetIngredientByIdAsync(ingredientId);
|
||||
|
||||
if (recipe == null || ingredient == null)
|
||||
{
|
||||
return NotFound("Recipe or Ingredient not found.");
|
||||
}
|
||||
|
||||
ViewBag.RecipeId = recipeId;
|
||||
ViewBag.IngredientId = ingredientId;
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: /Recipe/{recipeId}/RemoveIngredient/{ingredientId}
|
||||
[HttpPost("{recipeId}/RemoveIngredient/{ingredientId}")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RemoveIngredientConfirmed(Guid recipeId, Guid ingredientId)
|
||||
{
|
||||
await _recipeRepository.RemoveIngredientFromRecipeAsync(recipeId, ingredientId);
|
||||
TempData["SuccessMessage"] = "Ingredient removed successfully.";
|
||||
return RedirectToAction("Details", new { id = recipeId });
|
||||
}
|
||||
|
||||
// GET: /Recipe/FilterByDifficulty
|
||||
[HttpGet("FilterByDifficulty")]
|
||||
public async Task<IActionResult> FilterByDifficulty(Difficulty? selectedDifficulty)
|
||||
{
|
||||
var recipes = await _recipeRepository.GetRecipesByDifficultyAsync(selectedDifficulty);
|
||||
var viewModel = new FilterByDifficultyViewModel
|
||||
{
|
||||
SelectedDifficulty = selectedDifficulty,
|
||||
Recipes = recipes,
|
||||
};
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
// GET: /Recipe/{recipeId}/AddInstruction
|
||||
[HttpGet("{recipeId}/AddInstruction")]
|
||||
public async Task<IActionResult> AddInstruction(Guid recipeId)
|
||||
{
|
||||
var recipe = await _recipeRepository.GetRecipeByIdAsync(recipeId);
|
||||
if (recipe == null)
|
||||
{
|
||||
return NotFound("Recipe not found.");
|
||||
}
|
||||
|
||||
ViewBag.RecipeId = recipeId;
|
||||
return View(recipe);
|
||||
}
|
||||
|
||||
// POST: /Recipe/{recipeId}/AddInstruction
|
||||
[HttpPost("{recipeId}/AddInstruction")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> AddInstruction(Guid recipeId, string description, int number)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _instructionRepository.CreateInstructionToRecipeAsync(recipeId, description, number);
|
||||
return RedirectToAction("AddInstruction", new { recipeId });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, ex.Message);
|
||||
var recipe = await _recipeRepository.GetRecipeByIdAsync(recipeId);
|
||||
ViewBag.RecipeId = recipeId;
|
||||
return View(recipe);
|
||||
}
|
||||
}
|
||||
|
||||
// GET: /Recipe/Favorites
|
||||
[HttpGet("Favorites")]
|
||||
public async Task<IActionResult> Favorites()
|
||||
{
|
||||
var favoriteRecipes = await _favoriteRepository.GetFavoriteRecipesAsync();
|
||||
return View(favoriteRecipes);
|
||||
}
|
||||
|
||||
// POST: /Recipe/AddFavorite
|
||||
[HttpPost("AddFavorite")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> AddFavorite(Guid recipeId)
|
||||
{
|
||||
await _favoriteRepository.AddFavoriteAsync(recipeId);
|
||||
return RedirectToAction("Details", new { recipeId });
|
||||
}
|
||||
|
||||
// POST: /Recipe/RemoveFavorite
|
||||
[HttpPost("RemoveFavorite")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RemoveFavorite(Guid recipeId)
|
||||
{
|
||||
await _favoriteRepository.RemoveFavoriteAsync(recipeId);
|
||||
return RedirectToAction("Details", new { recipeId });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace Francesco.Recipes.World.Controllers
|
||||
{
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Francesco.Recipes.World.Models;
|
||||
using Francesco.Recipes.World.Repositories.ShoppingList;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
public class ShoppingListController : Controller
|
||||
{
|
||||
private readonly IShoppingListRepository _shoppingListRepository;
|
||||
private readonly FrancescosRecipesWorldDbContext _context;
|
||||
|
||||
public ShoppingListController(IShoppingListRepository shoppingListRepository, FrancescosRecipesWorldDbContext context)
|
||||
{
|
||||
_shoppingListRepository = shoppingListRepository;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
[HttpPost("CreateOrAddIngredients")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> CreateOrAddIngredients([FromBody] CreateOrAddIngredientRequestModel request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
if (request == null || request.IngredientIds == null || !request.IngredientIds.Any())
|
||||
{
|
||||
return BadRequest("No ingredients provided.");
|
||||
}
|
||||
|
||||
var shoppingList = await _shoppingListRepository.AddIngredientsToShoppingListAsync(request.RecipeId, request.IngredientIds);
|
||||
|
||||
if (shoppingList == null)
|
||||
{
|
||||
return BadRequest("Error creating shopping list.");
|
||||
}
|
||||
|
||||
return Json(new { shoppingListId = shoppingList.Id });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Francesco.Recipes.World.Controller.Unit
|
||||
{
|
||||
public class UnitController
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,8 @@
|
||||
using Francesco.Recipes.World.Models.BackendModels.MediaFile;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Shoppinglist;
|
||||
using Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Shoppinglist;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Unit;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -34,7 +35,9 @@
|
||||
|
||||
public DbSet<Favorit> Favorits => Set<Favorit>();
|
||||
|
||||
public DbSet<IngredientsShoppingList> IngredientsShoppingLists => Set<IngredientsShoppingList>();
|
||||
public DbSet<RecipeIngredientShoppingList> RecipeIngredientsShoppingLists => Set<RecipeIngredientShoppingList>();
|
||||
|
||||
public DbSet<RecipeShoppingList> RecipeShoppingLists => Set<RecipeShoppingList>();
|
||||
|
||||
public DbSet<ShoppingList> ShoppingLists => Set<ShoppingList>();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
@@ -29,6 +29,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.7" />
|
||||
<PackageReference Include="SecurityCodeScan.VS2019" Version="5.6.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -41,12 +42,16 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Controller\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include=".\stylecop.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Services\Category\" />
|
||||
<Folder Include="Services\MediaFile\" />
|
||||
<Folder Include="Services\Ingredient\" />
|
||||
<Folder Include="Services\Instruction\" />
|
||||
<Folder Include="Services\ShoppingList\" />
|
||||
<Folder Include="Services\Recipe\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
+521
@@ -0,0 +1,521 @@
|
||||
// <auto-generated />
|
||||
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
|
||||
|
||||
namespace Francesco.Recipes.World.Migrations
|
||||
{
|
||||
[DbContext(typeof(FrancescosRecipesWorldDbContext))]
|
||||
[Migration("20250324124459_CreateNewTableRecipeIngredientShoppinglist")]
|
||||
partial class CreateNewTableRecipeIngredientShoppinglist
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Category.Category", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Categories");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = new Guid("b248244f-f21c-4555-a14d-5dd49a2717cf"),
|
||||
Name = "Vorspeisen & Snacks"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("5186c5d6-5aff-4a6e-baf4-b61b72d889fe"),
|
||||
Name = "Erste Gänge"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("abbc6bb0-97ea-49f5-b31e-6507eb784fd1"),
|
||||
Name = "Hauptgerichte"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("90deec39-dcd0-422d-9018-ac8389e332e1"),
|
||||
Name = "Desserts & Süßspeisen"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("28e39168-701a-4084-81da-d96c987c462f"),
|
||||
Name = "Beilagen & Salate"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("20585b74-4805-4aff-a6df-aa6b7af04ff1"),
|
||||
Name = "Kuchen"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("adfb75ce-3ef3-428b-a5ca-b0c4c619d5ec"),
|
||||
Name = "Hefegebäck & Brot"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("d332f88d-d241-48c5-a2f2-bfd124eada7e"),
|
||||
Name = "Soßen & Saucen"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("23b1c740-e427-44f9-a6ea-d33d3f30f05a"),
|
||||
Name = "Marmeladen & Eingemachtes"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("0a91a200-dc76-4e00-b38c-b38cab5b69d7"),
|
||||
Name = "Getränke"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Favorit.Favorit", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Favorits");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Ingredient.Ingredient", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Ingredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList.RecipeIngredientShoppingList", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("IngredientId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("RecipeIngredientId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("ShoppingListId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IngredientId");
|
||||
|
||||
b.HasIndex("RecipeIngredientId");
|
||||
|
||||
b.HasIndex("ShoppingListId");
|
||||
|
||||
b.ToTable("RecipeIngredientsShoppingLists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Instruction.Instruction", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid>("RecipeId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RecipeId");
|
||||
|
||||
b.ToTable("Instructions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.MediaFile.MediaFile", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<byte[]>("Data")
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.Property<string>("FileName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<Guid>("InstructionId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("MimeType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<Guid?>("RecipeId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstructionId");
|
||||
|
||||
b.HasIndex("RecipeId");
|
||||
|
||||
b.ToTable("MediaFiles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("CategoryId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<TimeSpan>("CookingTime")
|
||||
.HasColumnType("time");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Difficulty")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid>("FavoritId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<bool>("IsFavorite")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTime?>("ModifiedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<TimeSpan>("PreparationTime")
|
||||
.HasColumnType("time");
|
||||
|
||||
b.Property<int>("Servings")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.HasIndex("FavoritId");
|
||||
|
||||
b.ToTable("Recipes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.RecipeIngredient.RecipeIngredient", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("IngredientId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid>("RecipeId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("UnitId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IngredientId");
|
||||
|
||||
b.HasIndex("RecipeId");
|
||||
|
||||
b.HasIndex("UnitId");
|
||||
|
||||
b.ToTable("RecipeIngredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Shoppinglist.ShoppingList", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<DateTime?>("ModifiedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ShoppingLists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Unit.Unit", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Symbol")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Units");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = new Guid("62eb2c11-8768-46fb-9db0-c77f940bb4aa"),
|
||||
Name = "liter",
|
||||
Symbol = "l"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("6c41f7e6-ca75-49cc-8541-cebd5a9c560b"),
|
||||
Name = "gramm",
|
||||
Symbol = "g"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("7e3d1b86-ac48-45d6-814e-d3492a86db1d"),
|
||||
Name = "kilogramm",
|
||||
Symbol = "kg"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("0c3648ec-4981-42c8-abf8-18bf1a2ff4c2"),
|
||||
Name = "stücke",
|
||||
Symbol = "stk"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("d82f4abd-e4e4-4104-a9c0-1acdeaa701f5"),
|
||||
Name = "blatt",
|
||||
Symbol = "blatt"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("df5cb4c3-4de6-4c6f-be8c-da41b2986408"),
|
||||
Name = "messerspitze",
|
||||
Symbol = "msp"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("e45a3af2-2ed6-4ac4-b06f-b4175663a7be"),
|
||||
Name = "stange",
|
||||
Symbol = "stange"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("7ea2f51d-7493-4f19-a663-1f309186d3ae"),
|
||||
Name = "bund",
|
||||
Symbol = "bund"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("66556e0e-eb2b-4bc3-9a56-135dd508ed09"),
|
||||
Name = "zehe",
|
||||
Symbol = "zehe"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("2e9894ac-14fa-43fd-ba07-35cdd8ebd461"),
|
||||
Name = "teelöffel",
|
||||
Symbol = "TL"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("24a91b89-3389-4465-89e4-2f70d2ea6fd7"),
|
||||
Name = "esslöffel",
|
||||
Symbol = "EL"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList.RecipeIngredientShoppingList", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Ingredient.Ingredient", null)
|
||||
.WithMany("IngredientShoppingLists")
|
||||
.HasForeignKey("IngredientId");
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.RecipeIngredient.RecipeIngredient", "RecipeIngredient")
|
||||
.WithMany()
|
||||
.HasForeignKey("RecipeIngredientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Shoppinglist.ShoppingList", "ShoppingList")
|
||||
.WithMany("RecipeIngredientShoppingLists")
|
||||
.HasForeignKey("ShoppingListId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("RecipeIngredient");
|
||||
|
||||
b.Navigation("ShoppingList");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Instruction.Instruction", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", "Recipe")
|
||||
.WithMany("Instructions")
|
||||
.HasForeignKey("RecipeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Recipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.MediaFile.MediaFile", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Instruction.Instruction", "Instruction")
|
||||
.WithMany("MediaFiles")
|
||||
.HasForeignKey("InstructionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", "Recipe")
|
||||
.WithMany("MediaFiles")
|
||||
.HasForeignKey("RecipeId");
|
||||
|
||||
b.Navigation("Instruction");
|
||||
|
||||
b.Navigation("Recipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Category.Category", null)
|
||||
.WithMany("Recipes")
|
||||
.HasForeignKey("CategoryId");
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Favorit.Favorit", "Favorit")
|
||||
.WithMany("Recipe")
|
||||
.HasForeignKey("FavoritId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Favorit");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.RecipeIngredient.RecipeIngredient", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Ingredient.Ingredient", "Ingredient")
|
||||
.WithMany("RecipeIngredients")
|
||||
.HasForeignKey("IngredientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", "Recipe")
|
||||
.WithMany("RecipeIngredients")
|
||||
.HasForeignKey("RecipeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Unit.Unit", "Unit")
|
||||
.WithMany("RecipeIngredient")
|
||||
.HasForeignKey("UnitId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Ingredient");
|
||||
|
||||
b.Navigation("Recipe");
|
||||
|
||||
b.Navigation("Unit");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Category.Category", b =>
|
||||
{
|
||||
b.Navigation("Recipes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Favorit.Favorit", b =>
|
||||
{
|
||||
b.Navigation("Recipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Ingredient.Ingredient", b =>
|
||||
{
|
||||
b.Navigation("IngredientShoppingLists");
|
||||
|
||||
b.Navigation("RecipeIngredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Instruction.Instruction", b =>
|
||||
{
|
||||
b.Navigation("MediaFiles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", b =>
|
||||
{
|
||||
b.Navigation("Instructions");
|
||||
|
||||
b.Navigation("MediaFiles");
|
||||
|
||||
b.Navigation("RecipeIngredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Shoppinglist.ShoppingList", b =>
|
||||
{
|
||||
b.Navigation("RecipeIngredientShoppingLists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Unit.Unit", b =>
|
||||
{
|
||||
b.Navigation("RecipeIngredient");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Francesco.Recipes.World.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class CreateNewTableRecipeIngredientShoppinglist : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
}
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "IngredientsShoppingLists");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "Number",
|
||||
table: "Instructions",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(max)");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "RecipeIngredientsShoppingLists",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
ShoppingListId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
RecipeIngredientId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
IngredientId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_RecipeIngredientsShoppingLists", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_RecipeIngredientsShoppingLists_Ingredients_IngredientId",
|
||||
column: x => x.IngredientId,
|
||||
principalTable: "Ingredients",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_RecipeIngredientsShoppingLists_RecipeIngredients_RecipeIngredientId",
|
||||
column: x => x.RecipeIngredientId,
|
||||
principalTable: "RecipeIngredients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_RecipeIngredientsShoppingLists_ShoppingLists_ShoppingListId",
|
||||
column: x => x.ShoppingListId,
|
||||
principalTable: "ShoppingLists",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_RecipeIngredientsShoppingLists_IngredientId",
|
||||
table: "RecipeIngredientsShoppingLists",
|
||||
column: "IngredientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_RecipeIngredientsShoppingLists_RecipeIngredientId",
|
||||
table: "RecipeIngredientsShoppingLists",
|
||||
column: "RecipeIngredientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_RecipeIngredientsShoppingLists_ShoppingListId",
|
||||
table: "RecipeIngredientsShoppingLists",
|
||||
column: "ShoppingListId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
}
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "RecipeIngredientsShoppingLists");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Number",
|
||||
table: "Instructions",
|
||||
type: "nvarchar(max)",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "IngredientsShoppingLists",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
IngredientId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
ShoppinglistId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_IngredientsShoppingLists", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_IngredientsShoppingLists_Ingredients_IngredientId",
|
||||
column: x => x.IngredientId,
|
||||
principalTable: "Ingredients",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_IngredientsShoppingLists_ShoppingLists_ShoppinglistId",
|
||||
column: x => x.ShoppinglistId,
|
||||
principalTable: "ShoppingLists",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_IngredientsShoppingLists_IngredientId",
|
||||
table: "IngredientsShoppingLists",
|
||||
column: "IngredientId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_IngredientsShoppingLists_ShoppinglistId",
|
||||
table: "IngredientsShoppingLists",
|
||||
column: "ShoppinglistId");
|
||||
}
|
||||
}
|
||||
}
|
||||
+572
@@ -0,0 +1,572 @@
|
||||
// <auto-generated />
|
||||
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Francesco.Recipes.World.Migrations
|
||||
{
|
||||
[DbContext(typeof(FrancescosRecipesWorldDbContext))]
|
||||
[Migration("20250402120842_UpdateShoppingLIstLogic")]
|
||||
partial class UpdateShoppingLIstLogic
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.11")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Category.Category", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Categories");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = new Guid("b248244f-f21c-4555-a14d-5dd49a2717cf"),
|
||||
Name = "Vorspeisen & Snacks"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("5186c5d6-5aff-4a6e-baf4-b61b72d889fe"),
|
||||
Name = "Erste Gänge"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("abbc6bb0-97ea-49f5-b31e-6507eb784fd1"),
|
||||
Name = "Hauptgerichte"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("90deec39-dcd0-422d-9018-ac8389e332e1"),
|
||||
Name = "Desserts & Süßspeisen"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("28e39168-701a-4084-81da-d96c987c462f"),
|
||||
Name = "Beilagen & Salate"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("20585b74-4805-4aff-a6df-aa6b7af04ff1"),
|
||||
Name = "Kuchen"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("adfb75ce-3ef3-428b-a5ca-b0c4c619d5ec"),
|
||||
Name = "Hefegebäck & Brot"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("d332f88d-d241-48c5-a2f2-bfd124eada7e"),
|
||||
Name = "Soßen & Saucen"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("23b1c740-e427-44f9-a6ea-d33d3f30f05a"),
|
||||
Name = "Marmeladen & Eingemachtes"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("0a91a200-dc76-4e00-b38c-b38cab5b69d7"),
|
||||
Name = "Getränke"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Favorit.Favorit", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Favorits");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Ingredient.Ingredient", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Ingredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList.RecipeIngredientShoppingList", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("IngredientId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<bool>("IsChecked")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<Guid>("RecipeIngredientId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("RecipeShoppingListId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IngredientId");
|
||||
|
||||
b.HasIndex("RecipeIngredientId");
|
||||
|
||||
b.HasIndex("RecipeShoppingListId");
|
||||
|
||||
b.ToTable("RecipeIngredientsShoppingLists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Instruction.Instruction", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid>("RecipeId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RecipeId");
|
||||
|
||||
b.ToTable("Instructions");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.MediaFile.MediaFile", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<byte[]>("Data")
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.Property<string>("FileName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<Guid>("InstructionId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("MimeType")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<Guid?>("RecipeId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InstructionId");
|
||||
|
||||
b.HasIndex("RecipeId");
|
||||
|
||||
b.ToTable("MediaFiles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("CategoryId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<TimeSpan>("CookingTime")
|
||||
.HasColumnType("time");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<int>("Difficulty")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid>("FavoritId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<bool>("IsFavorite")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<DateTime?>("ModifiedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<TimeSpan>("PreparationTime")
|
||||
.HasColumnType("time");
|
||||
|
||||
b.Property<int>("Servings")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CategoryId");
|
||||
|
||||
b.HasIndex("FavoritId");
|
||||
|
||||
b.ToTable("Recipes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.RecipeIngredient.RecipeIngredient", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("IngredientId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid>("RecipeId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("UnitId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IngredientId");
|
||||
|
||||
b.HasIndex("RecipeId");
|
||||
|
||||
b.HasIndex("UnitId");
|
||||
|
||||
b.ToTable("RecipeIngredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList.RecipeShoppingList", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("RecipeId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("ShoppingListId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RecipeId");
|
||||
|
||||
b.HasIndex("ShoppingListId");
|
||||
|
||||
b.ToTable("RecipeShoppingLists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Shoppinglist.ShoppingList", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<DateTime?>("ModifiedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ShoppingLists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Unit.Unit", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Symbol")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Units");
|
||||
|
||||
b.HasData(
|
||||
new
|
||||
{
|
||||
Id = new Guid("62eb2c11-8768-46fb-9db0-c77f940bb4aa"),
|
||||
Name = "liter",
|
||||
Symbol = "l"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("6c41f7e6-ca75-49cc-8541-cebd5a9c560b"),
|
||||
Name = "gramm",
|
||||
Symbol = "g"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("7e3d1b86-ac48-45d6-814e-d3492a86db1d"),
|
||||
Name = "kilogramm",
|
||||
Symbol = "kg"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("0c3648ec-4981-42c8-abf8-18bf1a2ff4c2"),
|
||||
Name = "stücke",
|
||||
Symbol = "stk"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("d82f4abd-e4e4-4104-a9c0-1acdeaa701f5"),
|
||||
Name = "blatt",
|
||||
Symbol = "blatt"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("df5cb4c3-4de6-4c6f-be8c-da41b2986408"),
|
||||
Name = "messerspitze",
|
||||
Symbol = "msp"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("e45a3af2-2ed6-4ac4-b06f-b4175663a7be"),
|
||||
Name = "stange",
|
||||
Symbol = "stange"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("7ea2f51d-7493-4f19-a663-1f309186d3ae"),
|
||||
Name = "bund",
|
||||
Symbol = "bund"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("66556e0e-eb2b-4bc3-9a56-135dd508ed09"),
|
||||
Name = "zehe",
|
||||
Symbol = "zehe"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("2e9894ac-14fa-43fd-ba07-35cdd8ebd461"),
|
||||
Name = "teelöffel",
|
||||
Symbol = "TL"
|
||||
},
|
||||
new
|
||||
{
|
||||
Id = new Guid("24a91b89-3389-4465-89e4-2f70d2ea6fd7"),
|
||||
Name = "esslöffel",
|
||||
Symbol = "EL"
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList.RecipeIngredientShoppingList", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Ingredient.Ingredient", null)
|
||||
.WithMany("IngredientShoppingLists")
|
||||
.HasForeignKey("IngredientId");
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.RecipeIngredient.RecipeIngredient", "RecipeIngredient")
|
||||
.WithMany()
|
||||
.HasForeignKey("RecipeIngredientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList.RecipeShoppingList", "RecipeShoppingList")
|
||||
.WithMany("SelectedIngredients")
|
||||
.HasForeignKey("RecipeShoppingListId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("RecipeIngredient");
|
||||
|
||||
b.Navigation("RecipeShoppingList");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Instruction.Instruction", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", "Recipe")
|
||||
.WithMany("Instructions")
|
||||
.HasForeignKey("RecipeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Recipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.MediaFile.MediaFile", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Instruction.Instruction", "Instruction")
|
||||
.WithMany("MediaFiles")
|
||||
.HasForeignKey("InstructionId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", "Recipe")
|
||||
.WithMany("MediaFiles")
|
||||
.HasForeignKey("RecipeId");
|
||||
|
||||
b.Navigation("Instruction");
|
||||
|
||||
b.Navigation("Recipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Category.Category", "Category")
|
||||
.WithMany("Recipes")
|
||||
.HasForeignKey("CategoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Favorit.Favorit", "Favorit")
|
||||
.WithMany("Recipe")
|
||||
.HasForeignKey("FavoritId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Category");
|
||||
|
||||
b.Navigation("Favorit");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.RecipeIngredient.RecipeIngredient", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Ingredient.Ingredient", "Ingredient")
|
||||
.WithMany("RecipeIngredients")
|
||||
.HasForeignKey("IngredientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", "Recipe")
|
||||
.WithMany("RecipeIngredients")
|
||||
.HasForeignKey("RecipeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Unit.Unit", "Unit")
|
||||
.WithMany("RecipeIngredient")
|
||||
.HasForeignKey("UnitId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Ingredient");
|
||||
|
||||
b.Navigation("Recipe");
|
||||
|
||||
b.Navigation("Unit");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList.RecipeShoppingList", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", "Recipe")
|
||||
.WithMany()
|
||||
.HasForeignKey("RecipeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Shoppinglist.ShoppingList", "ShoppingList")
|
||||
.WithMany("RecipeIngredientShoppingLists")
|
||||
.HasForeignKey("ShoppingListId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Recipe");
|
||||
|
||||
b.Navigation("ShoppingList");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Category.Category", b =>
|
||||
{
|
||||
b.Navigation("Recipes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Favorit.Favorit", b =>
|
||||
{
|
||||
b.Navigation("Recipe");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Ingredient.Ingredient", b =>
|
||||
{
|
||||
b.Navigation("IngredientShoppingLists");
|
||||
|
||||
b.Navigation("RecipeIngredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Instruction.Instruction", b =>
|
||||
{
|
||||
b.Navigation("MediaFiles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", b =>
|
||||
{
|
||||
b.Navigation("Instructions");
|
||||
|
||||
b.Navigation("MediaFiles");
|
||||
|
||||
b.Navigation("RecipeIngredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList.RecipeShoppingList", b =>
|
||||
{
|
||||
b.Navigation("SelectedIngredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Shoppinglist.ShoppingList", b =>
|
||||
{
|
||||
b.Navigation("RecipeIngredientShoppingLists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Unit.Unit", b =>
|
||||
{
|
||||
b.Navigation("RecipeIngredient");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Francesco.Recipes.World.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UpdateShoppingLIstLogic : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
}
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_RecipeIngredientsShoppingLists_ShoppingLists_ShoppingListId",
|
||||
table: "RecipeIngredientsShoppingLists");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Recipes_Categories_CategoryId",
|
||||
table: "Recipes");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "ShoppingListId",
|
||||
table: "RecipeIngredientsShoppingLists",
|
||||
newName: "RecipeShoppingListId");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_RecipeIngredientsShoppingLists_ShoppingListId",
|
||||
table: "RecipeIngredientsShoppingLists",
|
||||
newName: "IX_RecipeIngredientsShoppingLists_RecipeShoppingListId");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "CategoryId",
|
||||
table: "Recipes",
|
||||
type: "uniqueidentifier",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uniqueidentifier",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsChecked",
|
||||
table: "RecipeIngredientsShoppingLists",
|
||||
type: "bit",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "RecipeShoppingLists",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
ShoppingListId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
RecipeId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_RecipeShoppingLists", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_RecipeShoppingLists_Recipes_RecipeId",
|
||||
column: x => x.RecipeId,
|
||||
principalTable: "Recipes",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_RecipeShoppingLists_ShoppingLists_ShoppingListId",
|
||||
column: x => x.ShoppingListId,
|
||||
principalTable: "ShoppingLists",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_RecipeShoppingLists_RecipeId",
|
||||
table: "RecipeShoppingLists",
|
||||
column: "RecipeId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_RecipeShoppingLists_ShoppingListId",
|
||||
table: "RecipeShoppingLists",
|
||||
column: "ShoppingListId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_RecipeIngredientsShoppingLists_RecipeShoppingLists_RecipeShoppingListId",
|
||||
table: "RecipeIngredientsShoppingLists",
|
||||
column: "RecipeShoppingListId",
|
||||
principalTable: "RecipeShoppingLists",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.NoAction);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Recipes_Categories_CategoryId",
|
||||
table: "Recipes",
|
||||
column: "CategoryId",
|
||||
principalTable: "Categories",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(migrationBuilder));
|
||||
}
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_RecipeIngredientsShoppingLists_RecipeShoppingLists_RecipeShoppingListId",
|
||||
table: "RecipeIngredientsShoppingLists");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Recipes_Categories_CategoryId",
|
||||
table: "Recipes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "RecipeShoppingLists");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsChecked",
|
||||
table: "RecipeIngredientsShoppingLists");
|
||||
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "RecipeShoppingListId",
|
||||
table: "RecipeIngredientsShoppingLists",
|
||||
newName: "ShoppingListId");
|
||||
|
||||
migrationBuilder.RenameIndex(
|
||||
name: "IX_RecipeIngredientsShoppingLists_RecipeShoppingListId",
|
||||
table: "RecipeIngredientsShoppingLists",
|
||||
newName: "IX_RecipeIngredientsShoppingLists_ShoppingListId");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "CategoryId",
|
||||
table: "Recipes",
|
||||
type: "uniqueidentifier",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uniqueidentifier");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_RecipeIngredientsShoppingLists_ShoppingLists_ShoppingListId",
|
||||
table: "RecipeIngredientsShoppingLists",
|
||||
column: "ShoppingListId",
|
||||
principalTable: "ShoppingLists",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Recipes_Categories_CategoryId",
|
||||
table: "Recipes",
|
||||
column: "CategoryId",
|
||||
principalTable: "Categories",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
+83
-22
@@ -1,9 +1,10 @@
|
||||
// <auto-generated />
|
||||
|
||||
using System;
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
@@ -117,25 +118,33 @@ namespace Francesco.Recipes.World.Migrations
|
||||
b.ToTable("Ingredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList.IngredientsShoppingList", b =>
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList.RecipeIngredientShoppingList", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("IngredientId")
|
||||
b.Property<Guid?>("IngredientId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("ShoppinglistId")
|
||||
b.Property<bool>("IsChecked")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<Guid>("RecipeIngredientId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("RecipeShoppingListId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IngredientId");
|
||||
|
||||
b.HasIndex("ShoppinglistId");
|
||||
b.HasIndex("RecipeIngredientId");
|
||||
|
||||
b.ToTable("IngredientsShoppingLists");
|
||||
b.HasIndex("RecipeShoppingListId");
|
||||
|
||||
b.ToTable("RecipeIngredientsShoppingLists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Instruction.Instruction", b =>
|
||||
@@ -148,9 +157,8 @@ namespace Francesco.Recipes.World.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("Number")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
b.Property<int>("Number")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<Guid>("RecipeId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
@@ -198,7 +206,7 @@ namespace Francesco.Recipes.World.Migrations
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid?>("CategoryId")
|
||||
b.Property<Guid>("CategoryId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<TimeSpan>("CookingTime")
|
||||
@@ -270,6 +278,27 @@ namespace Francesco.Recipes.World.Migrations
|
||||
b.ToTable("RecipeIngredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList.RecipeShoppingList", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("RecipeId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.Property<Guid>("ShoppingListId")
|
||||
.HasColumnType("uniqueidentifier");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RecipeId");
|
||||
|
||||
b.HasIndex("ShoppingListId");
|
||||
|
||||
b.ToTable("RecipeShoppingLists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Shoppinglist.ShoppingList", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@@ -374,23 +403,27 @@ namespace Francesco.Recipes.World.Migrations
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList.IngredientsShoppingList", b =>
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList.RecipeIngredientShoppingList", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Ingredient.Ingredient", "Ingredient")
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Ingredient.Ingredient", null)
|
||||
.WithMany("IngredientShoppingLists")
|
||||
.HasForeignKey("IngredientId")
|
||||
.HasForeignKey("IngredientId");
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.RecipeIngredient.RecipeIngredient", "RecipeIngredient")
|
||||
.WithMany()
|
||||
.HasForeignKey("RecipeIngredientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Shoppinglist.ShoppingList", "Shoppinglist")
|
||||
.WithMany("IngredientsShoppingLists")
|
||||
.HasForeignKey("ShoppinglistId")
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList.RecipeShoppingList", "RecipeShoppingList")
|
||||
.WithMany("SelectedIngredients")
|
||||
.HasForeignKey("RecipeShoppingListId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Ingredient");
|
||||
b.Navigation("RecipeIngredient");
|
||||
|
||||
b.Navigation("Shoppinglist");
|
||||
b.Navigation("RecipeShoppingList");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Instruction.Instruction", b =>
|
||||
@@ -423,9 +456,11 @@ namespace Francesco.Recipes.World.Migrations
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Category.Category", null)
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Category.Category", "Category")
|
||||
.WithMany("Recipes")
|
||||
.HasForeignKey("CategoryId");
|
||||
.HasForeignKey("CategoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Favorit.Favorit", "Favorit")
|
||||
.WithMany("Recipe")
|
||||
@@ -433,6 +468,8 @@ namespace Francesco.Recipes.World.Migrations
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Category");
|
||||
|
||||
b.Navigation("Favorit");
|
||||
});
|
||||
|
||||
@@ -463,6 +500,25 @@ namespace Francesco.Recipes.World.Migrations
|
||||
b.Navigation("Unit");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList.RecipeShoppingList", b =>
|
||||
{
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe", "Recipe")
|
||||
.WithMany()
|
||||
.HasForeignKey("RecipeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Francesco.Recipes.World.Models.BackendModels.Shoppinglist.ShoppingList", "ShoppingList")
|
||||
.WithMany("RecipeIngredientShoppingLists")
|
||||
.HasForeignKey("ShoppingListId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Recipe");
|
||||
|
||||
b.Navigation("ShoppingList");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Category.Category", b =>
|
||||
{
|
||||
b.Navigation("Recipes");
|
||||
@@ -494,9 +550,14 @@ namespace Francesco.Recipes.World.Migrations
|
||||
b.Navigation("RecipeIngredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList.RecipeShoppingList", b =>
|
||||
{
|
||||
b.Navigation("SelectedIngredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Shoppinglist.ShoppingList", b =>
|
||||
{
|
||||
b.Navigation("IngredientsShoppingLists");
|
||||
b.Navigation("RecipeIngredientShoppingLists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Francesco.Recipes.World.Models.BackendModels.Unit.Unit", b =>
|
||||
|
||||
@@ -11,6 +11,6 @@
|
||||
|
||||
public virtual ICollection<RecipeIngredient> RecipeIngredients { get; set; } = new List<RecipeIngredient>();
|
||||
|
||||
public virtual ICollection<IngredientsShoppingList> IngredientShoppingLists { get; set; } = new List<IngredientsShoppingList>();
|
||||
public virtual ICollection<RecipeIngredientShoppingList> IngredientShoppingLists { get; set; } = new List<RecipeIngredientShoppingList>();
|
||||
}
|
||||
}
|
||||
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
namespace Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList
|
||||
{
|
||||
using Francesco.Recipes.World.Models.BackendModels.Ingredient;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Shoppinglist;
|
||||
|
||||
public class IngredientsShoppingList
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public ShoppingList Shoppinglist { get; set; } = new ();
|
||||
|
||||
public Ingredient Ingredient { get; set; } = new ();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,22 @@
|
||||
namespace Francesco.Recipes.World.Models.BackendModels.Recipe
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Francesco.Recipes.World.Models.BackendModels.Recipe
|
||||
{
|
||||
public enum Difficulty
|
||||
{
|
||||
VeryEasy = 1,
|
||||
[Display(Name = "Sehr einfach")]
|
||||
VeryEasy = 0,
|
||||
|
||||
Easy = 2,
|
||||
[Display(Name = "Einfach")]
|
||||
Easy = 1,
|
||||
|
||||
Medium = 3,
|
||||
[Display(Name = "Mittel")]
|
||||
Medium = 2,
|
||||
|
||||
Hard = 4,
|
||||
[Display(Name = "Schwer")]
|
||||
Hard = 3,
|
||||
|
||||
Expert = 5,
|
||||
[Display(Name = "Experte")]
|
||||
Expert = 4,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
|
||||
using Francesco.Recipes.World.Models.BackendModels.Category;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Favorit;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Instruction;
|
||||
using Francesco.Recipes.World.Models.BackendModels.MediaFile;
|
||||
@@ -34,4 +35,6 @@ public class Recipe : ITimeStampedEntity
|
||||
public virtual ICollection<MediaFile> MediaFiles { get; set; } = new List<MediaFile>();
|
||||
|
||||
public virtual Favorit Favorit { get; set; } = new ();
|
||||
|
||||
public virtual Category Category { get; set; } = new ();
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
namespace Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList
|
||||
{
|
||||
using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient;
|
||||
using Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList;
|
||||
|
||||
public class RecipeIngredientShoppingList
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public virtual RecipeShoppingList RecipeShoppingList { get; set; } = new ();
|
||||
|
||||
public virtual RecipeIngredient RecipeIngredient { get; set; } = new ();
|
||||
|
||||
public bool IsChecked { get; set; } = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList
|
||||
{
|
||||
using Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Shoppinglist;
|
||||
|
||||
public class RecipeShoppingList
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public virtual ShoppingList ShoppingList { get; set; } = new ShoppingList();
|
||||
|
||||
public virtual Recipe Recipe { get; set; } = new Recipe();
|
||||
|
||||
public virtual ICollection<RecipeIngredientShoppingList> SelectedIngredients { get; set; } = new List<RecipeIngredientShoppingList>();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Francesco.Recipes.World.Models.BackendModels.Shoppinglist
|
||||
{
|
||||
using Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList;
|
||||
using Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList;
|
||||
|
||||
public class ShoppingList : ITimeStampedEntity
|
||||
{
|
||||
@@ -10,6 +10,6 @@
|
||||
|
||||
public DateTime? ModifiedAt { get; set; }
|
||||
|
||||
public virtual ICollection<IngredientsShoppingList> IngredientsShoppingLists { get; set; } = new List<IngredientsShoppingList>();
|
||||
public virtual ICollection<RecipeShoppingList> RecipeShoppingList { get; set; } = new List<RecipeShoppingList>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Francesco.Recipes.World.Models
|
||||
{
|
||||
public class CreateOrAddIngredientRequestModel
|
||||
{
|
||||
public Guid RecipeId { get; set; }
|
||||
|
||||
public List<Guid> IngredientIds { get; set; } = new ();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
using Francesco.Recipes.World.Data;
|
||||
|
||||
using Francesco.Recipes.World.Repositories;
|
||||
|
||||
using FrancescoRecipesWorld.Repositories;
|
||||
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
using Francesco.Recipes.World.Repositories.Category;
|
||||
using Francesco.Recipes.World.Repositories.Favorit;
|
||||
using Francesco.Recipes.World.Repositories.Ingredient;
|
||||
using Francesco.Recipes.World.Repositories.Instruction;
|
||||
using Francesco.Recipes.World.Repositories.MediaFile;
|
||||
using Francesco.Recipes.World.Repositories.Recipe;
|
||||
using Francesco.Recipes.World.Repositories.ShoppingList;
|
||||
using Francesco.Recipes.World.Repositories.Unit;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
@@ -20,9 +21,6 @@ var connectionString = builder.Configuration.GetConnectionString("FrancescosReci
|
||||
services.AddDbContext<FrancescosRecipesWorldDbContext>(options =>
|
||||
options.UseSqlServer(connectionString));
|
||||
|
||||
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
|
||||
.AddEntityFrameworkStores<FrancescosRecipesWorldDbContext>();
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllersWithViews();
|
||||
|
||||
@@ -34,6 +32,14 @@ builder.Services.AddScoped<IUnitRepository, UnitRepository>();
|
||||
|
||||
builder.Services.AddScoped<ICategoryRepository, CategoryRepository>();
|
||||
|
||||
builder.Services.AddScoped<IShoppingListRepository, ShoppingListRepository>();
|
||||
|
||||
builder.Services.AddScoped<IMediaFileRepository, MediaFileRepository>();
|
||||
|
||||
builder.Services.AddScoped<IInstructionRepository, InstructionRepository>();
|
||||
|
||||
builder.Services.AddScoped<IFavoriteRepository, FavoritRepository>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
@@ -45,20 +51,18 @@ if (!app.Environment.IsDevelopment())
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
Console.WriteLine("Standard Numeric Format Specifiers");
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapDefaultControllerRoute();
|
||||
|
||||
app.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
pattern: "{controller=Category}/{action=Index}/{id?}");
|
||||
|
||||
app.Run();
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Category
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Category;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class CategoryRepository : ICategoryRepository
|
||||
{
|
||||
private readonly FrancescosRecipesWorldDbContext _context;
|
||||
|
||||
public CategoryRepository(FrancescosRecipesWorldDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Category> GetCategoryByIdAsync(Guid categoryId)
|
||||
{
|
||||
var category = await _context.Categories.FindAsync(categoryId);
|
||||
return category ?? throw new InvalidDataException($"Category {categoryId} not found.");
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Category>> GetAllCategoriesAsync()
|
||||
{
|
||||
return await _context.Categories.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Recipe>> GetRecipesByCategoryAsync(Guid categoryId)
|
||||
{
|
||||
var category = await _context.Categories
|
||||
.Include(c => c.Recipes)
|
||||
.FirstOrDefaultAsync(c => c.Id == categoryId);
|
||||
|
||||
if (category == null)
|
||||
{
|
||||
throw new InvalidDataException($"Category {categoryId} not found.");
|
||||
}
|
||||
|
||||
return category.Recipes;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Category>> GetAllCategoriesWithRecipesAsync()
|
||||
{
|
||||
return await _context.Categories
|
||||
.Include(c => c.Recipes)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Category
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Category;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
|
||||
public interface ICategoryRepository
|
||||
{
|
||||
Task<Category> GetCategoryByIdAsync(Guid categoryId);
|
||||
|
||||
Task<IEnumerable<Category>> GetAllCategoriesAsync();
|
||||
|
||||
Task<IEnumerable<Recipe>> GetRecipesByCategoryAsync(Guid categoryId);
|
||||
|
||||
Task<IEnumerable<Category>> GetAllCategoriesWithRecipesAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Favorit
|
||||
{
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class FavoritRepository : IFavoriteRepository
|
||||
{
|
||||
private readonly FrancescosRecipesWorldDbContext _context;
|
||||
|
||||
public FavoritRepository(FrancescosRecipesWorldDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Recipe>> GetFavoriteRecipesAsync()
|
||||
{
|
||||
return await _context.Recipes
|
||||
.Where(r => r.IsFavorite)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> IsFavoriteAsync(Guid recipeId)
|
||||
{
|
||||
return await _context.Recipes
|
||||
.AnyAsync(r => r.Id == recipeId && r.IsFavorite);
|
||||
}
|
||||
|
||||
public async Task AddFavoriteAsync(Guid recipeId)
|
||||
{
|
||||
var recipe = await _context.Recipes.FindAsync(recipeId);
|
||||
if (recipe != null && !recipe.IsFavorite)
|
||||
{
|
||||
recipe.IsFavorite = true;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RemoveFavoriteAsync(Guid recipeId)
|
||||
{
|
||||
var recipe = await _context.Recipes.FindAsync(recipeId);
|
||||
if (recipe != null && recipe.IsFavorite)
|
||||
{
|
||||
recipe.IsFavorite = false;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Favorit
|
||||
{
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
|
||||
public interface IFavoriteRepository
|
||||
{
|
||||
Task<IEnumerable<Recipe>> GetFavoriteRecipesAsync();
|
||||
|
||||
Task<bool> IsFavoriteAsync(Guid recipeId);
|
||||
|
||||
Task AddFavoriteAsync(Guid recipeId);
|
||||
|
||||
Task RemoveFavoriteAsync(Guid recipeId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Ingredient
|
||||
{
|
||||
using Francesco.Recipes.World.Models.BackendModels.Ingredient;
|
||||
using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient;
|
||||
|
||||
public interface IIngredientRepository
|
||||
{
|
||||
Task UpdateIngredientAsync(Ingredient ingredient);
|
||||
|
||||
Task<List<RecipeIngredient>> GetIngredientsByRecipeIdAsync(Guid recipeId);
|
||||
|
||||
Task<List<Ingredient>> GetIngredientsByNameAsync(string name);
|
||||
|
||||
Task<Ingredient> GetIngredientByIdAsync(Guid ingredientId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
|
||||
namespace Francesco.Recipes.World.Repositories.Ingredient
|
||||
{
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Ingredient;
|
||||
using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class IngredientRepository : IIngredientRepository
|
||||
{
|
||||
private readonly FrancescosRecipesWorldDbContext _context;
|
||||
|
||||
public IngredientRepository(
|
||||
FrancescosRecipesWorldDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task UpdateRecipeIngredientAsync(RecipeIngredient recipeIngredient)
|
||||
{
|
||||
if (recipeIngredient == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(recipeIngredient));
|
||||
}
|
||||
|
||||
var existingRecipeIngredient = await _context.RecipeIngredients
|
||||
.Include(ri => ri.Ingredient)
|
||||
.Include(ri => ri.Unit)
|
||||
.FirstOrDefaultAsync(ri => ri.Id == recipeIngredient.Id);
|
||||
|
||||
if (existingRecipeIngredient == null)
|
||||
{
|
||||
throw new InvalidOperationException($"RecipeIngredient with ID {recipeIngredient.Id} not found.");
|
||||
}
|
||||
|
||||
existingRecipeIngredient.Quantity = recipeIngredient.Quantity;
|
||||
existingRecipeIngredient.Ingredient = recipeIngredient.Ingredient;
|
||||
existingRecipeIngredient.Unit = recipeIngredient.Unit;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<List<Ingredient>> GetIngredientsByNameAsync(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return await _context.Ingredients.ToListAsync();
|
||||
}
|
||||
|
||||
return await _context.Ingredients
|
||||
.Where(i => i.Name.ToLower().Contains(name.ToLower()))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<RecipeIngredient>> GetIngredientsByRecipeIdAsync(Guid recipeId)
|
||||
{
|
||||
return await _context.RecipeIngredients
|
||||
.Include(ri => ri.Ingredient)
|
||||
.Include(ri => ri.Unit)
|
||||
.Where(ri => ri.Recipe.Id == recipeId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateIngredientAsync(Ingredient ingredient)
|
||||
{
|
||||
if (ingredient == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ingredient));
|
||||
}
|
||||
|
||||
var existingIngredient = await _context.Ingredients.FindAsync(ingredient.Id);
|
||||
if (existingIngredient == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Ingredient with ID {ingredient.Id} not found.");
|
||||
}
|
||||
|
||||
existingIngredient.Name = ingredient.Name;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<Ingredient> GetIngredientByIdAsync(Guid ingredientId)
|
||||
{
|
||||
var ingredient = await _context.Ingredients.FindAsync(ingredientId);
|
||||
return ingredient ?? throw new InvalidDataException($"Address {ingredientId} not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Instruction
|
||||
{
|
||||
using Francesco.Recipes.World.Models.BackendModels.Instruction;
|
||||
|
||||
public interface IInstructionRepository
|
||||
{
|
||||
Task<Instruction> GetInstructionAsync(Guid instructionId);
|
||||
|
||||
Task<Instruction> CreateInstructionToRecipeAsync(Guid recipeId, string description, int number);
|
||||
|
||||
Task<List<Instruction>> GetInstructionsByRecipeIdAsync(Guid recipeId);
|
||||
|
||||
Task RemoveInstructionFromRecipeAsync(Guid recipeId, Guid instructionId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Instruction
|
||||
{
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Instruction;
|
||||
using Francesco.Recipes.World.Repositories.Recipe;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class InstructionRepository : IInstructionRepository
|
||||
{
|
||||
private readonly FrancescosRecipesWorldDbContext _context;
|
||||
private readonly IRecipeRepository _recipeRepository;
|
||||
|
||||
public InstructionRepository(FrancescosRecipesWorldDbContext context, IRecipeRepository recipeRepository)
|
||||
{
|
||||
_recipeRepository = recipeRepository;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Instruction> GetInstructionAsync(Guid instructionId)
|
||||
{
|
||||
var instruction = await _context.Instructions.FindAsync(instructionId);
|
||||
return instruction ?? throw new InvalidDataException($"Instruction {instructionId} not found.");
|
||||
}
|
||||
|
||||
public async Task<Instruction> CreateInstructionToRecipeAsync(Guid recipeId, string description, int number)
|
||||
{
|
||||
var recipe = await _recipeRepository.GetRecipeAsync(recipeId);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(description))
|
||||
{
|
||||
throw new ArgumentException("Description cannot be empty", nameof(description));
|
||||
}
|
||||
|
||||
if (number <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(number), "Number must be greater than 0.");
|
||||
}
|
||||
|
||||
var newInstruction = new Instruction
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Description = description,
|
||||
Number = number,
|
||||
Recipe = recipe,
|
||||
};
|
||||
|
||||
_context.Instructions.Add(newInstruction);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return newInstruction;
|
||||
}
|
||||
|
||||
public async Task RemoveInstructionFromRecipeAsync(Guid recipeId, Guid instructionId)
|
||||
{
|
||||
var recipe = await _recipeRepository.GetRecipeAsync(recipeId);
|
||||
|
||||
if (recipe.Instructions == null || !recipe.Instructions.Any())
|
||||
{
|
||||
await _context.Entry(recipe)
|
||||
.Collection(r => r.Instructions)
|
||||
.LoadAsync();
|
||||
}
|
||||
|
||||
var instructionToRemove = recipe.Instructions?.FirstOrDefault(i => i.Id == instructionId);
|
||||
|
||||
if (instructionToRemove != null)
|
||||
{
|
||||
recipe.Instructions?.Remove(instructionToRemove);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<Instruction>> GetInstructionsByRecipeIdAsync(Guid recipeId)
|
||||
{
|
||||
return await _context.Instructions
|
||||
.Include(i => i.Recipe)
|
||||
.Where(i => i.Recipe.Id == recipeId)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Francesco.Recipes.World.Repositories.MediaFile
|
||||
{
|
||||
public interface IMediaFileRepository
|
||||
{
|
||||
Task ReplaceInstructionImageAsync(Guid instructionId, Guid mediaFileIdToReplace, string fileName, string mimeType, byte[] newMediaData);
|
||||
|
||||
Task UploadInstructionImageAsync(Guid instructionId, IFormFile? photo);
|
||||
|
||||
Task UploadRecipeMediaAsync(Guid recipeId, IFormFile mediaFile);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
namespace Francesco.Recipes.World.Repositories.MediaFile
|
||||
{
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Francesco.Recipes.World.Models.BackendModels.MediaFile;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
using Francesco.Recipes.World.Repositories.Instruction;
|
||||
using Francesco.Recipes.World.Repositories.Recipe;
|
||||
|
||||
public class MediaFileRepository : IMediaFileRepository
|
||||
{
|
||||
private readonly IInstructionRepository _instructionRepository;
|
||||
private readonly FrancescosRecipesWorldDbContext _context;
|
||||
private readonly IRecipeRepository _recipeRepository;
|
||||
|
||||
public MediaFileRepository(IInstructionRepository instructionRepository, FrancescosRecipesWorldDbContext context, IRecipeRepository recipeRepository)
|
||||
{
|
||||
_instructionRepository = instructionRepository;
|
||||
_context = context;
|
||||
_recipeRepository = recipeRepository;
|
||||
}
|
||||
|
||||
public async Task ReplaceInstructionImageAsync(Guid instructionId, Guid mediaFileIdToReplace, string fileName, string mimeType, byte[] newMediaData)
|
||||
{
|
||||
var instruction = await _instructionRepository.GetInstructionAsync(instructionId);
|
||||
|
||||
var mediaToReplace = instruction.MediaFiles.FirstOrDefault(m => m.Id == mediaFileIdToReplace);
|
||||
if (mediaToReplace == null)
|
||||
{
|
||||
throw new InvalidOperationException("The specified media file does not exist.");
|
||||
}
|
||||
|
||||
_context.MediaFiles.Remove(mediaToReplace);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
var newMedia = new MediaFile
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
FileName = fileName,
|
||||
MimeType = mimeType,
|
||||
Data = newMediaData,
|
||||
Instruction = instruction,
|
||||
};
|
||||
|
||||
_context.MediaFiles.Add(newMedia);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UploadInstructionImageAsync(Guid instructionId, IFormFile? photo)
|
||||
{
|
||||
if (photo is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(photo));
|
||||
}
|
||||
|
||||
var instruction = await _instructionRepository.GetInstructionAsync(instructionId);
|
||||
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await photo.CopyToAsync(memoryStream);
|
||||
|
||||
var instructionImage = new MediaFile
|
||||
{
|
||||
FileName = photo.FileName,
|
||||
MimeType = photo.ContentType,
|
||||
Data = memoryStream.ToArray(),
|
||||
Instruction = instruction,
|
||||
};
|
||||
|
||||
_context.Add(instructionImage);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UploadRecipeMediaAsync(Guid recipeId, IFormFile mediaFile)
|
||||
{
|
||||
if (mediaFile == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mediaFile));
|
||||
}
|
||||
|
||||
var recipe = await _recipeRepository.GetRecipeAsync(recipeId);
|
||||
|
||||
var isImage = mediaFile.ContentType.StartsWith("image/");
|
||||
var isVideo = mediaFile.ContentType.StartsWith("video/");
|
||||
|
||||
if (!isImage && !isVideo)
|
||||
{
|
||||
throw new InvalidOperationException("Only image or video files are allowed.");
|
||||
}
|
||||
|
||||
if (isImage)
|
||||
{
|
||||
await RemoveExistingMediaAsync(recipe, "image/");
|
||||
}
|
||||
else
|
||||
{
|
||||
await RemoveExistingMediaAsync(recipe, "video/");
|
||||
}
|
||||
|
||||
using var memoryStream = new MemoryStream();
|
||||
await mediaFile.CopyToAsync(memoryStream);
|
||||
|
||||
var newMedia = new MediaFile
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
FileName = mediaFile.FileName,
|
||||
MimeType = mediaFile.ContentType,
|
||||
Data = memoryStream.ToArray(),
|
||||
Recipe = recipe,
|
||||
};
|
||||
|
||||
_context.MediaFiles.Add(newMedia);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task RemoveExistingMediaAsync(Recipe recipe, string mediaTypePrefix)
|
||||
{
|
||||
var existingMedia = recipe.MediaFiles?.FirstOrDefault(m => m.MimeType?.StartsWith(mediaTypePrefix) == true);
|
||||
if (existingMedia != null)
|
||||
{
|
||||
_context.MediaFiles.Remove(existingMedia);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Recipe
|
||||
{
|
||||
using Francesco.Recipes.World.Models.BackendModels.Category;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
|
||||
public interface IRecipeRepository
|
||||
{
|
||||
Task<Recipe> GetRecipeAsync(Guid recipeId);
|
||||
|
||||
Task<Recipe?> GetRecipeByIdAsync(Guid id);
|
||||
|
||||
Task AddOrCreateIngredientToRecipeAsync(Guid recipeId, string ingredientName, int quantity, Guid unitId);
|
||||
|
||||
Task RemoveIngredientFromRecipeAsync(Guid recipeId, Guid ingredientId);
|
||||
|
||||
Task<IEnumerable<Recipe>> GetRecipesByNameAndIngredientAsync(string name, string ingredient);
|
||||
|
||||
Task<IReadOnlyCollection<Recipe>> GetRecipesByDifficultyAsync(Difficulty? difficulty);
|
||||
|
||||
Task<Recipe> CreateRecipeForCategoryAsync(Category category, string name, string description, Difficulty difficulty, int servings, TimeSpan preparationTime, TimeSpan cookingTime);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Recipe
|
||||
{
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Category;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Ingredient;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient;
|
||||
using Francesco.Recipes.World.Repositories.Ingredient;
|
||||
using Francesco.Recipes.World.Repositories.Unit;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class RecipeRepository : IRecipeRepository
|
||||
{
|
||||
private readonly FrancescosRecipesWorldDbContext _context;
|
||||
private readonly IIngredientRepository _ingredientRepository;
|
||||
private readonly IUnitRepository _unitRepository;
|
||||
|
||||
public RecipeRepository(
|
||||
FrancescosRecipesWorldDbContext context, IIngredientRepository ingredientRepository, IUnitRepository unitRepository)
|
||||
{
|
||||
_context = context;
|
||||
_ingredientRepository = ingredientRepository;
|
||||
_unitRepository = unitRepository;
|
||||
}
|
||||
|
||||
public async Task<Recipe> GetRecipeAsync(Guid recipeId)
|
||||
{
|
||||
var recipe = await _context.Recipes.FindAsync(recipeId);
|
||||
return recipe ?? throw new InvalidDataException($"Address {recipeId} not found.");
|
||||
}
|
||||
|
||||
public async Task<Recipe?> GetRecipeByIdAsync(Guid recipeId)
|
||||
{
|
||||
return await _context.Recipes
|
||||
.Include(r => r.RecipeIngredients)
|
||||
.ThenInclude(ri => ri.Ingredient)
|
||||
.Include(r => r.RecipeIngredients)
|
||||
.ThenInclude(ri => ri.Unit)
|
||||
.Include(r => r.MediaFiles)
|
||||
.Include(r => r.Instructions)
|
||||
.FirstOrDefaultAsync(r => r.Id == recipeId);
|
||||
}
|
||||
|
||||
public async Task AddOrCreateIngredientToRecipeAsync(Guid recipeId, string ingredientName, int quantity, Guid unitId)
|
||||
{
|
||||
var recipe = await GetRecipeAsync(recipeId);
|
||||
var unit = await _unitRepository.GetUnitByIdAsync(unitId);
|
||||
|
||||
if (quantity <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(quantity), "Die Menge muss größer als 0 sein.");
|
||||
}
|
||||
|
||||
var ingredients = await _ingredientRepository.GetIngredientsByNameAsync(ingredientName);
|
||||
var exactMatch = ingredients
|
||||
.FirstOrDefault(i => i.Name.Equals(ingredientName, StringComparison.OrdinalIgnoreCase));
|
||||
Ingredient ingredient;
|
||||
|
||||
if (exactMatch == null)
|
||||
{
|
||||
ingredient = new Ingredient
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = ingredientName,
|
||||
};
|
||||
_context.Ingredients.Add(ingredient);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
ingredient = exactMatch;
|
||||
}
|
||||
|
||||
var existingEntry = await _context.RecipeIngredients
|
||||
.FirstOrDefaultAsync(ri => ri.Recipe.Id == recipeId && ri.Ingredient.Id == ingredient.Id);
|
||||
if (existingEntry != null)
|
||||
{
|
||||
throw new InvalidOperationException("Das Rezept enthält diese Zutat bereits.");
|
||||
}
|
||||
|
||||
var recipeIngredient = new RecipeIngredient
|
||||
{
|
||||
Recipe = recipe,
|
||||
Ingredient = ingredient,
|
||||
Unit = unit,
|
||||
Quantity = quantity,
|
||||
};
|
||||
|
||||
_context.Add(recipeIngredient);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<Recipe> CreateRecipeForCategoryAsync(
|
||||
Category category,
|
||||
string name,
|
||||
string description,
|
||||
Difficulty difficulty,
|
||||
int servings,
|
||||
TimeSpan preparationTime,
|
||||
TimeSpan cookingTime)
|
||||
{
|
||||
if (category == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(category), "Category cannot be null.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name), "Name cannot be empty.");
|
||||
}
|
||||
|
||||
if (servings <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(servings), "Servings must be greater than 0.");
|
||||
}
|
||||
|
||||
var recipe = new Recipe
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = name,
|
||||
Description = description,
|
||||
Difficulty = difficulty,
|
||||
Servings = servings,
|
||||
PreparationTime = preparationTime,
|
||||
CookingTime = cookingTime,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
Category = category,
|
||||
};
|
||||
|
||||
_context.Recipes.Add(recipe);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return recipe;
|
||||
}
|
||||
|
||||
public async Task RemoveIngredientFromRecipeAsync(Guid recipeId, Guid ingredientId)
|
||||
{
|
||||
var recipeIngredient = await _context.RecipeIngredients
|
||||
.FirstOrDefaultAsync(ri => ri.Recipe.Id == recipeId && ri.Ingredient.Id == ingredientId);
|
||||
|
||||
if (recipeIngredient == null)
|
||||
{
|
||||
throw new ArgumentException("Diese Zutat ist nicht mit dem Rezept verknüpft.");
|
||||
}
|
||||
|
||||
_context.RecipeIngredients.Remove(recipeIngredient);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Recipe>> GetRecipesByNameAndIngredientAsync(string name, string ingredient)
|
||||
{
|
||||
var query = _context.Recipes
|
||||
.Include(r => r.RecipeIngredients)
|
||||
.ThenInclude(ri => ri.Ingredient)
|
||||
.AsQueryable();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
query = query.Where(r => r.Name.Contains(name, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ingredient))
|
||||
{
|
||||
var ingredientMatches = await _ingredientRepository.GetIngredientsByNameAsync(ingredient);
|
||||
var ingredientIds = ingredientMatches.Select(i => i.Id).ToList();
|
||||
|
||||
if (ingredientIds.Any())
|
||||
{
|
||||
query = query.Where(r => r.RecipeIngredients.Any(ri => ingredientIds.Contains(ri.Ingredient.Id)));
|
||||
}
|
||||
}
|
||||
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<Recipe>> GetRecipesByDifficultyAsync(Difficulty? difficulty)
|
||||
{
|
||||
if (!difficulty.HasValue)
|
||||
{
|
||||
return await _context.Recipes
|
||||
.Include(r => r.Category)
|
||||
.Include(r => r.RecipeIngredients)
|
||||
.ThenInclude(ri => ri.Ingredient)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
return await _context.Recipes
|
||||
.Where(r => r.Difficulty == difficulty.Value)
|
||||
.Include(r => r.Category)
|
||||
.Include(r => r.RecipeIngredients)
|
||||
.ThenInclude(ri => ri.Ingredient)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace Francesco.Recipes.World.Repositories.ShoppingList
|
||||
{
|
||||
using Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Shoppinglist;
|
||||
|
||||
public interface IShoppingListRepository
|
||||
{
|
||||
Task<ShoppingList> AddIngredientsToShoppingListAsync(Guid recipeId, List<Guid> ingredientIds);
|
||||
|
||||
Task<IEnumerable<RecipeIngredientShoppingList>> GetIngredientsOfRecipeInListAsync(Guid shoppingListRecipeId);
|
||||
|
||||
Task UpdateIngredientCheckedAsync(Guid shoppingListRecipeId, Guid recipeIngredientId, bool isChecked);
|
||||
|
||||
Task RemoveRecipeIfEmptyAsync(Guid shoppingListRecipeId);
|
||||
|
||||
Task DeleteShoppingListAsync(Guid shoppingListId);
|
||||
|
||||
Task<Recipe?> GetRecipeByNameAndImageAsync(string recipeName, string imageFileName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
namespace Francesco.Recipes.World.Repositories.ShoppingList
|
||||
{
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
using Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Shoppinglist;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class ShoppingListRepository : IShoppingListRepository
|
||||
{
|
||||
private readonly FrancescosRecipesWorldDbContext _context;
|
||||
|
||||
public ShoppingListRepository(FrancescosRecipesWorldDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<ShoppingList> AddIngredientsToShoppingListAsync(Guid recipeId, List<Guid> ingredientIds)
|
||||
{
|
||||
if (ingredientIds == null || !ingredientIds.Any())
|
||||
{
|
||||
throw new ArgumentNullException(nameof(ingredientIds));
|
||||
}
|
||||
|
||||
var recipe = await _context.Recipes
|
||||
.Include(r => r.RecipeIngredients)
|
||||
.FirstOrDefaultAsync(r => r.Id == recipeId);
|
||||
|
||||
if (recipe == null)
|
||||
{
|
||||
throw new Exception("Recipe not found.");
|
||||
}
|
||||
|
||||
var shoppingList = await _context.ShoppingLists
|
||||
.Include(sl => sl.RecipeShoppingList)
|
||||
.ThenInclude(rsl => rsl.SelectedIngredients)
|
||||
.Include(sl => sl.RecipeShoppingList)
|
||||
.ThenInclude(rsl => rsl.Recipe)
|
||||
.FirstOrDefaultAsync(sl => sl.RecipeShoppingList.Any(rsl => rsl.Recipe.Id == recipeId));
|
||||
|
||||
var recipeIngredients = await _context.RecipeIngredients
|
||||
.Where(ri => ingredientIds.Contains(ri.Id) && ri.Recipe.Id == recipeId)
|
||||
.ToListAsync();
|
||||
|
||||
if (!recipeIngredients.Any())
|
||||
{
|
||||
throw new Exception("No valid ingredients found.");
|
||||
}
|
||||
|
||||
if (shoppingList == null)
|
||||
{
|
||||
shoppingList = new ShoppingList
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
RecipeShoppingList = new List<RecipeShoppingList>(),
|
||||
};
|
||||
|
||||
var newRecipeList = new RecipeShoppingList
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Recipe = recipe,
|
||||
SelectedIngredients = recipeIngredients.Select(ri => new RecipeIngredientShoppingList
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
RecipeIngredient = ri,
|
||||
IsChecked = false,
|
||||
}).ToList(),
|
||||
};
|
||||
|
||||
shoppingList.RecipeShoppingList.Add(newRecipeList);
|
||||
_context.ShoppingLists.Add(shoppingList);
|
||||
}
|
||||
else
|
||||
{
|
||||
var existingRecipeList = shoppingList.RecipeShoppingList
|
||||
.FirstOrDefault(rsl => rsl.Recipe.Id == recipeId);
|
||||
|
||||
if (existingRecipeList == null)
|
||||
{
|
||||
existingRecipeList = new RecipeShoppingList
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Recipe = recipe,
|
||||
SelectedIngredients = new List<RecipeIngredientShoppingList>(),
|
||||
};
|
||||
|
||||
shoppingList.RecipeShoppingList.Add(existingRecipeList);
|
||||
}
|
||||
|
||||
foreach (var ri in recipeIngredients)
|
||||
{
|
||||
if (!existingRecipeList.SelectedIngredients.Any(si => si.RecipeIngredient.Id == ri.Id))
|
||||
{
|
||||
existingRecipeList.SelectedIngredients.Add(new RecipeIngredientShoppingList
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
RecipeIngredient = ri,
|
||||
IsChecked = false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
shoppingList.ModifiedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
return shoppingList;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<RecipeIngredientShoppingList>> GetIngredientsOfRecipeInListAsync(Guid shoppingListRecipeId)
|
||||
{
|
||||
return await _context.RecipeIngredientsShoppingLists
|
||||
.Include(i => i.RecipeIngredient)
|
||||
.ThenInclude(ri => ri.Ingredient)
|
||||
.Include(i => i.RecipeIngredient.Unit)
|
||||
.Where(i => i.RecipeShoppingList.Id == shoppingListRecipeId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateIngredientCheckedAsync(Guid shoppingListRecipeId, Guid recipeIngredientId, bool isChecked)
|
||||
{
|
||||
var item = await _context.RecipeIngredientsShoppingLists
|
||||
.FirstOrDefaultAsync(i =>
|
||||
i.RecipeShoppingList.Id == shoppingListRecipeId &&
|
||||
i.RecipeIngredient.Id == recipeIngredientId);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
throw new Exception("Zutat nicht gefunden.");
|
||||
}
|
||||
|
||||
item.IsChecked = isChecked;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task RemoveRecipeIfEmptyAsync(Guid shoppingListRecipeId)
|
||||
{
|
||||
var recipeEntry = await _context.RecipeShoppingLists
|
||||
.Include(r => r.SelectedIngredients)
|
||||
.FirstOrDefaultAsync(r => r.Id == shoppingListRecipeId);
|
||||
|
||||
if (recipeEntry != null && !recipeEntry.SelectedIngredients.Any())
|
||||
{
|
||||
_context.RecipeShoppingLists.Remove(recipeEntry);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeleteShoppingListAsync(Guid shoppingListId)
|
||||
{
|
||||
var list = await _context.ShoppingLists
|
||||
.FirstOrDefaultAsync(sl => sl.Id == shoppingListId);
|
||||
|
||||
if (list != null)
|
||||
{
|
||||
_context.ShoppingLists.Remove(list);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Recipe?> GetRecipeByNameAndImageAsync(string recipeName, string imageFileName)
|
||||
{
|
||||
return await _context.Recipes
|
||||
.Where(r => r.Name == recipeName && r.MediaFiles.Any(mf => mf.FileName == imageFileName))
|
||||
.Include(r => r.MediaFiles.Where(mf => mf.FileName == imageFileName))
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Unit
|
||||
{
|
||||
using Francesco.Recipes.World.Models.BackendModels.Unit;
|
||||
|
||||
public interface IUnitRepository
|
||||
{
|
||||
Task<Unit> GetUnitByIdAsync(Guid unitId);
|
||||
|
||||
Task<Unit> AddUnitAsync(string name, string symbol);
|
||||
|
||||
Task<IEnumerable<Unit>> GetAllUnitsAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Unit
|
||||
{
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Unit;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public class UnitRepository : IUnitRepository
|
||||
{
|
||||
private readonly FrancescosRecipesWorldDbContext _context;
|
||||
|
||||
public UnitRepository(
|
||||
FrancescosRecipesWorldDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<Unit> GetUnitByIdAsync(Guid unitId)
|
||||
{
|
||||
var unit = await _context.Units.FindAsync(unitId);
|
||||
return unit ?? throw new InvalidDataException($"Address {unitId} not found.");
|
||||
}
|
||||
|
||||
public async Task<Unit> AddUnitAsync(string name, string symbol)
|
||||
{
|
||||
var unit = new Unit
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = name,
|
||||
Symbol = symbol,
|
||||
};
|
||||
|
||||
_context.Units.Add(unit);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return unit;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Unit>> GetAllUnitsAsync()
|
||||
{
|
||||
return await _context.Units.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Francesco.Recipes.World.Views.Category
|
||||
{
|
||||
using Francesco.Recipes.World.Models.BackendModels.Category;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
|
||||
public class CategoryRecipesViewModel
|
||||
{
|
||||
public Category Category { get; set; } = new ();
|
||||
|
||||
public IEnumerable<Recipe> Recipes { get; set; } = new List<Recipe>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
@model Francesco.Recipes.World.Models.BackendModels.Category.Category
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Category Details";
|
||||
}
|
||||
|
||||
<h2>Category Details</h2>
|
||||
|
||||
<div>
|
||||
<h4>Category</h4>
|
||||
<hr />
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">
|
||||
Name
|
||||
</dt>
|
||||
<dd class="col-sm-10">
|
||||
@Model.Name
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div>
|
||||
<a asp-action="Index" class="btn btn-primary">Back to List</a>
|
||||
</div>
|
||||
@@ -0,0 +1,28 @@
|
||||
@model IEnumerable<Francesco.Recipes.World.Models.BackendModels.Category.Category>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Categories";
|
||||
}
|
||||
|
||||
<h2>Categories</h2>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var category in Model)
|
||||
{
|
||||
<tr>
|
||||
<td>@category.Name</td>
|
||||
<td>
|
||||
<a asp-action="Details" asp-route-id="@category.Id" class="btn btn-primary">Details</a>
|
||||
<a asp-action="Recipes" asp-route-id="@category.Id" class="btn btn-secondary">Recipes</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -1,8 +0,0 @@
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Welcome</h1>
|
||||
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
</div>
|
||||
@@ -0,0 +1,59 @@
|
||||
@model Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Add Instructions";
|
||||
}
|
||||
|
||||
<h1>Add Instructions to @Model.Name</h1>
|
||||
|
||||
<div class="instructions-list">
|
||||
<h3>Existing Instructions</h3>
|
||||
<ul>
|
||||
@foreach (var instruction in Model.Instructions.OrderBy(i => i.Number))
|
||||
{
|
||||
<li>@instruction.Number. @instruction.Description</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="add-instruction-form">
|
||||
<h3>Add New Instruction</h3>
|
||||
<form id="instruction-form" method="post" asp-action="AddInstruction" asp-controller="Recipe">
|
||||
<input type="hidden" name="recipeId" value="@Model.Id" />
|
||||
<div class="form-group">
|
||||
<label for="description">Description</label>
|
||||
<input type="text" class="form-control" id="description" name="description" required />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="number">Number</label>
|
||||
<input type="number" class="form-control" id="number" name="number" required min="1" />
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" onclick="addInstructionToRecipe()">Add Instruction</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
async function addInstructionToRecipe() {
|
||||
var form = document.getElementById('instruction-form');
|
||||
var recipeId = form.querySelector('input[name="recipeId"]').value;
|
||||
var description = form.querySelector('input[name="description"]').value;
|
||||
var number = form.querySelector('input[name="number"]').value;
|
||||
|
||||
var response = await fetch('/Recipe/' + recipeId + '/AddInstruction', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ recipeId: recipeId, description: description, number: number })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Failed to add instruction.');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
@{
|
||||
ViewData["Title"] = "Add or Create Ingredient to Recipe";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
|
||||
<form asp-action="AddOrCreateIngredient" method="post">
|
||||
<input type="hidden" id="RecipeId" name="recipeId" value="@ViewBag.RecipeId" />
|
||||
<div class="form-group">
|
||||
<label for="IngredientName" class="control-label">Ingredient Name</label>
|
||||
<input type="text" id="IngredientName" name="ingredientName" class="form-control" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="Quantity" class="control-label">Quantity</label>
|
||||
<input type="number" id="Quantity" name="quantity" class="form-control" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="UnitId" class="control-label">Unit</label>
|
||||
<select id="UnitId" name="unitId" class="form-control" asp-items="ViewBag.Units"></select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Add Ingredient</button>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
@model IEnumerable<Francesco.Recipes.World.Views.Category.CategoryRecipesViewModel>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Category Recipes";
|
||||
}
|
||||
|
||||
<h1>Category Recipes</h1>
|
||||
|
||||
@foreach (var categoryRecipes in Model)
|
||||
{
|
||||
<div class="category-section">
|
||||
<h2>@categoryRecipes.Category.Name</h2>
|
||||
<a asp-action="Create" asp-route-categoryId="@categoryRecipes.Category.Id" class="btn btn-primary">Rezept erstellen</a>
|
||||
<div class="recipes">
|
||||
@foreach (var recipe in categoryRecipes.Recipes)
|
||||
{
|
||||
<div class="recipe-card">
|
||||
<div class="recipe-image">
|
||||
@if (recipe.MediaFiles.Any() && recipe.MediaFiles.First().Data != null)
|
||||
{
|
||||
var mediaFile = recipe.MediaFiles.First();
|
||||
if (mediaFile.Data != null)
|
||||
{
|
||||
<img src="data:@mediaFile.MimeType;base64,@Convert.ToBase64String(mediaFile.Data)" alt="@recipe.Name" class="img-fluid" />
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div class="recipe-info">
|
||||
<h3>@recipe.Name</h3>
|
||||
<p>@recipe.Description</p>
|
||||
<p><strong>Difficulty:</strong> @recipe.Difficulty</p>
|
||||
<p><strong>Servings:</strong> @recipe.Servings</p>
|
||||
<p><strong>Preparation Time:</strong> @recipe.PreparationTime</p>
|
||||
<p><strong>Cooking Time:</strong> @recipe.CookingTime</p>
|
||||
<div class="recipe-favorite">
|
||||
@if (recipe.IsFavorite)
|
||||
{
|
||||
<form method="post" asp-action="RemoveFavorite" asp-controller="Recipe">
|
||||
<input type="hidden" name="recipeId" value="@recipe.Id" />
|
||||
<button type="submit" class="btn btn-danger">Remove from Favorites</button>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form method="post" asp-action="AddFavorite" asp-controller="Recipe">
|
||||
<input type="hidden" name="recipeId" value="@recipe.Id" />
|
||||
<button type="submit" class="btn btn-primary">Add to Favorites</button>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="recipe-card add-recipe-card">
|
||||
<a asp-action="Create" asp-route-categoryId="@categoryRecipes.Category.Id" class="btn btn-primary">Rezept hinzufügen</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<style>
|
||||
.category-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.recipes {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.recipe-card {
|
||||
width: 200px;
|
||||
margin: 1rem;
|
||||
padding: 1rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.recipe-image img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.recipe-info {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.add-recipe-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f8f8f8;
|
||||
border: 2px dashed #ccc;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
@model Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe
|
||||
@using Francesco.Recipes.World.Models.BackendModels.Recipe
|
||||
@{
|
||||
ViewData["Title"] = "Create Recipe";
|
||||
}
|
||||
|
||||
<h1>Create Recipe</h1>
|
||||
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label asp-for="Name"></label>
|
||||
<input asp-for="Name" class="form-control" />
|
||||
<span asp-validation-for="Name" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Description"></label>
|
||||
<textarea asp-for="Description" class="form-control"></textarea>
|
||||
<span asp-validation-for="Description" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Difficulty"></label>
|
||||
<select asp-for="Difficulty" class="form-control">
|
||||
<option value="">Select Difficulty</option>
|
||||
@foreach (var difficulty in Enum.GetValues(typeof(Difficulty)))
|
||||
{
|
||||
<option value="@difficulty">@difficulty</option>
|
||||
}
|
||||
</select>
|
||||
<span asp-validation-for="Difficulty" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Servings"></label>
|
||||
<input asp-for="Servings" class="form-control" />
|
||||
<span asp-validation-for="Servings" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="PreparationTime"></label>
|
||||
<input asp-for="PreparationTime" class="form-control" />
|
||||
<span asp-validation-for="PreparationTime" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="CookingTime"></label>
|
||||
<input asp-for="CookingTime" class="form-control" />
|
||||
<span asp-validation-for="CookingTime" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="photo">Photo</label>
|
||||
<input type="file" name="photo" class="form-control" />
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Create</button>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{
|
||||
await Html.RenderPartialAsync("_ValidationScriptsPartial");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
@model Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Recipe Details";
|
||||
}
|
||||
|
||||
<h1>@Model.Name</h1>
|
||||
|
||||
<div class="recipe-details">
|
||||
<div class="recipe-image">
|
||||
@if (Model.MediaFiles.Any() && Model.MediaFiles.First().Data != null)
|
||||
{
|
||||
var mediaFile = Model.MediaFiles.First();
|
||||
if (mediaFile.Data != null)
|
||||
{
|
||||
<img src="data:@mediaFile.MimeType;base64,@Convert.ToBase64String(mediaFile.Data)" alt="@Model.Name" class="img-fluid" />
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div class="recipe-info">
|
||||
<p><strong>Description:</strong> @Model.Description</p>
|
||||
<p><strong>Difficulty:</strong> @Model.Difficulty</p>
|
||||
<p><strong>Servings:</strong> @Model.Servings</p>
|
||||
<p><strong>Preparation Time:</strong> @Model.PreparationTime</p>
|
||||
<p><strong>Cooking Time:</strong> @Model.CookingTime</p>
|
||||
</div>
|
||||
<div class="recipe-ingredients">
|
||||
<h3>Ingredients</h3>
|
||||
<form id="ingredient-form">
|
||||
<ul id="ingredient-list">
|
||||
@foreach (var ingredient in Model.RecipeIngredients)
|
||||
{
|
||||
<li id="ingredient-@ingredient.Id">
|
||||
<input type="checkbox" name="ingredientIds" value="@ingredient.Id" />
|
||||
@ingredient.Ingredient.Name - @ingredient.Quantity @(ingredient.Unit != null ? ingredient.Unit.Symbol : string.Empty)
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
<button type="button" class="btn btn-primary" onclick="addSelectedIngredientsToShoppingList()">Add Selected to Shopping List</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
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.');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
@using Francesco.Recipes.World.Models.BackendModels.Recipe
|
||||
@model Francesco.Recipes.World.Views.Recipe.FilterByDifficultyViewModel
|
||||
|
||||
<h1>Rezepte nach Schwierigkeitsgrad</h1>
|
||||
|
||||
<div class="mb-3">
|
||||
<a asp-controller="Recipe" asp-action="Create" class="btn btn-primary">Neues Rezept erstellen</a>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<form asp-controller="Recipe" asp-action="FilterByDifficulty" method="get" id="filterForm">
|
||||
<div class="form-group">
|
||||
<label asp-for="SelectedDifficulty" class="form-label">Schwierigkeitsgrad</label>
|
||||
<select asp-for="SelectedDifficulty" asp-items="Html.GetEnumSelectList<Difficulty>()" class="form-select" onchange="submitForm()">
|
||||
<option value="">Alle Schwierigkeitsgrade</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@if (Model?.Recipes != null && Model.Recipes.Any())
|
||||
{
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>Schwierigkeitsgrad</th>
|
||||
<th>Portionen</th>
|
||||
<th>Zubereitungszeit</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var recipe in Model.Recipes)
|
||||
{
|
||||
<tr>
|
||||
<td>@recipe.Name</td>
|
||||
<td>@(recipe.Description?.Length > 100 ? recipe.Description.Substring(0, 100) + "..." : recipe.Description)</td>
|
||||
<td>@recipe.?Difficulty</td>
|
||||
<td>@recipe.Servings</td>
|
||||
<td>@($"{recipe.PreparationTime.TotalMinutes} Min.")</td>
|
||||
<td>
|
||||
<a asp-controller="Recipe" asp-action="Details" asp-route-id="@recipe.Id" class="btn btn-sm btn-info">Details</a>
|
||||
<a asp-controller="Recipe" asp-action="Edit" asp-route-id="@recipe.Id" class="btn btn-sm btn-primary">Bearbeiten</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="col-12">
|
||||
<p>Keine Rezepte gefunden.</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
function submitForm() {
|
||||
document.getElementById('filterForm').submit();
|
||||
}
|
||||
</script>
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Francesco.Recipes.World.Views.Recipe
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
|
||||
public class FilterByDifficultyViewModel
|
||||
{
|
||||
public Difficulty? SelectedDifficulty { get; set; }
|
||||
|
||||
public IReadOnlyCollection<Recipe> Recipes { get; set; } = new List<Recipe>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
@model Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Remove Ingredient";
|
||||
}
|
||||
|
||||
<h1>Remove Ingredient</h1>
|
||||
|
||||
<h3>Are you sure you want to remove the ingredient '@ViewBag.IngredientName' from this recipe?</h3>
|
||||
|
||||
<form asp-action="RemoveIngredientConfirmed" asp-route-categoryId="@ViewBag.CategoryId" asp-route-recipeId="@ViewBag.RecipeId" asp-route-ingredientId="@ViewBag.IngredientId" method="post">
|
||||
<input type="hidden" name="categoryId" value="@ViewBag.CategoryId" />
|
||||
<input type="hidden" name="recipeId" value="@ViewBag.RecipeId" />
|
||||
<input type="hidden" name="ingredientId" value="@ViewBag.IngredientId" />
|
||||
<div class="form-group">
|
||||
<input type="submit" value="Remove" class="btn btn-danger" />
|
||||
<a asp-action="Details" asp-route-id="@ViewBag.RecipeId" class="btn btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@await Html.PartialAsync("_ValidationScriptsPartial")
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
@model IEnumerable<Francesco.Recipes.World.Models.BackendModels.RecipeIngredient.RecipeIngredient>
|
||||
|
||||
<div class="recipe-ingredients">
|
||||
<h3>Zutaten</h3>
|
||||
<form id="ingredient-form">
|
||||
<ul id="ingredient-list">
|
||||
@foreach (var ingredient in Model)
|
||||
{
|
||||
<li id="ingredient-@ingredient.Id">
|
||||
<input type="checkbox" name="ingredientIds" value="@ingredient.Id" />
|
||||
@ingredient.Ingredient.Name - @ingredient.Quantity @ingredient.Unit.Symbol
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
<button type="button" class="btn btn-primary" onclick="addSelectedIngredientsToShoppingList()">Ausgewählte zur Einkaufsliste hinzufügen</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
function addSelectedIngredientsToShoppingList() {
|
||||
var form = document.getElementById('ingredient-form');
|
||||
var formData = new FormData(form);
|
||||
var selectedIngredientIds = formData.getAll('ingredientIds');
|
||||
|
||||
fetch('/ShoppingList/AddSelectedIngredients', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ ingredientIds: selectedIngredientIds })
|
||||
}).then(response => {
|
||||
if (response.ok) {
|
||||
alert('Ausgewählte Zutaten wurden zur Einkaufsliste hinzugefügt.');
|
||||
} else {
|
||||
alert('Fehler beim Hinzufügen der ausgewählten Zutaten zur Einkaufsliste.');
|
||||
}
|
||||
}).catch(error => {
|
||||
alert('Ein Fehler ist aufgetreten: ' + error.message);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
}
|
||||
@@ -44,6 +44,7 @@
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.4/dist/htmx.js" integrity="sha384-oeUn82QNXPuVkGCkcrInrS1twIxKhkZiFfr2TdiuObZ3n3yIeMiqcRzkIcguaof1" crossorigin="anonymous"></script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
@model Francesco.Recipes.World.Models.BackendModels.Shoppinglist.ShoppingList
|
||||
@using Francesco.Recipes.World.Models.BackendModels.RecipeShoppingList
|
||||
@using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient
|
||||
@using Francesco.Recipes.World.Models.BackendModels.Recipe
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Einkaufsliste Details";
|
||||
}
|
||||
|
||||
<h2>Einkaufsliste Details</h2>
|
||||
|
||||
@if (TempData["SuccessMessage"] != null)
|
||||
{
|
||||
<div class="alert alert-success">
|
||||
@TempData["SuccessMessage"]
|
||||
</div>
|
||||
}
|
||||
|
||||
<div>
|
||||
<h4>Einkaufsliste</h4>
|
||||
<hr />
|
||||
<dl class="row">
|
||||
<dt class="col-sm-2">
|
||||
ID
|
||||
</dt>
|
||||
<dd class="col-sm-10">
|
||||
@Model.Id
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<h4>Rezepte</h4>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rezeptname</th>
|
||||
<th>Zutaten</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var recipeShoppingList in Model.RecipeShoppingList)
|
||||
{
|
||||
<tr>
|
||||
<td>@recipeShoppingList.Recipe.Name</td>
|
||||
<td>
|
||||
<ul>
|
||||
@foreach (var ingredient in recipeShoppingList.SelectedIngredients)
|
||||
{
|
||||
<li>@ingredient.RecipeIngredient.Ingredient.Name - @ingredient.RecipeIngredient.Quantity @ingredient.RecipeIngredient.Unit.Name</li>
|
||||
}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
Reference in New Issue
Block a user