diff --git a/assets/css/custom.css b/assets/css/custom.css index d6bb99c..4831251 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -3,7 +3,6 @@ body { background-color: #142E35; /* Dark green background */ color: #ffffff; /* White text */ font-family: 'Poppins', sans-serif; - padding-top: 40px; /* Make space for garland */ } /* Headings */ @@ -15,7 +14,7 @@ h1, h2, h3, h4, h5, h6 { input, button, .btn { - border-radius: 8px !important; + border-radius: 8px; } @@ -93,8 +92,8 @@ body::before { .form-control:focus { background-color: rgba(0, 0, 0, 0.3); - border-color: #C83434; /* Coral red accent */ - box-shadow: 0 0 0 0.25rem rgba(200, 52, 52, 0.25); + border-color: #0a1a1f; /* Dark green accent */ + box-shadow: 0 0 0 0.25rem rgba(10, 26, 31, 0.25); color: #ffffff; } @@ -136,6 +135,7 @@ body::before { background-color: rgba(255, 255, 255, 0.15); border: none; color: #ffffff; + padding: 12px 30px; } /* Shopping List & Recipe Cards */ @@ -258,8 +258,8 @@ animation: fall linear infinite; } .form-check-input:focus { - border-color: #C83434; - box-shadow: 0 0 0 0.25rem rgba(200, 52, 52, 0.25); + border-color: #0a1a1f; + box-shadow: 0 0 0 0.25rem rgba(10, 26, 31, 0.25); } @media print { @@ -306,11 +306,21 @@ animation: fall linear infinite; color: white; } -#recipe-form-modal .modal-header, #add-product-modal .modal-header, #confirmationModal .modal-header { +#recipe-form-modal .modal-header, +#add-product-modal .modal-header, +#confirmRemoveModal .modal-header { border-bottom: 1px solid rgba(255, 255, 255, 0.2); } -#recipe-form-modal .btn-close, #add-product-modal .btn-close, #confirmationModal .btn-close { +#recipe-form-modal .modal-footer, +#add-product-modal .modal-footer, +#confirmRemoveModal .modal-footer { + border-top: 1px solid rgba(255, 255, 255, 0.2); +} + +#recipe-form-modal .btn-close, +#add-product-modal .btn-close, +#confirmRemoveModal .btn-close { filter: invert(1); } @@ -344,10 +354,10 @@ animation: fall linear infinite; } /* Shopping List Quantity Controls */ -.btn-quantity-modifier { - width: 28px; - height: 28px; - border-radius: 50% !important; +.btn.btn-quantity-modifier { + width: 32px; + height: 32px; + border-radius: 50%; padding: 0; font-size: 1.2rem; font-weight: bold; @@ -355,16 +365,16 @@ animation: fall linear infinite; display: inline-flex; align-items: center; justify-content: center; - border: none !important; - outline: none !important; - box-shadow: none !important; + border: 1px solid rgba(255, 255, 255, 0.2); + outline: none; + box-shadow: none; color: white; - background-color: #142E35; /* Very dark green */ + background-color: rgba(255, 255, 255, 0.1); transition: background-color 0.2s ease; } -.btn-quantity-modifier:hover { - background-color: #1e3c46; /* Slightly lighter on hover */ +.btn.btn-quantity-modifier:hover { + background-color: rgba(255, 255, 255, 0.2); } .quantity-controls .quantity { @@ -375,3 +385,6 @@ animation: fall linear infinite; .card .btn.delete-recipe { padding: .25rem .5rem; } + + + diff --git a/assets/images/christmas/bauble.svg b/assets/images/christmas/bauble.svg new file mode 100644 index 0000000..1ec02e3 --- /dev/null +++ b/assets/images/christmas/bauble.svg @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/assets/images/christmas/candy-cane.svg b/assets/images/christmas/candy-cane.svg new file mode 100644 index 0000000..0cbbb88 --- /dev/null +++ b/assets/images/christmas/candy-cane.svg @@ -0,0 +1,31 @@ + \ No newline at end of file diff --git a/assets/images/christmas/star.svg b/assets/images/christmas/star.svg new file mode 100644 index 0000000..28580d6 --- /dev/null +++ b/assets/images/christmas/star.svg @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/assets/js/main.js b/assets/js/main.js index 62b69ad..654379d 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -451,61 +451,60 @@ const app = { const key = e.target.dataset.key; const [name, unit] = key.split('|'); + // Find or create the item in additionalProducts to track adjustments if (!app.state.additionalProducts) { app.state.additionalProducts = []; } - let productToModify = app.state.additionalProducts.find(p => p.name.toLowerCase() === name.toLowerCase() && p.unit.toLowerCase() === unit.toLowerCase()); + if (!productToModify) { + const properName = name.charAt(0).toUpperCase() + name.slice(1); + productToModify = { + name: properName, + quantity: 0, + unit: unit, + source: 'Additional product' + }; + app.state.additionalProducts.push(productToModify); + } - if (productToModify && productToModify.quantity > 0) { - productToModify.quantity--; - app.ui.updateShoppingList(); - } else { - // It's a recipe ingredient, show confirmation - let recipeName = ''; - let ingredientName = ''; - let recipeId = null; - let ingredientIndex = -1; - - for (const recipe of app.state.recipes) { - const foundIngredientIndex = recipe.ingredients.findIndex(ing => ing.name.toLowerCase() === name.toLowerCase() && ing.unit.toLowerCase() === unit.toLowerCase()); - if (foundIngredientIndex !== -1) { - recipeName = recipe.name; - ingredientName = recipe.ingredients[foundIngredientIndex].name; - recipeId = recipe.id; - ingredientIndex = foundIngredientIndex; - break; - } - } - - if (recipeId !== null) { - const confirmationKey = `${recipeId}-${name.toLowerCase()}-${unit.toLowerCase()}`; - const recipeToUpdate = app.state.recipes.find(r => r.id === recipeId); - - const proceedWithRemoval = () => { - if (recipeToUpdate && recipeToUpdate.ingredients[ingredientIndex].quantity > 0) { - recipeToUpdate.ingredients[ingredientIndex].quantity--; - } - app.ui.updateShoppingList(); - }; - - if (app.state.confirmedRecipeProducts.includes(confirmationKey)) { - proceedWithRemoval(); - } else { - document.getElementById('modal-recipe-name').textContent = recipeName; - document.getElementById('modal-ingredient-name').textContent = ingredientName; - - const confirmModal = new bootstrap.Modal(document.getElementById('confirmRemoveModal')); - confirmModal.show(); - - document.getElementById('confirm-remove-btn').onclick = () => { - app.state.confirmedRecipeProducts.push(confirmationKey); - proceedWithRemoval(); - confirmModal.hide(); - }; - } + // Check if this ingredient is part of any recipe + let isRecipeIngredient = false; + let recipeNameForModal = ''; + for (const recipe of app.state.recipes) { + if (recipe.ingredients.some(ing => ing.name.toLowerCase() === name.toLowerCase() && ing.unit.toLowerCase() === unit.toLowerCase())) { + isRecipeIngredient = true; + recipeNameForModal = recipe.name; // Just need one name for the modal + break; } } + + // If it's a recipe ingredient and we're about to remove from the recipe's contribution + if (isRecipeIngredient && productToModify.quantity <= 0) { + const confirmationKey = `${name.toLowerCase()}-${unit.toLowerCase()}`; + + if (app.state.confirmedRecipeProducts.includes(confirmationKey)) { + productToModify.quantity--; + app.ui.updateShoppingList(); + } else { + document.getElementById('modal-recipe-name').textContent = recipeNameForModal; + document.getElementById('modal-ingredient-name').textContent = productToModify.name; + + const confirmModal = new bootstrap.Modal(document.getElementById('confirmRemoveModal')); + confirmModal.show(); + + document.getElementById('confirm-remove-btn').onclick = () => { + app.state.confirmedRecipeProducts.push(confirmationKey); + productToModify.quantity--; + app.ui.updateShoppingList(); + confirmModal.hide(); + }; + } + } else if (productToModify.quantity > 0) { + // It's not a recipe ingredient about to be removed, but has been added via '+' + productToModify.quantity--; + app.ui.updateShoppingList(); + } + // If not a recipe ingredient and quantity is 0, do nothing. } }); @@ -519,7 +518,11 @@ const app = { app.dom.recipeSearchInput.addEventListener('input', function() { const searchTerm = app.dom.recipeSearchInput.value.toLowerCase(); - const filteredRecipes = app.state.recipes.filter(recipe => recipe.name.toLowerCase().includes(searchTerm)); + const filteredRecipes = app.state.recipes.filter(recipe => { + const recipeName = recipe.name.toLowerCase(); + const ingredients = recipe.ingredients.map(ing => ing.name.toLowerCase()).join(' '); + return recipeName.includes(searchTerm) || ingredients.includes(searchTerm); + }); app.ui.renderRecipeCards(filteredRecipes); }); diff --git a/assets/pasted-20251130-190335-947532cc.png b/assets/pasted-20251130-190335-947532cc.png new file mode 100644 index 0000000..f7d0958 Binary files /dev/null and b/assets/pasted-20251130-190335-947532cc.png differ diff --git a/assets/pasted-20251130-190720-ae17f828.png b/assets/pasted-20251130-190720-ae17f828.png new file mode 100644 index 0000000..f7d0958 Binary files /dev/null and b/assets/pasted-20251130-190720-ae17f828.png differ diff --git a/index.php b/index.php index 5246f34..2b5c707 100644 --- a/index.php +++ b/index.php @@ -32,6 +32,8 @@
+ +