Menu - Pre 9 .net

This commit is contained in:
Elias Jansson
2025-05-06 13:19:13 +02:00
parent 3cdf630af2
commit a140a59f66
19 changed files with 849 additions and 378 deletions

View File

@@ -31,7 +31,7 @@
<ItemGroup>
<Folder Include="Views\NewFolder\" />
<Folder Include="wwwroot\uploads\" />
<Folder Include="wwwroot\images\meals\" />
</ItemGroup>
</Project>

View File

@@ -1,7 +1,14 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Aberwyn.Models;
using Aberwyn.Data;
using System;
using System.Globalization;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Aberwyn.Controllers
{
@@ -16,35 +23,89 @@ namespace Aberwyn.Controllers
_env = env;
}
public IActionResult Veckomeny(int week = -1)
public IActionResult Veckomeny(int? week, int? year)
{
var menuService = new MenuService(_configuration, _env);
int currentWeek = week == -1 ? ISOWeek.GetWeekOfYear(DateTime.Now) : week;
int currentYear = DateTime.Now.Year;
// Hämta meny från DB
var menus = menuService.GetWeeklyMenu(currentWeek, currentYear);
var today = DateTime.Today;
int resolvedWeek = week ?? ISOWeek.GetWeekOfYear(today);
int resolvedYear = year ?? today.Year;
var model = new WeeklyMenuViewModel
var menus = menuService.GetWeeklyMenu(resolvedWeek, resolvedYear);
var vm = new WeeklyMenuViewModel
{
WeekNumber = currentWeek,
Year = currentYear,
WeekNumber = resolvedWeek,
Year = resolvedYear,
WeeklyMenus = menus,
AvailableCooks = menuService.GetUsers()
};
return View(model);
var recent = menuService
.GetMenuEntriesByDateRange(DateTime.Now.AddDays(-28), DateTime.Now)
.Select(x => new WeeklyMenuViewModel.RecentMenuEntry
{
Date = x.Date,
BreakfastMealName = x.BreakfastMealName,
LunchMealName = x.LunchMealName,
DinnerMealName = x.DinnerMealName
})
.ToList();
vm.RecentEntries = recent;
ViewBag.AvailableMeals = menuService.GetMeals();
return View(vm);
}
[HttpPost]
public IActionResult AddMealAjax([FromBody] Meal meal)
{
if (meal == null || string.IsNullOrWhiteSpace(meal.Name))
return BadRequest("Ogiltigt måltidsobjekt.");
var service = new MenuService(_configuration, _env);
// Kontrollera om en måltid med samma namn redan finns
var existing = service.GetMeals()
.FirstOrDefault(m => m.Name.Equals(meal.Name, StringComparison.OrdinalIgnoreCase));
if (existing == null)
{
// Fyll i CreatedAt om det inte sätts automatiskt i databasen
meal.CreatedAt = DateTime.Now;
service.SaveOrUpdateMeal(meal);
existing = meal;
}
else
{
// Om måltiden finns men saknar data (t.ex. är bara ett namn) kan vi uppdatera den
existing.Description = meal.Description;
existing.ProteinType = meal.ProteinType;
existing.CarbType = meal.CarbType;
existing.RecipeUrl = meal.RecipeUrl;
existing.ImageUrl = meal.ImageUrl;
service.SaveOrUpdateMeal(existing);
}
return Json(new
{
Id = existing.Id,
Name = existing.Name,
Description = existing.Description,
ProteinType = existing.ProteinType,
CarbType = existing.CarbType,
RecipeUrl = existing.RecipeUrl
});
}
public IActionResult MealAdmin()
{
var service = new MenuService(_configuration, _env);
var meals = service.GetMealsDetailed(); // Se till att denna metod finns och returnerar fullständiga Meal-objekt
var meals = service.GetMealsDetailed();
return View("MealAdmin", meals);
}
[HttpGet]
public JsonResult SearchMeals(string term)
{
@@ -64,17 +125,16 @@ namespace Aberwyn.Controllers
return Json(result);
}
[HttpPost]
public IActionResult SaveVeckomeny(IFormCollection form)
public IActionResult SaveVeckomeny(IFormCollection form, int week, int year)
{
var menuService = new MenuService(_configuration, _env);
var allMeals = menuService.GetMeals();
var model = new MenuViewModel
{
WeekNumber = ISOWeek.GetWeekOfYear(DateTime.Now),
Year = DateTime.Now.Year,
WeekNumber = week,
Year = year,
WeeklyMenus = new List<WeeklyMenu>()
};
@@ -82,62 +142,76 @@ namespace Aberwyn.Controllers
foreach (var key in form.Keys.Where(k => k.StartsWith("Meal[", StringComparison.OrdinalIgnoreCase)))
{
var match = System.Text.RegularExpressions.Regex.Match(key, @"Meal\[(\d+)\]\[(\w+)\]");
if (match.Success)
var match = Regex.Match(key, @"Meal\[(\d+)\]\[(\w+)\]");
if (!match.Success) continue;
int day = int.Parse(match.Groups[1].Value);
string mealType = match.Groups[2].Value;
string mealName = form[key];
string cook = form[$"Cook[{day}]"];
if (!entriesByDay.TryGetValue(day, out var entry))
{
int day = int.Parse(match.Groups[1].Value);
string mealType = match.Groups[2].Value;
string mealName = form[key];
string cook = form[$"Cook[{day}][{mealType}]"];
if (string.IsNullOrWhiteSpace(mealName)) continue;
var meal = allMeals.FirstOrDefault(m => m.Name.Equals(mealName, StringComparison.OrdinalIgnoreCase));
if (meal == null)
entry = new WeeklyMenu
{
meal = new Meal { Name = mealName };
menuService.SaveMeal(meal);
WeekNumber = week,
Year = year,
DayOfWeek = day + 1
};
entriesByDay[day] = entry;
}
entry.Cook = cook;
if (string.IsNullOrWhiteSpace(mealName))
{
switch (mealType.ToLower())
{
case "lunch":
entry.LunchMealId = null;
entry.LunchMealName = null;
break;
case "middag":
entry.DinnerMealId = null;
entry.DinnerMealName = null;
break;
case "frukost":
entry.BreakfastMealId = null;
entry.BreakfastMealName = null;
break;
}
continue;
}
if (!entriesByDay.TryGetValue(day, out var entry))
{
entry = new WeeklyMenu
{
WeekNumber = model.WeekNumber,
Year = model.Year
};
entriesByDay[day] = entry;
}
var meal = allMeals.FirstOrDefault(m => m.Name.Equals(mealName, StringComparison.OrdinalIgnoreCase))
?? new Meal { Name = mealName };
entry.DayOfWeek = day + 1; // se till att alltid sätta rätt dag
entry.Cook = cook;
if (meal.Id == 0)
menuService.SaveOrUpdateMeal(meal);
if (mealType.Equals("Lunch", StringComparison.OrdinalIgnoreCase))
{
switch (mealType.ToLower())
{
case "lunch":
entry.LunchMealId = meal.Id;
entry.LunchMealName = meal.Name;
}
else if (mealType.Equals("Middag", StringComparison.OrdinalIgnoreCase))
{
break;
case "middag":
entry.DinnerMealId = meal.Id;
entry.DinnerMealName = meal.Name;
}
else if (mealType.Equals("Frukost", StringComparison.OrdinalIgnoreCase))
{
break;
case "frukost":
entry.BreakfastMealId = meal.Id;
entry.BreakfastMealName = meal.Name;
}
break;
}
}
model.WeeklyMenus = entriesByDay.Values.ToList();
menuService.UpdateWeeklyMenu(model);
return RedirectToAction("Veckomeny");
return RedirectToAction("Veckomeny", new { week, year });
}
}
}
}

