Bunch of fixes and session extensions
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Elias Jansson
2025-06-10 18:59:04 +02:00
parent e3eb2dc7cb
commit 465f9afc99
10 changed files with 106 additions and 51 deletions

View File

@@ -11,10 +11,6 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{423CD237-7404-4355-868F-CE5861835258}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{423CD237-7404-4355-868F-CE5861835258}.Debug|Any CPU.Build.0 = Debug|Any CPU
{423CD237-7404-4355-868F-CE5861835258}.Release|Any CPU.ActiveCfg = Release|Any CPU
{423CD237-7404-4355-868F-CE5861835258}.Release|Any CPU.Build.0 = Release|Any CPU
{F5586986-B726-4E05-B31B-2E24CA5B2B89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F5586986-B726-4E05-B31B-2E24CA5B2B89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5586986-B726-4E05-B31B-2E24CA5B2B89}.Debug|Any CPU.Build.0 = Debug|Any CPU {F5586986-B726-4E05-B31B-2E24CA5B2B89}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5586986-B726-4E05-B31B-2E24CA5B2B89}.Release|Any CPU.ActiveCfg = Release|Any CPU {F5586986-B726-4E05-B31B-2E24CA5B2B89}.Release|Any CPU.ActiveCfg = Release|Any CPU

View File

