diff --git a/Aberwyn/Controllers/FoodMenuController.cs b/Aberwyn/Controllers/FoodMenuController.cs index 5701729..9572a3a 100644 --- a/Aberwyn/Controllers/FoodMenuController.cs +++ b/Aberwyn/Controllers/FoodMenuController.cs @@ -230,6 +230,11 @@ namespace Aberwyn.Controllers WeekNumber = resolvedWeek, Year = resolvedYear, WeeklyMenus = menus, + WishList = _context.MealWishes + .Include(w => w.RequestedByUser) + .Where(w => !w.IsArchived) + .OrderByDescending(w => w.CreatedAt) + .ToList() }; var recent = menuService @@ -501,6 +506,66 @@ namespace Aberwyn.Controllers return View(new DoughPlan { AntalPizzor = 8, ViktPerPizza = 220 }); } + [HttpGet] + public IActionResult GetRecentMenuEntries(int weeksBack = 4) + { + var today = DateTime.Today; + + // Datum för måndag i nuvarande vecka + int deltaToMonday = ((int)today.DayOfWeek + 6) % 7; // måndag=0, söndag=6 + var thisWeekMonday = today.AddDays(-deltaToMonday); + + // Startdatum: måndag X veckor bak + var startMonday = thisWeekMonday.AddDays(-7 * weeksBack); + + // Slutdatum: söndag för förra veckan + var endSunday = thisWeekMonday.AddDays(-1); + + // Hämta alla veckomenyer inom intervallet + var allMenus = _context.WeeklyMenus + .Where(w => w.Date >= startMonday && w.Date <= endSunday) + .OrderBy(w => w.Date) + .ToList(); + + // Hämta alla relevanta meal IDs + var mealIds = allMenus + .SelectMany(w => new[] { w.BreakfastMealId, w.LunchMealId, w.DinnerMealId }) + .Where(id => id.HasValue) + .Select(id => id!.Value) + .Distinct() + .ToList(); + + var meals = _context.Meals + .Where(m => mealIds.Contains(m.Id)) + .ToDictionary(m => m.Id, m => m.Name); + + // Skapa entries + var entries = allMenus + .Select(w => new RecentMenuEntry + { + Date = w.Date, + WeekNumber = ISOWeek.GetWeekOfYear(w.Date), + Year = w.Date.Year, + BreakfastMealName = w.BreakfastMealId.HasValue && meals.ContainsKey(w.BreakfastMealId.Value) + ? meals[w.BreakfastMealId.Value] + : "—", + LunchMealName = w.LunchMealId.HasValue && meals.ContainsKey(w.LunchMealId.Value) + ? meals[w.LunchMealId.Value] + : "—", + DinnerMealName = w.DinnerMealId.HasValue && meals.ContainsKey(w.DinnerMealId.Value) + ? meals[w.DinnerMealId.Value] + : "—" + }) + .OrderBy(e => e.Date) + .ToList(); + + return Json(entries); + } + + + + + diff --git a/Aberwyn/Controllers/MealMenuApiController.cs b/Aberwyn/Controllers/MealMenuApiController.cs index 32eb005..b0f7f5d 100644 --- a/Aberwyn/Controllers/MealMenuApiController.cs +++ b/Aberwyn/Controllers/MealMenuApiController.cs @@ -1,7 +1,8 @@ // MealMenuApiController.cs -using Aberwyn.Models; using Aberwyn.Data; +using Aberwyn.Models; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using System.Net.Http.Headers; @@ -104,8 +105,12 @@ namespace Aberwyn.Controllers return StatusCode(500, "Failed to add meal."); } + + + + #region Skolmat -[HttpGet("skolmat")] + [HttpGet("skolmat")] public async Task GetSkolmat(int week, [FromQuery] string sensor = "sensor.engelbrektsskolan") { var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3M2Q5ODIyYzU4ZWI0MjM4OWEyMGQ2MWQ2MWVhOWYzYyIsImlhdCI6MTc0OTE1MzY1MCwiZXhwIjoyMDY0NTEzNjUwfQ.8C_dKm7P1BbFVJKc_wT76YnQqiZxkP9EzrsLbfD0Ml8"; diff --git a/Aberwyn/Controllers/MealWishController.cs b/Aberwyn/Controllers/MealWishController.cs new file mode 100644 index 0000000..8d792bd --- /dev/null +++ b/Aberwyn/Controllers/MealWishController.cs @@ -0,0 +1,124 @@ +using Aberwyn.Data; +using Aberwyn.Models; // Byt till din namespace +using Humanizer; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System; +using System.Security.Claims; + + +[ApiController] +[Route("api/[controller]")] +public class MealWishController : ControllerBase +{ + private readonly ApplicationDbContext _context; + + public MealWishController(ApplicationDbContext context) + { + _context = context; + } + + // 1. Skapa en ny önskan + [HttpPost("create")] + public async Task> Create([FromBody] CreateMealWishDto dto) + { + var wish = new MealWish + { + Name = dto.Name, + Recipe = dto.Recipe + }; + + var userId = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? "anonymous"; + wish.RequestedByUserId = userId; + wish.CreatedAt = DateTime.UtcNow; + wish.IsArchived = false; + wish.IsImported = false; + + _context.MealWishes.Add(wish); + await _context.SaveChangesAsync(); + + return Ok(MealWishDto.FromEntity(wish)); // ✅ returnera DTO + } + + // 2. Hämta alla önskningar (admin) + [HttpGet("all")] + public async Task GetAll(bool includeArchived = false) + { + var wishes = await _context.MealWishes + .Include(w => w.RequestedByUser) // hämta användaren + .Where(w => includeArchived || !w.IsArchived) + .OrderByDescending(w => w.CreatedAt) + .Select(w => new { + w.Id, + w.Name, + w.Recipe, + RequestedByUserName = w.RequestedByUser != null ? w.RequestedByUser.UserName : "Okänd", + CreatedAt = w.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd HH:mm"), + w.IsArchived, + w.IsImported + }) + .ToListAsync(); + + return Ok(wishes); + } + + + // 3. Avfärda / arkivera + [HttpPost("{id}/archive")] + public async Task Archive(int id) + { + var wish = await _context.MealWishes.FindAsync(id); + if (wish == null) return NotFound(); + + wish.IsArchived = true; + await _context.SaveChangesAsync(); + + return Ok(wish); + } + + // 4. Importera till Meals + [HttpPost("{id}/import")] + public async Task Import(int id) + { + var wish = await _context.MealWishes.FindAsync(id); + if (wish == null) return NotFound(); + + if (wish.IsImported) + return BadRequest("Denna rätt har redan importerats."); + + // Skapa en ny Meal + var meal = new Meal + { + Name = wish.Name, + Instructions = wish.Recipe, + IsAvailable = true, + CreatedAt = DateTime.UtcNow, + IsPublished = false + }; + + _context.Meals.Add(meal); + await _context.SaveChangesAsync(); + + wish.LinkedMealId = meal.Id; + wish.IsImported = true; + wish.IsArchived = true; + await _context.SaveChangesAsync(); + + return Ok(wish); + } + + [HttpGet("search")] + public async Task Search([FromQuery] string name) + { + if (string.IsNullOrWhiteSpace(name)) return BadRequest(); + + var meals = await _context.Meals + .Where(m => m.Name.Contains(name)) + .OrderBy(m => m.Name) + .Take(10) + .Select(m => new { m.Id, m.Name }) + .ToListAsync(); + + return Ok(meals); + } +} diff --git a/Aberwyn/Data/ApplicationDbContext.cs b/Aberwyn/Data/ApplicationDbContext.cs index 2897c87..fde6a6b 100644 --- a/Aberwyn/Data/ApplicationDbContext.cs +++ b/Aberwyn/Data/ApplicationDbContext.cs @@ -37,6 +37,7 @@ namespace Aberwyn.Data ); builder.Entity() .OwnsOne(t => t.Metadata); + } @@ -66,6 +67,8 @@ namespace Aberwyn.Data public DbSet RssFeeds { get; set; } public DbSet DownloadRules { get; set; } public DbSet UserTorrentSeen { get; set; } + public DbSet MealWishes { get; set; } + } } diff --git a/Aberwyn/Data/MenuService.cs b/Aberwyn/Data/MenuService.cs index 79bde2d..1aeaa37 100644 --- a/Aberwyn/Data/MenuService.cs +++ b/Aberwyn/Data/MenuService.cs @@ -435,6 +435,8 @@ public List GetWeeklyMenu(int weekNumber, int year) } return results; } + + public List GetMealCategories() { return _context.MealCategories.OrderBy(c => c.DisplayOrder).ToList(); diff --git a/Aberwyn/Migrations/20250921133610_AddMealWish.Designer.cs b/Aberwyn/Migrations/20250921133610_AddMealWish.Designer.cs new file mode 100644 index 0000000..86cdf0f --- /dev/null +++ b/Aberwyn/Migrations/20250921133610_AddMealWish.Designer.cs @@ -0,0 +1,1349 @@ +// +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("20250921133610_AddMealWish")] + partial class AddMealWish + { + 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.Property("PaymentStatus") + .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("DefaultPaymentStatus") + .HasColumnType("int"); + + 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("Name") + .HasColumnType("longtext"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("BudgetPeriods"); + }); + + modelBuilder.Entity("Aberwyn.Models.DoughPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AntalPizzor") + .HasColumnType("int"); + + b.Property("Datum") + .HasColumnType("datetime(6)"); + + b.Property("Jast") + .HasColumnType("double"); + + b.Property("Mjol") + .HasColumnType("double"); + + b.Property("Namn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Olja") + .HasColumnType("double"); + + b.Property("Salt") + .HasColumnType("double"); + + b.Property("TotalDeg") + .HasColumnType("double"); + + b.Property("Vatten") + .HasColumnType("double"); + + b.Property("ViktPerPizza") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("DoughPlans"); + }); + + 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.LabIngredient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Item") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Quantity") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipeLabEntryId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipeLabEntryId"); + + b.ToTable("LabIngredients"); + }); + + modelBuilder.Entity("Aberwyn.Models.LabVersionIngredient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Item") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Quantity") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipeLabVersionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipeLabVersionId"); + + b.ToTable("LabVersionIngredients"); + }); + + modelBuilder.Entity("Aberwyn.Models.Meal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CarbType") + .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("IsPublished") + .HasColumnType("tinyint(1)"); + + b.Property("MealCategoryId") + .HasColumnType("int"); + + 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.HasIndex("MealCategoryId"); + + b.ToTable("Meals"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Color") + .HasColumnType("longtext"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("DisplayOrder") + .HasColumnType("int"); + + b.Property("Icon") + .HasColumnType("longtext"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Slug") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("MealCategories"); + + b.HasData( + new + { + Id = 1, + Color = "#f97316", + DisplayOrder = 1, + Icon = "🍕", + IsActive = true, + Name = "Pizza", + Slug = "pizza" + }); + }); + + modelBuilder.Entity("Aberwyn.Models.MealRating", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("MealId") + .HasColumnType("int"); + + b.Property("Rating") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("MealId"); + + b.ToTable("MealRatings"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealWish", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsArchived") + .HasColumnType("tinyint(1)"); + + b.Property("IsImported") + .HasColumnType("tinyint(1)"); + + b.Property("LinkedMealId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Recipe") + .HasColumnType("longtext"); + + b.Property("RequestedByUserId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("LinkedMealId"); + + b.ToTable("MealWishes"); + }); + + 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.Property("PizzaOrderId") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("PizzaOrderId"); + + b.HasIndex("UserId"); + + b.ToTable("PushSubscribers"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseMealId") + .HasColumnType("int"); + + b.Property("Category") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Inspiration") + .HasColumnType("longtext"); + + b.Property("Notes") + .HasColumnType("longtext"); + + b.Property("Rating") + .HasColumnType("int"); + + b.Property("Tags") + .HasColumnType("longtext"); + + b.Property("TestedBy") + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("BaseMealId"); + + b.ToTable("RecipeLabEntries"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Instructions") + .HasColumnType("longtext"); + + b.Property("RecipeLabEntryId") + .HasColumnType("int"); + + b.Property("ResultNotes") + .HasColumnType("longtext"); + + b.Property("VersionLabel") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("RecipeLabEntryId"); + + b.ToTable("RecipeLabVersions"); + }); + + modelBuilder.Entity("Aberwyn.Models.StoredPushSubscription", 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.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("PushSubscriptions"); + }); + + 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.UserPreferences", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("NotifyBudget") + .HasColumnType("tinyint(1)"); + + b.Property("NotifyMenu") + .HasColumnType("tinyint(1)"); + + b.Property("NotifyPizza") + .HasColumnType("tinyint(1)"); + + b.HasKey("UserId"); + + b.ToTable("UserPreferences"); + }); + + 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("DownloadRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AutoDownload") + .HasColumnType("tinyint(1)"); + + b.Property("CategoryFilter") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("KeywordFilter") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MaxSize") + .HasColumnType("bigint"); + + b.Property("MinSeeders") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("DownloadRules"); + }); + + 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("RssFeed", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LastChecked") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Url") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("RssFeeds"); + }); + + modelBuilder.Entity("TorrentItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Category") + .HasColumnType("longtext"); + + b.Property("Completed") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DownloadKey") + .HasColumnType("longtext"); + + b.Property("InfoHash") + .HasColumnType("varchar(255)"); + + b.Property("Leechers") + .HasColumnType("int"); + + b.Property("MagnetLink") + .HasColumnType("longtext"); + + b.Property("MovieName") + .HasColumnType("longtext"); + + b.Property("PublishDate") + .HasColumnType("datetime(6)"); + + b.Property("RssSource") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Seeders") + .HasColumnType("int"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Token") + .HasColumnType("longtext"); + + b.Property("TorrentUrl") + .HasColumnType("longtext"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("InfoHash") + .IsUnique(); + + b.ToTable("TorrentItems"); + }); + + modelBuilder.Entity("UserTorrentSeen", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("InfoHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SeenDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("UserTorrentSeen"); + }); + + 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", null) + .WithMany("Items") + .HasForeignKey("BudgetCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Aberwyn.Models.BudgetItemDefinition", "BudgetItemDefinition") + .WithMany() + .HasForeignKey("BudgetItemDefinitionId"); + + 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("Aberwyn.Models.LabIngredient", b => + { + b.HasOne("Aberwyn.Models.RecipeLabEntry", "Entry") + .WithMany("Ingredients") + .HasForeignKey("RecipeLabEntryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entry"); + }); + + modelBuilder.Entity("Aberwyn.Models.LabVersionIngredient", b => + { + b.HasOne("Aberwyn.Models.RecipeLabVersion", "Version") + .WithMany("Ingredients") + .HasForeignKey("RecipeLabVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Version"); + }); + + modelBuilder.Entity("Aberwyn.Models.Meal", b => + { + b.HasOne("Aberwyn.Models.MealCategory", "Category") + .WithMany("Meals") + .HasForeignKey("MealCategoryId"); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealRating", b => + { + b.HasOne("Aberwyn.Models.Meal", "Meal") + .WithMany() + .HasForeignKey("MealId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Meal"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealWish", b => + { + b.HasOne("Aberwyn.Models.Meal", "LinkedMeal") + .WithMany() + .HasForeignKey("LinkedMealId"); + + b.Navigation("LinkedMeal"); + }); + + modelBuilder.Entity("Aberwyn.Models.PushSubscriber", b => + { + b.HasOne("Aberwyn.Models.PizzaOrder", "PizzaOrder") + .WithMany() + .HasForeignKey("PizzaOrderId"); + + b.HasOne("Aberwyn.Models.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PizzaOrder"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabEntry", b => + { + b.HasOne("Aberwyn.Models.Meal", "BaseMeal") + .WithMany() + .HasForeignKey("BaseMealId"); + + b.Navigation("BaseMeal"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabVersion", b => + { + b.HasOne("Aberwyn.Models.RecipeLabEntry", "Entry") + .WithMany("Versions") + .HasForeignKey("RecipeLabEntryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entry"); + }); + + modelBuilder.Entity("Aberwyn.Models.StoredPushSubscription", b => + { + b.HasOne("Aberwyn.Models.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Aberwyn.Models.UserPreferences", b => + { + b.HasOne("Aberwyn.Models.ApplicationUser", "User") + .WithOne("Preferences") + .HasForeignKey("Aberwyn.Models.UserPreferences", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + 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("TorrentItem", b => + { + b.OwnsOne("MovieMetadata", "Metadata", b1 => + { + b1.Property("TorrentItemId") + .HasColumnType("int"); + + b1.Property("Actors") + .HasColumnType("longtext"); + + b1.Property("Director") + .HasColumnType("longtext"); + + b1.Property("Genre") + .HasColumnType("longtext"); + + b1.Property("ImdbID") + .HasColumnType("longtext"); + + b1.Property("ImdbRating") + .HasColumnType("longtext"); + + b1.Property("Plot") + .HasColumnType("longtext"); + + b1.Property("Poster") + .HasColumnType("longtext"); + + b1.Property("Providers") + .HasColumnType("longtext"); + + b1.Property("Runtime") + .HasColumnType("longtext"); + + b1.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b1.Property("Year") + .HasColumnType("longtext"); + + b1.HasKey("TorrentItemId"); + + b1.ToTable("TorrentItems"); + + b1.WithOwner() + .HasForeignKey("TorrentItemId"); + }); + + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Aberwyn.Models.ApplicationUser", b => + { + b.Navigation("Preferences") + .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"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealCategory", b => + { + b.Navigation("Meals"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabEntry", b => + { + b.Navigation("Ingredients"); + + b.Navigation("Versions"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabVersion", b => + { + b.Navigation("Ingredients"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Aberwyn/Migrations/20250921133610_AddMealWish.cs b/Aberwyn/Migrations/20250921133610_AddMealWish.cs new file mode 100644 index 0000000..777063b --- /dev/null +++ b/Aberwyn/Migrations/20250921133610_AddMealWish.cs @@ -0,0 +1,53 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Aberwyn.Migrations +{ + public partial class AddMealWish : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "MealWishes", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Recipe = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + LinkedMealId = table.Column(type: "int", nullable: true), + RequestedByUserId = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + IsArchived = table.Column(type: "tinyint(1)", nullable: false), + IsImported = table.Column(type: "tinyint(1)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MealWishes", x => x.Id); + table.ForeignKey( + name: "FK_MealWishes_Meals_LinkedMealId", + column: x => x.LinkedMealId, + principalTable: "Meals", + principalColumn: "Id"); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_MealWishes_LinkedMealId", + table: "MealWishes", + column: "LinkedMealId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "MealWishes"); + } + } +} diff --git a/Aberwyn/Migrations/20250921161413_AddMealWishUserRelation.Designer.cs b/Aberwyn/Migrations/20250921161413_AddMealWishUserRelation.Designer.cs new file mode 100644 index 0000000..4e1eab7 --- /dev/null +++ b/Aberwyn/Migrations/20250921161413_AddMealWishUserRelation.Designer.cs @@ -0,0 +1,1359 @@ +// +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("20250921161413_AddMealWishUserRelation")] + partial class AddMealWishUserRelation + { + 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.Property("PaymentStatus") + .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("DefaultPaymentStatus") + .HasColumnType("int"); + + 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("Name") + .HasColumnType("longtext"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("BudgetPeriods"); + }); + + modelBuilder.Entity("Aberwyn.Models.DoughPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AntalPizzor") + .HasColumnType("int"); + + b.Property("Datum") + .HasColumnType("datetime(6)"); + + b.Property("Jast") + .HasColumnType("double"); + + b.Property("Mjol") + .HasColumnType("double"); + + b.Property("Namn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Olja") + .HasColumnType("double"); + + b.Property("Salt") + .HasColumnType("double"); + + b.Property("TotalDeg") + .HasColumnType("double"); + + b.Property("Vatten") + .HasColumnType("double"); + + b.Property("ViktPerPizza") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("DoughPlans"); + }); + + 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.LabIngredient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Item") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Quantity") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipeLabEntryId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipeLabEntryId"); + + b.ToTable("LabIngredients"); + }); + + modelBuilder.Entity("Aberwyn.Models.LabVersionIngredient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Item") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Quantity") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipeLabVersionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipeLabVersionId"); + + b.ToTable("LabVersionIngredients"); + }); + + modelBuilder.Entity("Aberwyn.Models.Meal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CarbType") + .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("IsPublished") + .HasColumnType("tinyint(1)"); + + b.Property("MealCategoryId") + .HasColumnType("int"); + + 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.HasIndex("MealCategoryId"); + + b.ToTable("Meals"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Color") + .HasColumnType("longtext"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("DisplayOrder") + .HasColumnType("int"); + + b.Property("Icon") + .HasColumnType("longtext"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Slug") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("MealCategories"); + + b.HasData( + new + { + Id = 1, + Color = "#f97316", + DisplayOrder = 1, + Icon = "🍕", + IsActive = true, + Name = "Pizza", + Slug = "pizza" + }); + }); + + modelBuilder.Entity("Aberwyn.Models.MealRating", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("MealId") + .HasColumnType("int"); + + b.Property("Rating") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("MealId"); + + b.ToTable("MealRatings"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealWish", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsArchived") + .HasColumnType("tinyint(1)"); + + b.Property("IsImported") + .HasColumnType("tinyint(1)"); + + b.Property("LinkedMealId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Recipe") + .HasColumnType("longtext"); + + b.Property("RequestedByUserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("LinkedMealId"); + + b.HasIndex("RequestedByUserId"); + + b.ToTable("MealWishes"); + }); + + 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.Property("PizzaOrderId") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("PizzaOrderId"); + + b.HasIndex("UserId"); + + b.ToTable("PushSubscribers"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseMealId") + .HasColumnType("int"); + + b.Property("Category") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Inspiration") + .HasColumnType("longtext"); + + b.Property("Notes") + .HasColumnType("longtext"); + + b.Property("Rating") + .HasColumnType("int"); + + b.Property("Tags") + .HasColumnType("longtext"); + + b.Property("TestedBy") + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("BaseMealId"); + + b.ToTable("RecipeLabEntries"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Instructions") + .HasColumnType("longtext"); + + b.Property("RecipeLabEntryId") + .HasColumnType("int"); + + b.Property("ResultNotes") + .HasColumnType("longtext"); + + b.Property("VersionLabel") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("RecipeLabEntryId"); + + b.ToTable("RecipeLabVersions"); + }); + + modelBuilder.Entity("Aberwyn.Models.StoredPushSubscription", 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.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("PushSubscriptions"); + }); + + 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.UserPreferences", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("NotifyBudget") + .HasColumnType("tinyint(1)"); + + b.Property("NotifyMenu") + .HasColumnType("tinyint(1)"); + + b.Property("NotifyPizza") + .HasColumnType("tinyint(1)"); + + b.HasKey("UserId"); + + b.ToTable("UserPreferences"); + }); + + 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("DownloadRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AutoDownload") + .HasColumnType("tinyint(1)"); + + b.Property("CategoryFilter") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("KeywordFilter") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MaxSize") + .HasColumnType("bigint"); + + b.Property("MinSeeders") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("DownloadRules"); + }); + + 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("RssFeed", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LastChecked") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Url") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("RssFeeds"); + }); + + modelBuilder.Entity("TorrentItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Category") + .HasColumnType("longtext"); + + b.Property("Completed") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DownloadKey") + .HasColumnType("longtext"); + + b.Property("InfoHash") + .HasColumnType("varchar(255)"); + + b.Property("Leechers") + .HasColumnType("int"); + + b.Property("MagnetLink") + .HasColumnType("longtext"); + + b.Property("MovieName") + .HasColumnType("longtext"); + + b.Property("PublishDate") + .HasColumnType("datetime(6)"); + + b.Property("RssSource") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Seeders") + .HasColumnType("int"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Token") + .HasColumnType("longtext"); + + b.Property("TorrentUrl") + .HasColumnType("longtext"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("InfoHash") + .IsUnique(); + + b.ToTable("TorrentItems"); + }); + + modelBuilder.Entity("UserTorrentSeen", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("InfoHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SeenDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("UserTorrentSeen"); + }); + + 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", null) + .WithMany("Items") + .HasForeignKey("BudgetCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Aberwyn.Models.BudgetItemDefinition", "BudgetItemDefinition") + .WithMany() + .HasForeignKey("BudgetItemDefinitionId"); + + 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("Aberwyn.Models.LabIngredient", b => + { + b.HasOne("Aberwyn.Models.RecipeLabEntry", "Entry") + .WithMany("Ingredients") + .HasForeignKey("RecipeLabEntryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entry"); + }); + + modelBuilder.Entity("Aberwyn.Models.LabVersionIngredient", b => + { + b.HasOne("Aberwyn.Models.RecipeLabVersion", "Version") + .WithMany("Ingredients") + .HasForeignKey("RecipeLabVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Version"); + }); + + modelBuilder.Entity("Aberwyn.Models.Meal", b => + { + b.HasOne("Aberwyn.Models.MealCategory", "Category") + .WithMany("Meals") + .HasForeignKey("MealCategoryId"); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealRating", b => + { + b.HasOne("Aberwyn.Models.Meal", "Meal") + .WithMany() + .HasForeignKey("MealId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Meal"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealWish", b => + { + b.HasOne("Aberwyn.Models.Meal", "LinkedMeal") + .WithMany() + .HasForeignKey("LinkedMealId"); + + b.HasOne("Aberwyn.Models.ApplicationUser", "RequestedByUser") + .WithMany() + .HasForeignKey("RequestedByUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LinkedMeal"); + + b.Navigation("RequestedByUser"); + }); + + modelBuilder.Entity("Aberwyn.Models.PushSubscriber", b => + { + b.HasOne("Aberwyn.Models.PizzaOrder", "PizzaOrder") + .WithMany() + .HasForeignKey("PizzaOrderId"); + + b.HasOne("Aberwyn.Models.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PizzaOrder"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabEntry", b => + { + b.HasOne("Aberwyn.Models.Meal", "BaseMeal") + .WithMany() + .HasForeignKey("BaseMealId"); + + b.Navigation("BaseMeal"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabVersion", b => + { + b.HasOne("Aberwyn.Models.RecipeLabEntry", "Entry") + .WithMany("Versions") + .HasForeignKey("RecipeLabEntryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entry"); + }); + + modelBuilder.Entity("Aberwyn.Models.StoredPushSubscription", b => + { + b.HasOne("Aberwyn.Models.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Aberwyn.Models.UserPreferences", b => + { + b.HasOne("Aberwyn.Models.ApplicationUser", "User") + .WithOne("Preferences") + .HasForeignKey("Aberwyn.Models.UserPreferences", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + 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("TorrentItem", b => + { + b.OwnsOne("MovieMetadata", "Metadata", b1 => + { + b1.Property("TorrentItemId") + .HasColumnType("int"); + + b1.Property("Actors") + .HasColumnType("longtext"); + + b1.Property("Director") + .HasColumnType("longtext"); + + b1.Property("Genre") + .HasColumnType("longtext"); + + b1.Property("ImdbID") + .HasColumnType("longtext"); + + b1.Property("ImdbRating") + .HasColumnType("longtext"); + + b1.Property("Plot") + .HasColumnType("longtext"); + + b1.Property("Poster") + .HasColumnType("longtext"); + + b1.Property("Providers") + .HasColumnType("longtext"); + + b1.Property("Runtime") + .HasColumnType("longtext"); + + b1.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b1.Property("Year") + .HasColumnType("longtext"); + + b1.HasKey("TorrentItemId"); + + b1.ToTable("TorrentItems"); + + b1.WithOwner() + .HasForeignKey("TorrentItemId"); + }); + + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Aberwyn.Models.ApplicationUser", b => + { + b.Navigation("Preferences") + .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"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealCategory", b => + { + b.Navigation("Meals"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabEntry", b => + { + b.Navigation("Ingredients"); + + b.Navigation("Versions"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabVersion", b => + { + b.Navigation("Ingredients"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Aberwyn/Migrations/20250921161413_AddMealWishUserRelation.cs b/Aberwyn/Migrations/20250921161413_AddMealWishUserRelation.cs new file mode 100644 index 0000000..d194451 --- /dev/null +++ b/Aberwyn/Migrations/20250921161413_AddMealWishUserRelation.cs @@ -0,0 +1,56 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Aberwyn.Migrations +{ + public partial class AddMealWishUserRelation : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "RequestedByUserId", + table: "MealWishes", + type: "varchar(255)", + nullable: false, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_MealWishes_RequestedByUserId", + table: "MealWishes", + column: "RequestedByUserId"); + + migrationBuilder.AddForeignKey( + name: "FK_MealWishes_AspNetUsers_RequestedByUserId", + table: "MealWishes", + column: "RequestedByUserId", + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_MealWishes_AspNetUsers_RequestedByUserId", + table: "MealWishes"); + + migrationBuilder.DropIndex( + name: "IX_MealWishes_RequestedByUserId", + table: "MealWishes"); + + migrationBuilder.AlterColumn( + name: "RequestedByUserId", + table: "MealWishes", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(255)") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + } + } +} diff --git a/Aberwyn/Migrations/20250921170530_AddDateToWeeklyMenu.Designer.cs b/Aberwyn/Migrations/20250921170530_AddDateToWeeklyMenu.Designer.cs new file mode 100644 index 0000000..f701300 --- /dev/null +++ b/Aberwyn/Migrations/20250921170530_AddDateToWeeklyMenu.Designer.cs @@ -0,0 +1,1362 @@ +// +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("20250921170530_AddDateToWeeklyMenu")] + partial class AddDateToWeeklyMenu + { + 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.Property("PaymentStatus") + .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("DefaultPaymentStatus") + .HasColumnType("int"); + + 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("Name") + .HasColumnType("longtext"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("BudgetPeriods"); + }); + + modelBuilder.Entity("Aberwyn.Models.DoughPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AntalPizzor") + .HasColumnType("int"); + + b.Property("Datum") + .HasColumnType("datetime(6)"); + + b.Property("Jast") + .HasColumnType("double"); + + b.Property("Mjol") + .HasColumnType("double"); + + b.Property("Namn") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Olja") + .HasColumnType("double"); + + b.Property("Salt") + .HasColumnType("double"); + + b.Property("TotalDeg") + .HasColumnType("double"); + + b.Property("Vatten") + .HasColumnType("double"); + + b.Property("ViktPerPizza") + .HasColumnType("double"); + + b.HasKey("Id"); + + b.ToTable("DoughPlans"); + }); + + 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.LabIngredient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Item") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Quantity") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipeLabEntryId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipeLabEntryId"); + + b.ToTable("LabIngredients"); + }); + + modelBuilder.Entity("Aberwyn.Models.LabVersionIngredient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Item") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Quantity") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RecipeLabVersionId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RecipeLabVersionId"); + + b.ToTable("LabVersionIngredients"); + }); + + modelBuilder.Entity("Aberwyn.Models.Meal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CarbType") + .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("IsPublished") + .HasColumnType("tinyint(1)"); + + b.Property("MealCategoryId") + .HasColumnType("int"); + + 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.HasIndex("MealCategoryId"); + + b.ToTable("Meals"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Color") + .HasColumnType("longtext"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("DisplayOrder") + .HasColumnType("int"); + + b.Property("Icon") + .HasColumnType("longtext"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Slug") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("MealCategories"); + + b.HasData( + new + { + Id = 1, + Color = "#f97316", + DisplayOrder = 1, + Icon = "🍕", + IsActive = true, + Name = "Pizza", + Slug = "pizza" + }); + }); + + modelBuilder.Entity("Aberwyn.Models.MealRating", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("MealId") + .HasColumnType("int"); + + b.Property("Rating") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("MealId"); + + b.ToTable("MealRatings"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealWish", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsArchived") + .HasColumnType("tinyint(1)"); + + b.Property("IsImported") + .HasColumnType("tinyint(1)"); + + b.Property("LinkedMealId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Recipe") + .HasColumnType("longtext"); + + b.Property("RequestedByUserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("LinkedMealId"); + + b.HasIndex("RequestedByUserId"); + + b.ToTable("MealWishes"); + }); + + 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.Property("PizzaOrderId") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("PizzaOrderId"); + + b.HasIndex("UserId"); + + b.ToTable("PushSubscribers"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BaseMealId") + .HasColumnType("int"); + + b.Property("Category") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Inspiration") + .HasColumnType("longtext"); + + b.Property("Notes") + .HasColumnType("longtext"); + + b.Property("Rating") + .HasColumnType("int"); + + b.Property("Tags") + .HasColumnType("longtext"); + + b.Property("TestedBy") + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("BaseMealId"); + + b.ToTable("RecipeLabEntries"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabVersion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Instructions") + .HasColumnType("longtext"); + + b.Property("RecipeLabEntryId") + .HasColumnType("int"); + + b.Property("ResultNotes") + .HasColumnType("longtext"); + + b.Property("VersionLabel") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("RecipeLabEntryId"); + + b.ToTable("RecipeLabVersions"); + }); + + modelBuilder.Entity("Aberwyn.Models.StoredPushSubscription", 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.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("PushSubscriptions"); + }); + + 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.UserPreferences", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("NotifyBudget") + .HasColumnType("tinyint(1)"); + + b.Property("NotifyMenu") + .HasColumnType("tinyint(1)"); + + b.Property("NotifyPizza") + .HasColumnType("tinyint(1)"); + + b.HasKey("UserId"); + + b.ToTable("UserPreferences"); + }); + + 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("Date") + .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("DownloadRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AutoDownload") + .HasColumnType("tinyint(1)"); + + b.Property("CategoryFilter") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("KeywordFilter") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MaxSize") + .HasColumnType("bigint"); + + b.Property("MinSeeders") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("DownloadRules"); + }); + + 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("RssFeed", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LastChecked") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Url") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("RssFeeds"); + }); + + modelBuilder.Entity("TorrentItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Category") + .HasColumnType("longtext"); + + b.Property("Completed") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DownloadKey") + .HasColumnType("longtext"); + + b.Property("InfoHash") + .HasColumnType("varchar(255)"); + + b.Property("Leechers") + .HasColumnType("int"); + + b.Property("MagnetLink") + .HasColumnType("longtext"); + + b.Property("MovieName") + .HasColumnType("longtext"); + + b.Property("PublishDate") + .HasColumnType("datetime(6)"); + + b.Property("RssSource") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Seeders") + .HasColumnType("int"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Token") + .HasColumnType("longtext"); + + b.Property("TorrentUrl") + .HasColumnType("longtext"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("InfoHash") + .IsUnique(); + + b.ToTable("TorrentItems"); + }); + + modelBuilder.Entity("UserTorrentSeen", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("InfoHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SeenDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("UserTorrentSeen"); + }); + + 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", null) + .WithMany("Items") + .HasForeignKey("BudgetCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Aberwyn.Models.BudgetItemDefinition", "BudgetItemDefinition") + .WithMany() + .HasForeignKey("BudgetItemDefinitionId"); + + 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("Aberwyn.Models.LabIngredient", b => + { + b.HasOne("Aberwyn.Models.RecipeLabEntry", "Entry") + .WithMany("Ingredients") + .HasForeignKey("RecipeLabEntryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entry"); + }); + + modelBuilder.Entity("Aberwyn.Models.LabVersionIngredient", b => + { + b.HasOne("Aberwyn.Models.RecipeLabVersion", "Version") + .WithMany("Ingredients") + .HasForeignKey("RecipeLabVersionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Version"); + }); + + modelBuilder.Entity("Aberwyn.Models.Meal", b => + { + b.HasOne("Aberwyn.Models.MealCategory", "Category") + .WithMany("Meals") + .HasForeignKey("MealCategoryId"); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealRating", b => + { + b.HasOne("Aberwyn.Models.Meal", "Meal") + .WithMany() + .HasForeignKey("MealId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Meal"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealWish", b => + { + b.HasOne("Aberwyn.Models.Meal", "LinkedMeal") + .WithMany() + .HasForeignKey("LinkedMealId"); + + b.HasOne("Aberwyn.Models.ApplicationUser", "RequestedByUser") + .WithMany() + .HasForeignKey("RequestedByUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LinkedMeal"); + + b.Navigation("RequestedByUser"); + }); + + modelBuilder.Entity("Aberwyn.Models.PushSubscriber", b => + { + b.HasOne("Aberwyn.Models.PizzaOrder", "PizzaOrder") + .WithMany() + .HasForeignKey("PizzaOrderId"); + + b.HasOne("Aberwyn.Models.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PizzaOrder"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabEntry", b => + { + b.HasOne("Aberwyn.Models.Meal", "BaseMeal") + .WithMany() + .HasForeignKey("BaseMealId"); + + b.Navigation("BaseMeal"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabVersion", b => + { + b.HasOne("Aberwyn.Models.RecipeLabEntry", "Entry") + .WithMany("Versions") + .HasForeignKey("RecipeLabEntryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Entry"); + }); + + modelBuilder.Entity("Aberwyn.Models.StoredPushSubscription", b => + { + b.HasOne("Aberwyn.Models.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Aberwyn.Models.UserPreferences", b => + { + b.HasOne("Aberwyn.Models.ApplicationUser", "User") + .WithOne("Preferences") + .HasForeignKey("Aberwyn.Models.UserPreferences", "UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + 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("TorrentItem", b => + { + b.OwnsOne("MovieMetadata", "Metadata", b1 => + { + b1.Property("TorrentItemId") + .HasColumnType("int"); + + b1.Property("Actors") + .HasColumnType("longtext"); + + b1.Property("Director") + .HasColumnType("longtext"); + + b1.Property("Genre") + .HasColumnType("longtext"); + + b1.Property("ImdbID") + .HasColumnType("longtext"); + + b1.Property("ImdbRating") + .HasColumnType("longtext"); + + b1.Property("Plot") + .HasColumnType("longtext"); + + b1.Property("Poster") + .HasColumnType("longtext"); + + b1.Property("Providers") + .HasColumnType("longtext"); + + b1.Property("Runtime") + .HasColumnType("longtext"); + + b1.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b1.Property("Year") + .HasColumnType("longtext"); + + b1.HasKey("TorrentItemId"); + + b1.ToTable("TorrentItems"); + + b1.WithOwner() + .HasForeignKey("TorrentItemId"); + }); + + b.Navigation("Metadata"); + }); + + modelBuilder.Entity("Aberwyn.Models.ApplicationUser", b => + { + b.Navigation("Preferences") + .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"); + }); + + modelBuilder.Entity("Aberwyn.Models.MealCategory", b => + { + b.Navigation("Meals"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabEntry", b => + { + b.Navigation("Ingredients"); + + b.Navigation("Versions"); + }); + + modelBuilder.Entity("Aberwyn.Models.RecipeLabVersion", b => + { + b.Navigation("Ingredients"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Aberwyn/Migrations/20250921170530_AddDateToWeeklyMenu.cs b/Aberwyn/Migrations/20250921170530_AddDateToWeeklyMenu.cs new file mode 100644 index 0000000..cac7b23 --- /dev/null +++ b/Aberwyn/Migrations/20250921170530_AddDateToWeeklyMenu.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Aberwyn.Migrations +{ + public partial class AddDateToWeeklyMenu : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Date", + table: "WeeklyMenu", + type: "datetime(6)", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Date", + table: "WeeklyMenu"); + } + } +} diff --git a/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs b/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs index 7df10d8..8e31661 100644 --- a/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Aberwyn/Migrations/ApplicationDbContextModelSnapshot.cs @@ -484,6 +484,44 @@ namespace Aberwyn.Migrations b.ToTable("MealRatings"); }); + modelBuilder.Entity("Aberwyn.Models.MealWish", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsArchived") + .HasColumnType("tinyint(1)"); + + b.Property("IsImported") + .HasColumnType("tinyint(1)"); + + b.Property("LinkedMealId") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Recipe") + .HasColumnType("longtext"); + + b.Property("RequestedByUserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("LinkedMealId"); + + b.HasIndex("RequestedByUserId"); + + b.ToTable("MealWishes"); + }); + modelBuilder.Entity("Aberwyn.Models.PizzaOrder", b => { b.Property("Id") @@ -719,6 +757,9 @@ namespace Aberwyn.Migrations b.Property("CreatedAt") .HasColumnType("datetime(6)"); + b.Property("Date") + .HasColumnType("datetime(6)"); + b.Property("DayOfWeek") .HasColumnType("int"); @@ -1097,6 +1138,23 @@ namespace Aberwyn.Migrations b.Navigation("Meal"); }); + modelBuilder.Entity("Aberwyn.Models.MealWish", b => + { + b.HasOne("Aberwyn.Models.Meal", "LinkedMeal") + .WithMany() + .HasForeignKey("LinkedMealId"); + + b.HasOne("Aberwyn.Models.ApplicationUser", "RequestedByUser") + .WithMany() + .HasForeignKey("RequestedByUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("LinkedMeal"); + + b.Navigation("RequestedByUser"); + }); + modelBuilder.Entity("Aberwyn.Models.PushSubscriber", b => { b.HasOne("Aberwyn.Models.PizzaOrder", "PizzaOrder") diff --git a/Aberwyn/Models/MenuViewModel.cs b/Aberwyn/Models/MenuViewModel.cs index 22d8586..b3afcb7 100644 --- a/Aberwyn/Models/MenuViewModel.cs +++ b/Aberwyn/Models/MenuViewModel.cs @@ -12,6 +12,7 @@ namespace Aberwyn.Models public List WeeklyMenus { get; set; } // List of weekly menu entries public int WeekNumber { get; set; } // Week number for the menu public int Year { get; set; } // Year for the menu + } public class WeeklyMenuDto { @@ -40,6 +41,7 @@ public class WeeklyMenu public int? LunchMealId { get; set; } public int? DinnerMealId { get; set; } public string? Cook { get; set; } + public DateTime Date { get; set; } public int WeekNumber { get; set; } public int Year { get; set; } public DateTime CreatedAt { get; set; } @@ -59,6 +61,8 @@ public class WeeklyMenu public class RecentMenuEntry { public DateTime Date { get; set; } + public int WeekNumber { get; set; } // Lägg till vecka + public int Year { get; set; } // Lägg till år public string BreakfastMealName { get; set; } public string LunchMealName { get; set; } public string DinnerMealName { get; set; } @@ -176,6 +180,51 @@ public class WeeklyMenu public List Meals { get; set; } = new(); } + public class MealWish + { + [Key] + public int Id { get; set; } + [Required] + public string Name { get; set; } + public string? Recipe { get; set; } + public int? LinkedMealId { get; set; } + [ForeignKey("LinkedMealId")] + public Meal? LinkedMeal { get; set; } + [Required] + public string RequestedByUserId { get; set; } + [ForeignKey(nameof(RequestedByUserId))] + public ApplicationUser? RequestedByUser { get; set; } + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + public bool IsArchived { get; set; } = false; + public bool IsImported { get; set; } = false; + } + + + public class CreateMealWishDto + { + public string Name { get; set; } + public string? Recipe { get; set; } + } + public class MealWishDto + { + public int Id { get; set; } + public string Name { get; set; } = ""; + public string? Recipe { get; set; } + public bool IsArchived { get; set; } + public bool IsImported { get; set; } + + public static MealWishDto FromEntity(MealWish wish) + { + return new MealWishDto + { + Id = wish.Id, + Name = wish.Name, + Recipe = wish.Recipe, + IsArchived = wish.IsArchived, + IsImported = wish.IsImported + }; + } + } public class MealRating { diff --git a/Aberwyn/Models/WeeklyMenuViewModel.cs b/Aberwyn/Models/WeeklyMenuViewModel.cs index 30430c6..028df59 100644 --- a/Aberwyn/Models/WeeklyMenuViewModel.cs +++ b/Aberwyn/Models/WeeklyMenuViewModel.cs @@ -14,6 +14,7 @@ namespace Aberwyn.Models public List RecentEntries { get; set; } = new(); public List WeeklyMenus { get; set; } = new(); public List AvailableCooks { get; set; } = new(); + public List WishList { get; set; } = new(); public List PreviousWeeks { get; set; } = new(); public class RecentMenuEntry @@ -37,6 +38,8 @@ namespace Aberwyn.Models ) ); } + + } diff --git a/Aberwyn/Views/FoodMenu/Veckomeny.cshtml b/Aberwyn/Views/FoodMenu/Veckomeny.cshtml index 8d36044..a939a4e 100644 --- a/Aberwyn/Views/FoodMenu/Veckomeny.cshtml +++ b/Aberwyn/Views/FoodMenu/Veckomeny.cshtml @@ -1,153 +1,278 @@ @using System.Globalization @model Aberwyn.Models.WeeklyMenuViewModel + @{ ViewData["Title"] = "Veckomeny"; var days = new[] { "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag", "Söndag" }; } + + + Veckomeny + + + - - + -
- if (!val) return; + +
+

Veckomeny - Vecka @Model.WeekNumber

+
+ ← Föregående vecka + Vecka @Model.WeekNumber + Nästa vecka → +
+ +
+ + + + + + + + + + + + @for (int i = 0; i < 7; i++) + { + var dinnerEntry = Model.GetMealEntry(i, "Middag"); + var lunchEntry = Model.GetMealEntry(i, "Lunch"); + var breakfastEntry = Model.GetMealEntry(i, "Frukost"); + + + + + + + + + + + + } + +
DagMåltiderKock
+ + @days[i] + +
+
+ + +
+
+
+ +
+
+
+ + + +
+
+ + + +
+
+
+
+
+ + + - const isKnown = window.knownMeals.some(name => name.toLowerCase() === val); - input.classList.add(isKnown ? 'existing-entry' : 'new-entry'); - } - }"> -
-