View File

@@ -15,16 +15,62 @@ namespace Aberwyn.Controllers
_env = env;
}
[HttpGet]
public IActionResult View(int id)
public IActionResult View(int id, bool edit = false)
{
var service = new MenuService(_configuration, _env);
var meal = service.GetMealById(id);
ViewData["IsEditing"] = edit; // → här
if (meal == null)
{
return NotFound();
}
return View("View", meal);
}
[HttpGet]
[Route("Meal/Tooltip/{id}")]
public IActionResult Tooltip(int id)
{
try
{
var service = new MenuService(_configuration, _env);
var meal = service.GetMealById(id);
if (meal == null)
return Content("<div>Hittade ingen rätt.</div>", "text/html");
// Se till att Description aldrig är null
var desc = meal.Description ?? "";
// Gör en kort beskrivning
var shortDesc = desc.Length > 100
? desc.Substring(0, 100) + "…"
: desc;
// Bygg upp en enkel HTML-snutt
var html =
$"<div class='tooltip-content'>" +
$" <strong>{meal.Name}</strong><br/>" +
(string.IsNullOrWhiteSpace(shortDesc)
? "<em>Ingen beskrivning tillgänglig.</em>"
: $"<em>{shortDesc}</em>") +
"</div>";
return Content(html, "text/html");
}
catch (Exception ex)
{
// För felsökning kan du logga ex.Message, men för tillfället returnera det så vi ser det i devtools
return StatusCode(500, $"<pre>{ex.Message}</pre>");
}
}
[HttpGet]
public IActionResult Edit(int? id)
{
@@ -34,30 +80,33 @@ namespace Aberwyn.Controllers
}
[HttpPost]
public async Task<IActionResult> SaveMeal(Meal meal, IFormFile ImageFile)
public IActionResult SaveMeal(Meal meal, IFormFile ImageFile)
{
var service = new MenuService(_configuration, _env);
if (ImageFile != null && ImageFile.Length > 0)
{
var uploadsFolder = Path.Combine(_env.ContentRootPath, "wwwroot/uploads");
Directory.CreateDirectory(uploadsFolder); // skapa om saknas
var uniqueFileName = Guid.NewGuid().ToString() + Path.GetExtension(ImageFile.FileName);
var filePath = Path.Combine(uploadsFolder, uniqueFileName);
var fileName = Path.GetFileNameWithoutExtension(ImageFile.FileName);
var extension = Path.GetExtension(ImageFile.FileName);
var uniqueName = $"{fileName}_{Guid.NewGuid()}{extension}";
var imagePath = Path.Combine("wwwroot/images/meals", uniqueName);
using (var fileStream = new FileStream(filePath, FileMode.Create))
using (var stream = new FileStream(imagePath, FileMode.Create))
{
await ImageFile.CopyToAsync(fileStream);
ImageFile.CopyTo(stream);
}
meal.ImageUrl = "/uploads/" + uniqueFileName;
// Spara relativ sökväg för visning
meal.ImageUrl = $"/images/meals/{uniqueName}";
}
service.SaveOrUpdateMeal(meal);
return RedirectToAction("Edit", new { id = meal.Id });
return RedirectToAction("View", new { id = meal.Id });
}
[HttpPost]
public IActionResult DeleteMeal(int id)
{

View File

@@ -2,6 +2,8 @@
using Aberwyn.Models;
using Aberwyn.Data;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace Aberwyn.Controllers
{
@@ -11,71 +13,45 @@ namespace Aberwyn.Controllers
{
private readonly MenuService _menuService;
public MealMenuApiController(MenuService menuService)
public MealMenuApiController(IConfiguration configuration, IHostEnvironment env)
{
_menuService = menuService;
_menuService = new MenuService(configuration, env);
}
// API endpoint to fetch the weekly menu
[HttpGet("menu")]
public IActionResult GetMenu(int weekNumber, int year)
{
var menu = _menuService.GetWeeklyMenu(weekNumber, year);
if (menu == null || !menu.Any())
{
// Return an empty object or array instead of a 404 error
return Ok(new List<WeeklyMenu>()); // Return empty list if no menu is found
}
return Ok(menu); // Return the menu as a JSON response
return Ok(menu ?? new List<WeeklyMenu>());
}
// API endpoint to fetch the weekly menu
[HttpGet("getMeals")]
public IActionResult GetMeals()
{
var meals = _menuService.GetMeals();
if (meals == null || !meals.Any())
{
// Return an empty object or array instead of a 404 error
return Ok(new List<Meal>()); // Return empty list if no menu is found
}
return Ok(meals); // Return the menu as a JSON response
return Ok(meals ?? new List<Meal>());
}
// API endpoint to save the updated menu (if you need this)
[HttpPut("menu")]
public IActionResult SaveMenu([FromBody] MenuViewModel weeklyMenu)
{
// Process and save the menu (you may need to write this logic)
_menuService.UpdateWeeklyMenu(weeklyMenu);
return Ok("Menu saved successfully");
}
[HttpPost("addMeal")]
public IActionResult AddMeal([FromBody] Meal meal)
{
if (meal == null || string.IsNullOrWhiteSpace(meal.Name))
{
return BadRequest("Meal Name is required.");
}
// Använd AddMeal som returnerar det nya ID:t
var mealId = _menuService.AddMeal(meal);
if (mealId > 0)
{
return Ok(new { Message = "Meal added successfully", MealId = mealId });
}
return StatusCode(500, "Failed to add meal.");
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Aberwyn.Models;
using System.Globalization;
namespace Aberwyn.Data
{
@@ -197,7 +198,7 @@ namespace Aberwyn.Data
using var connection = GetConnection();
connection.Open();
string query = @"SELECT Id, Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt
string query = @"SELECT Id, Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt, ImageUrl
FROM Meals WHERE Id = @id";
using var cmd = new MySqlCommand(query, connection);
@@ -214,6 +215,7 @@ namespace Aberwyn.Data
ProteinType = reader.IsDBNull(reader.GetOrdinal("ProteinType")) ? null : reader.GetString(reader.GetOrdinal("ProteinType")),
CarbType = reader.IsDBNull(reader.GetOrdinal("CarbType")) ? null : reader.GetString(reader.GetOrdinal("CarbType")),
RecipeUrl = reader.IsDBNull(reader.GetOrdinal("RecipeUrl")) ? null : reader.GetString(reader.GetOrdinal("RecipeUrl")),
ImageUrl = reader.IsDBNull(reader.GetOrdinal("ImageUrl")) ? null : reader.GetString(reader.GetOrdinal("ImageUrl")),
CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt"))
};
@@ -234,44 +236,48 @@ namespace Aberwyn.Data
public void SaveOrUpdateMeal(Meal meal)
{
using var connection = GetConnection();
connection.Open();
using var conn = new MySqlConnection(_configuration.GetConnectionString("DefaultConnection"));
conn.Open();
string query;
bool isNew = meal.Id == 0;
if (isNew)
using var cmd = conn.CreateCommand();
if (meal.Id == 0)
{
query = @"INSERT INTO Meals (Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt)
VALUES (@name, @desc, @protein, @carb, @url, @created)";
cmd.CommandText = @"INSERT INTO Meals
(Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt, ImageUrl)
VALUES (@Name, @Description, @ProteinType, @CarbType, @RecipeUrl, @CreatedAt, @ImageUrl);
SELECT LAST_INSERT_ID();";
}
else
{
query = @"UPDATE Meals
SET Name = @name, Description = @desc, ProteinType = @protein, CarbType = @carb, RecipeUrl = @url
WHERE Id = @id";
cmd.CommandText = @"UPDATE Meals
SET Name = @Name, Description = @Description, ProteinType = @ProteinType,
CarbType = @CarbType, RecipeUrl = @RecipeUrl, ImageUrl = @ImageUrl
WHERE Id = @Id";
cmd.Parameters.AddWithValue("@Id", meal.Id);
}
using var cmd = new MySqlCommand(query, connection);
cmd.Parameters.AddWithValue("@name", meal.Name);
cmd.Parameters.AddWithValue("@desc", meal.Description ?? "");
cmd.Parameters.AddWithValue("@protein", meal.ProteinType ?? "");
cmd.Parameters.AddWithValue("@carb", meal.CarbType ?? "");
cmd.Parameters.AddWithValue("@url", meal.RecipeUrl ?? "");
cmd.Parameters.AddWithValue("@Name", meal.Name ?? "");
cmd.Parameters.AddWithValue("@Description", meal.Description ?? "");
cmd.Parameters.AddWithValue("@ProteinType", meal.ProteinType ?? "");
cmd.Parameters.AddWithValue("@CarbType", meal.CarbType ?? "");
cmd.Parameters.AddWithValue("@RecipeUrl", meal.RecipeUrl ?? "");
cmd.Parameters.AddWithValue("@CreatedAt", meal.CreatedAt == default ? DateTime.Now : meal.CreatedAt);
cmd.Parameters.AddWithValue("@ImageUrl", meal.ImageUrl ?? "");
if (isNew)
if (meal.Id == 0)
{
cmd.Parameters.AddWithValue("@created", meal.CreatedAt == default ? DateTime.Now : meal.CreatedAt);
var insertedId = Convert.ToInt32(cmd.ExecuteScalar());
meal.Id = insertedId; // ← Sätt ID direkt på objektet
}
else
{
cmd.Parameters.AddWithValue("@id", meal.Id);
cmd.ExecuteNonQuery();
}
cmd.ExecuteNonQuery();
}
public void UpdateWeeklyMenu(MenuViewModel menuData)
{
if (menuData == null || menuData.WeeklyMenus == null)
@@ -384,6 +390,54 @@ namespace Aberwyn.Data
_ => throw new System.ArgumentException("Invalid day name")
};
}
public List<WeeklyMenu> GetMenuEntriesByDateRange(DateTime startDate, DateTime endDate)
{
var results = new List<WeeklyMenu>();
// Hitta start- och slut-år/veckonummer
int startWeek = ISOWeek.GetWeekOfYear(startDate);
int startYear = startDate.Year;
int endWeek = ISOWeek.GetWeekOfYear(endDate);
int endYear = endDate.Year;
// Loopar över alla veckor från start → slut
var allMenus = new List<WeeklyMenu>();
int w = startWeek, y = startYear;
while (y < endYear || (y == endYear && w <= endWeek))
{
allMenus.AddRange(GetWeeklyMenu(w, y));
// öka vecka, hantera årsskifte
int weeksInYear = ISOWeek.GetWeeksInYear(y);
if (w == weeksInYear)
{
w = 1;
y++;
}
else
{
w++;
}
}
// Filtrera på datumintervall
foreach (var menu in allMenus)
{
// Konvertera DayOfWeek (1=Mon…7=Sun) till System.DayOfWeek
var dow = (DayOfWeek)(menu.DayOfWeek % 7);
var date = ISOWeek.ToDateTime(menu.Year, menu.WeekNumber, dow);
if (date.Date >= startDate.Date && date.Date <= endDate.Date)
{
menu.Date = date; // Se till att WeeklyMenu har en Date-property
results.Add(menu);
}
}
return results;
}
}
}

View File

@@ -24,9 +24,16 @@ namespace Aberwyn.Models
public string BreakfastMealName { get; set; } // 👈 Och denna
public string LunchMealName { get; set; }
public string DinnerMealName { get; set; }
public DateTime Date { get; set; }
}
public class RecentMenuEntry
{
public DateTime Date { get; set; }
public string BreakfastMealName { get; set; }
public string LunchMealName { get; set; }
public string DinnerMealName { get; set; }
}
public class Meal
{
public int Id { get; set; }

View File

@@ -5,18 +5,30 @@ using System.Linq;
namespace Aberwyn.Models
{
public class WeeklyMenuViewModel
{
public int WeekNumber { get; set; }
public int Year { get; set; }
public List<RecentMenuEntry> RecentEntries { get; set; } = new();
public List<WeeklyMenu> WeeklyMenus { get; set; } = new();
public List<User> AvailableCooks { get; set; } = new();
// Ny lista för översikt
public List<WeeklyMenuViewModel> PreviousWeeks { get; set; } = new();
public class RecentMenuEntry
{
public DateTime Date { get; set; }
public string BreakfastMealName { get; set; }
public string LunchMealName { get; set; }
public string DinnerMealName { get; set; }
}
public WeeklyMenu GetMealEntry(int day, string type)
{
int dayOfWeek = day + 1;
return WeeklyMenus.FirstOrDefault(m =>
m.DayOfWeek == dayOfWeek &&
(
@@ -27,4 +39,6 @@ namespace Aberwyn.Models
);
}
}
}

View File

@@ -1,28 +1,36 @@
using Microsoft.EntityFrameworkCore; // Add this for DbContext
using Aberwyn.Data; // Include your data namespace
using MySql.EntityFrameworkCore.Extensions; // For MySQL support
using Microsoft.EntityFrameworkCore;
using Aberwyn.Data;
using MySql.EntityFrameworkCore.Extensions;
using System.Text;
using System.Globalization;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages(); // Add this line to enable Razor Pages
builder.Services.AddHttpClient(); // Register HttpClient
builder.Services.AddControllersWithViews()
.AddJsonOptions(opts =>
{
// Behåll propertynamn som i C#-klassen (PascalCase)
opts.JsonSerializerOptions.PropertyNamingPolicy = null;
// Ignorera null-värden vid serialisering
opts.JsonSerializerOptions.IgnoreNullValues = true;
});
builder.Services.AddRazorPages();
builder.Services.AddHttpClient();
// Configure your DbContext with MySQL
builder.Services.AddDbContext<BudgetContext>(options =>
options.UseMySQL(builder.Configuration.GetConnectionString("BudgetDb")));
// Register your BudgetService with a scoped lifetime
// Register your services
builder.Services.AddScoped<BudgetService>();
builder.Services.AddScoped<MenuService>();
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
@@ -31,6 +39,7 @@ builder.Services.Configure<RequestLocalizationOptions>(options =>
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
var app = builder.Build();
// Configure the HTTP request pipeline.
@@ -40,20 +49,19 @@ if (!app.Environment.IsDevelopment())
app.UseHsts();
}
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
// Map controller endpoints (including AJAX JSON actions)
app.MapControllers();
// Map default controller route
// Conventional MVC route
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
// Map Razor Pages
app.MapRazorPages(); // Add this line to map Razor Pages
app.MapRazorPages();
app.Run();

View File

@@ -1,93 +1,229 @@
@model WeeklyMenuViewModel
@using System.Globalization
@model Aberwyn.Models.WeeklyMenuViewModel
@{
ViewData["Title"] = "Veckomeny";
var days = new[] { "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag", "Söndag" };
var mealTypes = new[] { "Frukost", "Lunch", "Middag" };
var allMeals = ViewBag.AvailableMeals as List<Aberwyn.Models.Meal>;
}
<h1>Veckomeny - Vecka @Model.WeekNumber</h1>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<div class="week-nav">
<a asp-action="Veckomeny" asp-route-week="@(@Model.WeekNumber - 1)">Föregående vecka</a>
<a asp-action="Veckomeny" asp-route-week="@(@Model.WeekNumber + 1)">Nästa vecka</a>
<style>
[x-cloak] { display: none !important; }
[x-show] {
transition: all 0.3s ease-in-out;
overflow: hidden;
}
.rotate-chevron {
transition: transform 0.3s ease;
}
.rotate-chevron.open {
transform: rotate(90deg);
}
.meal-input {
padding: 4px 6px;
font-size: 0.85rem;
width: 100%;
border-radius: 8px;
border: 1px solid #546E7A;
background: #445A6E;
color: #ECEFF1;
}
.meal-input.new-entry {
border-color: #FFC107; /* Orange */
background: inherit; /* Lägg till detta om nödvändigt */
}
.meal-input.existing-entry {
border-color: #4CAF50; /* Grön */
background: inherit; /* Lägg till detta också */
}
.add-meal-wrapper {
display: flex;
align-items: flex-start;
gap: 12px;
margin-top: 12px;
}
.add-meal-wrapper input[type=text] {
width: 160px;
padding: 4px 6px;
font-size: 0.85rem;
}
.delete-btn {
background: none;
border: none;
cursor: pointer;
vertical-align: middle;
padding: 0;
}
.delete-btn i {
margin-left: 6px;
color: #FF8A65;
}
.week-nav a {
color: #FFEB3B;
text-decoration: none;
font-weight: bold;
}
.week-nav a:hover {
text-decoration: underline;
}
</style>
<script>
window.knownMeals = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(
(ViewBag.AvailableMeals as List<Aberwyn.Models.Meal>)?.Select(m => m.Name.Trim()).ToList() ?? new List<string>()));
</script>
<div class="weekly-menu-wrapper"
style="display:grid; grid-template-columns:1fr; gap:16px; padding:16px; background:#263238;"
x-data="{
highlightNew(event) {
const input = event.target;
const val = input.value?.trim().toLowerCase();
input.classList.remove('new-entry', 'existing-entry');
if (!val) return;
const isKnown = window.knownMeals.some(name => name.toLowerCase() === val);
input.classList.add(isKnown ? 'existing-entry' : 'new-entry');
}
}"
<!-- Rest of the content remains unchanged -->
<section class="weekly-editor">
<h1 style="color:#ECEFF1; margin-bottom:12px;">Veckomeny - Vecka @Model.WeekNumber</h1>
<div class="week-nav" style="margin-bottom:12px; display:flex; align-items:center; color:#B0BEC5; font-size:0.9rem;">
<a asp-action="Veckomeny" asp-route-week="@(Model.WeekNumber - 1)" asp-route-year="@Model.Year" style="margin-right:8px;">&larr; Föregående vecka</a>
<span style="font-weight:bold; color:#ECEFF1;">Vecka @Model.WeekNumber</span>
<a asp-action="Veckomeny" asp-route-week="@(Model.WeekNumber + 1)" asp-route-year="@Model.Year" style="margin-left:8px;">Nästa vecka &rarr;</a>
</div>
<form method="post" asp-action="SaveVeckomeny">
<input type="hidden" name="week" value="@Model.WeekNumber" />
<input type="hidden" name="year" value="@Model.Year" />
<table style="width:100%; border-collapse:separate; border-spacing:0 4px; font-size:0.9rem;">
<thead>
<tr style="background:#37474F; color:#ECEFF1;">
<th style="padding:8px 12px; text-align:left; border-radius:4px 0 0 4px;">Dag</th>
<th style="padding:8px 12px; text-align:left;">Middag</th>
<th style="padding:8px 12px; text-align:left;">Kock</th>
</tr>
</thead>
<tbody>
@for (int i = 0; i < 7; i++) {
var dinnerEntry = Model.GetMealEntry(i, "Middag");
var lunchEntry = Model.GetMealEntry(i, "Lunch");
var breakfastEntry = Model.GetMealEntry(i, "Frukost");
<tbody x-data="{ showExtra: false }">
<tr style="background:#2A3A48; color:#ECEFF1;">
<td style="padding:8px 12px; font-weight:bold; vertical-align:top; width:12%;">
<button type="button" x-on:click="showExtra = !showExtra" style="margin-right:6px; background:none; border:none; color:#ECEFF1; font-size:1rem; cursor:pointer;">
<i :class="showExtra ? 'fa-solid fa-chevron-right rotate-chevron open' : 'fa-solid fa-chevron-right rotate-chevron'"></i>
</button>
@days[i]
</td>
<td style="padding:8px 12px; vertical-align:top;">
<div style="display:flex; align-items:center; gap:4px;">
<input type="text" name="Meal[@i][Middag]" value="@dinnerEntry?.DinnerMealName" placeholder="Lägg till middag..." list="meals-list" class="meal-input" :tabindex="showExtra ? 0 : -1" x-on:input="highlightNew($event)" />
<button type="button" class="delete-btn" title="Rensa middag" onclick="this.previousElementSibling.value='';">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
</td>
<td style="padding:8px 12px; vertical-align:top; width:14%;">
<select name="Cook[@i]" :tabindex="showExtra ? 0 : -1" style="width:100%; padding:6px; border-radius:8px; border:1px solid #546E7A; background:#445A6E; color:#ECEFF1; font-size:0.9rem;">
<option value="">Välj kock</option>
@foreach(var user in Model.AvailableCooks) {
var selected = Model.WeeklyMenus.FirstOrDefault(m => m.DayOfWeek == i+1)?.Cook == user.Username;
<option value="@user.Username" selected="@selected">@user.Name</option>
}
</select>
</td>
</tr>
<tr x-show="showExtra" x-transition x-cloak style="background:#2A3A48; color:#ECEFF1;">
<td></td>
<td colspan="2">
<div style="display:flex; flex-direction:column; gap:8px;">
<div style="display:flex; align-items:center; gap:4px;">
<label style="font-size:0.85rem; color:#B0BEC5; min-width:60px;">Frukost:</label>
<input type="text" name="Meal[@i][Frukost]" value="@breakfastEntry?.BreakfastMealName" placeholder="Lägg till frukost..." list="meals-list" class="meal-input" :tabindex="showExtra ? 0 : -1" x-on:input="highlightNew($event)" />
<button type="button" class="delete-btn" title="Rensa frukost" onclick="this.previousElementSibling.value='';">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
<div style="display:flex; align-items:center; gap:4px;">
<label style="font-size:0.85rem; color:#B0BEC5; min-width:60px;">Lunch:</label>
<input type="text" name="Meal[@i][Lunch]" value="@lunchEntry?.LunchMealName" placeholder="Lägg till lunch..." list="meals-list" class="meal-input" :tabindex="showExtra ? 0 : -1" x-on:input="highlightNew($event)" />
<button type="button" class="delete-btn" title="Rensa lunch" onclick="this.previousElementSibling.value='';">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
</div>
</td>
</tr>
</tbody>
}
</tbody>
</table>
<!-- Save button och övrigt -->
<div class="add-meal-wrapper">
<button type="submit" style="padding:8px 16px; border:none; border-radius:8px; background:#FFEB3B; font-weight:bold; font-size:0.9rem;">Spara veckomeny</button>
</div>
<aside class="recent-history" style="background:#37474F; border-radius:8px; color:#ECEFF1; padding:16px; margin-top:24px;">
<h2 style="margin:0 0 8px; font-size:1rem;">Översikt senaste 4 veckor</h2>
@{
var lastWeeks = Enumerable.Range(1, 4)
.Select(i => DateTime.Now.AddDays(-7 * i))
.Select(dt => new { Year = dt.Year, Week = ISOWeek.GetWeekOfYear(dt) })
.Distinct().ToList();
}
<table style="width:100%; border-collapse:collapse; font-size:0.85rem;">
<thead>
<tr style="background:#546E7A; color:#ECEFF1;">
<th style="padding:4px 8px;">Vecka</th>
@foreach (var d in days)
{
<th style="padding:4px 8px;">@d</th>
}
</tr>
</thead>
<tbody>
@foreach (var w in lastWeeks)
{
<tr style="background:#2A3A48; color:#ECEFF1;">
<td style="padding:4px 8px; font-weight:bold;">@w.Week</td>
@for (int idx = 1; idx <= 7; idx++)
{
var entry = Model.RecentEntries?.FirstOrDefault(e =>
e.Date.Year == w.Year &&
ISOWeek.GetWeekOfYear(e.Date) == w.Week &&
((int)e.Date.DayOfWeek == (idx % 7))
);
<td style="padding:4px 8px; white-space:nowrap;">@(entry?.DinnerMealName ?? "—")</td>
}
</tr>
}
</tbody>
</table>
</aside>
<datalist id="meals-list">
@foreach(var m in ViewBag.AvailableMeals as List<Aberwyn.Models.Meal>) {
<option value="@m.Name" id="@m.Name"></option>
}
</datalist>
</form>
</div>
<form method="post" asp-action="SaveVeckomeny">
<table class="menu-table">
<thead>
<tr>
<th>Dag</th>
@foreach (var mealType in mealTypes)
{
<th>@mealType</th>
}
</tr>
</thead>
<tbody>
@for (int i = 0; i < 7; i++)
{
<tr>
<td>@days[i]</td>
@foreach (var mealType in mealTypes)
{
var entry = Model.GetMealEntry(i, mealType);
var mealName = mealType switch
{
"Frukost" => entry?.BreakfastMealName,
"Lunch" => entry?.LunchMealName,
"Middag" => entry?.DinnerMealName,
_ => null
};
<td>
<input type="text" name="Meal[@i][@mealType]"
value="@mealName"
placeholder="Lägg till måltid..."
class="meal-input" data-day="@i" data-type="@mealType" list="meals-list" />
<select name="Cook[@i][@mealType]">
<option value="">Vem lagar?</option>
@foreach (var user in Model.AvailableCooks)
{
if (entry?.Cook == user.Username)
{
<option value="@user.Username" selected>@user.Name</option>
}
else
{
<option value="@user.Username">@user.Name</option>
}
}
</select>
</td>
}
</tr>
}
</tbody>
</table>
<button type="submit">Spara veckomeny</button>
</form>
<datalist id="meals-list"></datalist>
@section Scripts {
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(function() {
$('.meal-input').on('focus input', function() {
const input = $(this);
const term = input.val();
input.attr('list', 'meals-list'); // tvinga list-id
$.getJSON('/FoodMenu/SearchMeals', { term: term }, function(data) {
const list = $('#meals-list');
list.empty();
data.forEach(name => {
list.append(`<option value="${name}">`);
});
});
});
});
function showMealInfo(name, cook) {
alert(`Måltid: ${name}\nLagas av: ${cook}`);
}
</script>
}