@@ -99,7 +99,7 @@ namespace Aberwyn.Areas.Identity.Pages.Account
{ {
// This doesn't count login failures towards account lockout // This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true // To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(Input.UserName, Input.Password, Input.RememberMe, lockoutOnFailure: false); var result = await _signInManager.PasswordSignInAsync(Input.UserName, Input.Password, isPersistent: true, lockoutOnFailure: false);
if (result.Succeeded) if (result.Succeeded)
{ {
_logger.LogInformation("User logged in."); _logger.LogInformation("User logged in.");

View File

@@ -20,21 +20,33 @@ namespace Aberwyn.Controllers
} }
[HttpGet] [HttpGet]
public IActionResult View(int id, bool edit = false) public IActionResult View(int? id, bool edit = false)
{ {
var service = _menuService; Meal meal;
var meal = service.GetMealById(id);
ViewData["IsEditing"] = edit; // → här if (id.HasValue)
if (meal == null)
{ {
return NotFound(); meal = _menuService.GetMealById(id.Value);
if (meal == null)
return NotFound();
}
else
{
meal = new Meal
{
Name = "",
Description = "",
Ingredients = new List<Ingredient>(),
IsAvailable = true,
CreatedAt = DateTime.Now
};
} }
ViewData["IsEditing"] = edit;
return View("View", meal); return View("View", meal);
} }
[HttpGet] [HttpGet]
[Route("Meal/Tooltip/{id}")] [Route("Meal/Tooltip/{id}")]
public IActionResult Tooltip(int id) public IActionResult Tooltip(int id)

View File

@@ -77,9 +77,7 @@ public class MenuService
} }
public void SaveMeal2(Meal meal)
public void SaveMeal(Meal meal)
{ {
if (string.IsNullOrWhiteSpace(meal?.Name)) return; if (string.IsNullOrWhiteSpace(meal?.Name)) return;
@@ -104,7 +102,38 @@ public class MenuService
_context.SaveChanges(); _context.SaveChanges();
} }
public int GenerateMissingThumbnails() public void SaveMeal(Meal meal)
{
if (string.IsNullOrWhiteSpace(meal?.Name)) return;
meal.Name = meal.Name.Trim();
meal.CreatedAt = meal.CreatedAt == default ? DateTime.Now : meal.CreatedAt;
if (meal.Id == 0)
{
// Ny måltid
_context.Meals.Add(meal);
}
else
{
// Uppdatera existerande utan tracking-krockar
var existing = _context.Meals
.Include(m => m.Ingredients)
.FirstOrDefault(m => m.Id == meal.Id);
if (existing != null)
{
_context.Entry(existing).CurrentValues.SetValues(meal);
// OBS: Ingredienser hanteras separat
}
}
_context.SaveChanges();
}
public int GenerateMissingThumbnails()
{ {
var updatedCount = 0; var updatedCount = 0;
var meals = _context.Meals var meals = _context.Meals
@@ -173,10 +202,17 @@ public int GenerateMissingThumbnails()
{ {
var existing = _context.Ingredients.Where(i => i.MealId == mealId); var existing = _context.Ingredients.Where(i => i.MealId == mealId);
_context.Ingredients.RemoveRange(existing); _context.Ingredients.RemoveRange(existing);
foreach (var ing in ingredients)
{
ing.MealId = mealId;
}
_context.Ingredients.AddRange(ingredients); _context.Ingredients.AddRange(ingredients);
_context.SaveChanges(); _context.SaveChanges();
} }
public List<Meal> GetMeals() public List<Meal> GetMeals()
{ {
return _context.Meals.ToList(); return _context.Meals.ToList();
@@ -216,6 +252,8 @@ public int GenerateMissingThumbnails()
public void SaveOrUpdateMealWithIngredients(Meal meal) public void SaveOrUpdateMealWithIngredients(Meal meal)
{ {
var isNew = meal.Id == 0;
SaveMeal(meal); SaveMeal(meal);
if (meal.Ingredients != null && meal.Ingredients.Count > 0) if (meal.Ingredients != null && meal.Ingredients.Count > 0)
@@ -224,6 +262,7 @@ public int GenerateMissingThumbnails()
} }
} }
public List<Meal> GetMealsByCategory(string category) public List<Meal> GetMealsByCategory(string category)
{ {
return _context.Meals return _context.Meals

View File

@@ -149,6 +149,9 @@ builder.Services.Configure<VapidOptions>(builder.Configuration.GetSection("Vapid
builder.Services.ConfigureApplicationCookie(options => builder.Services.ConfigureApplicationCookie(options =>
{ {
options.LoginPath = "/Identity/Account/Login"; options.LoginPath = "/Identity/Account/Login";
options.ExpireTimeSpan = TimeSpan.FromDays(30);
options.SlidingExpiration = true;
options.Cookie.IsEssential = true;
}); });
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
@@ -244,6 +247,9 @@ if (setup.IsConfigured)
await IdentityDataInitializer.SeedData(services, setup); await IdentityDataInitializer.SeedData(services, setup);
} }
} }
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage(); // Detta ger stacktraces i browsern
}
app.Run(); app.Run();

View File

@@ -143,8 +143,9 @@
</aside> </aside>
<datalist id="meals-list"> <datalist id="meals-list">
@foreach (var m in ViewBag.AvailableMeals as List<Aberwyn.Models.Meal>) { @foreach (var meal in (List<Meal>)ViewBag.AvailableMeals)
<option value="@m.Name" /> {
<option value="@meal.Id">@meal.Name</option>
} }
</datalist> </datalist>
</form> </form>

View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="sv">
<head>
<meta charset="UTF-8">
<title>Offline</title>
<style>
body { font-family: sans-serif; text-align: center; padding: 2rem; background: #1F2C3C; color: white; }
</style>
</head>
<body>
<h1>🌐 Du är offline</h1>
<p>LEWEL är inte tillgänglig just nu. Kontrollera din internetanslutning och försök igen.</p>
</body>
</html>

View File

@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Aberwyn.Views.Home
{
public class OfflineModel : PageModel
{
public void OnGet()
{
}
}
}

View File

@@ -64,6 +64,10 @@
<label for="CarbType">Kolhydrat</label> <label for="CarbType">Kolhydrat</label>
<input type="text" name="CarbType" value="@Model.CarbType" class="form-control" /> <input type="text" name="CarbType" value="@Model.CarbType" class="form-control" />
</div> </div>
<div class="form-group">
<label for="Category">Kategori</label>
<input type="text" name="Category" value="@Model.Category" class="form-control" />
</div>
<div class="form-group"> <div class="form-group">
<label for="RecipeUrl">Receptlänk</label> <label for="RecipeUrl">Receptlänk</label>
@@ -79,7 +83,7 @@
<div class="form-group"> <div class="form-group">
<label>Ingredienser</label> <label>Ingredienser</label>
<div id="ingredients-list"> <div id="ingredients-list">
@for (int i = 0; i < Model.Ingredients.Count; i++) @for (int i = 0; i < (Model.Ingredients?.Count ?? 0); i++)
{ {
<div class="ingredient-row"> <div class="ingredient-row">
<input type="text" name="Ingredients[@i].Quantity" placeholder="Mängd" value="@Model.Ingredients[i].Quantity" class="form-control ingredient-qty" /> <input type="text" name="Ingredients[@i].Quantity" placeholder="Mängd" value="@Model.Ingredients[i].Quantity" class="form-control ingredient-qty" />
@@ -215,7 +219,7 @@
closeImageModal(); closeImageModal();
} }
}); });
$(document).ready(function () { $(document).ready(function () {
if ($('#Instructions').length > 0) { if ($('#Instructions').length > 0) {
$('#Instructions').trumbowyg({ $('#Instructions').trumbowyg({
autogrow: true, autogrow: true,
@@ -223,35 +227,6 @@
}); });
} }
}); });
if ('serviceWorker' in navigator && 'PushManager' in window) {
navigator.serviceWorker.register('/service-worker.js')
.then(function (registration) {
console.log('Service Worker registered', registration);
return registration.pushManager.getSubscription()
.then(async function (subscription) {
if (subscription) {
console.log('Already subscribed to push notifications.');
return subscription;
}
const response = await fetch('/api/push/vapid-public-key');
const vapidPublicKey = await response.text();
const convertedVapidKey = urlBase64ToUint8Array(vapidPublicKey);
return registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: convertedVapidKey
});
});
}).then(function (subscription) {
return fetch('/api/push/subscribe', {
method: 'POST',
body: JSON.stringify(subscription),
headers: {
'Content-Type': 'application/json'
}
});
});
}
</script> </script>

View File

@@ -4,7 +4,7 @@
"start_url": "/", "start_url": "/",
"display": "standalone", "display": "standalone",
"background_color": "#1F2C3C", "background_color": "#1F2C3C",
"theme_color": "#6a0dad", "theme_color": "#1F2C3C",
"icons": [ "icons": [
{ {
"src": "/images/lewel-icon.png", "src": "/images/lewel-icon.png",