From 258ffb782f47ed8f586c54ce4217d0e540b2904f Mon Sep 17 00:00:00 2001 From: Francesco Lorenzo D'Amico Date: Mon, 2 Jun 2025 12:34:35 +0200 Subject: [PATCH] Move all methods from ShoppingListDetail to main JS and improve JS usage --- Francesco.Recipes.World/wwwroot/js/site.js | 580 ++++++++++++++------- 1 file changed, 402 insertions(+), 178 deletions(-) diff --git a/Francesco.Recipes.World/wwwroot/js/site.js b/Francesco.Recipes.World/wwwroot/js/site.js index 12bdab8..3639c5e 100644 --- a/Francesco.Recipes.World/wwwroot/js/site.js +++ b/Francesco.Recipes.World/wwwroot/js/site.js @@ -1,191 +1,415 @@ -htmx.on('htmx:afterSwap', (event) => { - if (event.target.id === 'instructions-container') { - console.log('Instructions reloaded.'); - } - if (event.target.id === 'ingredients-container') { - console.log('Ingredients reloaded.'); - } -}); +(function (window, document) { + const selectedIngredients = new Set(); + const recipeIngredients = window.recipeIngredients || {}; + let activeRecipeId = Object.keys(recipeIngredients)[0]; + let recipeId; -let recipeId; -function setRecipeId(id) { - recipeId = id; -} + htmx && htmx.on('htmx:afterSwap', (event) => { + if (event.target.id === 'instructions-container') { + console.log('Instructions reloaded.'); + } + if (event.target.id === 'ingredients-container') { + console.log('Ingredients reloaded.'); + } + }); - -async function moveInstructionUp(instructionId, recipeIdParam) { - const idToUse = recipeIdParam || recipeId; - - if (!idToUse) { - alert('Recipe ID is not set. Please select a recipe first.'); - return; + function setRecipeId(id) { + recipeId = id; } - try { - const response = await fetch(`/Recipe/${idToUse}/Instruction/${instructionId}/move-up`, { - method: 'POST', - headers: { - 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value - } + + async function updateRecipeCount() { + try { + const response = await fetch('/ShoppingList/RecipeCount'); + if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`); + const data = await response.json(); + document.getElementById('recipeCount').textContent = data.count; + } catch (err) { + console.error("Fehler beim Aktualisieren der Rezeptanzahl:", err); + } + } + + const countUpdateInterval = setInterval(updateRecipeCount, 5000); + + document.addEventListener('shopping-list-changed', updateRecipeCount); + + window.addEventListener('beforeunload', function () { + clearInterval(countUpdateInterval); + }); + + document.addEventListener('DOMContentLoaded', function () { + document.getElementById('carouselLeft')?.addEventListener('click', function () { + document.getElementById('recipeCarousel').scrollBy({ left: -200, behavior: 'smooth' }); + }); + document.getElementById('carouselRight')?.addEventListener('click', function () { + document.getElementById('recipeCarousel').scrollBy({ left: 200, behavior: 'smooth' }); }); - if (response.ok) { - htmx.ajax('GET', `/Recipe/${idToUse}/Instructions`, '#instructions-container'); - } else { - const error = await response.json(); - alert(error.Error || 'Failed to move instruction up.'); - } - } catch (error) { - console.error('Error moving instruction up:', error); - } -} + if (activeRecipeId) setActiveCard(activeRecipeId); - -async function moveInstructionDown(instructionId, recipeIdParam) { - const idToUse = recipeIdParam || recipeId; - - if (!idToUse) { - alert('Recipe ID is not set. Please select a recipe first.'); - return; - } - - try { - const response = await fetch(`/Recipe/${idToUse}/Instruction/${instructionId}/move-down`, { - method: 'POST', - headers: { - 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value - } - }); - - if (response.ok) { - htmx.ajax('GET', `/Recipe/${idToUse}/Instructions`, '#instructions-container'); - } else { - const error = await response.json(); - alert(error.Error || 'Failed to move instruction down.'); - } - } catch (error) { - console.error('Error moving instruction down:', error); - } -} - - -async function removeInstruction(instructionId, recipeIdParam) { - const idToUse = recipeIdParam || recipeId; - - if (!idToUse) { - alert('Recipe ID is not set. Please select a recipe first.'); - return; - } - - if (!confirm('Möchtest du diesen Schritt wirklich löschen?')) return; - - try { - const response = await fetch(`/${idToUse}/RemoveInstruction/${instructionId}`, { - method: 'POST', - headers: { - 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value - } - }); - - if (response.ok) { - const element = document.getElementById(`instruction-${instructionId}`); - if (element) { - element.remove(); - } - } else { - const error = await response.json(); - alert(error.Error || 'Fehler beim Löschen der Anweisung.'); - } - } catch (error) { - console.error('Fehler beim Löschen:', error); - } -} - -function addInstruction() { - const container = document.getElementById('instructions-container'); - const index = document.querySelectorAll('.instruction-item').length; - - const newInstructionHtml = ` -
-
- - - -
-
- - -
-
- `; - container.insertAdjacentHTML('beforeend', newInstructionHtml); -} - - -async function removeIngredient(ingredientId) { - if (!confirm('Möchtest du diese Zutat wirklich löschen?')) return; - - try { - const response = await fetch(`/${recipeId}/RemoveIngredient/${ingredientId}`, { - method: 'POST', - headers: { - 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value - } - }); - - if (response.ok) { - const element = document.getElementById(`ingredient-${ingredientId}`); - if (element) { - element.remove(); - } - } else { - const error = await response.json(); - alert(error.Error || 'Fehler beim Löschen der Zutat.'); - } - } catch (error) { - console.error('Fehler beim Löschen:', error); - } -} - - -async function addIngredient() { - const container = document.getElementById('ingredients-container'); - const index = document.querySelectorAll('.ingredient-item').length; - - const newIngredientHtml = ` -
-
- - - - -
-
- `; - container.insertAdjacentHTML('beforeend', newIngredientHtml); - - const addedElement = document.getElementById(`ingredient-new-${index}`); - const unitSelect = addedElement.querySelector('.unit-select'); - - try { - const response = await fetch('/Unit/GetAllUnits'); - if (response.ok) { - const units = await response.json(); - console.log('Fetched units:', units); - unitSelect.innerHTML = ''; - units.forEach(unit => { - const option = new Option(unit.name, unit.id); - unitSelect.add(option); + document.querySelectorAll('.recipe-card').forEach(card => { + card.addEventListener('click', function () { + setActiveCard(this.dataset.recipeId); }); + }); + }); + + function renderIngredientList(recipeId) { + const list = document.getElementById('ingredientList'); + list.innerHTML = ''; + const ingredients = recipeIngredients[recipeId] || []; + if (ingredients.length === 0) { + list.innerHTML = '
  • Keine Zutaten vorhanden.
  • '; + return; + } + ingredients.forEach(ingredient => { + const isSelected = selectedIngredients.has(ingredient.id); + const li = document.createElement('li'); + li.className = 'list-group-item d-flex justify-content-between align-items-center'; + if (isSelected) li.classList.add('selected-ingredient'); + li.innerHTML = ` + + ${ingredient.name} + ${ingredient.amount} ${ingredient.unit} + + + `; + list.appendChild(li); + }); + updateRemoveSelectedButton(); + } + + function setActiveCard(recipeId) { + document.querySelectorAll('.recipe-card').forEach(card => { + card.classList.toggle('active-recipe-card', card.dataset.recipeId === recipeId); + }); + activeRecipeId = recipeId; + renderIngredientList(recipeId); + } + + function toggleIngredientSelection(ingredientId, button) { + const listItem = button.closest('li'); + if (selectedIngredients.has(ingredientId)) { + selectedIngredients.delete(ingredientId); + listItem.classList.remove('selected-ingredient'); } else { - console.error('Failed to fetch units'); + selectedIngredients.add(ingredientId); + listItem.classList.add('selected-ingredient'); + } + updateRemoveSelectedButton(); + } + + function updateRemoveSelectedButton() { + document.getElementById('removeSelectedButton').style.display = + selectedIngredients.size > 0 ? 'inline-block' : 'none'; + } + + async function removeSelectedIngredients() { + if (selectedIngredients.size === 0) return; + + const shoppingListIds = []; + selectedIngredients.forEach(ingredientId => { + for (const recipeId in recipeIngredients) { + const ingredients = recipeIngredients[recipeId]; + const ingredient = ingredients.find(ing => ing.id === ingredientId); + if (ingredient && ingredient.shoppingListId && + ingredient.shoppingListId !== "00000000-0000-0000-0000-000000000000") { + shoppingListIds.push(ingredient.shoppingListId); + } + } + }); + + if (shoppingListIds.length === 0) { + console.warn("Keine gültigen Shopping List IDs gefunden"); + return; + } + + try { + const response = await fetch('/ShoppingList/RemoveIngredients', { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value, + 'Cache-Control': 'no-cache', + 'Pragma': 'no-cache' + }, + body: JSON.stringify(shoppingListIds) + }); + + if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`); + + const result = await response.json(); + + if (result.success) { + window.location.reload(); + } else { + console.error("Fehler beim Entfernen der Zutaten:", result.error); + alert("Beim Entfernen der Zutaten ist ein Fehler aufgetreten."); + } + } catch (err) { + console.error("Fehler beim Entfernen der Zutaten:", err); + alert("Beim Entfernen der Zutaten ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut."); + } + } + + async function removeRecipe(recipeId) { + let confirmed = false; + + if (window.Swal) { + const result = await Swal.fire({ + title: 'Rezept entfernen?', + text: 'Möchten Sie dieses Rezept wirklich aus der Einkaufsliste entfernen?', + icon: 'warning', + showCancelButton: true, + confirmButtonText: 'Ja, entfernen', + cancelButtonText: 'Abbrechen', + confirmButtonColor: '#d33', + cancelButtonColor: '#3085d6' + }); + confirmed = result.isConfirmed; + } else { + confirmed = confirm('Möchten Sie dieses Rezept wirklich aus der Einkaufsliste entfernen?'); + } + + if (!confirmed) return; + + try { + const tokenElement = document.querySelector('input[name="__RequestVerificationToken"]'); + if (!tokenElement) { + console.error('Anti-forgery token not found'); + alert('Fehler: Anti-Forgery Token nicht gefunden'); + return; + } + + const response = await fetch(`/ShoppingList/RemoveRecipeFromList/${recipeId}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'RequestVerificationToken': tokenElement.value + } + }); + + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + const result = await response.json(); + + if (result.success) { + if (window.Swal) { + await Swal.fire({ + title: 'Erfolgreich!', + text: 'Das Rezept wurde entfernt.', + icon: 'success', + timer: 1500, + showConfirmButton: false + }); + } else { + alert('Das Rezept wurde erfolgreich entfernt.'); + } + window.location.reload(); + } else { + const errorMsg = result.error || 'Unbekannter Fehler'; + console.error('Fehler beim Entfernen:', errorMsg); + if (window.Swal) { + await Swal.fire({ + title: 'Fehler', + text: errorMsg, + icon: 'error' + }); + } else { + alert('Fehler: ' + errorMsg); + } + } + } catch (err) { + console.error('Fehler beim Entfernen des Rezepts:', err); + if (window.Swal) { + await Swal.fire({ + title: 'Fehler', + text: 'Beim Löschen ist ein Fehler aufgetreten.', + icon: 'error' + }); + } else { + alert('Beim Löschen ist ein Fehler aufgetreten.'); + } + } + } + + + async function moveInstructionUp(instructionId, recipeIdParam) { + const idToUse = recipeIdParam || recipeId; + if (!idToUse) { + alert('Recipe ID is not set. Please select a recipe first.'); + return; + } + + try { + const response = await fetch(`/Recipe/${idToUse}/Instruction/${instructionId}/move-up`, { + method: 'POST', + headers: { + 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value + } + }); + + if (response.ok) { + htmx.ajax('GET', `/Recipe/${idToUse}/Instructions`, '#instructions-container'); + } else { + const error = await response.json(); + alert(error.Error || 'Failed to move instruction up.'); + } + } catch (error) { + console.error('Error moving instruction up:', error); + } + } + + async function moveInstructionDown(instructionId, recipeIdParam) { + const idToUse = recipeIdParam || recipeId; + if (!idToUse) { + alert('Recipe ID is not set. Please select a recipe first.'); + return; + } + + try { + const response = await fetch(`/Recipe/${idToUse}/Instruction/${instructionId}/move-down`, { + method: 'POST', + headers: { + 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value + } + }); + + if (response.ok) { + htmx.ajax('GET', `/Recipe/${idToUse}/Instructions`, '#instructions-container'); + } else { + const error = await response.json(); + alert(error.Error || 'Failed to move instruction down.'); + } + } catch (error) { + console.error('Error moving instruction down:', error); + } + } + + async function removeInstruction(instructionId, recipeIdParam) { + const idToUse = recipeIdParam || recipeId; + if (!idToUse) { + alert('Recipe ID is not set. Please select a recipe first.'); + return; + } + if (!confirm('Möchtest du diesen Schritt wirklich löschen?')) return; + + try { + const response = await fetch(`/${idToUse}/RemoveInstruction/${instructionId}`, { + method: 'POST', + headers: { + 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value + } + }); + + if (response.ok) { + const element = document.getElementById(`instruction-${instructionId}`); + if (element) element.remove(); + } else { + const error = await response.json(); + alert(error.Error || 'Fehler beim Löschen der Anweisung.'); + } + } catch (error) { + console.error('Fehler beim Löschen:', error); + } + } + + function addInstruction() { + const container = document.getElementById('instructions-container'); + const index = document.querySelectorAll('.instruction-item').length; + + const newInstructionHtml = ` +
    +
    + + + +
    +
    + + +
    +
    + `; + container.insertAdjacentHTML('beforeend', newInstructionHtml); + } + + async function removeIngredient(ingredientId) { + if (!confirm('Möchtest du diese Zutat wirklich löschen?')) return; + + try { + const response = await fetch(`/${recipeId}/RemoveIngredient/${ingredientId}`, { + method: 'POST', + headers: { + 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value + } + }); + + if (response.ok) { + const element = document.getElementById(`ingredient-${ingredientId}`); + if (element) element.remove(); + } else { + const error = await response.json(); + alert(error.Error || 'Fehler beim Löschen der Zutat.'); + } + } catch (error) { + console.error('Fehler beim Löschen:', error); + } + } + + async function addIngredient() { + const container = document.getElementById('ingredients-container'); + const index = document.querySelectorAll('.ingredient-item').length; + + const newIngredientHtml = ` +
    +
    + + + + +
    +
    + `; + container.insertAdjacentHTML('beforeend', newIngredientHtml); + + const addedElement = document.getElementById(`ingredient-new-${index}`); + const unitSelect = addedElement.querySelector('.unit-select'); + + try { + const response = await fetch('/Unit/GetAllUnits'); + if (response.ok) { + const units = await response.json(); + unitSelect.innerHTML = ''; + units.forEach(unit => { + const option = new Option(unit.name, unit.id); + unitSelect.add(option); + }); + } else { + unitSelect.innerHTML = ''; + } + } catch (error) { unitSelect.innerHTML = ''; } - } catch (error) { - console.error('Error fetching units:', error); - unitSelect.innerHTML = ''; } -} + window.Francesco = { + setRecipeId, + moveInstructionUp, + moveInstructionDown, + removeInstruction, + addInstruction, + removeIngredient, + addIngredient, + toggleIngredientSelection, + removeSelectedIngredients, + removeRecipe, + }; + +})(window, document);