Veckomeny - Vecka @Model.WeekNumber

-
- ← Föregående vecka - Vecka @Model.WeekNumber - Nästa vecka →
-
- - - - - - - - - - - - @for (int i = 0; i < 7; i++) { - var dinnerEntry = Model.GetMealEntry(i, "Middag"); - var lunchEntry = Model.GetMealEntry(i, "Lunch"); - var breakfastEntry = Model.GetMealEntry(i, "Frukost"); - - - - - - - - - - - - } - -
DagMåltiderKock
- - @days[i] - -
-
- - -
-
-
- -
-
-
- - - -
-
- - - -
-
-
+ + + + + + \ No newline at end of file diff --git a/Aberwyn/Views/Home/Menu.cshtml b/Aberwyn/Views/Home/Menu.cshtml index 8b95389..71f2857 100644 --- a/Aberwyn/Views/Home/Menu.cshtml +++ b/Aberwyn/Views/Home/Menu.cshtml @@ -65,7 +65,7 @@
- + + + + +
+ +

Önska mat

+ + + + +
    +
  • + {{ meal.Name }} +
  • +
+ + + + + + +
diff --git a/Aberwyn/Views/Shared/_Layout.cshtml b/Aberwyn/Views/Shared/_Layout.cshtml index 0e158ff..c559a12 100644 --- a/Aberwyn/Views/Shared/_Layout.cshtml +++ b/Aberwyn/Views/Shared/_Layout.cshtml @@ -51,12 +51,11 @@ @if (User.IsInRole("Admin")) {
  • - Torrents - - @if (ViewBag.NewTorrentCount > 0) - { - @ViewBag.NewTorrentCount - } + T + @if (ViewBag.NewTorrentCount > 0) + { + @ViewBag.NewTorrentCount + }
  • diff --git a/Aberwyn/wwwroot/css/Veckomeny.css b/Aberwyn/wwwroot/css/Veckomeny.css index 147e6a7..1947723 100644 --- a/Aberwyn/wwwroot/css/Veckomeny.css +++ b/Aberwyn/wwwroot/css/Veckomeny.css @@ -184,3 +184,144 @@ padding: 8px 12px; vertical-align: top; } + + +.highlight { + background: linear-gradient(270deg, #ff6ec4, #7873f5, #4ade80, #facc15, #f87171); + background-size: 1000% 1000%; + animation: rainbow 6s linear infinite; + border-radius: 4px; + color: white; + padding: 2px 4px; +} + +@keyframes rainbow { + 0% { + background-position: 0% 50%; + } + + 50% { + background-position: 100% 50%; + } + + 100% { + background-position: 0% 50%; + } +} + + +.menu-wishlist-wrapper { + display: flex; + gap: 2rem; + align-items: flex-start; + width: 100%; +} + +/* Veckomenyn tar 2/3 av bredden */ +.weekly-editor { + flex: 2; + min-width: 400px; +} + +.wishlist { + max-width: 100%; + position: sticky; + top: 1rem; +} + +.wishlist-grid { + display: grid; + gap: 1rem; +} + +.wishlist-card { + background: #fff; + border-radius: 12px; + padding: 1rem; + box-shadow: 0 4px 12px rgba(0,0,0,0.1); + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + + .wishlist-card:hover { + transform: translateY(-3px); + box-shadow: 0 6px 16px rgba(0,0,0,0.15); + } + +.wishlist-header { + font-size: 1.1rem; + margin-bottom: 0.25rem; +} + +.wishlist-meta { + display: block; + font-size: 0.85rem; + color: #666; +} + +.wishlist-body { + margin: 0.5rem 0; + font-size: 0.9rem; + color: #444; +} + +.wishlist-actions { + display: flex; + gap: 0.5rem; + margin-top: 0.5rem; +} + + .wishlist-actions .btn { + border: none; + padding: 0.4rem 0.8rem; + border-radius: 8px; + cursor: pointer; + font-size: 0.85rem; + display: flex; + align-items: center; + gap: 0.3rem; + } + + .wishlist-actions .btn.import { + background: #4ade80; + color: white; + } + + .wishlist-actions .btn.import:hover { + background: #22c55e; + } + + .wishlist-actions .btn.archive { + background: #f87171; + color: white; + } + + .wishlist-actions .btn.archive:hover { + background: #dc2626; + } + +/* Wishlist + Recent history till höger */ +.wishlist-wrapper { + flex: 1; + display: flex; + flex-direction: column; + gap: 1.5rem; +} + + + +.recent-history { + background: var(--header); + border-radius: 8px; + color: var(--text); + padding: 16px; +} +/* Wrap för horisontell scroll */ +.recent-table-wrapper { + overflow-x: auto; +} + +.recent-history-fullwidth table { + width: 100%; + min-width: 600px; /* säkerställer scroll om skärm <600px */ + border-collapse: collapse; +} \ No newline at end of file diff --git a/Aberwyn/wwwroot/css/menu.css b/Aberwyn/wwwroot/css/menu.css index 8a7a08e..4228f30 100644 --- a/Aberwyn/wwwroot/css/menu.css +++ b/Aberwyn/wwwroot/css/menu.css @@ -322,4 +322,116 @@ h1 { .school-meal-title .chevron.rotated { transform: rotate(0deg); } - \ No newline at end of file + +/* Flyout popup */ +.wish-flyout { + position: fixed; + bottom: 80px; /* ovanför plustecknet */ + right: 20px; + width: 300px; + max-width: 90%; + background-color: var(--card-bg); + border-radius: var(--border-radius); + box-shadow: 0 4px 12px rgba(0,0,0,0.3); + padding: 12px; + z-index: 1001; + display: flex; + flex-direction: column; +} + +/* Responsiv på mobil */ +@media (max-width: 480px) { + .wish-flyout { + right: 10px; + width: calc(100% - 20px); + bottom: 70px; + } +} + +.wish-flyout label { + font-weight: bold; + margin-top: 8px; +} + +.wish-flyout input, +.wish-flyout textarea { + width: 100%; + margin-top: 4px; + padding: 6px; + border-radius: var(--border-radius); + border: 1px solid #ccc; + font-size: 0.9rem; + box-sizing: border-box; +} + +.wish-flyout textarea { + resize: vertical; + min-height: 60px; +} + +.flyout-buttons { + display: flex; + justify-content: flex-end; + gap: 6px; + margin-top: 10px; +} + + .flyout-buttons button { + padding: 6px 12px; + border-radius: var(--border-radius); + border: none; + cursor: pointer; + } + + .flyout-buttons button[type="submit"] { + background-color: var(--accent); + color: #fff; + } + + .flyout-buttons button[type="button"] { + background-color: #ccc; + color: var(--text); + } +/* Floating button fixed i nedre högra hörnet */ +.floating-btn { + position: fixed; /* fixed istället för relative/absolute */ + bottom: 20px; /* avstånd från botten */ + right: 20px; /* avstånd från höger */ + width: 50px; + height: 50px; + border-radius: 50%; + background-color: var(--accent); + color: #fff; + font-size: 28px; + border: none; + cursor: pointer; + box-shadow: 0 4px 8px rgba(0,0,0,0.3); + z-index: 1000; /* högre än andra element så den ligger ovanpå */ +} + + .floating-btn:hover { + background-color: #2B6CB0; + } +.autocomplete-list { + list-style: none; + padding: 0; + margin: 4px 0 0 0; + border: 1px solid #ccc; + border-radius: 4px; + max-height: 150px; + overflow-y: auto; + background: var(--card-bg); + position: absolute; + width: 90%; /* Anpassa efter input */ + z-index: 1000; +} + + .autocomplete-list li { + padding: 6px 10px; + cursor: pointer; + } + + .autocomplete-list li:hover { + background-color: var(--accent); + color: #fff; + } diff --git a/Aberwyn/wwwroot/js/menu.js b/Aberwyn/wwwroot/js/menu.js index 8917d82..2887eb1 100644 --- a/Aberwyn/wwwroot/js/menu.js +++ b/Aberwyn/wwwroot/js/menu.js @@ -17,7 +17,7 @@ angular.module('mealMenuApp', ['ngSanitize']) $scope.daysOfWeek = ["Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag", "Söndag"]; $scope.loadMenu = function () { - console.log("Hämtar meny för vecka:", $scope.selectedWeek, $scope.selectedYear); + //console.log("Hämtar meny för vecka:", $scope.selectedWeek, $scope.selectedYear); $http.get('/api/mealMenuApi/getWeeklyMenu', { params: { weekNumber: $scope.selectedWeek, year: $scope.selectedYear } @@ -28,8 +28,8 @@ angular.module('mealMenuApp', ['ngSanitize']) rawMenu.forEach(item => { const dayIndex = item.DayOfWeek - 1; const day = $scope.daysOfWeek[dayIndex]; - console.warn("Item:", item); - console.log("day: " + day + "(" + dayIndex + " ) item: " + item.DinnerMealName); + //console.warn("Item:", item); + //console.log("day: " + day + "(" + dayIndex + " ) item: " + item.DinnerMealName); const thumb = item.DinnerMealThumbnail; $scope.menu[day] = { @@ -39,12 +39,12 @@ angular.module('mealMenuApp', ['ngSanitize']) lunchMealName: item.LunchMealName, dinnerMealId: item.DinnerMealId, dinnerMealName: item.DinnerMealName, - imageUrl: thumb ? `data:image/webp;base64,${thumb}` : '/img/default-thumbnail.webp' + imageUrl: thumb ? `data:image/webp;base64,${thumb}` : '' }; }); }).catch(err => console.error("Fel vid hämtning av meny:", err)); - //$scope.loadSchoolMeals(); // Lägg till här + $scope.loadSchoolMeals(); }; $scope.schoolMeals = []; @@ -104,7 +104,7 @@ angular.module('mealMenuApp', ['ngSanitize']) }; $scope.getDayImage = function (day) { - return $scope.menu[day]?.imageUrl || '/img/default-thumbnail.webp'; + return $scope.menu[day]?.imageUrl || ''; }; $scope.getMealIdByDay = function (day) { @@ -152,6 +152,76 @@ angular.module('mealMenuApp', ['ngSanitize']) if (viewBtn) viewBtn.textContent = $scope.getViewIcon(); }, 0); }; + $scope.isLoggedIn = true; // sätt till true/false beroende på användarstatus + $scope.showWishForm = false; + $scope.showRecipeField = true; // kan styras av autocomplete + $scope.newWishName = ""; + $scope.newWishRecipe = ""; + + $scope.showWishFlyout = false; + + $scope.toggleWishFlyout = function () { + $scope.showWishFlyout = !$scope.showWishFlyout; + } + + $scope.closeWishFlyout = function () { + $scope.showWishFlyout = false; + $scope.newWishName = ""; + $scope.newWishRecipe = ""; + $scope.showRecipeField = true; + } + + $scope.existingMeals = []; + $scope.selectedMeal = null; + + $scope.checkExistingMeal = function () { + if (!$scope.newWishName || $scope.newWishName.length < 2) { + $scope.existingMeals = []; + $scope.showRecipeField = true; + return; + } + + fetch('/api/MealWish/search?name=' + encodeURIComponent($scope.newWishName)) + .then(res => res.json()) + .then(data => { + $scope.existingMeals = data; + $scope.$apply(); // viktigt annars uppdateras inte ng-repeat + }) + .catch(err => console.error(err)); + } + + $scope.selectExistingMeal = function (meal) { + $scope.newWishName = meal.Name; + $scope.selectedMeal = meal; + $scope.showRecipeField = true; + $scope.existingMeals = []; + } + + $scope.submitWish = function () { + if (!$scope.newWishName) return; + if ($scope.existingMeal) { + wish.LinkedMealId = $scope.existingMeal.Id; + wish.Recipe = $scope.Recipe; + } + const payload = { + Name: $scope.newWishName, + Recipe: $scope.newWishRecipe + }; + + fetch('/api/MealWish/create', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }) + .then(res => res.json()) + .then(data => { + console.log("Önskan skickad:", data); + $scope.closeWishFlyout(); + $scope.$apply(); + }) + .catch(err => console.error(err)); + } + console.log("Initierar meny och måltidsladdning..."); $scope.loadMenu();