View File

@@ -1,54 +1,97 @@
@model Aberwyn.Models.MenuViewModel
@{
// Define a flag to hide the sidebar if the URL path matches '/nosidebar' (relative part after the controller)
bool hideSidebar = Context.Request.Path.Value.EndsWith("/nosidebar", StringComparison.OrdinalIgnoreCase);
Layout = hideSidebar ? null : "_Layout"; // No layout if the path ends with '/nosidebar'
var hideSidebar = Context.Request.Path.Value.EndsWith("/nosidebar", StringComparison.OrdinalIgnoreCase);
Layout = hideSidebar ? null : "_Layout";
}
<!DOCTYPE html>
<html lang="en" ng-app="mealMenuApp">
<html lang="sv" ng-app="mealMenuApp">
<head>
<meta charset="utf-8">
<title>Meal Menu Overview</title>
<meta charset="utf-8" />
<title>Veckomeny</title>
<!-- AngularJS -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular-sanitize.js"></script>
<script src="~/js/menu.js"></script>
<link rel="stylesheet" type="text/css" href="~/css/meal-menu.css">
<!-- CSS -->
<link rel="stylesheet" href="~/css/meal-menu.css" />
<style>
.view-selector { position: absolute; top: 24px; right: 24px; }
.view-selector select { padding: 6px 10px; border-radius: 4px; border: none; background: #546E7A; color: #ECEFF1; font-size: 0.95rem; cursor: pointer; }
.list-view .day-item { background: #37474F; margin-bottom: 16px; padding: 16px; border-radius: 8px; }
.list-view .day-header { font-size: 1.4rem; color: #FFEB3B; margin-bottom: 8px; position: relative; }
.list-view .day-header::after { content: ''; position: absolute; bottom: -4px; left: 0; width: 40px; height: 3px; background: #FFEB3B; border-radius: 2px; }
.meal-selection { margin: 6px 0; font-size: 1rem; }
.meal-selection a { color: #ECEFF1; text-decoration: none; }
.meal-selection a:hover { text-decoration: underline; }
.not-assigned { color: #B0BEC5; font-style: italic; }
.card-view .card-container { display: grid; grid-template-columns: repeat(auto-fill,minmax(180px,1fr)); gap: 16px; }
.meal-card { position: relative; height: 180px; color: #fff; background-size: cover; background-position: center; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 8px rgba(0,0,0,0.3); display: flex; align-items: flex-end; cursor: pointer; }
.meal-card::before { content: ''; position: absolute; inset: 0; background: linear-gradient(to top,rgba(0,0,0,0.7),transparent); }
.card-content { position: relative; padding: 12px; z-index: 1; width: 100%; }
.card-content .day { font-size: 1.2rem; font-weight: bold; margin-bottom: 6px; }
.card-content .meal { font-size: 0.9rem; line-height: 1.2; }
</style>
</head>
<body ng-controller="MealMenuController">
<body ng-controller="MealMenuController" ng-init="viewMode='list'">
<div class="meal-menu-page" style="position:relative">
<h1>Veckomeny</h1>
<div class="meal-menu-page">
<h1 class="page-title">Meal Menu Overview</h1>
<div class="date-picker">
<button type="button" class="date-btn" ng-click="goToPreviousWeek()">Föregående vecka</button>
<span class="week-info">Vecka {{ selectedWeek }} - {{ selectedYear }}</span>
<button type="button" class="date-btn" ng-click="goToNextWeek()">Nästa vecka</button>
</div>
<div class="meal-menu-container">
<div ng-repeat="day in daysOfWeek" class="day-item">
<div class="day-header">{{ day }}</div>
<div class="meal-info" ng-if="!isEditing">
<div ng-if="menu[day]">
<div ng-if="menu[day].breakfastMealName">
<span><strong>Frukost:</strong> {{ menu[day].breakfastMealName }}</span>
</div>
<div ng-if="menu[day].lunchMealName">
<span><strong>Lunch:</strong> {{ menu[day].lunchMealName }}</span>
</div>
<div ng-if="menu[day].dinnerMealName">
<span><strong>Middag:</strong> {{ menu[day].dinnerMealName }}</span>
</div>
</div>
<div ng-if="!menu[day]">
<span class="not-assigned">Inte bestämd</span>
</div>
</div>
</div>
</div>
<!-- Vecko-navigering -->
<div class="date-picker">
<button ng-click="goToPreviousWeek()">← Föregående vecka</button>
<span>Vecka {{selectedWeek}} {{selectedYear}}</span>
<button ng-click="goToNextWeek()">Nästa vecka</button>
</div>
<!-- Vy-väljare -->
<div class="view-selector">
<label for="viewModeSelect">Visa:</label>
<select id="viewModeSelect" ng-model="viewMode">
<option value="list">Lista</option>
<option value="card">Kort</option>
</select>
</div>
<!-- Vy: List eller Kort -->
<div ng-switch="viewMode">
<div ng-switch-when="list" class="list-view">
<div ng-repeat="day in daysOfWeek" class="day-item">
<div class="day-header">{{day}}</div>
<div class="meal-info" ng-if="menu[day]">
<div ng-if="menu[day].breakfastMealId" class="meal-selection">
<a href="/Meal/View/{{menu[day].breakfastMealId}}" target="_blank"><strong>Frukost:</strong> {{menu[day].breakfastMealName}}</a>
</div>
<div ng-if="menu[day].lunchMealId" class="meal-selection">
<a href="/Meal/View/{{menu[day].lunchMealId}}" target="_blank"><strong>Lunch:</strong> {{menu[day].lunchMealName}}</a>
</div>
<div ng-if="menu[day].dinnerMealId" class="meal-selection">
<a href="/Meal/View/{{menu[day].dinnerMealId}}" target="_blank"><strong>Middag:</strong> {{menu[day].dinnerMealName}}</a>
</div>
</div>
<div ng-if="!menu[day]"><span class="not-assigned">Inte bestämd</span></div>
</div>
</div>
<div ng-switch-when="card" class="card-view">
<div class="card-container">
<div ng-repeat="day in daysOfWeek"
class="meal-card"
ng-style="{'background-image':'url('+getDayImage(day)+')'}"
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>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -40,7 +40,7 @@
<div>
<label for="ImageFile">Bild</label>
<input type="file" name="ImageFile" />
<input type="file" name="ImageFile" value="@Model.ImageUrl" />
</div>
<button type="submit">Spara</button>

View File

@@ -1,90 +1,192 @@
@model Aberwyn.Models.Meal
@{
ViewData["Title"] = Model.Name;
bool isEditing = (bool)(ViewData["IsEditing"] ?? false);
var imageUrl = string.IsNullOrEmpty(Model.ImageUrl) ? "/images/placeholder-meal.jpg" : Model.ImageUrl;
}
<div class="meal-container">
<h1>@Model.Name</h1>
@if (!string.IsNullOrEmpty(Model.ImageUrl))
{
<img src="@Model.ImageUrl" alt="@Model.Name" class="meal-image" />
}
@if (!string.IsNullOrEmpty(Model.Description))
{
<p class="description">@Model.Description</p>
}
<div class="buttons">
<button onclick="toggleRecipe()">Visa Recept</button>
<a class="edit-button" asp-controller="Meal" asp-action="Edit" asp-route-id="@Model.Id">Redigera</a>
<div class="meal-header">
<img src="@imageUrl" alt="@Model.Name" class="meal-image" />
<div class="meal-meta">
<h1 class="meal-title">@Model.Name</h1>
<p class="description">@Model.Description</p>
</div>
</div>
<div id="recipe-section" style="display:none;">
<h2>Så här gör du</h2>
<p>(Tillagningsinstruktioner kommer snart...)</p>
@if (isEditing)
{
<form asp-action="SaveMeal" method="post" enctype="multipart/form-data">
<input type="hidden" name="Id" value="@Model.Id" />
<div class="form-group">
<label for="ImageFile">Bild</label>
<input type="file" name="ImageFile" accept="image/*" value="@Model.ImageUrl" class="form-control" />
</div>
<div class="form-group">
<label for="Name">Namn</label>
<input type="text" name="Name" value="@Model.Name" class="form-control" required />
</div>
<div class="form-group">
<label for="Description">Beskrivning</label>
<textarea name="Description" class="form-control">@Model.Description</textarea>
</div>
<div class="form-group">
<label for="ProteinType">Protein</label>
<input type="text" name="ProteinType" value="@Model.ProteinType" class="form-control" />
</div>
<div class="form-group">
<label for="CarbType">Kolhydrat</label>
<input type="text" name="CarbType" value="@Model.CarbType" class="form-control" />
</div>
<div class="form-group">
<label for="RecipeUrl">Receptlänk</label>
<input type="url" name="RecipeUrl" value="@Model.RecipeUrl" class="form-control" />
</div>
<div class="buttons">
<button type="submit" class="btn">Spara</button>
<button type="submit" formaction="@Url.Action("DeleteMeal", new { id = Model.Id })" formmethod="post" onclick="return confirm('Vill du verkligen ta bort denna måltid?');" class="btn-outline">Ta bort</button>
<a href="@Url.Action("View", new { id = Model.Id, edit = false })" class="btn-outline">Avbryt</a>
</div>
</form>
}
else
{
<div class="meal-details">
@if (!string.IsNullOrEmpty(Model.ProteinType))
{
<p><span class="label">Protein:</span> @Model.ProteinType</p>
}
@if (!string.IsNullOrEmpty(Model.CarbType))
{
<p><span class="label">Kolhydrat:</span> @Model.CarbType</p>
}
</div>
@if (!string.IsNullOrEmpty(Model.RecipeUrl))
{
<p><a href="@Model.RecipeUrl" target="_blank">Öppna fullständigt recept</a></p>
<p><a href="@Model.RecipeUrl" class="recipe-link" target="_blank">Visa Recept</a></p>
}
<div class="buttons">
<button type="button" class="btn-outline" onclick="toggleRecipe()">Visa Tillagning</button>
<a class="btn-outline" href="@Url.Action("View", new { id = Model.Id, edit = true })">Redigera</a>
</div>
}
<div id="recipe-section" style="display:none; margin-top:1.5rem;">
<h2>Så här gör du</h2>
<p class="placeholder">(Tillagningsinstruktioner kommer snart...)</p>
@if (!string.IsNullOrEmpty(Model.RecipeUrl))
{
<p><a href="@Model.RecipeUrl" class="recipe-link" target="_blank">Gå till fullständigt recept</a></p>
}
</div>
</div>
<script>
function toggleRecipe() {
var section = document.getElementById('recipe-section');
if (section.style.display === 'none') {
section.style.display = 'block';
} else {
section.style.display = 'none';
}
const section = document.getElementById('recipe-section');
section.style.display = section.style.display === 'none' ? 'block' : 'none';
}
</script>
<style>
.meal-container {
max-width: 700px;
max-width: 900px;
margin: 2rem auto;
background: #fff;
padding: 2rem;
border-radius: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
text-align: center;
border-radius: 16px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
font-family: 'Segoe UI', sans-serif;
}
.meal-container h1 {
.meal-header {
display: flex;
flex-direction: row;
gap: 1.5rem;
align-items: center;
}
.meal-meta {
flex: 1;
}
.meal-title {
font-size: 2.5rem;
font-weight: bold;
margin-bottom: 1rem;
color: #333;
}
.meal-container .meal-image {
width: 100%;
max-height: 400px;
.meal-image {
width: 250px;
height: 250px;
object-fit: cover;
border-radius: 10px;
margin-bottom: 1rem;
border-radius: 12px;
border: 1px solid #ccc;
}
.meal-container .description {
font-style: italic;
.description {
font-size: 1.1rem;
color: #555;
margin-top: 0.5rem;
margin-bottom: 1.5rem;
}
.meal-container .buttons {
margin-bottom: 2rem;
.meal-details p {
font-size: 1rem;
color: #333;
margin: 0.3rem 0;
}
.meal-container .buttons button, .meal-container .buttons .edit-button {
.label {
font-weight: bold;
color: #6a0dad;
}
.form-group {
margin-bottom: 1rem;
}
.form-group label {
font-weight: 600;
margin-bottom: 0.3rem;
display: block;
}
.form-control {
width: 100%;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 1rem;
}
.buttons {
margin-top: 1.5rem;
}
.btn,
.btn-outline {
background-color: #6a0dad;
color: white;
border: none;
padding: 0.7rem 1.5rem;
border-radius: 5px;
margin: 0.5rem;
text-decoration: none;
padding: 0.6rem 1.4rem;
border-radius: 6px;
margin: 0.4rem;
cursor: pointer;
font-size: 1rem;
text-decoration: none;
}
.meal-container .buttons .edit-button {
display: inline-block;
.btn-outline {
background-color: transparent;
border: 2px solid #6a0dad;
color: #6a0dad;
}
.meal-container a {
.recipe-link {
color: #6a0dad;
text-decoration: underline;
font-weight: 500;
}
.placeholder {
color: #999;
font-style: italic;
}
</style>

View File

@@ -0,0 +1,8 @@
@model dynamic
<div class="tooltip-content">
<strong>@Model.Name</strong><br />
<em>@Model.ShortDescription</em><br />
@if (Model.Ingredients != null) {
<div><u>Ingredienser:</u> @String.Join(", ", Model.Ingredients)</div>
}
</div>

View File

@@ -208,3 +208,7 @@
.new-meal-form button:hover {
background-color: #F57C00;
}
.meal-hover {
cursor: pointer;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -1,99 +1,84 @@
angular.module('mealMenuApp', [])
.controller('MealMenuController', function ($scope, $http) {
$scope.isEditing = false;
$scope.toggleEditMode = function () {
$scope.isEditing = !$scope.isEditing;
};
angular.module('mealMenuApp', ['ngSanitize'])
.controller('MealMenuController', function ($scope, $http, $sce) {
console.log("Controller initierad");
$scope.viewMode = 'list';
$scope.tooltip = {};
$scope.meals = [];
$scope.menu = {};
$scope.showNewMealForm = false;
$scope.newMeal = {};
$scope.selectedMealDay = null;
$scope.selectedMealType = null;
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 () {
$http.get('/api/mealMenuApi/getMeals')
.then(response => {
$scope.meals = response.data;
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;
return res;
})
.catch(error => console.error("Error fetching meals:", error));
.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', {
params: { weekNumber: $scope.selectedWeek, year: $scope.selectedYear }
}).then(response => {
console.log("Veckomenydata:", response.data); // ✅ logga ut
}).then(res => {
console.log("Menyposter hämtade:", res.data);
$scope.menu = {};
response.data.forEach(item => {
const dayOfWeek = $scope.daysOfWeek[item.dayOfWeek - 1];
if (!$scope.menu[dayOfWeek]) $scope.menu[dayOfWeek] = {};
if (item.breakfastMealId) {
$scope.menu[dayOfWeek].breakfastMealId = item.breakfastMealId;
$scope.menu[dayOfWeek].breakfastMealName = getMealNameById(item.breakfastMealId);
}
if (item.lunchMealId) {
$scope.menu[dayOfWeek].lunchMealId = item.lunchMealId;
$scope.menu[dayOfWeek].lunchMealName = getMealNameById(item.lunchMealId);
}
if (item.dinnerMealId) {
$scope.menu[dayOfWeek].dinnerMealId = item.dinnerMealId;
$scope.menu[dayOfWeek].dinnerMealName = getMealNameById(item.dinnerMealId);
res.data.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?.ImageUrl) {
$scope.menu[day].imageUrl = m.ImageUrl;
}
}
});
});
}).catch(error => console.error("Error fetching weekly menu:", error));
console.log("Byggd meny:", $scope.menu);
}).catch(err => console.error("Fel vid hämtning av veckomeny:", err));
};
function getMealNameById(mealId) {
const meal = $scope.meals.find(m => m.id === mealId);
return meal ? meal.name : "Unknown Meal";
}
$scope.handleMealSelection = function (day, mealType) {
if ($scope.menu[day][mealType + "MealId"] === "new") {
$scope.showNewMealForm = true;
$scope.newMeal = { name: "", description: "", proteinType: "", carbType: "", recipeUrl: "" };
$scope.selectedMealDay = day;
$scope.selectedMealType = mealType;
}
$scope.openMeal = function (mealId) {
if (!mealId) return;
window.open('/Meal/View/' + mealId, '_blank');
};
$scope.saveNewMeal = function () {
if (!$scope.newMeal.name) {
alert("Meal name is required");
return;
}
$http.post('/api/mealMenuApi/addMeal', $scope.newMeal)
.then(response => {
const addedMeal = response.data;
$scope.meals.push(addedMeal);
$scope.menu[$scope.selectedMealDay][$scope.selectedMealType + "MealId"] = addedMeal.id;
$scope.showNewMealForm = false;
$scope.newMeal = {}; // Reset new meal data after save
})
.catch(error => console.error("Error saving new meal:", error));
$scope.getDayImage = function (day) {
const item = $scope.menu[day];
return (item && item.imageUrl) || '/images/default-meal.jpg';
};
$scope.cancelNewMeal = function () {
$scope.showNewMealForm = false;
$scope.newMeal = {}; // Reset new meal data when canceled
$scope.getMealIdByDay = function (day) {
const item = $scope.menu[day] || {};
return item.dinnerMealId || item.lunchMealId || item.breakfastMealId || null;
};
function getWeek(date) {
const day = date.getDay() || 7;
date.setDate(date.getDate() + 4 - day);
const yearStart = new Date(date.getFullYear(), 0, 1);
return Math.ceil(((date - yearStart) / 86400000 + 1) / 7);
}
$scope.goToPreviousWeek = function () {
if ($scope.selectedWeek === 1) {
$scope.selectedYear--;
@@ -114,6 +99,17 @@
$scope.loadMenu();
};
$scope.loadMeals();
$scope.loadMenu();
function getWeek(d) {
d = new Date(d.getFullYear(), d.getMonth(), d.getDate());
const dayNum = d.getDay() || 7;
d.setDate(d.getDate() + 4 - dayNum);
const yearStart = new Date(d.getFullYear(), 0, 1);
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
}
console.log("Initierar måltidsladdning...");
$scope.loadMeals().then(() => {
console.log("Laddar meny efter måltider...");
$scope.loadMenu();
});
});