From addef3a3ad88aae2e2f004c1d942b7158371c007 Mon Sep 17 00:00:00 2001 From: Elias Jansson Date: Thu, 22 May 2025 19:19:51 +0200 Subject: [PATCH] Menu and meal upgrades, fixed base64 images --- Aberwyn/Controllers/MealController.cs | 26 +- Aberwyn/Controllers/MealMenuApiController.cs | 6 +- Aberwyn/Data/MenuService.cs | 47 ++- ...0250522074358_AddMealImageData.Designer.cs | 380 ++++++++++++++++++ .../20250522074358_AddMealImageData.cs | 19 + Aberwyn/Models/MenuViewModel.cs | 25 ++ Aberwyn/Views/Meal/Meal.cshtml | 153 +++++-- Aberwyn/Views/Meal/View.cshtml | 97 ++++- ...e_2919e406-aa1c-4ff1-aa3b-562c41538241.png | Bin 0 -> 1353969 bytes ...e_8ed11611-689b-432d-b5ab-a45d08b4f08d.png | Bin 0 -> 1073167 bytes ...e_b2c768d0-78a6-41bf-a729-f53320ff9340.png | Bin 0 -> 1073167 bytes Aberwyn/wwwroot/js/menu.js | 37 +- 12 files changed, 701 insertions(+), 89 deletions(-) create mode 100644 Aberwyn/Migrations/20250522074358_AddMealImageData.Designer.cs create mode 100644 Aberwyn/Migrations/20250522074358_AddMealImageData.cs create mode 100644 Aberwyn/wwwroot/images/meals/image_2919e406-aa1c-4ff1-aa3b-562c41538241.png create mode 100644 Aberwyn/wwwroot/images/meals/image_8ed11611-689b-432d-b5ab-a45d08b4f08d.png create mode 100644 Aberwyn/wwwroot/images/meals/image_b2c768d0-78a6-41bf-a729-f53320ff9340.png diff --git a/Aberwyn/Controllers/MealController.cs b/Aberwyn/Controllers/MealController.cs index d21051c..12b5649 100644 --- a/Aberwyn/Controllers/MealController.cs +++ b/Aberwyn/Controllers/MealController.cs @@ -81,33 +81,35 @@ namespace Aberwyn.Controllers } [HttpPost] - public IActionResult SaveMeal(Meal meal, IFormFile ImageFile) + public IActionResult SaveMeal(Meal meal, IFormFile ImageFile, string ExistingImageUrl) { var service = new MenuService(_configuration, _env); if (ImageFile != null && ImageFile.Length > 0) { - 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 stream = new FileStream(imagePath, FileMode.Create)) + using var ms = new MemoryStream(); + ImageFile.CopyTo(ms); + meal.ImageData = ms.ToArray(); + meal.ImageMimeType = ImageFile.ContentType; + } + else + { + // ✅ Hämta tidigare måltid och kopiera bilddata + var existingMeal = service.GetMealById(meal.Id); + if (existingMeal != null) { - ImageFile.CopyTo(stream); + meal.ImageData = existingMeal.ImageData; + meal.ImageMimeType = existingMeal.ImageMimeType; } - - // Spara relativ sökväg för visning - meal.ImageUrl = $"/images/meals/{uniqueName}"; } service.SaveOrUpdateMeal(meal); - return RedirectToAction("View", new { id = meal.Id }); } + [HttpPost] public IActionResult DeleteMeal(int id) { diff --git a/Aberwyn/Controllers/MealMenuApiController.cs b/Aberwyn/Controllers/MealMenuApiController.cs index 6fd5a72..20bf044 100644 --- a/Aberwyn/Controllers/MealMenuApiController.cs +++ b/Aberwyn/Controllers/MealMenuApiController.cs @@ -28,10 +28,12 @@ namespace Aberwyn.Controllers [HttpGet("getMeals")] public IActionResult GetMeals() { - var meals = _menuService.GetMeals(); - return Ok(meals ?? new List()); + var meals = _menuService.GetMealsDetailed(); // Hämtar med ImageData + var mealDtos = meals.Select(MealDto.FromMeal).ToList(); + return Ok(mealDtos); } + [HttpPut("menu")] public IActionResult SaveMenu([FromBody] MenuViewModel weeklyMenu) { diff --git a/Aberwyn/Data/MenuService.cs b/Aberwyn/Data/MenuService.cs index c22bf2d..7a559be 100644 --- a/Aberwyn/Data/MenuService.cs +++ b/Aberwyn/Data/MenuService.cs @@ -141,7 +141,7 @@ namespace Aberwyn.Data using (var connection = GetConnection()) { connection.Open(); - string query = "SELECT Id, Name, ImageUrl FROM Meals"; + string query = "SELECT Id, Name, ImageUrl, ImageData, ImageMimeType FROM Meals"; using (var cmd = new MySqlCommand(query, connection)) { using (var reader = cmd.ExecuteReader()) @@ -152,6 +152,8 @@ namespace Aberwyn.Data { Id = reader.GetInt32("Id"), Name = reader.GetString("Name"), + ImageData = reader.IsDBNull(reader.GetOrdinal("ImageData")) ? null : (byte[])reader["ImageData"], + ImageMimeType = reader.IsDBNull(reader.GetOrdinal("ImageMimeType")) ? null : reader.GetString(reader.GetOrdinal("ImageMimeType")), ImageUrl = reader.IsDBNull(reader.GetOrdinal("ImageUrl")) ? null : reader.GetString(reader.GetOrdinal("ImageUrl")) }); } @@ -168,7 +170,7 @@ namespace Aberwyn.Data { connection.Open(); string query = @" - SELECT Id, Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt, ImageUrl + SELECT Id, Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt, ImageUrl, ImageData, ImageMimeType FROM Meals ORDER BY CreatedAt DESC"; @@ -186,7 +188,10 @@ namespace Aberwyn.Data 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")) + CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt")), + ImageData = reader.IsDBNull(reader.GetOrdinal("ImageData")) ? null : (byte[])reader["ImageData"], + ImageMimeType = reader.IsDBNull(reader.GetOrdinal("ImageMimeType")) ? null : reader.GetString(reader.GetOrdinal("ImageMimeType")) + }); } } @@ -200,8 +205,7 @@ namespace Aberwyn.Data using var connection = GetConnection(); connection.Open(); - string query = @"SELECT Id, Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt, ImageUrl - FROM Meals WHERE Id = @id"; + string query = @"SELECT Id, Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt, ImageUrl, ImageData, ImageMimeType FROM Meals WHERE Id = @id"; using var cmd = new MySqlCommand(query, connection); cmd.Parameters.AddWithValue("@id", id); @@ -218,7 +222,11 @@ namespace Aberwyn.Data 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")) + CreatedAt = reader.GetDateTime(reader.GetOrdinal("CreatedAt")), + ImageData = reader.IsDBNull(reader.GetOrdinal("ImageData")) ? null : (byte[])reader["ImageData"], + ImageMimeType = reader.IsDBNull(reader.GetOrdinal("ImageMimeType")) ? null : reader.GetString(reader.GetOrdinal("ImageMimeType")) + + }; } @@ -244,16 +252,25 @@ namespace Aberwyn.Data using var cmd = conn.CreateCommand(); if (meal.Id == 0) { - cmd.CommandText = @"INSERT INTO Meals - (Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt, ImageUrl) - VALUES (@Name, @Description, @ProteinType, @CarbType, @RecipeUrl, @CreatedAt, @ImageUrl); + cmd.CommandText = @" + INSERT INTO Meals + (Name, Description, ProteinType, CarbType, RecipeUrl, CreatedAt, ImageUrl, ImageData, ImageMimeType) + VALUES + (@Name, @Description, @ProteinType, @CarbType, @RecipeUrl, @CreatedAt, @ImageUrl, @ImageData, @ImageMimeType); SELECT LAST_INSERT_ID();"; } else { - cmd.CommandText = @"UPDATE Meals - SET Name = @Name, Description = @Description, ProteinType = @ProteinType, - CarbType = @CarbType, RecipeUrl = @RecipeUrl, ImageUrl = @ImageUrl + cmd.CommandText = @" + UPDATE Meals + SET Name = @Name, + Description = @Description, + ProteinType = @ProteinType, + CarbType = @CarbType, + RecipeUrl = @RecipeUrl, + ImageUrl = @ImageUrl, + ImageData = @ImageData, + ImageMimeType = @ImageMimeType WHERE Id = @Id"; cmd.Parameters.AddWithValue("@Id", meal.Id); } @@ -266,10 +283,14 @@ namespace Aberwyn.Data cmd.Parameters.AddWithValue("@CreatedAt", meal.CreatedAt == default ? DateTime.Now : meal.CreatedAt); cmd.Parameters.AddWithValue("@ImageUrl", meal.ImageUrl ?? ""); + // ✨ Här är nyckeln + cmd.Parameters.AddWithValue("@ImageData", (object?)meal.ImageData ?? DBNull.Value); + cmd.Parameters.AddWithValue("@ImageMimeType", meal.ImageMimeType ?? (object)DBNull.Value); + if (meal.Id == 0) { var insertedId = Convert.ToInt32(cmd.ExecuteScalar()); - meal.Id = insertedId; // ← Sätt ID direkt på objektet + meal.Id = insertedId; } else { diff --git a/Aberwyn/Migrations/20250522074358_AddMealImageData.Designer.cs b/Aberwyn/Migrations/20250522074358_AddMealImageData.Designer.cs new file mode 100644 index 0000000..d8ec42a --- /dev/null +++ b/Aberwyn/Migrations/20250522074358_AddMealImageData.Designer.cs @@ -0,0 +1,380 @@ +// +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("20250522074358_AddMealImageData")] + partial class AddMealImageData + { + 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("Id") + .HasColumnType("varchar(255)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("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.BudgetCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("BudgetPeriodId") + .HasColumnType("int"); + + b.Property("Color") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Order") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("BudgetPeriodId"); + + b.ToTable("BudgetCategories"); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("BudgetCategoryId") + .HasColumnType("int"); + + b.Property("IncludeInSummary") + .HasColumnType("tinyint(1)"); + + b.Property("IsExpense") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Order") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("BudgetCategoryId"); + + b.ToTable("BudgetItems"); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetPeriod", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Month") + .HasColumnType("int"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("BudgetPeriods"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("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", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("RoleId") + .HasColumnType("varchar(255)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b => + { + b.HasOne("Aberwyn.Models.BudgetPeriod", "BudgetPeriod") + .WithMany("Categories") + .HasForeignKey("BudgetPeriodId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BudgetPeriod"); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetItem", b => + { + b.HasOne("Aberwyn.Models.BudgetCategory", "BudgetCategory") + .WithMany("Items") + .HasForeignKey("BudgetCategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BudgetCategory"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Aberwyn.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Aberwyn.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", 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", b => + { + b.HasOne("Aberwyn.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Aberwyn.Models.BudgetPeriod", b => + { + b.Navigation("Categories"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Aberwyn/Migrations/20250522074358_AddMealImageData.cs b/Aberwyn/Migrations/20250522074358_AddMealImageData.cs new file mode 100644 index 0000000..c7c0fb2 --- /dev/null +++ b/Aberwyn/Migrations/20250522074358_AddMealImageData.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Aberwyn.Migrations +{ + public partial class AddMealImageData : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Aberwyn/Models/MenuViewModel.cs b/Aberwyn/Models/MenuViewModel.cs index dba83cd..311c01a 100644 --- a/Aberwyn/Models/MenuViewModel.cs +++ b/Aberwyn/Models/MenuViewModel.cs @@ -44,6 +44,31 @@ namespace Aberwyn.Models public string RecipeUrl { get; set; } public string ImageUrl { get; set; } public DateTime CreatedAt { get; set; } + public byte[] ImageData { get; set; } + public string ImageMimeType { get; set; } // t.ex. "image/jpeg" + } + + public class MealDto + { + public int Id { get; set; } + public string Name { get; set; } + public string? ImageUrl { get; set; } + public string? ImageData { get; set; } // base64 + public string? ImageMimeType { get; set; } + + public static MealDto FromMeal(Meal meal) + { + return new MealDto + { + Id = meal.Id, + Name = meal.Name, + ImageUrl = meal.ImageUrl, + ImageMimeType = meal.ImageMimeType, + ImageData = meal.ImageData != null ? Convert.ToBase64String(meal.ImageData) : null + }; + } + } + } diff --git a/Aberwyn/Views/Meal/Meal.cshtml b/Aberwyn/Views/Meal/Meal.cshtml index e9560f1..94b6601 100644 --- a/Aberwyn/Views/Meal/Meal.cshtml +++ b/Aberwyn/Views/Meal/Meal.cshtml @@ -7,45 +7,59 @@

@(isNew ? "Skapa ny måltid" : Model.Name)

-
- @if (!isNew) - { - - } + + @if (!isNew) + { + + } -
- - +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ Förhandsvisning + Klicka här för att klistra in eller dra in en bild +
+ +
+ + + +
+ + + + + diff --git a/Aberwyn/Views/Meal/View.cshtml b/Aberwyn/Views/Meal/View.cshtml index f97fb44..d9b3e87 100644 --- a/Aberwyn/Views/Meal/View.cshtml +++ b/Aberwyn/Views/Meal/View.cshtml @@ -2,12 +2,26 @@ @{ ViewData["Title"] = Model.Name; bool isEditing = (bool)(ViewData["IsEditing"] ?? false); - var imageUrl = string.IsNullOrEmpty(Model.ImageUrl) ? "/images/placeholder-meal.jpg" : Model.ImageUrl; + string imageSrc; + if (Model.ImageData != null && Model.ImageData.Length > 0) + { + var base64 = Convert.ToBase64String(Model.ImageData); + var mime = string.IsNullOrEmpty(Model.ImageMimeType) ? "image/jpeg" : Model.ImageMimeType; + imageSrc = $"data:{mime};base64,{base64}"; + } + else + { + imageSrc = "/images/placeholder-meal.jpg"; + } + }
- @Model.Name +
+ @Model.Name +
+

@Model.Name

@Model.Description

@@ -18,11 +32,9 @@ {
+ -
- - -
+
@@ -91,14 +103,87 @@
+ + +