From bc69acd75df4ddcb7b7f77125b288567c800ec24 Mon Sep 17 00:00:00 2001 From: Jeas0001 Date: Tue, 29 Apr 2025 08:44:23 +0200 Subject: [PATCH 1/6] Recipe controller --- backend/API/BusinessLogic/RecipeLogic.cs | 81 +++++++++++++++++++ backend/API/BusinessLogic/UserLogic.cs | 10 ++- backend/API/Controllers/RecipeController.cs | 61 ++++++++++++++ backend/API/DBAccess/RecipeDBaccess.cs | 79 ++++++++++++++++++ .../DBAccess/{DbAccess.cs => UserDBAccess.cs} | 7 +- backend/API/Startup.cs | 4 +- 6 files changed, 234 insertions(+), 8 deletions(-) create mode 100644 backend/API/BusinessLogic/RecipeLogic.cs create mode 100644 backend/API/Controllers/RecipeController.cs create mode 100644 backend/API/DBAccess/RecipeDBaccess.cs rename backend/API/DBAccess/{DbAccess.cs => UserDBAccess.cs} (96%) diff --git a/backend/API/BusinessLogic/RecipeLogic.cs b/backend/API/BusinessLogic/RecipeLogic.cs new file mode 100644 index 0000000..eb32f5f --- /dev/null +++ b/backend/API/BusinessLogic/RecipeLogic.cs @@ -0,0 +1,81 @@ +using API.DBAccess; +using API.Models.RecipeModels; +using Microsoft.AspNetCore.Mvc; + +namespace API.BusinessLogic +{ + public class RecipeLogic + { + private readonly RecipeDBaccess _dbAccess; + private readonly IConfiguration _configuration; + + public RecipeLogic(IConfiguration configuration, RecipeDBaccess dbAccess) + { + _dbAccess = dbAccess; + _configuration = configuration; + } + + public async Task GetPrefereredRecipes(int userId) + { + PrefereredRecipes recipes = await _dbAccess.ReadPrefereredRecipes(userId); + if (recipes == null) { recipes = await _dbAccess.CreateMissingLists(userId); } + if (recipes == null || recipes.Id == 0) { return new ConflictObjectResult(new { message = "Could not find any recipes" }); } + + return new OkObjectResult(recipes); + } + + public async Task GetRecipe(int recipeId) + { + var recipe = await _dbAccess.ReadRecipe(recipeId); + + if (recipe == null || recipe.Id == 0) { return new ConflictObjectResult(new { message = "Could not find any recipe" }); } + + return new OkObjectResult(recipe); + } + + public async Task CreateRecipe(Recipe recipe, int prefereredRecipesId) + { + var prefereredRecipes = await _dbAccess.ReadAllRecipe(prefereredRecipesId); + + foreach (var item in prefereredRecipes.Recipes) + { + if (item.Name == recipe.Name) + { + return new ConflictObjectResult(new { message = "Recipe name is already in use." }); + } + } + + return await _dbAccess.CreateRecipe(recipe, prefereredRecipesId); + } + + public async Task EditRecipe(Recipe recipe, int recipeId, int userId) + { + var prefereredRecipes = await _dbAccess.ReadPrefereredRecipes(userId); + var dish = await _dbAccess.ReadRecipe(recipeId); + + foreach (var item in prefereredRecipes.Recipes) + { + if (item.Name == recipe.Name) + { + return new ConflictObjectResult(new { message = "Recipe name is already in use." }); + } + } + + dish.Name = recipe.Name; + dish.Description = recipe.Description; + dish.Directions = recipe.Directions; + dish.Ingredients = recipe.Ingredients; + + return await _dbAccess.UpdateRecipe(dish); + } + + public async Task DeleteRecipe(int recipeId) + { + var recipe = await _dbAccess.ReadRecipe(recipeId); + + if (recipe != null) { return await _dbAccess.DeleteUser(recipe); } + + return new ConflictObjectResult(new { message = "Invalid user" }); + } + } +} diff --git a/backend/API/BusinessLogic/UserLogic.cs b/backend/API/BusinessLogic/UserLogic.cs index 07fb887..c835bf4 100644 --- a/backend/API/BusinessLogic/UserLogic.cs +++ b/backend/API/BusinessLogic/UserLogic.cs @@ -1,8 +1,8 @@ using API.DBAccess; using API.Models.UserModels; +using API.Models.RecipeModels; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; -using Org.BouncyCastle.Asn1.Ocsp; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; @@ -13,10 +13,10 @@ namespace API.BusinessLogic { public class UserLogic { - private readonly DbAccess _dbAccess; + private readonly UserDBAccess _dbAccess; private readonly IConfiguration _configuration; - public UserLogic(IConfiguration configuration, DbAccess dbAccess) + public UserLogic(IConfiguration configuration, UserDBAccess dbAccess) { _dbAccess = dbAccess; _configuration = configuration; @@ -60,12 +60,16 @@ namespace API.BusinessLogic string salt = Guid.NewGuid().ToString(); string hashedPassword = ComputeHash(userDTO.Password, SHA256.Create(), salt); + PrefereredRecipes recipes = new PrefereredRecipes(); + recipes.Recipes = new List(); + User user = new User { UserName = userDTO.UserName, Email = userDTO.Email, Password = hashedPassword, Salt = salt, + PrefereredRecipes = recipes, }; return await _dbAccess.CreateUser(user); diff --git a/backend/API/Controllers/RecipeController.cs b/backend/API/Controllers/RecipeController.cs new file mode 100644 index 0000000..113adac --- /dev/null +++ b/backend/API/Controllers/RecipeController.cs @@ -0,0 +1,61 @@ +using API.BusinessLogic; +using API.Models.RecipeModels; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Security.Claims; + +namespace API.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class RecipeController :Controller + { + private readonly RecipeLogic _recipeLogic; + + public RecipeController(RecipeLogic recipeLogic) + { + _recipeLogic = recipeLogic; + } + + [Authorize] + [HttpGet("getall")] + public async Task ReadPrefereredRecipes() + { + var claims = HttpContext.User.Claims; + string userIdString = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value; + int userId = Convert.ToInt32(userIdString); + return await _recipeLogic.GetPrefereredRecipes(userId); + } + + [Authorize] + [HttpGet("get/{recipeId}")] + public async Task ReadRecipe(int recipeId) + { + return await _recipeLogic.GetRecipe(recipeId); + } + + [Authorize] + [HttpPost("create/{prefereredRecipesId}")] + public async Task CreateRecipe([FromBody] Recipe recipe, int prefereredRecipesId) + { + return await _recipeLogic.CreateRecipe(recipe, prefereredRecipesId); + } + + [Authorize] + [HttpPut("edit/{recipeId}")] + public async Task EditRecipe([FromBody] Recipe recipe, int recipeId) + { + var claims = HttpContext.User.Claims; + string userIdString = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value; + int userId = Convert.ToInt32(userIdString); + return await _recipeLogic.EditRecipe(recipe, recipeId, userId); + } + + [Authorize] + [HttpDelete("delete/{recipeId}")] + public async Task DeleteRecipe(int recipeId) + { + return await _recipeLogic.DeleteRecipe(recipeId); + } + } +} diff --git a/backend/API/DBAccess/RecipeDBaccess.cs b/backend/API/DBAccess/RecipeDBaccess.cs new file mode 100644 index 0000000..10b1091 --- /dev/null +++ b/backend/API/DBAccess/RecipeDBaccess.cs @@ -0,0 +1,79 @@ +using API.Models.RecipeModels; +using API.Models.UserModels; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace API.DBAccess +{ + public class RecipeDBaccess + { + private readonly DBContext _context; + + public RecipeDBaccess(DBContext context) + { + _context = context; + } + + public async Task ReadPrefereredRecipes(int userId) + { + var recipes = await _context.Users.Include(u => u.PrefereredRecipes).ThenInclude(p => p.Recipes).FirstOrDefaultAsync(u => u.Id == userId); + + return recipes.PrefereredRecipes; + } + + public async Task ReadAllRecipe(int prefereredRecipesId) + { + return await _context.PrefereredRecipes.Include(p => p.Recipes).FirstOrDefaultAsync(r => r.Id == prefereredRecipesId); + } + + public async Task ReadRecipe(int recipeId) + { + return await _context.Recipes.Include(r => r.Ingredients).FirstOrDefaultAsync(r => r.Id == recipeId); + } + + public async Task CreateMissingLists(int userId) + { + var user = await _context.Users.FirstOrDefaultAsync(u => u.Id == userId); + user.PrefereredRecipes = new PrefereredRecipes(); + user.PrefereredRecipes.Recipes = new List(); + + _context.SaveChangesAsync(); + + return await ReadPrefereredRecipes(userId); + } + + public async Task CreateRecipe(Recipe recipe, int prefereredRecipeId) + { + var recipes = await _context.PrefereredRecipes.Include(p => p.Recipes).FirstOrDefaultAsync(p => p.Id == prefereredRecipeId); + + recipes.Recipes.Add(recipe); + + bool saved = await _context.SaveChangesAsync() > 1; + + if (saved) { return new OkObjectResult(saved); } + + return new ConflictObjectResult(new { message = "Could not save to database" }); + } + + public async Task UpdateRecipe(Recipe recipe) + { + _context.Entry(recipe).State = EntityState.Modified; + + bool saved = await _context.SaveChangesAsync() >= 1; + + if (saved) { return new OkObjectResult(recipe); } + + return new ConflictObjectResult(new { message = "Could not save to database" }); + } + + public async Task DeleteUser(Recipe recipe) + { + _context.Recipes.Remove(recipe); + bool saved = await _context.SaveChangesAsync() >= 0; + + if (saved) { return new OkObjectResult(saved); } + + return new ConflictObjectResult(new { message = "Could not save to database" }); + } + } +} diff --git a/backend/API/DBAccess/DbAccess.cs b/backend/API/DBAccess/UserDBAccess.cs similarity index 96% rename from backend/API/DBAccess/DbAccess.cs rename to backend/API/DBAccess/UserDBAccess.cs index f994c5a..2e3f07b 100644 --- a/backend/API/DBAccess/DbAccess.cs +++ b/backend/API/DBAccess/UserDBAccess.cs @@ -4,11 +4,11 @@ using Microsoft.EntityFrameworkCore; namespace API.DBAccess { - public class DbAccess + public class UserDBAccess { private readonly DBContext _context; - public DbAccess(DBContext context) + public UserDBAccess(DBContext context) { _context = context; } @@ -65,8 +65,7 @@ namespace API.DBAccess if (saved) { return new OkObjectResult(user); } - return new ConflictObjectResult(new { message = "Could not save to database" }); - + return new ConflictObjectResult(new { message = "Could not save to database" }) } public async Task UpdatePassword(User user) diff --git a/backend/API/Startup.cs b/backend/API/Startup.cs index c0d4f97..3969135 100644 --- a/backend/API/Startup.cs +++ b/backend/API/Startup.cs @@ -25,8 +25,10 @@ namespace API services.AddDbContext(options => options.UseMySQL(_configuration.GetConnectionString("Database"))); - services.AddScoped(); + services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddControllers(); From b67f9f944c7b12b3683658f35069180875dc9bb7 Mon Sep 17 00:00:00 2001 From: Jeas0001 Date: Tue, 29 Apr 2025 09:04:35 +0200 Subject: [PATCH 2/6] Forgotten ; --- backend/API/DBAccess/UserDBAccess.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/API/DBAccess/UserDBAccess.cs b/backend/API/DBAccess/UserDBAccess.cs index 2e3f07b..0ea3779 100644 --- a/backend/API/DBAccess/UserDBAccess.cs +++ b/backend/API/DBAccess/UserDBAccess.cs @@ -65,7 +65,7 @@ namespace API.DBAccess if (saved) { return new OkObjectResult(user); } - return new ConflictObjectResult(new { message = "Could not save to database" }) + return new ConflictObjectResult(new { message = "Could not save to database" }); } public async Task UpdatePassword(User user) From 6743e9c7aa096bf2857a8158f5596bf1a08c592a Mon Sep 17 00:00:00 2001 From: LilleBRG Date: Tue, 29 Apr 2025 09:56:44 +0200 Subject: [PATCH 3/6] multiple ingredient rows can now be added --- .../mercantec/easyeat/CreateDishActivity.kt | 37 ++++++++++++++----- .../res/layout/activity_create_dish_form.xml | 17 ++++----- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/CreateDishActivity.kt b/app/app/src/main/java/tech/mercantec/easyeat/CreateDishActivity.kt index 21e10ac..7485b2b 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/CreateDishActivity.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/CreateDishActivity.kt @@ -1,25 +1,42 @@ package tech.mercantec.easyeat import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.widget.Button +import android.widget.LinearLayout import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.Toolbar class CreateDishActivity : AppCompatActivity() { + + private lateinit var ingredientContainer: LinearLayout + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_create_dish_form) - // Setup the Toolbar as ActionBar - val toolbar: Toolbar = findViewById(R.id.toolbar) - setSupportActionBar(toolbar) + ingredientContainer = findViewById(R.id.ingredientContainer) + val addButton: Button = findViewById(R.id.addIngredientButton) - // Enable the Up button - supportActionBar?.setDisplayHomeAsUpEnabled(true) + // Add the first ingredient row manually at startup + addIngredientRow() + + // Set up button to add new rows + addButton.setOnClickListener { + addIngredientRow() + } } - // Handle the Up button click - override fun onSupportNavigateUp(): Boolean { - onBackPressedDispatcher.onBackPressed() - return true + private fun addIngredientRow() { + val inflater = LayoutInflater.from(this) + val ingredientRow = inflater.inflate(R.layout.activity_create_dish_ingredient_row, null) + + // Handle remove button in each row + val removeButton: Button = ingredientRow.findViewById(R.id.removeButton) + removeButton.setOnClickListener { + ingredientContainer.removeView(ingredientRow) + } + + ingredientContainer.addView(ingredientRow) } } diff --git a/app/app/src/main/res/layout/activity_create_dish_form.xml b/app/app/src/main/res/layout/activity_create_dish_form.xml index b64643f..62e333a 100644 --- a/app/app/src/main/res/layout/activity_create_dish_form.xml +++ b/app/app/src/main/res/layout/activity_create_dish_form.xml @@ -6,14 +6,6 @@ android:orientation="vertical" android:padding="16dp"> - - - - + +