This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -364,3 +364,4 @@ FodyWeavers.xsd
|
|||||||
|
|
||||||
# Setupfil för Aberwyn
|
# Setupfil för Aberwyn
|
||||||
infrastructure/setup.json
|
infrastructure/setup.json
|
||||||
|
Aberwyn/Infrastructure/setup.json
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
<!-- Övrigt -->
|
<!-- Övrigt -->
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.15.1" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.15.1" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.18" />
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.18" />
|
||||||
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.9" />
|
||||||
<PackageReference Include="WebPush" Version="1.0.12" />
|
<PackageReference Include="WebPush" Version="1.0.12" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace Aberwyn.Controllers
|
|||||||
[Authorize(Roles = "Admin")]
|
[Authorize(Roles = "Admin")]
|
||||||
public class AdminController : Controller
|
public class AdminController : Controller
|
||||||
{
|
{
|
||||||
|
private readonly MenuService _menuService;
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
private readonly RoleManager<IdentityRole> _roleManager;
|
private readonly RoleManager<IdentityRole> _roleManager;
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
@@ -18,12 +19,14 @@ namespace Aberwyn.Controllers
|
|||||||
private readonly ApplicationDbContext _context;
|
private readonly ApplicationDbContext _context;
|
||||||
|
|
||||||
public AdminController(
|
public AdminController(
|
||||||
|
MenuService menuService,
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
RoleManager<IdentityRole> roleManager,
|
RoleManager<IdentityRole> roleManager,
|
||||||
IConfiguration configuration,
|
IConfiguration configuration,
|
||||||
IHostEnvironment env,
|
IHostEnvironment env,
|
||||||
ApplicationDbContext context)
|
ApplicationDbContext context)
|
||||||
{
|
{
|
||||||
|
_menuService = menuService;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_roleManager = roleManager;
|
_roleManager = roleManager;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
@@ -144,6 +147,13 @@ namespace Aberwyn.Controllers
|
|||||||
TempData["Message"] = $"✅ Import av veckomenyer från extern databas klar ({sourceMenus.Count}).";
|
TempData["Message"] = $"✅ Import av veckomenyer från extern databas klar ({sourceMenus.Count}).";
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
|
[HttpPost]
|
||||||
|
[Authorize(Roles = "Admin")]
|
||||||
|
public IActionResult GenerateThumbnails()
|
||||||
|
{
|
||||||
|
var count = _menuService.GenerateMissingThumbnails();
|
||||||
|
return Ok($"{count} thumbnails skapades.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
using Aberwyn.Models;
|
using Aberwyn.Models;
|
||||||
using Aberwyn.Data;
|
using Aberwyn.Data;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
|
||||||
namespace Aberwyn.Controllers
|
namespace Aberwyn.Controllers
|
||||||
{
|
{
|
||||||
@@ -70,6 +72,15 @@ namespace Aberwyn.Controllers
|
|||||||
return StatusCode(500, $"<pre>{ex.Message}</pre>");
|
return StatusCode(500, $"<pre>{ex.Message}</pre>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
[HttpGet("Meal/Thumbnail/{id}")]
|
||||||
|
public IActionResult Thumbnail(int id)
|
||||||
|
{
|
||||||
|
var meal = _menuService.GetMealById(id);
|
||||||
|
if (meal == null || meal.ThumbnailData == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return File(meal.ThumbnailData, "image/webp"); // eller image/jpeg om du använder det
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -93,6 +104,8 @@ namespace Aberwyn.Controllers
|
|||||||
ImageFile.CopyTo(ms);
|
ImageFile.CopyTo(ms);
|
||||||
meal.ImageData = ms.ToArray();
|
meal.ImageData = ms.ToArray();
|
||||||
meal.ImageMimeType = ImageFile.ContentType;
|
meal.ImageMimeType = ImageFile.ContentType;
|
||||||
|
meal.ThumbnailData = GenerateThumbnail(ImageFile);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -111,8 +124,21 @@ namespace Aberwyn.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private byte[] GenerateThumbnail(IFormFile file)
|
||||||
|
{
|
||||||
|
using var image = SixLabors.ImageSharp.Image.Load(file.OpenReadStream());
|
||||||
|
image.Mutate(x => x.Resize(new ResizeOptions
|
||||||
|
{
|
||||||
|
Mode = ResizeMode.Max,
|
||||||
|
Size = new Size(300, 300) // eller vad du vill för korten
|
||||||
|
}));
|
||||||
|
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
image.SaveAsWebp(ms); // kräver ImageSharp.Webp-paketet
|
||||||
|
return ms.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
|||||||
@@ -33,6 +33,16 @@ namespace Aberwyn.Controllers
|
|||||||
return Ok(mealDtos);
|
return Ok(mealDtos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("getWeeklyMenu")]
|
||||||
|
public IActionResult GetWeeklyMenu(int weekNumber, int year)
|
||||||
|
{
|
||||||
|
var menuDtos = _menuService.GetWeeklyMenuDto(weekNumber, year);
|
||||||
|
Console.WriteLine("Hämtar meals: " + menuDtos);
|
||||||
|
return Ok(menuDtos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[HttpPut("menu")]
|
[HttpPut("menu")]
|
||||||
public IActionResult SaveMenu([FromBody] MenuViewModel weeklyMenu)
|
public IActionResult SaveMenu([FromBody] MenuViewModel weeklyMenu)
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
// Nya versionen av MenuService med Entity Framework
|
// Nya versionen av MenuService med Entity Framework
|
||||||
using Aberwyn.Models;
|
using Aberwyn.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.Formats.Webp;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using static Aberwyn.Data.SetupService;
|
using static Aberwyn.Data.SetupService;
|
||||||
|
|
||||||
@@ -26,6 +29,37 @@ public class MenuService
|
|||||||
return new MenuService(context);
|
return new MenuService(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<WeeklyMenuDto> GetWeeklyMenuDto(int weekNumber, int year)
|
||||||
|
{
|
||||||
|
var query = from wm in _context.WeeklyMenus
|
||||||
|
where wm.WeekNumber == weekNumber && wm.Year == year
|
||||||
|
join mDinner in _context.Meals on wm.DinnerMealId equals mDinner.Id into dinnerJoin
|
||||||
|
from mDinner in dinnerJoin.DefaultIfEmpty()
|
||||||
|
join mLunch in _context.Meals on wm.LunchMealId equals mLunch.Id into lunchJoin
|
||||||
|
from mLunch in lunchJoin.DefaultIfEmpty()
|
||||||
|
join mBreakfast in _context.Meals on wm.BreakfastMealId equals mBreakfast.Id into breakfastJoin
|
||||||
|
from mBreakfast in breakfastJoin.DefaultIfEmpty()
|
||||||
|
select new WeeklyMenuDto
|
||||||
|
{
|
||||||
|
Id = wm.Id,
|
||||||
|
DayOfWeek = wm.DayOfWeek,
|
||||||
|
WeekNumber = wm.WeekNumber,
|
||||||
|
Year = wm.Year,
|
||||||
|
BreakfastMealId = wm.BreakfastMealId,
|
||||||
|
LunchMealId = wm.LunchMealId,
|
||||||
|
DinnerMealId = wm.DinnerMealId,
|
||||||
|
BreakfastMealName = mBreakfast.Name,
|
||||||
|
LunchMealName = mLunch.Name,
|
||||||
|
DinnerMealName = mDinner.Name,
|
||||||
|
DinnerMealThumbnail = mDinner.ThumbnailData
|
||||||
|
};
|
||||||
|
|
||||||
|
return query.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void UpdateWeeklyMenu(MenuViewModel model)
|
public void UpdateWeeklyMenu(MenuViewModel model)
|
||||||
{
|
{
|
||||||
var existing = _context.WeeklyMenus
|
var existing = _context.WeeklyMenus
|
||||||
@@ -70,6 +104,38 @@ public class MenuService
|
|||||||
_context.SaveChanges();
|
_context.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GenerateMissingThumbnails()
|
||||||
|
{
|
||||||
|
var updatedCount = 0;
|
||||||
|
var meals = _context.Meals
|
||||||
|
.Where(m => m.ImageData != null && m.ThumbnailData == null)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var meal in meals)
|
||||||
|
{
|
||||||
|
using var ms = new MemoryStream(meal.ImageData);
|
||||||
|
using var image = Image.Load(ms);
|
||||||
|
|
||||||
|
image.Mutate(x => x.Resize(new ResizeOptions
|
||||||
|
{
|
||||||
|
Mode = ResizeMode.Max,
|
||||||
|
Size = new Size(300, 300)
|
||||||
|
}));
|
||||||
|
|
||||||
|
using var outStream = new MemoryStream();
|
||||||
|
var encoder = new WebpEncoder
|
||||||
|
{
|
||||||
|
Quality = 75
|
||||||
|
};
|
||||||
|
image.Save(outStream, encoder);
|
||||||
|
|
||||||
|
meal.ThumbnailData = outStream.ToArray();
|
||||||
|
updatedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.SaveChanges();
|
||||||
|
return updatedCount;
|
||||||
|
}
|
||||||
|
|
||||||
public List<WeeklyMenu> GetAllWeeklyMenus()
|
public List<WeeklyMenu> GetAllWeeklyMenus()
|
||||||
{
|
{
|
||||||
@@ -166,33 +232,49 @@ public class MenuService
|
|||||||
.OrderBy(m => m.Name)
|
.OrderBy(m => m.Name)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
public List<WeeklyMenu> GetWeeklyMenu(int weekNumber, int year)
|
public List<WeeklyMenu> GetWeeklyMenu(int weekNumber, int year)
|
||||||
{
|
{
|
||||||
var menus = _context.WeeklyMenus
|
var menus = _context.WeeklyMenus
|
||||||
.Where(m => m.WeekNumber == weekNumber && m.Year == year)
|
.Where(m => m.WeekNumber == weekNumber && m.Year == year)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
var mealIds = menus
|
||||||
|
.SelectMany(w => new int?[] { w.BreakfastMealId, w.LunchMealId, w.DinnerMealId })
|
||||||
|
.Where(id => id.HasValue)
|
||||||
|
.Select(id => id.Value)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var allMeals = _context.Meals
|
||||||
|
.Where(m => mealIds.Contains(m.Id))
|
||||||
|
.Select(m => new {
|
||||||
|
m.Id,
|
||||||
|
m.Name,
|
||||||
|
m.ThumbnailData // Vi tar med detta även om det bara används för middag
|
||||||
|
})
|
||||||
|
.ToList()
|
||||||
|
.ToDictionary(m => m.Id, m => m);
|
||||||
|
|
||||||
var allMeals = _context.Meals.ToDictionary(m => m.Id, m => m.Name);
|
|
||||||
|
|
||||||
foreach (var wm in menus)
|
foreach (var wm in menus)
|
||||||
{
|
{
|
||||||
wm.BreakfastMealName = wm.BreakfastMealId.HasValue && allMeals.TryGetValue(wm.BreakfastMealId.Value, out var breakfast)
|
if (wm.BreakfastMealId.HasValue && allMeals.TryGetValue(wm.BreakfastMealId.Value, out var breakfast))
|
||||||
? breakfast
|
wm.BreakfastMealName = breakfast.Name;
|
||||||
: null;
|
|
||||||
|
|
||||||
wm.LunchMealName = wm.LunchMealId.HasValue && allMeals.TryGetValue(wm.LunchMealId.Value, out var lunch)
|
if (wm.LunchMealId.HasValue && allMeals.TryGetValue(wm.LunchMealId.Value, out var lunch))
|
||||||
? lunch
|
wm.LunchMealName = lunch.Name;
|
||||||
: null;
|
|
||||||
|
|
||||||
wm.DinnerMealName = wm.DinnerMealId.HasValue && allMeals.TryGetValue(wm.DinnerMealId.Value, out var dinner)
|
if (wm.DinnerMealId.HasValue && allMeals.TryGetValue(wm.DinnerMealId.Value, out var dinner))
|
||||||
? dinner
|
{
|
||||||
: null;
|
wm.DinnerMealName = dinner.Name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return menus;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
return menus;
|
||||||
|
}
|
||||||
|
|
||||||
public List<WeeklyMenu> GetMenuEntriesByDateRange(DateTime startDate, DateTime endDate)
|
public List<WeeklyMenu> GetMenuEntriesByDateRange(DateTime startDate, DateTime endDate)
|
||||||
{
|
{
|
||||||
var results = new List<WeeklyMenu>();
|
var results = new List<WeeklyMenu>();
|
||||||
|
|||||||
679
Aberwyn/Migrations/20250605062804_AddThumbnailToMeal.Designer.cs
generated
Normal file
679
Aberwyn/Migrations/20250605062804_AddThumbnailToMeal.Designer.cs
generated
Normal file
@@ -0,0 +1,679 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Aberwyn.Data;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Aberwyn.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20250605062804_AddThumbnailToMeal")]
|
||||||
|
partial class AddThumbnailToMeal
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "6.0.36")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("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<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("AppSettings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int?>("BudgetCategoryDefinitionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("BudgetPeriodId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Color")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("Order")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("BudgetCategoryDefinitionId");
|
||||||
|
|
||||||
|
b.HasIndex("BudgetPeriodId");
|
||||||
|
|
||||||
|
b.ToTable("BudgetCategories");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.BudgetCategoryDefinition", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Color")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("BudgetCategoryDefinitions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.BudgetItem", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("Amount")
|
||||||
|
.HasColumnType("decimal(65,30)");
|
||||||
|
|
||||||
|
b.Property<int>("BudgetCategoryId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int?>("BudgetItemDefinitionId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<bool>("IncludeInSummary")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsExpense")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("Order")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("BudgetCategoryId");
|
||||||
|
|
||||||
|
b.HasIndex("BudgetItemDefinitionId");
|
||||||
|
|
||||||
|
b.ToTable("BudgetItems");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.BudgetItemDefinition", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("DefaultCategory")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool>("IncludeInSummary")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsExpense")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("BudgetItemDefinitions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.BudgetPeriod", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Month")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Order")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Year")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("BudgetPeriods");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.Ingredient", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Item")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("MealId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Quantity")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MealId");
|
||||||
|
|
||||||
|
b.ToTable("Ingredients");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.Meal", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("CarbType")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Category")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<byte[]>("ImageData")
|
||||||
|
.HasColumnType("longblob");
|
||||||
|
|
||||||
|
b.Property<string>("ImageMimeType")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("ImageUrl")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Instructions")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("ProteinType")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("RecipeUrl")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<byte[]>("ThumbnailData")
|
||||||
|
.HasColumnType("longblob");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Meals");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.PizzaOrder", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("CustomerName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("IngredientsJson")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime>("OrderedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("PizzaName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PizzaOrders");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.PushSubscriber", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Auth")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Endpoint")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("P256DH")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PushSubscribers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.TodoTask", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("AssignedTo")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<bool>("IsArchived")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<int>("Priority")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Tags")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("TodoTasks");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.WeeklyMenu", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int?>("BreakfastMealId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Cook")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<int>("DayOfWeek")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int?>("DinnerMealId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int?>("LunchMealId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("WeekNumber")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Year")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("WeeklyMenu", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("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<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Aberwyn.Models.BudgetCategoryDefinition", "Definition")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("BudgetCategoryDefinitionId");
|
||||||
|
|
||||||
|
b.HasOne("Aberwyn.Models.BudgetPeriod", "BudgetPeriod")
|
||||||
|
.WithMany("Categories")
|
||||||
|
.HasForeignKey("BudgetPeriodId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("BudgetPeriod");
|
||||||
|
|
||||||
|
b.Navigation("Definition");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.BudgetItem", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Aberwyn.Models.BudgetCategory", "BudgetCategory")
|
||||||
|
.WithMany("Items")
|
||||||
|
.HasForeignKey("BudgetCategoryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Aberwyn.Models.BudgetItemDefinition", "BudgetItemDefinition")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("BudgetItemDefinitionId");
|
||||||
|
|
||||||
|
b.Navigation("BudgetCategory");
|
||||||
|
|
||||||
|
b.Navigation("BudgetItemDefinition");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.Ingredient", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Aberwyn.Models.Meal", null)
|
||||||
|
.WithMany("Ingredients")
|
||||||
|
.HasForeignKey("MealId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Aberwyn.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Aberwyn.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", 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<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Aberwyn.Models.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Items");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.BudgetPeriod", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Categories");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Aberwyn.Models.Meal", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Ingredients");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
Aberwyn/Migrations/20250605062804_AddThumbnailToMeal.cs
Normal file
26
Aberwyn/Migrations/20250605062804_AddThumbnailToMeal.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Aberwyn.Migrations
|
||||||
|
{
|
||||||
|
public partial class AddThumbnailToMeal : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<byte[]>(
|
||||||
|
name: "ThumbnailData",
|
||||||
|
table: "Meals",
|
||||||
|
type: "longblob",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ThumbnailData",
|
||||||
|
table: "Meals");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -301,6 +301,9 @@ namespace Aberwyn.Migrations
|
|||||||
b.Property<string>("RecipeUrl")
|
b.Property<string>("RecipeUrl")
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<byte[]>("ThumbnailData")
|
||||||
|
.HasColumnType("longblob");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("Meals");
|
b.ToTable("Meals");
|
||||||
|
|||||||
@@ -13,6 +13,25 @@ namespace Aberwyn.Models
|
|||||||
public int WeekNumber { get; set; } // Week number for the menu
|
public int WeekNumber { get; set; } // Week number for the menu
|
||||||
public int Year { get; set; } // Year for the menu
|
public int Year { get; set; } // Year for the menu
|
||||||
}
|
}
|
||||||
|
public class WeeklyMenuDto
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int DayOfWeek { get; set; }
|
||||||
|
public int WeekNumber { get; set; }
|
||||||
|
public int Year { get; set; }
|
||||||
|
|
||||||
|
public int? BreakfastMealId { get; set; }
|
||||||
|
public int? LunchMealId { get; set; }
|
||||||
|
public int? DinnerMealId { get; set; }
|
||||||
|
|
||||||
|
public string? BreakfastMealName { get; set; }
|
||||||
|
public string? LunchMealName { get; set; }
|
||||||
|
public string? DinnerMealName { get; set; }
|
||||||
|
|
||||||
|
public byte[]? DinnerMealThumbnail { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class WeeklyMenu
|
public class WeeklyMenu
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
@@ -55,12 +74,14 @@ public class Meal
|
|||||||
public string? CarbType { get; set; }
|
public string? CarbType { get; set; }
|
||||||
public string? RecipeUrl { get; set; }
|
public string? RecipeUrl { get; set; }
|
||||||
public string? ImageUrl { get; set; }
|
public string? ImageUrl { get; set; }
|
||||||
|
|
||||||
public bool IsAvailable { get; set; }
|
public bool IsAvailable { get; set; }
|
||||||
public DateTime CreatedAt { get; set; }
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public byte[]? ThumbnailData { get; set; }
|
||||||
|
|
||||||
public byte[]? ImageData { get; set; } // 👈 Viktigt!
|
public byte[]? ImageData { get; set; }
|
||||||
public string? ImageMimeType { get; set; } // 👈 Viktigt!
|
public string? ImageMimeType { get; set; }
|
||||||
public string? Instructions { get; set; } // 👈 Viktigt!
|
public string? Instructions { get; set; }
|
||||||
|
|
||||||
public List<Ingredient> Ingredients { get; set; } = new();
|
public List<Ingredient> Ingredients { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,8 @@
|
|||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<button ng-click="generateThumbnails()">Generera thumbnails</button>
|
||||||
|
|
||||||
|
|
||||||
<h3>Testa Pushnotis</h3>
|
<h3>Testa Pushnotis</h3>
|
||||||
<form onsubmit="sendPush(event)">
|
<form onsubmit="sendPush(event)">
|
||||||
|
|||||||
@@ -56,10 +56,10 @@
|
|||||||
ng-click="openMeal(getMealIdByDay(day))">
|
ng-click="openMeal(getMealIdByDay(day))">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="day">{{day}}</div>
|
<div class="day">{{day}}</div>
|
||||||
<div class="meal" ng-if="menu[day].breakfastMealName">Frukost: {{menu[day].breakfastMealName}}</div>
|
|
||||||
<div class="meal" ng-if="menu[day].lunchMealName">Lunch: {{menu[day].lunchMealName}}</div>
|
|
||||||
<div class="meal" ng-if="menu[day].dinnerMealName">Middag: {{menu[day].dinnerMealName}}</div>
|
<div class="meal" ng-if="menu[day].dinnerMealName">Middag: {{menu[day].dinnerMealName}}</div>
|
||||||
<div class="meal" ng-if="!menu[day]">Inte bestämd</div>
|
<div class="meal" ng-if="menu[day].lunchMealName">Lunch: {{menu[day].lunchMealName}}</div>
|
||||||
|
<div class="meal" ng-if="menu[day].breakfastMealName">Frukost: {{menu[day].breakfastMealName}}</div>
|
||||||
|
<div class="meal" ng-if="menu[day] && !menu[day].breakfastMealName && !menu[day].lunchMealName && !menu[day].dinnerMealName">Inte bestämd</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
{
|
{
|
||||||
<ul class="nav-list">
|
<ul class="nav-list">
|
||||||
<li><a asp-controller="Home" asp-action="Index"><i class="fas fa-home"></i> Home</a></li>
|
<li><a asp-controller="Home" asp-action="Index"><i class="fas fa-home"></i> Home</a></li>
|
||||||
@if (User.IsInRole("Admin") || User.IsInRole("Budget"))
|
@if (User.IsInRole("Budget"))
|
||||||
{
|
{
|
||||||
<li><a asp-controller="Budget" asp-action="Index"><i class="fas fa-wallet"></i> Budget</a></li>
|
<li><a asp-controller="Budget" asp-action="Index"><i class="fas fa-wallet"></i> Budget</a></li>
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
{
|
{
|
||||||
<li><a asp-controller="FoodMenu" asp-action="PizzaOrder"><i class="fas fa-pizza-slice"></i> Beställ pizza</a></li>
|
<li><a asp-controller="FoodMenu" asp-action="PizzaOrder"><i class="fas fa-pizza-slice"></i> Beställ pizza</a></li>
|
||||||
}
|
}
|
||||||
@if (User.IsInRole("Admin") || User.IsInRole("Chef"))
|
@if (User.IsInRole("Chef"))
|
||||||
{
|
{
|
||||||
<li><a asp-controller="FoodMenu" asp-action="Veckomeny"><i class="fas fa-calendar-week"></i> Administrera Veckomeny</a></li>
|
<li><a asp-controller="FoodMenu" asp-action="Veckomeny"><i class="fas fa-calendar-week"></i> Administrera Veckomeny</a></li>
|
||||||
<li><a asp-controller="FoodMenu" asp-action="MealAdmin"><i class="fas fa-list"></i> Måltider</a></li>
|
<li><a asp-controller="FoodMenu" asp-action="MealAdmin"><i class="fas fa-list"></i> Måltider</a></li>
|
||||||
|
|||||||
@@ -215,14 +215,15 @@ h1 {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: rgba(0, 0, 0, 0.4);
|
background: rgba(0, 0, 0, 0.3); /* ev. öka från 0.4 till 0.5 */
|
||||||
text-align: center;
|
text-align: center;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-content .day {
|
.card-content .day {
|
||||||
font-size: 1rem;
|
font-size: 1.3rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
text-shadow: 0 1px 7px rgba(0, 0, 0, 1);
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -230,9 +231,14 @@ h1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card-content .meal {
|
.card-content .meal {
|
||||||
font-size: 0.8rem;
|
font-size: 1rem;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
|
text-shadow:
|
||||||
|
0 0 4px rgba(0, 0, 0, 0.9),
|
||||||
|
0 0 8px rgba(0, 0, 0, 0.7),
|
||||||
|
0 1px 10px rgba(0, 0, 0, 1);
|
||||||
|
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
.menu-header {
|
.menu-header {
|
||||||
|
|||||||
@@ -4,79 +4,46 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
angular.module('mealMenuApp', ['ngSanitize'])
|
angular.module('mealMenuApp', ['ngSanitize'])
|
||||||
.controller('MealMenuController', function ($scope, $http, $sce) {
|
.controller('MealMenuController', function ($scope, $http, $sce, $timeout) {
|
||||||
console.log("Controller initierad");
|
console.log("Controller initierad");
|
||||||
|
|
||||||
const savedView = localStorage.getItem('mealViewMode');
|
const savedView = localStorage.getItem('mealViewMode');
|
||||||
$scope.viewMode = savedView === 'card' || savedView === 'list' ? savedView : 'card';
|
$scope.viewMode = savedView === 'card' || savedView === 'list' ? savedView : 'card';
|
||||||
$scope.tooltip = {};
|
$scope.tooltip = {};
|
||||||
$scope.meals = [];
|
|
||||||
$scope.menu = {};
|
$scope.menu = {};
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
$scope.selectedWeek = getWeek(today);
|
$scope.selectedWeek = getWeek(today);
|
||||||
$scope.selectedYear = today.getFullYear();
|
$scope.selectedYear = today.getFullYear();
|
||||||
$scope.daysOfWeek = ["Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag", "Söndag"];
|
$scope.daysOfWeek = ["Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag", "Söndag"];
|
||||||
|
|
||||||
$scope.loadMeals = function () {
|
|
||||||
console.log("Hämtar måltider...");
|
|
||||||
return $http.get('/api/mealMenuApi/getMeals')
|
|
||||||
.then(res => {
|
|
||||||
console.log("Måltider hämtade:", res.data);
|
|
||||||
$scope.meals = res.data;
|
|
||||||
console.log("Alla måltider med bilder:", $scope.meals);
|
|
||||||
return res;
|
|
||||||
})
|
|
||||||
.catch(err => console.error("Fel vid hämtning av måltider:", err));
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.loadMenu = function () {
|
$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/menu', {
|
|
||||||
|
$http.get('/api/mealMenuApi/getWeeklyMenu', {
|
||||||
params: { weekNumber: $scope.selectedWeek, year: $scope.selectedYear }
|
params: { weekNumber: $scope.selectedWeek, year: $scope.selectedYear }
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
console.log("Menyposter hämtade:", res.data);
|
const rawMenu = res.data;
|
||||||
$scope.menu = {};
|
$scope.menu = {};
|
||||||
|
|
||||||
res.data.forEach(item => {
|
rawMenu.forEach(item => {
|
||||||
const dayIndex = item.DayOfWeek - 1;
|
const dayIndex = item.DayOfWeek - 1;
|
||||||
if (dayIndex < 0 || dayIndex >= $scope.daysOfWeek.length) {
|
|
||||||
console.warn("Ogiltig dag:", item.DayOfWeek);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const day = $scope.daysOfWeek[dayIndex];
|
const day = $scope.daysOfWeek[dayIndex];
|
||||||
$scope.menu[day] = {};
|
console.warn("Item:", item);
|
||||||
|
console.log("day: " + day + "(" + dayIndex + " ) item: " + item.DinnerMealName);
|
||||||
['breakfast', 'lunch', 'dinner'].forEach(type => {
|
const thumb = item.DinnerMealThumbnail;
|
||||||
// Konvertera till PascalCase
|
|
||||||
const capitalType = type.charAt(0).toUpperCase() + type.slice(1);
|
$scope.menu[day] = {
|
||||||
const idKey = capitalType + 'MealId';
|
breakfastMealId: item.BreakfastMealId,
|
||||||
const nameKey = capitalType + 'MealName';
|
breakfastMealName: item.BreakfastMealName,
|
||||||
|
lunchMealId: item.LunchMealId,
|
||||||
if (item[idKey]) {
|
lunchMealName: item.LunchMealName,
|
||||||
const m = $scope.meals.find(x => x.Id == item[idKey]);
|
dinnerMealId: item.DinnerMealId,
|
||||||
console.log(`Match för ${type} (${day}):`, m);
|
dinnerMealName: item.DinnerMealName,
|
||||||
|
imageUrl: thumb ? `data:image/webp;base64,${thumb}` : '/img/default-thumbnail.webp'
|
||||||
$scope.menu[day][type + 'MealId'] = item[idKey];
|
};
|
||||||
$scope.menu[day][type + 'MealName'] = m?.Name || item[nameKey] || 'Okänd rätt';
|
|
||||||
|
|
||||||
if (m && !$scope.menu[day].imageUrl) {
|
|
||||||
if (m.ImageData) {
|
|
||||||
const mime = m.ImageMimeType || "image/jpeg";
|
|
||||||
$scope.menu[day].imageUrl = `data:${mime};base64,${m.ImageData}`;
|
|
||||||
} else if (m.ImageUrl) {
|
|
||||||
$scope.menu[day].imageUrl = m.ImageUrl.startsWith('/')
|
|
||||||
? m.ImageUrl
|
|
||||||
: '/' + m.ImageUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Byggd meny:", $scope.menu);
|
}).catch(err => console.error("Fel vid hämtning av meny:", err));
|
||||||
}).catch(err => console.error("Fel vid hämtning av veckomeny:", err));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.openMeal = function (mealId) {
|
$scope.openMeal = function (mealId) {
|
||||||
@@ -85,7 +52,7 @@ angular.module('mealMenuApp', ['ngSanitize'])
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.getDayImage = function (day) {
|
$scope.getDayImage = function (day) {
|
||||||
return $scope.menu[day]?.imageUrl || '/images/default-meal.jpg';
|
return $scope.menu[day]?.imageUrl || '/img/default-thumbnail.webp';
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.getMealIdByDay = function (day) {
|
$scope.getMealIdByDay = function (day) {
|
||||||
@@ -120,34 +87,24 @@ angular.module('mealMenuApp', ['ngSanitize'])
|
|||||||
const yearStart = new Date(d.getFullYear(), 0, 1);
|
const yearStart = new Date(d.getFullYear(), 0, 1);
|
||||||
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
|
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.getViewIcon = function () {
|
$scope.getViewIcon = function () {
|
||||||
return $scope.viewMode === 'list' ? '🗒️' : '▣';
|
return $scope.viewMode === 'list' ? '🗒️' : '▣';
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.toggleView = function () {
|
$scope.toggleView = function () {
|
||||||
$scope.viewMode = $scope.viewMode === 'list' ? 'card' : 'list';
|
$scope.viewMode = $scope.viewMode === 'list' ? 'card' : 'list';
|
||||||
localStorage.setItem('mealViewMode', $scope.viewMode); // ← spara läget
|
localStorage.setItem('mealViewMode', $scope.viewMode);
|
||||||
|
|
||||||
$timeout(() => {
|
$timeout(() => {
|
||||||
const viewBtn = document.getElementById('toggle-view');
|
const viewBtn = document.getElementById('toggle-view');
|
||||||
if (viewBtn) viewBtn.textContent = $scope.getViewIcon();
|
if (viewBtn) viewBtn.textContent = $scope.getViewIcon();
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log("Initierar meny och måltidsladdning...");
|
||||||
|
$scope.loadMenu();
|
||||||
console.log("Initierar måltidsladdning...");
|
|
||||||
$scope.loadMeals().then(() => {
|
|
||||||
console.log("Laddar meny efter måltider...");
|
|
||||||
$scope.loadMenu();
|
|
||||||
setTimeout(() => {
|
|
||||||
const viewBtn = document.getElementById('toggle-view');
|
|
||||||
if (viewBtn) viewBtn.textContent = $scope.getViewIcon();
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
const themeBtn = document.getElementById('toggle-theme');
|
const themeBtn = document.getElementById('toggle-theme');
|
||||||
const viewBtn = document.getElementById('toggle-view');
|
const viewBtn = document.getElementById('toggle-view');
|
||||||
@@ -168,7 +125,4 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
setTheme(systemPrefersDark ? 'dark' : 'light');
|
setTheme(systemPrefersDark ? 'dark' : 'light');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initiera ikon för vy
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user