This commit is contained in:
23
Aberwyn/Controllers/ErrorController.cs
Normal file
23
Aberwyn/Controllers/ErrorController.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Aberwyn.Controllers
|
||||
{
|
||||
public class ErrorController : Controller
|
||||
{
|
||||
[Route("Error/{statusCode}")]
|
||||
public IActionResult HttpStatusCodeHandler(int statusCode)
|
||||
{
|
||||
ViewData["ErrorCode"] = statusCode;
|
||||
return View("Error");
|
||||
}
|
||||
|
||||
[Route("Error")]
|
||||
public IActionResult Error()
|
||||
{
|
||||
var exceptionFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
|
||||
ViewData["Exception"] = exceptionFeature?.Error;
|
||||
return View("Error");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,12 +32,12 @@ namespace Aberwyn.Controllers
|
||||
[HttpGet]
|
||||
public IActionResult PizzaOrder()
|
||||
{
|
||||
var pizzas = _menuService.GetMealsByCategory("Pizza")
|
||||
.Where(p => p.IsAvailable)
|
||||
.ToList();
|
||||
var meals = _menuService.GetMealsByCategoryName("Pizza", onlyAvailable: true);
|
||||
Console.WriteLine("Pizzas: ", meals);
|
||||
var dtoList = meals.Select(m => MealListDto.FromMeal(m)).ToList();
|
||||
ViewBag.Pizzas = dtoList;
|
||||
|
||||
ViewBag.Pizzas = pizzas;
|
||||
ViewBag.RestaurantIsOpen = GetRestaurantStatus();
|
||||
ViewBag.RestaurantIsOpen = GetRestaurantStatus();
|
||||
|
||||
int? lastId = HttpContext.Session.GetInt32("LastPizzaOrderId");
|
||||
if (lastId.HasValue)
|
||||
@@ -53,6 +53,7 @@ namespace Aberwyn.Controllers
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult EditPizzaOrder(int id)
|
||||
{
|
||||
@@ -152,10 +153,14 @@ namespace Aberwyn.Controllers
|
||||
|
||||
var allMeals = _menuService.GetMeals();
|
||||
|
||||
var categories = _menuService.GetMealCategories();
|
||||
var pizzaCategory = categories.FirstOrDefault(c => c.Name.Equals("Pizza", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
viewModel.AvailablePizzas = allMeals
|
||||
.Where(m => m.Category == "Pizza")
|
||||
.OrderBy(m => m.Name)
|
||||
.ToList();
|
||||
.Where(m => m.MealCategoryId == pizzaCategory?.Id)
|
||||
.OrderBy(m => m.Name)
|
||||
.ToList();
|
||||
|
||||
|
||||
ViewBag.RestaurantIsOpen = GetRestaurantStatus();
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Aberwyn.Controllers
|
||||
ViewBag.RestaurantIsOpen = isOpen;
|
||||
|
||||
var now = DateTime.Now;
|
||||
var showDate = now.Hour >= 18 ? now.Date.AddDays(1) : now.Date;
|
||||
var showDate = now.Hour >= 20 ? now.Date.AddDays(1) : now.Date;
|
||||
|
||||
var todaysMenu = _menuService.GetMenuForDate(showDate);
|
||||
|
||||
|
||||
@@ -45,7 +45,10 @@ namespace Aberwyn.Controllers
|
||||
CreatedAt = DateTime.Now
|
||||
};
|
||||
}
|
||||
|
||||
ViewBag.Categories = _menuService.GetMealCategories()
|
||||
.Where(c => c.IsActive)
|
||||
.OrderBy(c => c.Name)
|
||||
.ToList();
|
||||
ViewData["IsEditing"] = edit;
|
||||
return View("View", meal);
|
||||
}
|
||||
@@ -164,5 +167,35 @@ namespace Aberwyn.Controllers
|
||||
//service.DeleteMeal(id);
|
||||
return RedirectToAction("Edit"); // eller tillbaka till lista
|
||||
}
|
||||
|
||||
[Authorize(Roles = "Admin,Chef")]
|
||||
[HttpGet("/meal/categories")]
|
||||
public IActionResult Categories()
|
||||
{
|
||||
var categories = _menuService.GetMealCategories()
|
||||
.Select(cat => {
|
||||
cat.MealCount = _menuService.GetMealCountForCategory(cat.Id);
|
||||
return cat;
|
||||
}).ToList();
|
||||
|
||||
return View("MealCategories", categories);
|
||||
}
|
||||
|
||||
[Authorize(Roles = "Admin,Chef")]
|
||||
[HttpPost("/meal/categories/save")]
|
||||
public IActionResult SaveCategory(MealCategory category)
|
||||
{
|
||||
_menuService.SaveOrUpdateCategory(category);
|
||||
return RedirectToAction("Categories");
|
||||
}
|
||||
|
||||
[Authorize(Roles = "Admin,Chef")]
|
||||
[HttpPost("/meal/categories/delete")]
|
||||
public IActionResult DeleteCategory(int id)
|
||||
{
|
||||
_menuService.DeleteCategory(id);
|
||||
return RedirectToAction("Categories");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,20 @@ namespace Aberwyn.Data
|
||||
base.OnModelCreating(builder);
|
||||
|
||||
builder.Entity<WeeklyMenu>().ToTable("WeeklyMenu");
|
||||
builder.Entity<MealCategory>().HasData(
|
||||
new MealCategory
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Pizza",
|
||||
Slug = "pizza",
|
||||
Icon = "🍕",
|
||||
Color = "#f97316",
|
||||
IsActive = true,
|
||||
DisplayOrder = 1
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public DbSet<BudgetPeriod> BudgetPeriods { get; set; }
|
||||
@@ -33,6 +46,7 @@ namespace Aberwyn.Data
|
||||
public DbSet<Ingredient> Ingredients { get; set; }
|
||||
public DbSet<UserPreferences> UserPreferences { get; set; }
|
||||
public DbSet<StoredPushSubscription> PushSubscriptions { get; set; }
|
||||
public DbSet<MealCategory> MealCategories { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ public List<Meal> GetMealsSlim(bool includeThumbnail = false)
|
||||
public List<Meal> GetMealsByCategory(string category)
|
||||
{
|
||||
return _context.Meals
|
||||
.Where(m => m.Category == category)
|
||||
//.Where(m => m.Category == category)
|
||||
.Include(m => m.Ingredients)
|
||||
.OrderBy(m => m.Name)
|
||||
.ToList();
|
||||
@@ -383,6 +383,27 @@ public List<WeeklyMenu> GetWeeklyMenu(int weekNumber, int year)
|
||||
|
||||
return menu;
|
||||
}
|
||||
public List<Meal> GetMealsByCategoryName(string categoryName, string? searchTerm = null, bool onlyAvailable = false)
|
||||
{
|
||||
var query = _context.Meals
|
||||
.Include(m => m.Category)
|
||||
.Include(m => m.Ingredients)
|
||||
.Where(m => m.Category != null && m.Category.Name == categoryName);
|
||||
|
||||
if (onlyAvailable)
|
||||
query = query.Where(m => m.IsAvailable);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchTerm))
|
||||
{
|
||||
string lowered = searchTerm.Trim().ToLower();
|
||||
query = query.Where(m => m.Name.ToLower().Contains(lowered));
|
||||
}
|
||||
|
||||
return query
|
||||
.OrderBy(m => m.Name)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
public List<WeeklyMenu> GetMenuEntriesByDateRange(DateTime startDate, DateTime endDate)
|
||||
{
|
||||
@@ -414,5 +435,47 @@ public List<WeeklyMenu> GetWeeklyMenu(int weekNumber, int year)
|
||||
}
|
||||
return results;
|
||||
}
|
||||
public List<MealCategory> GetMealCategories()
|
||||
{
|
||||
return _context.MealCategories.OrderBy(c => c.DisplayOrder).ToList();
|
||||
}
|
||||
public int GetMealCountForCategory(int categoryId)
|
||||
{
|
||||
return _context.Meals.Count(m => m.MealCategoryId == categoryId);
|
||||
}
|
||||
|
||||
public void SaveOrUpdateCategory(MealCategory cat)
|
||||
{
|
||||
if (cat.Id == 0)
|
||||
{
|
||||
_context.MealCategories.Add(cat);
|
||||
}
|
||||
else
|
||||
{
|
||||
var existing = _context.MealCategories.Find(cat.Id);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.Name = cat.Name;
|
||||
existing.Slug = cat.Slug;
|
||||
existing.Description = cat.Description;
|
||||
existing.Icon = cat.Icon;
|
||||
existing.Color = cat.Color;
|
||||
existing.IsActive = cat.IsActive;
|
||||
existing.DisplayOrder = cat.DisplayOrder;
|
||||
}
|
||||
}
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
public void DeleteCategory(int id)
|
||||
{
|
||||
var cat = _context.MealCategories.Find(id);
|
||||
if (cat != null)
|
||||
{
|
||||
_context.MealCategories.Remove(cat);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
821
Aberwyn/Migrations/20250614211356_AddMealCategorySupport.Designer.cs
generated
Normal file
821
Aberwyn/Migrations/20250614211356_AddMealCategorySupport.Designer.cs
generated
Normal file
@@ -0,0 +1,821 @@
|
||||
// <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("20250614211356_AddMealCategorySupport")]
|
||||
partial class AddMealCategorySupport
|
||||
{
|
||||
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<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<int?>("MealCategoryId")
|
||||
.HasColumnType("int");
|
||||
|
||||
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.HasIndex("MealCategoryId");
|
||||
|
||||
b.ToTable("Meals");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Aberwyn.Models.MealCategory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Color")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("DisplayOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Icon")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("MealCategories");
|
||||
});
|
||||
|
||||
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.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("PushSubscribers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Aberwyn.Models.StoredPushSubscription", 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.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("PushSubscriptions");
|
||||
});
|
||||
|
||||
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.UserPreferences", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
b.Property<bool>("NotifyBudget")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("NotifyMenu")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("NotifyPizza")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.HasKey("UserId");
|
||||
|
||||
b.ToTable("UserPreferences");
|
||||
});
|
||||
|
||||
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("Aberwyn.Models.Meal", b =>
|
||||
{
|
||||
b.HasOne("Aberwyn.Models.MealCategory", "Category")
|
||||
.WithMany("Meals")
|
||||
.HasForeignKey("MealCategoryId");
|
||||
|
||||
b.Navigation("Category");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Aberwyn.Models.PushSubscriber", b =>
|
||||
{
|
||||
b.HasOne("Aberwyn.Models.ApplicationUser", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
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<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.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");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Aberwyn/Migrations/20250614211356_AddMealCategorySupport.cs
Normal file
19
Aberwyn/Migrations/20250614211356_AddMealCategorySupport.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Aberwyn.Migrations
|
||||
{
|
||||
public partial class AddMealCategorySupport : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,9 +267,6 @@ namespace Aberwyn.Migrations
|
||||
b.Property<string>("CarbType")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Category")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
@@ -291,6 +288,9 @@ namespace Aberwyn.Migrations
|
||||
b.Property<bool>("IsAvailable")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int?>("MealCategoryId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
@@ -306,9 +306,44 @@ namespace Aberwyn.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MealCategoryId");
|
||||
|
||||
b.ToTable("Meals");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Aberwyn.Models.MealCategory", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Color")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("DisplayOrder")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Icon")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("IsActive")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("MealCategories");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Aberwyn.Models.PizzaOrder", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -660,6 +695,15 @@ namespace Aberwyn.Migrations
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Aberwyn.Models.Meal", b =>
|
||||
{
|
||||
b.HasOne("Aberwyn.Models.MealCategory", "Category")
|
||||
.WithMany("Meals")
|
||||
.HasForeignKey("MealCategoryId");
|
||||
|
||||
b.Navigation("Category");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Aberwyn.Models.PushSubscriber", b =>
|
||||
{
|
||||
b.HasOne("Aberwyn.Models.ApplicationUser", "User")
|
||||
@@ -764,6 +808,11 @@ namespace Aberwyn.Migrations
|
||||
{
|
||||
b.Navigation("Ingredients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Aberwyn.Models.MealCategory", b =>
|
||||
{
|
||||
b.Navigation("Meals");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ 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 class WeeklyMenuDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int DayOfWeek { get; set; }
|
||||
@@ -73,7 +73,10 @@ public class WeeklyMenu
|
||||
|
||||
public string? Description { get; set; }
|
||||
public string? ProteinType { get; set; }
|
||||
public string? Category { get; set; }
|
||||
public int? MealCategoryId { get; set; }
|
||||
|
||||
[ForeignKey("MealCategoryId")]
|
||||
public MealCategory? Category { get; set; }
|
||||
public string? CarbType { get; set; }
|
||||
public string? RecipeUrl { get; set; }
|
||||
public string? ImageUrl { get; set; }
|
||||
@@ -122,24 +125,57 @@ public class WeeklyMenu
|
||||
|
||||
}
|
||||
public class MealListDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public string? Description { get; set; }
|
||||
public string? ThumbnailData { get; set; }
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public string? Description { get; set; }
|
||||
public string? ThumbnailData { get; set; }
|
||||
|
||||
public static MealListDto FromMeal(Meal meal, bool includeThumbnail = false)
|
||||
public List<IngredientDto> Ingredients { get; set; } = new();
|
||||
|
||||
public static MealListDto FromMeal(Meal meal, bool includeThumbnail = false)
|
||||
{
|
||||
return new MealListDto
|
||||
{
|
||||
return new MealListDto
|
||||
Id = meal.Id,
|
||||
Name = meal.Name,
|
||||
Description = meal.Description,
|
||||
ThumbnailData = includeThumbnail && meal.ThumbnailData != null
|
||||
? Convert.ToBase64String(meal.ThumbnailData)
|
||||
: null,
|
||||
Ingredients = meal.Ingredients?.Select(i => new IngredientDto
|
||||
{
|
||||
Id = meal.Id,
|
||||
Name = meal.Name,
|
||||
Description = meal.Description,
|
||||
ThumbnailData = includeThumbnail && meal.ThumbnailData != null
|
||||
? Convert.ToBase64String(meal.ThumbnailData)
|
||||
: null
|
||||
};
|
||||
}
|
||||
Item = i.Item
|
||||
}).ToList() ?? new List<IngredientDto>()
|
||||
};
|
||||
}
|
||||
|
||||
public class IngredientDto
|
||||
{
|
||||
public string Item { get; set; } = "";
|
||||
}
|
||||
}
|
||||
|
||||
public class MealCategory
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
public string? Slug { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public string? Icon { get; set; }
|
||||
public string? Color { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public int DisplayOrder { get; set; } = 0;
|
||||
[NotMapped] // krävs om du inte har kolumnen i databasen
|
||||
public int MealCount { get; set; }
|
||||
|
||||
public List<Meal> Meals { get; set; } = new();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -191,14 +191,6 @@ app.Use(async (context, next) =>
|
||||
|
||||
|
||||
|
||||
// Configure the HTTP request pipeline
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
|
||||
app.UseRouting();
|
||||
app.UseSession();
|
||||
app.UseAuthentication();
|
||||
@@ -211,6 +203,11 @@ app.MapControllerRoute(
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}");
|
||||
app.MapRazorPages();
|
||||
|
||||
app.UseExceptionHandler("/Error");
|
||||
app.UseStatusCodePagesWithReExecute("/Error/{0}");
|
||||
app.UseHsts();
|
||||
|
||||
|
||||
// Init: migrera databas och skapa admin
|
||||
if (setup.IsConfigured)
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:7290;http://localhost:5290;http://0.0.0.0:5000"
|
||||
"applicationUrl": "https://localhost:7290;http://localhost:5290"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
|
||||
75
Aberwyn/Views/Error/Error.cshtml
Normal file
75
Aberwyn/Views/Error/Error.cshtml
Normal file
@@ -0,0 +1,75 @@
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
var errorCode = ViewData["ErrorCode"] as int?;
|
||||
var exception = ViewData["Exception"] as Exception;
|
||||
var isDebug = System.Diagnostics.Debugger.IsAttached;
|
||||
}
|
||||
|
||||
<style>
|
||||
.error-page {
|
||||
max-width: 600px;
|
||||
margin: 4rem auto;
|
||||
padding: 2rem;
|
||||
background-color: #1F2C3C;
|
||||
color: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 0 10px rgba(0,0,0,0.4);
|
||||
font-family: 'Segoe UI', sans-serif;
|
||||
}
|
||||
.error-page h1 {
|
||||
font-size: 2.2rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #f87171;
|
||||
}
|
||||
.error-page .code {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #facc15;
|
||||
}
|
||||
.error-page pre {
|
||||
background-color: #2E3C4F;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
margin-top: 1rem;
|
||||
color: #fefefe;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.error-page details summary {
|
||||
cursor: pointer;
|
||||
color: #6a0dad;
|
||||
margin-top: 1rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
.error-page p {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="error-page">
|
||||
<h1>🚨 Något gick fel</h1>
|
||||
<a href="/" class="btn-outline">Till startsidan</a>
|
||||
|
||||
@if (errorCode.HasValue)
|
||||
{
|
||||
<div class="code">Statuskod: <strong>@errorCode</strong></div>
|
||||
}
|
||||
|
||||
@if (exception != null)
|
||||
{
|
||||
<h3>Felmeddelande:</h3>
|
||||
<pre>@exception.Message</pre>
|
||||
|
||||
@if (isDebug)
|
||||
{
|
||||
<details>
|
||||
<summary>Visa stacktrace</summary>
|
||||
<pre>@exception.StackTrace</pre>
|
||||
</details>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>Vi kunde tyvärr inte visa sidan. Det kan bero på ett tillfälligt fel.</p>
|
||||
}
|
||||
</div>
|
||||
@@ -2,7 +2,7 @@
|
||||
@using Newtonsoft.Json.Serialization
|
||||
@{
|
||||
ViewData["Title"] = "Beställ pizza";
|
||||
var pizzas = ViewBag.Pizzas as List<Meal>;
|
||||
var pizzas = ViewBag.Pizzas as List<MealListDto> ?? new List<MealListDto>();
|
||||
var previousOrder = ViewBag.PreviousOrder as PizzaOrder;
|
||||
var lastId = Context.Session.GetInt32("LastPizzaOrderId");
|
||||
var isCurrentOrder = previousOrder?.Id == lastId;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<span class="initial W">W</span>
|
||||
<span class="initial E2">E</span>
|
||||
<span class="initial L2">L</span></h1>
|
||||
<p class="welcome-subtitle">Idag är det <strong>@day</strong> – dagens meny är:</p>
|
||||
<p class="welcome-subtitle">Meny för <strong>@day</strong> är:</p>
|
||||
|
||||
@if (Model != null)
|
||||
{
|
||||
|
||||
74
Aberwyn/Views/Meal/MealCategories.cshtml
Normal file
74
Aberwyn/Views/Meal/MealCategories.cshtml
Normal file
@@ -0,0 +1,74 @@
|
||||
@model List<Aberwyn.Models.MealCategory>
|
||||
@{
|
||||
ViewData["Title"] = "Måltidskategorier";
|
||||
}
|
||||
|
||||
<h1 class="page-title">Måltidskategorier</h1>
|
||||
|
||||
<div class="category-list">
|
||||
@foreach (var cat in Model)
|
||||
{
|
||||
<form method="post" action="/meal/categories/save" class="category-row">
|
||||
<input type="hidden" name="Id" value="@cat.Id" />
|
||||
|
||||
<div class="name-with-preview">
|
||||
<div class="icon-preview" style="background-color: @cat.Color">
|
||||
@if (!string.IsNullOrWhiteSpace(cat.Icon))
|
||||
{
|
||||
<span>@cat.Icon</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>🍽️</span>
|
||||
}
|
||||
</div>
|
||||
<input name="Name" value="@cat.Name" placeholder="Namn" />
|
||||
</div>
|
||||
|
||||
<input name="Icon" value="@cat.Icon" placeholder="Ikon (emoji eller klass)" />
|
||||
<input name="Slug" value="@cat.Slug" placeholder="Slug" />
|
||||
<input name="Color" value="@cat.Color" type="color" title="Färg" />
|
||||
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="IsActive" @(cat.IsActive ? "checked" : "") />
|
||||
Aktiv
|
||||
</label>
|
||||
|
||||
<span class="meal-count">@cat.MealCount st</span>
|
||||
|
||||
<div class="row-actions">
|
||||
<button type="submit" title="Spara"><i class="fas fa-save"></i></button>
|
||||
<form method="post" action="/meal/categories/delete" style="display:inline;">
|
||||
<input type="hidden" name="id" value="@cat.Id" />
|
||||
<button type="submit" class="delete" title="Ta bort"><i class="fas fa-trash"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
||||
<!-- Ny kategori -->
|
||||
<form method="post" action="/meal/categories/save" class="category-row new">
|
||||
<div class="name-with-preview">
|
||||
<div class="icon-preview" style="background-color: #6a0dad">➕</div>
|
||||
<input name="Name" placeholder="Ny kategori" autofocus />
|
||||
</div>
|
||||
|
||||
<input name="Icon" placeholder="Ikon" />
|
||||
<input name="Slug" placeholder="Slug" />
|
||||
<input name="Color" type="color" value="#6a0dad" />
|
||||
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" name="IsActive" checked />
|
||||
Aktiv
|
||||
</label>
|
||||
|
||||
<span></span>
|
||||
|
||||
<div class="row-actions">
|
||||
<button type="submit" title="Lägg till"><i class="fas fa-plus"></i></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" />
|
||||
<link rel="stylesheet" href="/css/meal-categories.css">
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
imageSrc = "/images/placeholder-meal.jpg";
|
||||
imageSrc = "/images/fallback.jpg";
|
||||
}
|
||||
|
||||
bool isChef = HttpContextAccessor.HttpContext.User.IsInRole("Chef");
|
||||
@@ -64,11 +64,16 @@
|
||||
<label for="CarbType">Kolhydrat</label>
|
||||
<input type="text" name="CarbType" value="@Model.CarbType" class="form-control" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="Category">Kategori</label>
|
||||
<input type="text" name="Category" value="@Model.Category" class="form-control" />
|
||||
<label for="MealCategoryId">Kategori</label>
|
||||
<select asp-for="MealCategoryId" asp-items="@(new SelectList((List<MealCategory>)ViewBag.Categories, "Id", "Name", Model.MealCategoryId))" class="form-control">
|
||||
<option value="">-- Välj kategori --</option>
|
||||
</select>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="RecipeUrl">Receptlänk</label>
|
||||
<input type="url" name="RecipeUrl" value="@Model.RecipeUrl" class="form-control" />
|
||||
@@ -171,6 +176,30 @@
|
||||
<script src="https://cdn.jsdelivr.net/npm/trumbowyg@2.25.1/dist/trumbowyg.min.js"></script>
|
||||
|
||||
<script>
|
||||
function addIngredientRow() {
|
||||
const list = document.getElementById('ingredients-list');
|
||||
|
||||
const index = list.children.length;
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.className = 'ingredient-row';
|
||||
|
||||
const qtyInput = document.createElement('input');
|
||||
qtyInput.type = 'text';
|
||||
qtyInput.name = `Ingredients[${index}].Quantity`;
|
||||
qtyInput.placeholder = 'Mängd';
|
||||
qtyInput.className = 'form-control ingredient-qty';
|
||||
|
||||
const itemInput = document.createElement('input');
|
||||
itemInput.type = 'text';
|
||||
itemInput.name = `Ingredients[${index}].Item`;
|
||||
itemInput.placeholder = 'Ingrediens';
|
||||
itemInput.className = 'form-control ingredient-item';
|
||||
|
||||
div.appendChild(qtyInput);
|
||||
div.appendChild(itemInput);
|
||||
list.appendChild(div);
|
||||
}
|
||||
function toggleRecipe() {
|
||||
const section = document.getElementById('recipe-section');
|
||||
if (!section) return;
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
<ul class="dropdown-menu">
|
||||
<li><a asp-controller="FoodMenu" asp-action="Veckomeny">Planera Veckomeny</a></li>
|
||||
<li><a asp-controller="FoodMenu" asp-action="PizzaAdmin">Pizza Admin</a></li>
|
||||
<li><a asp-controller="Meal" asp-action="Categories">Hantera Kategorier</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
|
||||
110
Aberwyn/wwwroot/css/meal-categories.css
Normal file
110
Aberwyn/wwwroot/css/meal-categories.css
Normal file
@@ -0,0 +1,110 @@
|
||||
.page-title {
|
||||
font-size: 2rem;
|
||||
color: white;
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.category-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
max-width: 1200px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.category-row {
|
||||
display: grid;
|
||||
grid-template-columns: 2.5fr 1.2fr 1.2fr 1fr auto auto auto;
|
||||
gap: 0.5rem;
|
||||
background-color: #1F2C3C;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 8px;
|
||||
align-items: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.category-row.new {
|
||||
background-color: #2E3C4F;
|
||||
border: 2px dashed #6a0dad;
|
||||
}
|
||||
|
||||
.category-row input[type="text"],
|
||||
.category-row input[type="color"],
|
||||
.category-row input:not([type="checkbox"]) {
|
||||
padding: 0.4rem;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background-color: #334155;
|
||||
color: #fefefe;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
font-size: 0.9rem;
|
||||
color: #ddd;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.row-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.row-actions button {
|
||||
background-color: #6a0dad;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 0.4rem 0.6rem;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.row-actions button.delete {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
|
||||
.row-actions i {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.name-with-preview {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.icon-preview {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background-color: #6a0dad;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.1rem;
|
||||
color: white;
|
||||
flex-shrink: 0;
|
||||
border: 2px solid #ffffff22;
|
||||
}
|
||||
|
||||
.meal-count {
|
||||
font-size: 0.85rem;
|
||||
color: #94a3b8;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.category-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.meal-count {
|
||||
text-align: left;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user