This commit is contained in:
Flatlogic Bot 2025-11-30 19:15:26 +00:00
parent d7cd3adc0e
commit 71ee90fe50
8 changed files with 138 additions and 67 deletions

View File

@ -3,7 +3,6 @@ body {
background-color: #142E35; /* Dark green background */ background-color: #142E35; /* Dark green background */
color: #ffffff; /* White text */ color: #ffffff; /* White text */
font-family: 'Poppins', sans-serif; font-family: 'Poppins', sans-serif;
padding-top: 40px; /* Make space for garland */
} }
/* Headings */ /* Headings */
@ -15,7 +14,7 @@ h1, h2, h3, h4, h5, h6 {
input, input,
button, button,
.btn { .btn {
border-radius: 8px !important; border-radius: 8px;
} }
@ -93,8 +92,8 @@ body::before {
.form-control:focus { .form-control:focus {
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
border-color: #C83434; /* Coral red accent */ border-color: #0a1a1f; /* Dark green accent */
box-shadow: 0 0 0 0.25rem rgba(200, 52, 52, 0.25); box-shadow: 0 0 0 0.25rem rgba(10, 26, 31, 0.25);
color: #ffffff; color: #ffffff;
} }
@ -136,6 +135,7 @@ body::before {
background-color: rgba(255, 255, 255, 0.15); background-color: rgba(255, 255, 255, 0.15);
border: none; border: none;
color: #ffffff; color: #ffffff;
padding: 12px 30px;
} }
/* Shopping List & Recipe Cards */ /* Shopping List & Recipe Cards */
@ -258,8 +258,8 @@ animation: fall linear infinite;
} }
.form-check-input:focus { .form-check-input:focus {
border-color: #C83434; border-color: #0a1a1f;
box-shadow: 0 0 0 0.25rem rgba(200, 52, 52, 0.25); box-shadow: 0 0 0 0.25rem rgba(10, 26, 31, 0.25);
} }
@media print { @media print {
@ -306,11 +306,21 @@ animation: fall linear infinite;
color: white; 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); 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); filter: invert(1);
} }
@ -344,10 +354,10 @@ animation: fall linear infinite;
} }
/* Shopping List Quantity Controls */ /* Shopping List Quantity Controls */
.btn-quantity-modifier { .btn.btn-quantity-modifier {
width: 28px; width: 32px;
height: 28px; height: 32px;
border-radius: 50% !important; border-radius: 50%;
padding: 0; padding: 0;
font-size: 1.2rem; font-size: 1.2rem;
font-weight: bold; font-weight: bold;
@ -355,16 +365,16 @@ animation: fall linear infinite;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border: none !important; border: 1px solid rgba(255, 255, 255, 0.2);
outline: none !important; outline: none;
box-shadow: none !important; box-shadow: none;
color: white; color: white;
background-color: #142E35; /* Very dark green */ background-color: rgba(255, 255, 255, 0.1);
transition: background-color 0.2s ease; transition: background-color 0.2s ease;
} }
.btn-quantity-modifier:hover { .btn.btn-quantity-modifier:hover {
background-color: #1e3c46; /* Slightly lighter on hover */ background-color: rgba(255, 255, 255, 0.2);
} }
.quantity-controls .quantity { .quantity-controls .quantity {
@ -375,3 +385,6 @@ animation: fall linear infinite;
.card .btn.delete-recipe { .card .btn.delete-recipe {
padding: .25rem .5rem; padding: .25rem .5rem;
} }

View File

@ -0,0 +1,16 @@
<svg width="100" height="120" viewBox="0 0 100 120" xmlns="http://www.w3.org/2000/svg">
<!-- Bauble Cap -->
<rect x="40" y="0" width="20" height="10" fill="#f1c40f"/>
<!-- Bauble Loop -->
<path d="M 50 10 Q 55 20 60 10" stroke="#f1c40f" stroke-width="2" fill="none"/>
<!-- Main Bauble with Gradient -->
<defs>
<radialGradient id="baubleGradient" cx="0.3" cy="0.3" r="0.7">
<stop offset="0%" style="stop-color: #e74c3c; stop-opacity: 1" />
<stop offset="100%" style="stop-color: #c0392b; stop-opacity: 1" />
</radialGradient>
</defs>
<circle cx="50" cy="60" r="40" fill="url(#baubleGradient)"/>
</svg>

After

Width:  |  Height:  |  Size: 638 B

View File

@ -0,0 +1,31 @@
<svg width="125" height="195" viewBox="-5 0 105 155" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="candy-stripes" gradientTransform="rotate(45)">
<stop offset="0%" stop-color="#d51f28" />
<stop offset="12.5%" stop-color="#d51f28" />
<stop offset="12.5%" stop-color="white" />
<stop offset="25%" stop-color="white" />
<stop offset="25%" stop-color="#d51f28" />
<stop offset="37.5%" stop-color="#d51f28" />
<stop offset="37.5%" stop-color="white" />
<stop offset="50%" stop-color="white" />
<stop offset="50%" stop-color="#d51f28" />
<stop offset="62.5%" stop-color="#d51f28" />
<stop offset="62.5%" stop-color="white" />
<stop offset="75%" stop-color="white" />
<stop offset="75%" stop-color="#d51f28" />
<stop offset="87.5%" stop-color="#d51f28" />
<stop offset="87.5%" stop-color="white" />
<stop offset="100%" stop-color="white" />
</linearGradient>
</defs>
<path
d="M 75 140 V 50 C 75 22.38 52.62 0 25 0 S -25 22.38 -25 50"
transform="translate(25, 5)"
stroke="url(#candy-stripes)"
stroke-width="25"
fill="none"
stroke-linecap="round"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,6 @@
<svg width="100" height="100" viewBox="0 0 100 100">
<polygon
points="50,5 61,40 98,40 68,62 79,96 50,75 21,96 32,62 2,40 39,40"
fill="#FFD700"
/>
</svg>

After

Width:  |  Height:  |  Size: 165 B

View File

@ -451,61 +451,60 @@ const app = {
const key = e.target.dataset.key; const key = e.target.dataset.key;
const [name, unit] = key.split('|'); const [name, unit] = key.split('|');
// Find or create the item in additionalProducts to track adjustments
if (!app.state.additionalProducts) { if (!app.state.additionalProducts) {
app.state.additionalProducts = []; app.state.additionalProducts = [];
} }
let productToModify = app.state.additionalProducts.find(p => p.name.toLowerCase() === name.toLowerCase() && p.unit.toLowerCase() === unit.toLowerCase()); 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) { // Check if this ingredient is part of any recipe
productToModify.quantity--; let isRecipeIngredient = false;
app.ui.updateShoppingList(); let recipeNameForModal = '';
} 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) { for (const recipe of app.state.recipes) {
const foundIngredientIndex = recipe.ingredients.findIndex(ing => ing.name.toLowerCase() === name.toLowerCase() && ing.unit.toLowerCase() === unit.toLowerCase()); if (recipe.ingredients.some(ing => ing.name.toLowerCase() === name.toLowerCase() && ing.unit.toLowerCase() === unit.toLowerCase())) {
if (foundIngredientIndex !== -1) { isRecipeIngredient = true;
recipeName = recipe.name; recipeNameForModal = recipe.name; // Just need one name for the modal
ingredientName = recipe.ingredients[foundIngredientIndex].name;
recipeId = recipe.id;
ingredientIndex = foundIngredientIndex;
break; break;
} }
} }
if (recipeId !== null) { // If it's a recipe ingredient and we're about to remove from the recipe's contribution
const confirmationKey = `${recipeId}-${name.toLowerCase()}-${unit.toLowerCase()}`; if (isRecipeIngredient && productToModify.quantity <= 0) {
const recipeToUpdate = app.state.recipes.find(r => r.id === recipeId); const confirmationKey = `${name.toLowerCase()}-${unit.toLowerCase()}`;
const proceedWithRemoval = () => {
if (recipeToUpdate && recipeToUpdate.ingredients[ingredientIndex].quantity > 0) {
recipeToUpdate.ingredients[ingredientIndex].quantity--;
}
app.ui.updateShoppingList();
};
if (app.state.confirmedRecipeProducts.includes(confirmationKey)) { if (app.state.confirmedRecipeProducts.includes(confirmationKey)) {
proceedWithRemoval(); productToModify.quantity--;
app.ui.updateShoppingList();
} else { } else {
document.getElementById('modal-recipe-name').textContent = recipeName; document.getElementById('modal-recipe-name').textContent = recipeNameForModal;
document.getElementById('modal-ingredient-name').textContent = ingredientName; document.getElementById('modal-ingredient-name').textContent = productToModify.name;
const confirmModal = new bootstrap.Modal(document.getElementById('confirmRemoveModal')); const confirmModal = new bootstrap.Modal(document.getElementById('confirmRemoveModal'));
confirmModal.show(); confirmModal.show();
document.getElementById('confirm-remove-btn').onclick = () => { document.getElementById('confirm-remove-btn').onclick = () => {
app.state.confirmedRecipeProducts.push(confirmationKey); app.state.confirmedRecipeProducts.push(confirmationKey);
proceedWithRemoval(); productToModify.quantity--;
app.ui.updateShoppingList();
confirmModal.hide(); 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() { app.dom.recipeSearchInput.addEventListener('input', function() {
const searchTerm = app.dom.recipeSearchInput.value.toLowerCase(); 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); app.ui.renderRecipeCards(filteredRecipes);
}); });

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

View File

@ -32,6 +32,8 @@
</head> </head>
<body> <body>
<div id="snow-container"></div> <div id="snow-container"></div>
<nav class="navbar navbar-expand-lg navbar-dark shadow-sm"> <nav class="navbar navbar-expand-lg navbar-dark shadow-sm">