This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -364,3 +364,4 @@ FodyWeavers.xsd
|
||||
|
||||
# Setupfil för Aberwyn
|
||||
infrastructure/setup.json
|
||||
Aberwyn/Infrastructure/setup.json
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
<!-- Övrigt -->
|
||||
<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="SixLabors.ImageSharp" Version="3.1.9" />
|
||||
<PackageReference Include="WebPush" Version="1.0.12" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Aberwyn.Controllers
|
||||
[Authorize(Roles = "Admin")]
|
||||
public class AdminController : Controller
|
||||
{
|
||||
private readonly MenuService _menuService;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly RoleManager<IdentityRole> _roleManager;
|
||||
private readonly IConfiguration _configuration;
|
||||
@@ -18,12 +19,14 @@ namespace Aberwyn.Controllers
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public AdminController(
|
||||
MenuService menuService,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
RoleManager<IdentityRole> roleManager,
|
||||
IConfiguration configuration,
|
||||
IHostEnvironment env,
|
||||
ApplicationDbContext context)
|
||||
{
|
||||
_menuService = menuService;
|
||||
_userManager = userManager;
|
||||
_roleManager = roleManager;
|
||||
_configuration = configuration;
|
||||
@@ -144,6 +147,13 @@ namespace Aberwyn.Controllers
|
||||
TempData["Message"] = $"✅ Import av veckomenyer från extern databas klar ({sourceMenus.Count}).";
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
[HttpPost]
|
||||
[Authorize(Roles = "Admin")]
|
||||
public IActionResult GenerateThumbnails()
|
||||
{
|
||||
var count = _menuService.GenerateMissingThumbnails();
|
||||
return Ok($"{count} thumbnails skapades.");
|
||||
}
|
||||
|
||||
|
||||
[HttpPost]
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using Aberwyn.Models;
|
||||
using Aberwyn.Data;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SixLabors.ImageSharp;
|
||||
|
||||
namespace Aberwyn.Controllers
|
||||
{
|
||||
@@ -70,6 +72,15 @@ namespace Aberwyn.Controllers
|
||||
return StatusCode(500, $"<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);
|
||||
meal.ImageData = ms.ToArray();
|
||||
meal.ImageMimeType = ImageFile.ContentType;
|
||||
meal.ThumbnailData = GenerateThumbnail(ImageFile);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -111,8 +124,21 @@ namespace Aberwyn.Controllers
|
||||
}
|
||||
|
||||
|
||||
private byte[] GenerateThumbnail(IFormFile file)
|
||||
{
|
||||
using var image = SixLabors.ImageSharp.Image.Load(file.OpenReadStream());
|
||||
image.Mutate(x => x.Resize(new ResizeOptions
|
||||
{
|
||||
Mode = ResizeMode.Max,
|
||||
Size = new Size(300, 300) // eller vad du vill för korten
|
||||
}));
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
image.SaveAsWebp(ms); // kräver ImageSharp.Webp-paketet
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpPost]
|
||||
|
||||
@@ -33,6 +33,16 @@ namespace Aberwyn.Controllers
|
||||
return Ok(mealDtos);
|
||||
}
|
||||
|
||||
[HttpGet("getWeeklyMenu")]
|
||||
public IActionResult GetWeeklyMenu(int weekNumber, int year)
|
||||
{
|
||||
var menuDtos = _menuService.GetWeeklyMenuDto(weekNumber, year);
|
||||
Console.WriteLine("Hämtar meals: " + menuDtos);
|
||||
return Ok(menuDtos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpPut("menu")]
|
||||
public IActionResult SaveMenu([FromBody] MenuViewModel weeklyMenu)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Nya versionen av MenuService med Entity Framework
|
||||
using Aberwyn.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Webp;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using System.Globalization;
|
||||
using static Aberwyn.Data.SetupService;
|
||||
|
||||
@@ -26,6 +29,37 @@ public class MenuService
|
||||
return new MenuService(context);
|
||||
}
|
||||
|
||||
public List<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)
|
||||
{
|
||||
var existing = _context.WeeklyMenus
|
||||
@@ -70,6 +104,38 @@ public class MenuService
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
public int GenerateMissingThumbnails()
|
||||
{
|
||||
var updatedCount = 0;
|
||||
var meals = _context.Meals
|
||||
.Where(m => m.ImageData != null && m.ThumbnailData == null)
|
||||
.ToList();
|
||||
|
||||
foreach (var meal in meals)
|
||||
{
|
||||
using var ms = new MemoryStream(meal.ImageData);
|
||||
using var image = Image.Load(ms);
|
||||
|
||||
image.Mutate(x => x.Resize(new ResizeOptions
|
||||
{
|
||||
Mode = ResizeMode.Max,
|
||||
Size = new Size(300, 300)
|
||||
}));
|
||||
|
||||
using var outStream = new MemoryStream();
|
||||
var encoder = new WebpEncoder
|
||||
{
|
||||
Quality = 75
|
||||
};
|
||||
image.Save(outStream, encoder);
|
||||
|
||||
meal.ThumbnailData = outStream.ToArray();
|
||||
updatedCount++;
|
||||
}
|
||||
|
||||
_context.SaveChanges();
|
||||
return updatedCount;
|
||||
}
|
||||
|
||||
public List<WeeklyMenu> GetAllWeeklyMenus()
|
||||
{
|
||||
@@ -166,33 +232,49 @@ public class MenuService
|
||||
.OrderBy(m => m.Name)
|
||||
.ToList();
|
||||
}
|
||||
public List<WeeklyMenu> GetWeeklyMenu(int weekNumber, int year)
|
||||
{
|
||||
var menus = _context.WeeklyMenus
|
||||
.Where(m => m.WeekNumber == weekNumber && m.Year == year)
|
||||
.ToList();
|
||||
public List<WeeklyMenu> GetWeeklyMenu(int weekNumber, int year)
|
||||
{
|
||||
var menus = _context.WeeklyMenus
|
||||
.Where(m => m.WeekNumber == weekNumber && m.Year == year)
|
||||
.ToList();
|
||||
|
||||
var mealIds = menus
|
||||
.SelectMany(w => new int?[] { w.BreakfastMealId, w.LunchMealId, w.DinnerMealId })
|
||||
.Where(id => id.HasValue)
|
||||
.Select(id => id.Value)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
var allMeals = _context.Meals
|
||||
.Where(m => mealIds.Contains(m.Id))
|
||||
.Select(m => new {
|
||||
m.Id,
|
||||
m.Name,
|
||||
m.ThumbnailData // Vi tar med detta även om det bara används för middag
|
||||
})
|
||||
.ToList()
|
||||
.ToDictionary(m => m.Id, m => m);
|
||||
|
||||
var allMeals = _context.Meals.ToDictionary(m => m.Id, m => m.Name);
|
||||
|
||||
foreach (var wm in menus)
|
||||
{
|
||||
wm.BreakfastMealName = wm.BreakfastMealId.HasValue && allMeals.TryGetValue(wm.BreakfastMealId.Value, out var breakfast)
|
||||
? breakfast
|
||||
: null;
|
||||
if (wm.BreakfastMealId.HasValue && allMeals.TryGetValue(wm.BreakfastMealId.Value, out var breakfast))
|
||||
wm.BreakfastMealName = breakfast.Name;
|
||||
|
||||
wm.LunchMealName = wm.LunchMealId.HasValue && allMeals.TryGetValue(wm.LunchMealId.Value, out var lunch)
|
||||
? lunch
|
||||
: null;
|
||||
if (wm.LunchMealId.HasValue && allMeals.TryGetValue(wm.LunchMealId.Value, out var lunch))
|
||||
wm.LunchMealName = lunch.Name;
|
||||
|
||||
wm.DinnerMealName = wm.DinnerMealId.HasValue && allMeals.TryGetValue(wm.DinnerMealId.Value, out var dinner)
|
||||
? dinner
|
||||
: null;
|
||||
if (wm.DinnerMealId.HasValue && allMeals.TryGetValue(wm.DinnerMealId.Value, out var dinner))
|
||||
{
|
||||
wm.DinnerMealName = dinner.Name;
|
||||
}
|
||||
}
|
||||
|
||||
return menus;
|
||||
}
|
||||
|
||||
|
||||
return menus;
|
||||
}
|
||||
|
||||
public List<WeeklyMenu> GetMenuEntriesByDateRange(DateTime startDate, DateTime endDate)
|
||||
{
|
||||
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")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<byte[]>("ThumbnailData")
|
||||
.HasColumnType("longblob");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Meals");
|
||||
|
||||
@@ -13,6 +13,25 @@ namespace Aberwyn.Models
|
||||
public int WeekNumber { get; set; } // Week number for the menu
|
||||
public int Year { get; set; } // Year for the menu
|
||||
}
|
||||
public class WeeklyMenuDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int DayOfWeek { get; set; }
|
||||
public int WeekNumber { get; set; }
|
||||
public int Year { get; set; }
|
||||
|
||||
public int? BreakfastMealId { get; set; }
|
||||
public int? LunchMealId { get; set; }
|
||||
public int? DinnerMealId { get; set; }
|
||||
|
||||
public string? BreakfastMealName { get; set; }
|
||||
public string? LunchMealName { get; set; }
|
||||
public string? DinnerMealName { get; set; }
|
||||
|
||||
public byte[]? DinnerMealThumbnail { get; set; }
|
||||
}
|
||||
|
||||
public class WeeklyMenu
|
||||
{
|
||||
public int Id { get; set; }
|
||||
@@ -55,12 +74,14 @@ public class Meal
|
||||
public string? CarbType { get; set; }
|
||||
public string? RecipeUrl { get; set; }
|
||||
public string? ImageUrl { get; set; }
|
||||
|
||||
public bool IsAvailable { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public byte[]? ThumbnailData { get; set; }
|
||||
|
||||
public byte[]? ImageData { get; set; } // 👈 Viktigt!
|
||||
public string? ImageMimeType { get; set; } // 👈 Viktigt!
|
||||
public string? Instructions { get; set; } // 👈 Viktigt!
|
||||
public byte[]? ImageData { get; set; }
|
||||
public string? ImageMimeType { get; set; }
|
||||
public string? Instructions { get; set; }
|
||||
|
||||
public List<Ingredient> Ingredients { get; set; } = new();
|
||||
}
|
||||
|
||||
@@ -70,6 +70,8 @@
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<button ng-click="generateThumbnails()">Generera thumbnails</button>
|
||||
|
||||
|
||||
<h3>Testa Pushnotis</h3>
|
||||
<form onsubmit="sendPush(event)">
|
||||
|
||||
@@ -56,10 +56,10 @@
|
||||
ng-click="openMeal(getMealIdByDay(day))">
|
||||
<div class="card-content">
|
||||
<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]">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>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
{
|
||||
<ul class="nav-list">
|
||||
<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>
|
||||
}
|
||||
@@ -42,7 +42,7 @@
|
||||
{
|
||||
<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="MealAdmin"><i class="fas fa-list"></i> Måltider</a></li>
|
||||
|
||||
@@ -215,14 +215,15 @@ h1 {
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
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;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.card-content .day {
|
||||
font-size: 1rem;
|
||||
font-size: 1.3rem;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 1px 7px rgba(0, 0, 0, 1);
|
||||
margin-bottom: 6px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@@ -230,9 +231,14 @@ h1 {
|
||||
}
|
||||
|
||||
.card-content .meal {
|
||||
font-size: 0.8rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.3;
|
||||
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;
|
||||
}
|
||||
.menu-header {
|
||||
|
||||
@@ -4,79 +4,46 @@
|
||||
}
|
||||
|
||||
angular.module('mealMenuApp', ['ngSanitize'])
|
||||
.controller('MealMenuController', function ($scope, $http, $sce) {
|
||||
.controller('MealMenuController', function ($scope, $http, $sce, $timeout) {
|
||||
console.log("Controller initierad");
|
||||
|
||||
const savedView = localStorage.getItem('mealViewMode');
|
||||
$scope.viewMode = savedView === 'card' || savedView === 'list' ? savedView : 'card';
|
||||
$scope.tooltip = {};
|
||||
$scope.meals = [];
|
||||
$scope.menu = {};
|
||||
const today = new Date();
|
||||
$scope.selectedWeek = getWeek(today);
|
||||
$scope.selectedYear = today.getFullYear();
|
||||
$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 () {
|
||||
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 }
|
||||
}).then(res => {
|
||||
console.log("Menyposter hämtade:", res.data);
|
||||
const rawMenu = res.data;
|
||||
$scope.menu = {};
|
||||
|
||||
res.data.forEach(item => {
|
||||
|
||||
rawMenu.forEach(item => {
|
||||
const dayIndex = item.DayOfWeek - 1;
|
||||
if (dayIndex < 0 || dayIndex >= $scope.daysOfWeek.length) {
|
||||
console.warn("Ogiltig dag:", item.DayOfWeek);
|
||||
return;
|
||||
}
|
||||
|
||||
const day = $scope.daysOfWeek[dayIndex];
|
||||
$scope.menu[day] = {};
|
||||
|
||||
['breakfast', 'lunch', 'dinner'].forEach(type => {
|
||||
// Konvertera till PascalCase
|
||||
const capitalType = type.charAt(0).toUpperCase() + type.slice(1);
|
||||
const idKey = capitalType + 'MealId';
|
||||
const nameKey = capitalType + 'MealName';
|
||||
|
||||
if (item[idKey]) {
|
||||
const m = $scope.meals.find(x => x.Id == item[idKey]);
|
||||
console.log(`Match för ${type} (${day}):`, m);
|
||||
|
||||
$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.warn("Item:", item);
|
||||
console.log("day: " + day + "(" + dayIndex + " ) item: " + item.DinnerMealName);
|
||||
const thumb = item.DinnerMealThumbnail;
|
||||
|
||||
$scope.menu[day] = {
|
||||
breakfastMealId: item.BreakfastMealId,
|
||||
breakfastMealName: item.BreakfastMealName,
|
||||
lunchMealId: item.LunchMealId,
|
||||
lunchMealName: item.LunchMealName,
|
||||
dinnerMealId: item.DinnerMealId,
|
||||
dinnerMealName: item.DinnerMealName,
|
||||
imageUrl: thumb ? `data:image/webp;base64,${thumb}` : '/img/default-thumbnail.webp'
|
||||
};
|
||||
});
|
||||
|
||||
console.log("Byggd meny:", $scope.menu);
|
||||
}).catch(err => console.error("Fel vid hämtning av veckomeny:", err));
|
||||
|
||||
}).catch(err => console.error("Fel vid hämtning av meny:", err));
|
||||
};
|
||||
|
||||
$scope.openMeal = function (mealId) {
|
||||
@@ -85,7 +52,7 @@ angular.module('mealMenuApp', ['ngSanitize'])
|
||||
};
|
||||
|
||||
$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) {
|
||||
@@ -120,34 +87,24 @@ angular.module('mealMenuApp', ['ngSanitize'])
|
||||
const yearStart = new Date(d.getFullYear(), 0, 1);
|
||||
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
|
||||
}
|
||||
|
||||
$scope.getViewIcon = function () {
|
||||
return $scope.viewMode === 'list' ? '🗒️' : '▣';
|
||||
};
|
||||
|
||||
$scope.toggleView = function () {
|
||||
$scope.viewMode = $scope.viewMode === 'list' ? 'card' : 'list';
|
||||
localStorage.setItem('mealViewMode', $scope.viewMode); // ← spara läget
|
||||
|
||||
localStorage.setItem('mealViewMode', $scope.viewMode);
|
||||
$timeout(() => {
|
||||
const viewBtn = document.getElementById('toggle-view');
|
||||
if (viewBtn) viewBtn.textContent = $scope.getViewIcon();
|
||||
}, 0);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
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);
|
||||
|
||||
});
|
||||
console.log("Initierar meny och måltidsladdning...");
|
||||
$scope.loadMenu();
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const themeBtn = document.getElementById('toggle-theme');
|
||||
const viewBtn = document.getElementById('toggle-view');
|
||||
@@ -168,7 +125,4 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
setTheme(systemPrefersDark ? 'dark' : 'light');
|
||||
}
|
||||
|
||||
// Initiera ikon för vy
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user