add list
This commit is contained in:
parent
3830ef477f
commit
7b0ece6750
@ -294,6 +294,43 @@ footer {
|
|||||||
padding: 20px 24px;
|
padding: 20px 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Recipe Card Selection */
|
||||||
|
.recipe-selection-card {
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
border: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recipe-selection-card.selected {
|
||||||
|
border-color: var(--brand-primary);
|
||||||
|
box-shadow: 0 15px 45px rgba(45, 106, 79, 0.15);
|
||||||
|
background-color: #F7FAF9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recipe-selection-card .select-recipe:checked {
|
||||||
|
background-color: var(--brand-primary);
|
||||||
|
border-color: var(--brand-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recipe-controls {
|
||||||
|
border: 1px solid #EAEAEA;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recipe-selection-card.selected .recipe-controls {
|
||||||
|
background-color: #ffffff !important;
|
||||||
|
border-color: var(--brand-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.recipe-selection-card .form-check-input {
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recipe-selection-card .form-check-label {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Category Label */
|
/* Category Label */
|
||||||
.recipe-category-label {
|
.recipe-category-label {
|
||||||
background-color: #E9F5EF;
|
background-color: #E9F5EF;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const app = {
|
|||||||
confirmedRecipeProducts: [],
|
confirmedRecipeProducts: [],
|
||||||
checkedItems: [],
|
checkedItems: [],
|
||||||
additionalProducts: [],
|
additionalProducts: [],
|
||||||
|
selectedRecipeIds: [],
|
||||||
user: null
|
user: null
|
||||||
},
|
},
|
||||||
api: {
|
api: {
|
||||||
@ -18,6 +19,7 @@ const app = {
|
|||||||
app.state.checkedItems = data.user.shopping_list.checkedItems || [];
|
app.state.checkedItems = data.user.shopping_list.checkedItems || [];
|
||||||
app.state.additionalProducts = data.user.shopping_list.additionalProducts || [];
|
app.state.additionalProducts = data.user.shopping_list.additionalProducts || [];
|
||||||
app.state.confirmedRecipeProducts = data.user.shopping_list.confirmedRecipeProducts || [];
|
app.state.confirmedRecipeProducts = data.user.shopping_list.confirmedRecipeProducts || [];
|
||||||
|
app.state.selectedRecipeIds = data.user.shopping_list.selectedRecipeIds || [];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
app.state.user = null;
|
app.state.user = null;
|
||||||
@ -36,7 +38,8 @@ const app = {
|
|||||||
shopping_list: {
|
shopping_list: {
|
||||||
checkedItems: app.state.checkedItems,
|
checkedItems: app.state.checkedItems,
|
||||||
additionalProducts: app.state.additionalProducts,
|
additionalProducts: app.state.additionalProducts,
|
||||||
confirmedRecipeProducts: app.state.confirmedRecipeProducts
|
confirmedRecipeProducts: app.state.confirmedRecipeProducts,
|
||||||
|
selectedRecipeIds: app.state.selectedRecipeIds
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -141,9 +144,20 @@ const app = {
|
|||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
|
// Remove from selectedRecipeIds if present
|
||||||
|
const index = app.state.selectedRecipeIds.indexOf(id.toString());
|
||||||
|
if (index > -1) {
|
||||||
|
app.state.selectedRecipeIds.splice(index, 1);
|
||||||
|
}
|
||||||
|
const indexNum = app.state.selectedRecipeIds.indexOf(Number(id));
|
||||||
|
if (indexNum > -1) {
|
||||||
|
app.state.selectedRecipeIds.splice(indexNum, 1);
|
||||||
|
}
|
||||||
|
|
||||||
await app.api.getRecipes();
|
await app.api.getRecipes();
|
||||||
app.ui.renderRecipeCards(app.state.recipes);
|
app.ui.renderRecipeCards(app.state.recipes);
|
||||||
app.ui.updateShoppingList();
|
app.ui.updateShoppingList();
|
||||||
|
app.api.saveShoppingList();
|
||||||
} else {
|
} else {
|
||||||
alert('Failed to delete recipe: ' + data.error);
|
alert('Failed to delete recipe: ' + data.error);
|
||||||
}
|
}
|
||||||
@ -166,8 +180,10 @@ const app = {
|
|||||||
cardCol.setAttribute('data-id', recipe.id);
|
cardCol.setAttribute('data-id', recipe.id);
|
||||||
cardCol.style.animationDelay = `${index * 0.1}s`;
|
cardCol.style.animationDelay = `${index * 0.1}s`;
|
||||||
|
|
||||||
|
const isSelected = app.state.selectedRecipeIds.includes(recipe.id.toString()) || app.state.selectedRecipeIds.includes(Number(recipe.id));
|
||||||
|
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
card.className = 'card h-100';
|
card.className = `card h-100 recipe-selection-card ${isSelected ? 'selected' : ''}`;
|
||||||
|
|
||||||
if (recipe.image_url) {
|
if (recipe.image_url) {
|
||||||
const img = document.createElement('img');
|
const img = document.createElement('img');
|
||||||
@ -183,10 +199,15 @@ const app = {
|
|||||||
const titleWrapper = document.createElement('div');
|
const titleWrapper = document.createElement('div');
|
||||||
titleWrapper.className = 'd-flex justify-content-between align-items-start mb-2 gap-2';
|
titleWrapper.className = 'd-flex justify-content-between align-items-start mb-2 gap-2';
|
||||||
|
|
||||||
const title = document.createElement('h5');
|
const selectionWrapper = document.createElement('div');
|
||||||
title.className = 'card-title mb-0';
|
selectionWrapper.className = 'form-check mb-0';
|
||||||
title.textContent = recipe.name;
|
selectionWrapper.innerHTML = `
|
||||||
titleWrapper.appendChild(title);
|
<input class="form-check-input select-recipe" type="checkbox" value="${recipe.id}" id="select-recipe-${recipe.id}" ${isSelected ? 'checked' : ''}>
|
||||||
|
<label class="form-check-label fw-bold h5 mb-0 ms-1" for="select-recipe-${recipe.id}">
|
||||||
|
${recipe.name}
|
||||||
|
</label>
|
||||||
|
`;
|
||||||
|
titleWrapper.appendChild(selectionWrapper);
|
||||||
|
|
||||||
if (recipe.category) {
|
if (recipe.category) {
|
||||||
const categoryLabel = document.createElement('div');
|
const categoryLabel = document.createElement('div');
|
||||||
@ -199,19 +220,35 @@ const app = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const text = document.createElement('p');
|
const text = document.createElement('p');
|
||||||
text.className = 'card-text text-muted';
|
text.className = 'card-text text-muted mb-3';
|
||||||
text.textContent = `${recipe.ingredients.length} ingredients`;
|
text.textContent = `${recipe.ingredients.length} ingredients`;
|
||||||
|
|
||||||
|
const controlsWrapper = document.createElement('div');
|
||||||
|
controlsWrapper.className = 'recipe-controls mb-3 p-2 bg-light rounded';
|
||||||
|
controlsWrapper.innerHTML = `
|
||||||
|
<div class="row g-2 align-items-center">
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="small text-muted mb-1 d-block">Guests</label>
|
||||||
|
<input type="number" class="form-control form-control-sm recipe-guests-input" value="${recipe.guests}" min="1">
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="small text-muted mb-1 d-block">Portions</label>
|
||||||
|
<input type="number" class="form-control form-control-sm recipe-portions-input" value="1" min="1">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
const buttonGroup = document.createElement('div');
|
const buttonGroup = document.createElement('div');
|
||||||
buttonGroup.className = 'mt-auto pt-2';
|
buttonGroup.className = 'mt-auto pt-2 d-flex gap-2';
|
||||||
buttonGroup.innerHTML = `
|
buttonGroup.innerHTML = `
|
||||||
<button class="btn btn-light btn-sm view-recipe"><i class="bi bi-eye"></i> View</button>
|
<button class="btn btn-light btn-sm view-recipe flex-grow-1"><i class="bi bi-eye"></i> View</button>
|
||||||
<button class="btn btn-light btn-sm edit-recipe"><i class="bi bi-pencil"></i> Edit</button>
|
<button class="btn btn-light btn-sm edit-recipe"><i class="bi bi-pencil"></i></button>
|
||||||
<button class="btn btn-danger btn-sm delete-recipe" title="Delete"><i class="bi bi-trash"></i></button>
|
<button class="btn btn-outline-danger btn-sm delete-recipe" title="Delete"><i class="bi bi-trash"></i></button>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
cardBody.appendChild(titleWrapper);
|
cardBody.appendChild(titleWrapper);
|
||||||
cardBody.appendChild(text);
|
cardBody.appendChild(text);
|
||||||
|
cardBody.appendChild(controlsWrapper);
|
||||||
cardBody.appendChild(buttonGroup);
|
cardBody.appendChild(buttonGroup);
|
||||||
card.appendChild(cardBody);
|
card.appendChild(cardBody);
|
||||||
cardCol.appendChild(card);
|
cardCol.appendChild(card);
|
||||||
@ -223,6 +260,11 @@ const app = {
|
|||||||
|
|
||||||
// 1. Process recipe ingredients and calculate total based on per-recipe inputs
|
// 1. Process recipe ingredients and calculate total based on per-recipe inputs
|
||||||
app.state.recipes.forEach(recipe => {
|
app.state.recipes.forEach(recipe => {
|
||||||
|
// Only include if selected
|
||||||
|
if (!app.state.selectedRecipeIds.includes(recipe.id.toString()) && !app.state.selectedRecipeIds.includes(Number(recipe.id))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const card = app.dom.recipeCardsContainer.querySelector(`[data-id="${recipe.id}"]`);
|
const card = app.dom.recipeCardsContainer.querySelector(`[data-id="${recipe.id}"]`);
|
||||||
if (!card) return;
|
if (!card) return;
|
||||||
|
|
||||||
@ -699,10 +741,34 @@ const app = {
|
|||||||
|
|
||||||
app.dom.recipeCardsContainer.addEventListener('click', function(e) {
|
app.dom.recipeCardsContainer.addEventListener('click', function(e) {
|
||||||
const target = e.target.closest('button');
|
const target = e.target.closest('button');
|
||||||
const card = e.target.closest('.col-12[data-id]');
|
const checkbox = e.target.closest('.select-recipe');
|
||||||
if (!card) return;
|
const cardCol = e.target.closest('.col-12[data-id]');
|
||||||
|
if (!cardCol) return;
|
||||||
|
|
||||||
const recipeId = card.getAttribute('data-id');
|
const recipeId = cardCol.getAttribute('data-id');
|
||||||
|
|
||||||
|
if (checkbox) {
|
||||||
|
const card = cardCol.querySelector('.card');
|
||||||
|
if (checkbox.checked) {
|
||||||
|
if (!app.state.selectedRecipeIds.includes(recipeId)) {
|
||||||
|
app.state.selectedRecipeIds.push(recipeId);
|
||||||
|
}
|
||||||
|
card.classList.add('selected');
|
||||||
|
} else {
|
||||||
|
const index = app.state.selectedRecipeIds.indexOf(recipeId);
|
||||||
|
if (index > -1) {
|
||||||
|
app.state.selectedRecipeIds.splice(index, 1);
|
||||||
|
}
|
||||||
|
const indexNum = app.state.selectedRecipeIds.indexOf(Number(recipeId));
|
||||||
|
if (indexNum > -1) {
|
||||||
|
app.state.selectedRecipeIds.splice(indexNum, 1);
|
||||||
|
}
|
||||||
|
card.classList.remove('selected');
|
||||||
|
}
|
||||||
|
app.ui.updateShoppingList();
|
||||||
|
app.api.saveShoppingList();
|
||||||
|
return; // Don't trigger other actions
|
||||||
|
}
|
||||||
|
|
||||||
if (target && target.classList.contains('delete-recipe')) {
|
if (target && target.classList.contains('delete-recipe')) {
|
||||||
if (confirm('Are you sure you want to delete this recipe?')) {
|
if (confirm('Are you sure you want to delete this recipe?')) {
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>_v16">
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>_v17">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -380,7 +380,7 @@
|
|||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="assets/js/main.js?v=<?php echo time(); ?>_v6"></script>
|
<script src="assets/js/main.js?v=<?php echo time(); ?>_v7"></script>
|
||||||
|
|
||||||
<!-- Confirmation Modal -->
|
<!-- Confirmation Modal -->
|
||||||
<div class="modal fade" id="confirmRemoveModal" tabindex="-1" aria-labelledby="confirmRemoveModalLabel" aria-hidden="true">
|
<div class="modal fade" id="confirmRemoveModal" tabindex="-1" aria-labelledby="confirmRemoveModalLabel" aria-hidden="true">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user