Files
Aberwyn/Aberwyn/Views/Meal/View.cshtml
Elias Jansson 8ebbb803e8
All checks were successful
continuous-integration/drone/push Build is passing
Meal rating and some fixes to meals
2025-07-07 15:22:12 +02:00

353 lines
14 KiB
Plaintext

@model Aberwyn.Models.Meal
@using Microsoft.AspNetCore.Authorization
@inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor
@{
ViewData["Title"] = Model.Name;
bool isEditing = (bool)(ViewData["IsEditing"] ?? false);
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/fallback.jpg";
}
bool isChef = HttpContextAccessor.HttpContext.User.IsInRole("Chef");
isEditing = isEditing && isChef;
}
<link rel="stylesheet" href="/css/meal.css">
<div class="meal-container">
<div class="meal-header">
<div class="meal-image-wrapper clickable" title="Bild på rätten">
<img id="imagePreview" src="@imageSrc" alt="@Model.Name" class="meal-image" />
</div>
@if (isEditing)
{
<div style="margin-top: 0.5rem;">
<button type="button" class="btn-outline" onclick="openImageModal()">Byt bild</button>
</div>
}
<div class="meal-meta">
<h1 class="meal-title">@Model.Name</h1>
<p class="description">@Model.Description</p>
</div>
</div>
@if (isEditing)
{
<form asp-action="SaveMeal" method="post" enctype="multipart/form-data">
<input type="hidden" name="Id" value="@Model.Id" />
<input type="file" id="ImageFile" name="ImageFile" accept="image/*" style="display: none;" />
<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="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" />
</div>
<div id="recipe-section">
<h2>Så här gör du</h2>
<div class="form-group">
<label for="Instructions">Tillagningsinstruktioner</label>
<textarea id="Instructions" name="Instructions">@Html.Raw(Model.Instructions)</textarea>
</div>
<div class="form-group">
<label>Ingredienser</label>
<div id="ingredients-list">
@for (int i = 0; i < (Model.Ingredients?.Count ?? 0); i++)
{
<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].Item" placeholder="Ingrediens" value="@Model.Ingredients[i].Item" class="form-control ingredient-item" />
</div>
}
</div>
<div style="margin-top: 0.5rem;">
<label for="bulkIngredients">Klistra in flera ingredienser</label>
<textarea id="bulkIngredients" placeholder="1 dl mjölk&#10;2 tsk socker" class="form-control" rows="3"></textarea>
<button type="button" class="btn-outline" onclick="parseBulkIngredients()">Lägg till från lista</button>
<button type="button" class="btn-outline" onclick="addIngredientRow()">+ Lägg till rad</button>
</div>
</div>
</div>
<div class="form-group">
<label>
<input type="checkbox" name="IsPublished" value="true" @(Model.IsPublished ? "checked" : "") />
Publicera recept på receptsidan
</label>
</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" class="recipe-link" target="_blank">Visa Recept</a></p>
}
@if (User.Identity.IsAuthenticated)
{
<div class="rating-box" data-meal-id="@Model.Id">
<p>Ditt betyg:</p>
<div class="star-container">
@for (int i = 1; i <= 5; i++)
{
<i class="fa fa-star" data-value="@i"></i>
}
</div>
</div>
}
<div class="buttons">
@if (isChef)
{
<a class="btn-outline" href="@Url.Action("View", new { id = Model.Id, edit = true })">Redigera</a>
}
<button type="button" class="btn-outline" onclick="toggleRecipe()">Recept</button>
</div>
<div id="recipe-section" style="display:none; margin-top:1.5rem;">
<h2>Så här gör du</h2>
@if (!string.IsNullOrWhiteSpace(Model.Instructions))
{
<div class="instructions-content">
@Html.Raw(Model.Instructions)
</div>
}
else
{
<p class="placeholder">Ingen instruktion har lagts till ännu.</p>
}
@if (Model.Ingredients != null && Model.Ingredients.Any())
{
<h3>Ingredienser</h3>
<ul>
@foreach (var ing in Model.Ingredients)
{
<li>@ing.Quantity @ing.Item</li>
}
</ul>
}
</div>
}
</div>
<div id="imageModal" style="display:none; position:fixed; inset:0; background:rgba(0,0,0,0.6); z-index:1000;">
<div style="background:white; margin:5% auto; padding:2rem; max-width:400px; border-radius:8px; text-align:center; position:relative;">
<h3>Klistra in eller dra in bild</h3>
<div id="dropZone" style="border:2px dashed #aaa; padding:2rem; cursor:pointer;">
<p>Släpp bild här eller klistra in (Ctrl+V)</p>
</div>
<input type="file" id="modalImageInput" accept="image/*" style="display:none;" />
<button type="button" onclick="document.getElementById('modalImageInput').click()">Välj bild från enhet</button>
<button onclick="closeImageModal()" style="margin-top:1rem;">Stäng</button>
</div>
</div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/trumbowyg@2.25.1/dist/ui/trumbowyg.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/trumbowyg@2.25.1/dist/trumbowyg.min.js"></script>
<script src="https://kit.fontawesome.com/yourkit.js" crossorigin="anonymous"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const ratingBox = document.querySelector(".rating-box");
if (!ratingBox) return;
const mealId = ratingBox.dataset.mealId;
const stars = ratingBox.querySelectorAll(".fa-star");
fetch(`/api/MealRatingApi/${mealId}`)
.then(res => res.json())
.then(rating => {
stars.forEach(star => {
if (parseInt(star.dataset.value) <= rating) {
star.classList.add("rated");
}
});
});
stars.forEach(star => {
star.addEventListener("click", () => {
const rating = star.dataset.value;
fetch("/api/MealRatingApi", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ mealId: mealId, rating: parseInt(rating) })
}).then(() => {
stars.forEach(s => s.classList.remove("rated"));
stars.forEach(s => {
if (parseInt(s.dataset.value) <= rating) {
s.classList.add("rated");
}
});
});
});
});
});
function addIngredientRow(quantity = '', item = '') {
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';
qtyInput.value = quantity;
const itemInput = document.createElement('input');
itemInput.type = 'text';
itemInput.name = `Ingredients[${index}].Item`;
itemInput.placeholder = 'Ingrediens';
itemInput.className = 'form-control ingredient-item';
itemInput.value = item;
div.appendChild(qtyInput);
div.appendChild(itemInput);
list.appendChild(div);
}
function parseBulkIngredients() {
const bulk = document.getElementById('bulkIngredients').value;
if (!bulk.trim()) return;
const lines = bulk.trim().split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed) continue;
// Försök dela på första mellanslag → mängd + ingrediens
const parts = trimmed.split(' ');
const quantity = parts.slice(0, 2).join(' '); // typ "2 dl" eller "1 tsk"
const item = parts.slice(2).join(' '); // resten
// fallback om bara ett ord: "ägg"
const safeQuantity = item ? quantity : '';
const safeItem = item || quantity;
addIngredientRow(safeQuantity, safeItem);
}
document.getElementById('bulkIngredients').value = '';
}
function toggleRecipe() {
const section = document.getElementById('recipe-section');
if (!section) return;
if (section.style.display === 'none' || section.style.display === '') {
section.style.display = 'block';
} else {
section.style.display = 'none';
}
}
function openImageModal() {
document.getElementById('imageModal').style.display = 'block';
}
function closeImageModal() {
document.getElementById('imageModal').style.display = 'none';
}
function handleImage(file) {
const reader = new FileReader();
reader.onload = e => {
document.getElementById("imagePreview").src = e.target.result;
const dt = new DataTransfer();
dt.items.add(file);
document.getElementById("ImageFile").files = dt.files;
};
reader.readAsDataURL(file);
}
document.getElementById('dropZone').addEventListener('dragover', e => {
e.preventDefault();
e.currentTarget.style.borderColor = '#6a0dad';
});
document.getElementById('dropZone').addEventListener('dragleave', e => {
e.currentTarget.style.borderColor = '#aaa';
});
document.getElementById('dropZone').addEventListener('drop', e => {
e.preventDefault();
const file = e.dataTransfer.files[0];
if (file && file.type.startsWith('image/')) {
handleImage(file);
closeImageModal();
}
});
document.getElementById('modalImageInput').addEventListener('change', e => {
const file = e.target.files[0];
if (file && file.type.startsWith('image/')) {
handleImage(file);
closeImageModal();
}
});
$(document).ready(function () {
if ($('#Instructions').length > 0) {
$('#Instructions').trumbowyg({
autogrow: true,
btns: [['strong', 'em'], ['unorderedList', 'orderedList'], ['link'], ['removeformat']]
});
}
});
</script>
<style>
</style>