.
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
elias
2025-06-11 10:33:01 +02:00
parent 3b112af9d3
commit 130d791e1b
7 changed files with 218 additions and 13 deletions

View File

@@ -18,7 +18,11 @@ namespace Aberwyn.Controllers
_configuration = configuration;
_env = env;
}
[HttpGet("/meal")]
public IActionResult Index()
{
return View("Index");
}
[HttpGet]
public IActionResult View(int id, bool edit = false)
{

View File

@@ -30,11 +30,12 @@ namespace Aberwyn.Controllers
[HttpGet("getMeals")]
public IActionResult GetMeals()
{
var meals = _menuService.GetMealsDetailed(); // Hämtar med ImageData
var mealDtos = meals.Select(MealDto.FromMeal).ToList();
var meals = _menuService.GetMealsSlim(true);
var mealDtos = meals.Select(m => MealDto.FromMeal(m, includeThumbnail: true)).ToList(); // 👈 fix
return Ok(mealDtos);
}
[HttpGet("getWeeklyMenu")]
public IActionResult GetWeeklyMenu(int weekNumber, int year)
{

View File

@@ -136,9 +136,35 @@ public int GenerateMissingThumbnails()
_context.SaveChanges();
return updatedCount;
}
public List<WeeklyMenu> GetAllWeeklyMenus()
public List<Meal> GetMealsSlim(bool includeThumbnail = false)
{
if (includeThumbnail)
{
return _context.Meals
.Select(m => new Meal
{
Id = m.Id,
Name = m.Name,
Description = m.Description,
ThumbnailData = m.ThumbnailData
})
.ToList();
}
else
{
return _context.Meals
.Select(m => new Meal
{
Id = m.Id,
Name = m.Name,
Description = m.Description
})
.ToList();
}
}
public List<WeeklyMenu> GetAllWeeklyMenus()
{
var menus = _context.WeeklyMenus.ToList();
var allMeals = _context.Meals.ToDictionary(m => m.Id, m => m.Name);

View File

@@ -104,20 +104,39 @@ public class Meal
public string? ImageData { get; set; } // base64
public string? ImageMimeType { get; set; }
public static MealDto FromMeal(Meal meal)
public static MealListDto FromMeal(Meal meal, bool includeThumbnail = false)
{
return new MealDto
return new MealListDto
{
Id = meal.Id,
Name = meal.Name,
Category = meal.Category,
IsAvailable = meal.IsAvailable,
ImageUrl = meal.ImageUrl,
ImageMimeType = meal.ImageMimeType,
ImageData = meal.ImageData != null ? Convert.ToBase64String(meal.ImageData) : null
Description = meal.Description,
ThumbnailData = includeThumbnail && meal.ThumbnailData != null
? Convert.ToBase64String(meal.ThumbnailData)
: null
};
}
}
}
public class MealListDto
{
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)
{
return new MealListDto
{
Id = meal.Id,
Name = meal.Name,
Description = meal.Description,
ThumbnailData = includeThumbnail && meal.ThumbnailData != null
? Convert.ToBase64String(meal.ThumbnailData)
: null
};
}
}
}

View File

@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="sv" ng-app="mealGalleryApp">
<head>
<meta charset="utf-8">
<title>Måltider</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
<link rel="stylesheet" href="/css/meal-gallery.css">
</head>
<body ng-controller="MealGalleryController">
<div class="meal-gallery-container" ng-controller="MealGalleryController">
<div class="meal-gallery-header">
<h1>Recept</h1>
<div class="search-container">
<input type="text" ng-model="search" placeholder="Sök recept...">
<i class="fa-solid fa-magnifying-glass"></i>
</div>
</div>
<div class="meal-gallery-grid">
<div class="meal-card" ng-repeat="meal in meals | filter:search">
<img ng-src="{{ meal.ThumbnailData ? 'data:image/webp;base64,' + meal.ThumbnailData : '/images/fallback.jpg' }}"
alt="{{ meal.Name }}">
<div class="meal-card-content">
<h3>{{ meal.Name }}</h3>
<p ng-if="meal.Description">{{ meal.Description }}</p>
<a class="btn-readmore" ng-href="/meal/view/{{ meal.Id }}">Läs mer</a>
</div>
</div>
</div>
</div>
</body>
<script>
angular.module("mealGalleryApp", []).controller("MealGalleryController", function ($scope, $http) {
$scope.meals = [];
$scope.search = "";
$http.get("/api/mealMenuApi/getMeals").then(res => {
$scope.meals = res.data;
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,107 @@
body {
background-color: #f7f7f7;
font-family: 'Segoe UI', sans-serif;
color: #222;
margin: 0;
}
.meal-gallery-container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem 1rem;
}
.meal-gallery-header {
text-align: center;
margin-bottom: 2rem;
}
.meal-gallery-header h1 {
font-size: 2.4rem;
font-weight: 600;
}
.search-container {
margin-top: 1rem;
position: relative;
max-width: 400px;
margin-inline: auto;
}
.search-container input {
width: 100%;
padding: 10px 38px 10px 12px;
border-radius: 25px;
border: 1px solid #ccc;
font-size: 1rem;
}
.search-container i {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
color: #888;
}
.meal-gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 24px;
}
.meal-card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
transition: transform 0.2s ease, box-shadow 0.2s ease;
display: flex;
flex-direction: column;
}
.meal-card:hover {
transform: scale(1.015);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.meal-card img {
width: 100%;
height: 200px;
object-fit: cover;
}
.meal-card-content {
padding: 16px;
display: flex;
flex-direction: column;
flex-grow: 1;
}
.meal-card-content h3 {
margin: 0 0 8px;
font-size: 1.2rem;
color: #111;
}
.meal-card-content p {
flex-grow: 1;
font-size: 0.95rem;
color: #555;
}
.btn-readmore {
align-self: flex-start;
background: #007d36;
color: white;
padding: 8px 12px;
border-radius: 6px;
text-decoration: none;
font-size: 0.95rem;
margin-top: 12px;
}
.btn-readmore:hover {
background: #005c27;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB