diff --git a/.gitignore b/.gitignore index 664d14a..d479b3a 100644 --- a/.gitignore +++ b/.gitignore @@ -364,3 +364,4 @@ FodyWeavers.xsd # Setupfil för Aberwyn infrastructure/setup.json +Aberwyn/Infrastructure/setup.json diff --git a/Aberwyn/Aberwyn.csproj b/Aberwyn/Aberwyn.csproj index cc74855..96925a8 100644 --- a/Aberwyn/Aberwyn.csproj +++ b/Aberwyn/Aberwyn.csproj @@ -45,6 +45,7 @@ + diff --git a/Aberwyn/Controllers/AdminController.cs b/Aberwyn/Controllers/AdminController.cs index a095b14..6039ee6 100644 --- a/Aberwyn/Controllers/AdminController.cs +++ b/Aberwyn/Controllers/AdminController.cs @@ -11,6 +11,7 @@ namespace Aberwyn.Controllers [Authorize(Roles = "Admin")] public class AdminController : Controller { + private readonly MenuService _menuService; private readonly UserManager _userManager; private readonly RoleManager _roleManager; private readonly IConfiguration _configuration; @@ -18,12 +19,14 @@ namespace Aberwyn.Controllers private readonly ApplicationDbContext _context; public AdminController( + MenuService menuService, UserManager userManager, RoleManager roleManager, IConfiguration configuration, IHostEnvironment env, ApplicationDbContext context) { + _menuService = menuService; _userManager = userManager; _roleManager = roleManager; _configuration = configuration; @@ -144,6 +147,13 @@ namespace Aberwyn.Controllers TempData["Message"] = $"✅ Import av veckomenyer från extern databas klar ({sourceMenus.Count})."; return RedirectToAction("Index"); } + [HttpPost] + [Authorize(Roles = "Admin")] + public IActionResult GenerateThumbnails() + { + var count = _menuService.GenerateMissingThumbnails(); + return Ok($"{count} thumbnails skapades."); + } [HttpPost] diff --git a/Aberwyn/Controllers/MealController.cs b/Aberwyn/Controllers/MealController.cs index fb0970b..da572a9 100644 --- a/Aberwyn/Controllers/MealController.cs +++ b/Aberwyn/Controllers/MealController.cs @@ -2,6 +2,8 @@ using Aberwyn.Models; using Aberwyn.Data; using Microsoft.AspNetCore.Authorization; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp; namespace Aberwyn.Controllers { @@ -70,6 +72,15 @@ namespace Aberwyn.Controllers return StatusCode(500, $"
{ex.Message}
"); } } + [HttpGet("Meal/Thumbnail/{id}")] + public IActionResult Thumbnail(int id) + { + var meal = _menuService.GetMealById(id); + if (meal == null || meal.ThumbnailData == null) + return NotFound(); + + return File(meal.ThumbnailData, "image/webp"); // eller image/jpeg om du använder det + } @@ -93,6 +104,8 @@ namespace Aberwyn.Controllers ImageFile.CopyTo(ms); meal.ImageData = ms.ToArray(); meal.ImageMimeType = ImageFile.ContentType; + meal.ThumbnailData = GenerateThumbnail(ImageFile); + } else { @@ -111,8 +124,21 @@ namespace Aberwyn.Controllers } + private byte[] GenerateThumbnail(IFormFile file) + { + using var image = SixLabors.ImageSharp.Image.Load(file.OpenReadStream()); + image.Mutate(x => x.Resize(new ResizeOptions + { + Mode = ResizeMode.Max, + Size = new Size(300, 300) // eller vad du vill för korten + })); + using var ms = new MemoryStream(); + image.SaveAsWebp(ms); // kräver ImageSharp.Webp-paketet + return ms.ToArray(); + } + [HttpPost] diff --git a/Aberwyn/Controllers/MealMenuApiController.cs b/Aberwyn/Controllers/MealMenuApiController.cs index 840e42e..d735a32 100644 --- a/Aberwyn/Controllers/MealMenuApiController.cs +++ b/Aberwyn/Controllers/MealMenuApiController.cs @@ -33,6 +33,16 @@ namespace Aberwyn.Controllers return Ok(mealDtos); } + [HttpGet("getWeeklyMenu")] + public IActionResult GetWeeklyMenu(int weekNumber, int year) + { + var menuDtos = _menuService.GetWeeklyMenuDto(weekNumber, year); + Console.WriteLine("Hämtar meals: " + menuDtos); + return Ok(menuDtos); + } + + + [HttpPut("menu")] public IActionResult SaveMenu([FromBody] MenuViewModel weeklyMenu) diff --git a/Aberwyn/Data/MenuService.cs b/Aberwyn/Data/MenuService.cs index b95ef19..0329304 100644 --- a/Aberwyn/Data/MenuService.cs +++ b/Aberwyn/Data/MenuService.cs @@ -1,6 +1,9 @@ // Nya versionen av MenuService med Entity Framework using Aberwyn.Models; using Microsoft.EntityFrameworkCore; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Webp; +using SixLabors.ImageSharp.Processing; using System.Globalization; using static Aberwyn.Data.SetupService; @@ -26,6 +29,37 @@ public class MenuService return new MenuService(context); } + public List GetWeeklyMenuDto(int weekNumber, int year) + { + var query = from wm in _context.WeeklyMenus + where wm.WeekNumber == weekNumber && wm.Year == year + join mDinner in _context.Meals on wm.DinnerMealId equals mDinner.Id into dinnerJoin + from mDinner in dinnerJoin.DefaultIfEmpty() + join mLunch in _context.Meals on wm.LunchMealId equals mLunch.Id into lunchJoin + from mLunch in lunchJoin.DefaultIfEmpty() + join mBreakfast in _context.Meals on wm.BreakfastMealId equals mBreakfast.Id into breakfastJoin + from mBreakfast in breakfastJoin.DefaultIfEmpty() + select new WeeklyMenuDto + { + Id = wm.Id, + DayOfWeek = wm.DayOfWeek, + WeekNumber = wm.WeekNumber, + Year = wm.Year, + BreakfastMealId = wm.BreakfastMealId, + LunchMealId = wm.LunchMealId, + DinnerMealId = wm.DinnerMealId, + BreakfastMealName = mBreakfast.Name, + LunchMealName = mLunch.Name, + DinnerMealName = mDinner.Name, + DinnerMealThumbnail = mDinner.ThumbnailData + }; + + return query.ToList(); + } + + + + public void UpdateWeeklyMenu(MenuViewModel model) { var existing = _context.WeeklyMenus @@ -70,6 +104,38 @@ public class MenuService _context.SaveChanges(); } +public int GenerateMissingThumbnails() +{ + var updatedCount = 0; + var meals = _context.Meals + .Where(m => m.ImageData != null && m.ThumbnailData == null) + .ToList(); + + foreach (var meal in meals) + { + using var ms = new MemoryStream(meal.ImageData); + using var image = Image.Load(ms); + + image.Mutate(x => x.Resize(new ResizeOptions + { + Mode = ResizeMode.Max, + Size = new Size(300, 300) + })); + + using var outStream = new MemoryStream(); + var encoder = new WebpEncoder + { + Quality = 75 + }; + image.Save(outStream, encoder); + + meal.ThumbnailData = outStream.ToArray(); + updatedCount++; + } + + _context.SaveChanges(); + return updatedCount; +} public List GetAllWeeklyMenus() { @@ -166,33 +232,49 @@ public class MenuService .OrderBy(m => m.Name) .ToList(); } - public List GetWeeklyMenu(int weekNumber, int year) - { - var menus = _context.WeeklyMenus - .Where(m => m.WeekNumber == weekNumber && m.Year == year) - .ToList(); +public List GetWeeklyMenu(int weekNumber, int year) +{ + var menus = _context.WeeklyMenus + .Where(m => m.WeekNumber == weekNumber && m.Year == year) + .ToList(); + + var mealIds = menus + .SelectMany(w => new int?[] { w.BreakfastMealId, w.LunchMealId, w.DinnerMealId }) + .Where(id => id.HasValue) + .Select(id => id.Value) + .Distinct() + .ToList(); + + var allMeals = _context.Meals + .Where(m => mealIds.Contains(m.Id)) + .Select(m => new { + m.Id, + m.Name, + m.ThumbnailData // Vi tar med detta även om det bara används för middag + }) + .ToList() + .ToDictionary(m => m.Id, m => m); - var allMeals = _context.Meals.ToDictionary(m => m.Id, m => m.Name); foreach (var wm in menus) { - wm.BreakfastMealName = wm.BreakfastMealId.HasValue && allMeals.TryGetValue(wm.BreakfastMealId.Value, out var breakfast) - ? breakfast - : null; + if (wm.BreakfastMealId.HasValue && allMeals.TryGetValue(wm.BreakfastMealId.Value, out var breakfast)) + wm.BreakfastMealName = breakfast.Name; - wm.LunchMealName = wm.LunchMealId.HasValue && allMeals.TryGetValue(wm.LunchMealId.Value, out var lunch) - ? lunch - : null; + if (wm.LunchMealId.HasValue && allMeals.TryGetValue(wm.LunchMealId.Value, out var lunch)) + wm.LunchMealName = lunch.Name; - wm.DinnerMealName = wm.DinnerMealId.HasValue && allMeals.TryGetValue(wm.DinnerMealId.Value, out var dinner) - ? dinner - : null; + if (wm.DinnerMealId.HasValue && allMeals.TryGetValue(wm.DinnerMealId.Value, out var dinner)) + { + wm.DinnerMealName = dinner.Name; + } } - return menus; - } + return menus; +} + public List GetMenuEntriesByDateRange(DateTime startDate, DateTime endDate) { var results = new List(); diff --git a/Aberwyn/Migrations/20250605062804_AddThumbnailToMeal.Designer.cs b/Aberwyn/Migrations/20250605062804_AddThumbnailToMeal.Designer.cs new file mode 100644 index 0000000..ad83b7c --- /dev/null +++ b/Aberwyn/Migrations/20250605062804_AddThumbnailToMeal.Designer.cs @@ -0,0 +1,679 @@ +// +using System; +using Aberwyn.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Aberwyn.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250605062804_AddThumbnailToMeal")] + partial class AddThumbnailToMeal + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.36") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Aberwyn.Models.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Aberwyn.Models.AppSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("AppSettings"); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BudgetCategoryDefinitionId") + .HasColumnType("int"); + + b.Property("BudgetPeriodId") + .HasColumnType("int"); + + b.Property("Color") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Order") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("BudgetCategoryDefinitionId"); + + b.HasIndex("BudgetPeriodId"); + + b.ToTable("BudgetCategories"); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetCategoryDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Color") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("BudgetCategoryDefinitions"); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("BudgetCategoryId") + .HasColumnType("int"); + + b.Property("BudgetItemDefinitionId") + .HasColumnType("int"); + + b.Property("IncludeInSummary") + .HasColumnType("tinyint(1)"); + + b.Property("IsExpense") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Order") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("BudgetCategoryId"); + + b.HasIndex("BudgetItemDefinitionId"); + + b.ToTable("BudgetItems"); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetItemDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("DefaultCategory") + .HasColumnType("longtext"); + + b.Property("IncludeInSummary") + .HasColumnType("tinyint(1)"); + + b.Property("IsExpense") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("BudgetItemDefinitions"); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetPeriod", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Month") + .HasColumnType("int"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("BudgetPeriods"); + }); + + modelBuilder.Entity("Aberwyn.Models.Ingredient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Item") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MealId") + .HasColumnType("int"); + + b.Property("Quantity") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("MealId"); + + b.ToTable("Ingredients"); + }); + + modelBuilder.Entity("Aberwyn.Models.Meal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CarbType") + .HasColumnType("longtext"); + + b.Property("Category") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("ImageData") + .HasColumnType("longblob"); + + b.Property("ImageMimeType") + .HasColumnType("longtext"); + + b.Property("ImageUrl") + .HasColumnType("longtext"); + + b.Property("Instructions") + .HasColumnType("longtext"); + + b.Property("IsAvailable") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ProteinType") + .HasColumnType("longtext"); + + b.Property("RecipeUrl") + .HasColumnType("longtext"); + + b.Property("ThumbnailData") + .HasColumnType("longblob"); + + b.HasKey("Id"); + + b.ToTable("Meals"); + }); + + modelBuilder.Entity("Aberwyn.Models.PizzaOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CustomerName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IngredientsJson") + .HasColumnType("longtext"); + + b.Property("OrderedAt") + .HasColumnType("datetime(6)"); + + b.Property("PizzaName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PizzaOrders"); + }); + + modelBuilder.Entity("Aberwyn.Models.PushSubscriber", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Auth") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Endpoint") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("P256DH") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PushSubscribers"); + }); + + modelBuilder.Entity("Aberwyn.Models.TodoTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AssignedTo") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsArchived") + .HasColumnType("tinyint(1)"); + + b.Property("Priority") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Tags") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("TodoTasks"); + }); + + modelBuilder.Entity("Aberwyn.Models.WeeklyMenu", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BreakfastMealId") + .HasColumnType("int"); + + b.Property("Cook") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DayOfWeek") + .HasColumnType("int"); + + b.Property("DinnerMealId") + .HasColumnType("int"); + + b.Property("LunchMealId") + .HasColumnType("int"); + + b.Property("WeekNumber") + .HasColumnType("int"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("WeeklyMenu", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("RoleId") + .HasColumnType("varchar(255)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b => + { + b.HasOne("Aberwyn.Models.BudgetCategoryDefinition", "Definition") + .WithMany() + .HasForeignKey("BudgetCategoryDefinitionId"); + + b.HasOne("Aberwyn.Models.BudgetPeriod", "BudgetPeriod") + .WithMany("Categories") + .HasForeignKey("BudgetPeriodId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BudgetPeriod"); + + b.Navigation("Definition"); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetItem", b => + { + b.HasOne("Aberwyn.Models.BudgetCategory", "BudgetCategory") + .WithMany("Items") + .HasForeignKey("BudgetCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Aberwyn.Models.BudgetItemDefinition", "BudgetItemDefinition") + .WithMany() + .HasForeignKey("BudgetItemDefinitionId"); + + b.Navigation("BudgetCategory"); + + b.Navigation("BudgetItemDefinition"); + }); + + modelBuilder.Entity("Aberwyn.Models.Ingredient", b => + { + b.HasOne("Aberwyn.Models.Meal", null) + .WithMany("Ingredients") + .HasForeignKey("MealId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Aberwyn.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Aberwyn.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Aberwyn.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Aberwyn.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetPeriod", b => + { + b.Navigation("Categories"); + }); + + modelBuilder.Entity("Aberwyn.Models.Meal", b => + { + b.Navigation("Ingredients"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Aberwyn/Migrations/20250605062804_AddThumbnailToMeal.cs b/Aberwyn/Migrations/20250605062804_AddThumbnailToMeal.cs new file mode 100644 index 0000000..7b078cf --- /dev/null +++ b/Aberwyn/Migrations/20250605062804_AddThumbnailToMeal.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Aberwyn.Migrations +{ + public partial class AddThumbnailToMeal : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ThumbnailData", + table: "Meals", + type: "longblob", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ThumbnailData", + table: "Meals"); + } + } +} diff --git a/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs b/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs index 78b08f3..35138ed 100644 --- a/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs @@ -301,6 +301,9 @@ namespace Aberwyn.Migrations b.Property("RecipeUrl") .HasColumnType("longtext"); + b.Property("ThumbnailData") + .HasColumnType("longblob"); + b.HasKey("Id"); b.ToTable("Meals"); diff --git a/Aberwyn/Models/MenuViewModel.cs b/Aberwyn/Models/MenuViewModel.cs index 1db01bf..d5b8299 100644 --- a/Aberwyn/Models/MenuViewModel.cs +++ b/Aberwyn/Models/MenuViewModel.cs @@ -13,6 +13,25 @@ namespace Aberwyn.Models public int WeekNumber { get; set; } // Week number for the menu public int Year { get; set; } // Year for the menu } + public class WeeklyMenuDto +{ + public int Id { get; set; } + + public int DayOfWeek { get; set; } + public int WeekNumber { get; set; } + public int Year { get; set; } + + public int? BreakfastMealId { get; set; } + public int? LunchMealId { get; set; } + public int? DinnerMealId { get; set; } + + public string? BreakfastMealName { get; set; } + public string? LunchMealName { get; set; } + public string? DinnerMealName { get; set; } + + public byte[]? DinnerMealThumbnail { get; set; } +} + public class WeeklyMenu { public int Id { get; set; } @@ -55,12 +74,14 @@ public class Meal public string? CarbType { get; set; } public string? RecipeUrl { get; set; } public string? ImageUrl { get; set; } + public bool IsAvailable { get; set; } public DateTime CreatedAt { get; set; } + public byte[]? ThumbnailData { get; set; } - public byte[]? ImageData { get; set; } // 👈 Viktigt! - public string? ImageMimeType { get; set; } // 👈 Viktigt! - public string? Instructions { get; set; } // 👈 Viktigt! + public byte[]? ImageData { get; set; } + public string? ImageMimeType { get; set; } + public string? Instructions { get; set; } public List Ingredients { get; set; } = new(); } diff --git a/Aberwyn/Views/Admin/Index.cshtml b/Aberwyn/Views/Admin/Index.cshtml index f30d4af..5a645f1 100644 --- a/Aberwyn/Views/Admin/Index.cshtml +++ b/Aberwyn/Views/Admin/Index.cshtml @@ -70,6 +70,8 @@ } + +

Testa Pushnotis

diff --git a/Aberwyn/Views/Home/Menu.cshtml b/Aberwyn/Views/Home/Menu.cshtml index 5e24439..0bd89cd 100644 --- a/Aberwyn/Views/Home/Menu.cshtml +++ b/Aberwyn/Views/Home/Menu.cshtml @@ -56,10 +56,10 @@ ng-click="openMeal(getMealIdByDay(day))">
{{day}}
-
Frukost: {{menu[day].breakfastMealName}}
-
Lunch: {{menu[day].lunchMealName}}
Middag: {{menu[day].dinnerMealName}}
-
Inte bestämd
+
Lunch: {{menu[day].lunchMealName}}
+
Frukost: {{menu[day].breakfastMealName}}
+
Inte bestämd
diff --git a/Aberwyn/Views/Shared/_Layout.cshtml b/Aberwyn/Views/Shared/_Layout.cshtml index e01e070..d91fd81 100644 --- a/Aberwyn/Views/Shared/_Layout.cshtml +++ b/Aberwyn/Views/Shared/_Layout.cshtml @@ -33,7 +33,7 @@ {