add list
This commit is contained in:
parent
3830ef477f
commit
7b0ece6750
@ -294,6 +294,43 @@ footer {
|
||||
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 */
|
||||
.recipe-category-label {
|
||||
background-color: #E9F5EF;
|
||||
|
||||
@ -5,6 +5,7 @@ const app = {
|
||||
confirmedRecipeProducts: [],
|
||||
checkedItems: [],
|
||||
additionalProducts: [],
|
||||
selectedRecipeIds: [],
|
||||
user: null
|
||||
},
|
||||
api: {
|
||||
@ -18,6 +19,7 @@ const app = {
|
||||
app.state.checkedItems = data.user.shopping_list.checkedItems || [];
|
||||
app.state.additionalProducts = data.user.shopping_list.additionalProducts || [];
|
||||
app.state.confirmedRecipeProducts = data.user.shopping_list.confirmedRecipeProducts || [];
|
||||
app.state.selectedRecipeIds = data.user.shopping_list.selectedRecipeIds || [];
|
||||
}
|
||||
} else {
|
||||
app.state.user = null;
|
||||
@ -36,7 +38,8 @@ const app = {
|
||||
shopping_list: {
|
||||
checkedItems: app.state.checkedItems,
|
||||
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();
|
||||
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();
|
||||
app.ui.renderRecipeCards(app.state.recipes);
|
||||
app.ui.updateShoppingList();
|
||||
app.api.saveShoppingList();
|
||||
} else {
|
||||
alert('Failed to delete recipe: ' + data.error);
|
||||
}
|
||||
@ -166,8 +180,10 @@ const app = {
|
||||
cardCol.setAttribute('data-id', recipe.id);
|
||||
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');
|
||||
card.className = 'card h-100';
|
||||
card.className = `card h-100 recipe-selection-card ${isSelected ? 'selected' : ''}`;
|
||||
|
||||
if (recipe.image_url) {
|
||||
const img = document.createElement('img');
|
||||
@ -183,10 +199,15 @@ const app = {
|
||||
const titleWrapper = document.createElement('div');
|
||||
titleWrapper.className = 'd-flex justify-content-between align-items-start mb-2 gap-2';
|
||||
|
||||
const title = document.createElement('h5');
|
||||
title.className = 'card-title mb-0';
|
||||
title.textContent = recipe.name;
|
||||
titleWrapper.appendChild(title);
|
||||
const selectionWrapper = document.createElement('div');
|
||||
selectionWrapper.className = 'form-check mb-0';
|
||||
selectionWrapper.innerHTML = `
|
||||
<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) {
|
||||
const categoryLabel = document.createElement('div');
|
||||
@ -199,19 +220,35 @@ const app = {
|
||||
}
|
||||
|
||||
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`;
|
||||
|
||||
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');
|
||||
buttonGroup.className = 'mt-auto pt-2';
|
||||
buttonGroup.className = 'mt-auto pt-2 d-flex gap-2';
|
||||
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 edit-recipe"><i class="bi bi-pencil"></i> Edit</button>
|
||||
<button class="btn btn-danger btn-sm delete-recipe" title="Delete"><i class="bi bi-trash"></i></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></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(text);
|
||||
cardBody.appendChild(controlsWrapper);
|
||||
cardBody.appendChild(buttonGroup);
|
||||
card.appendChild(cardBody);
|
||||
cardCol.appendChild(card);
|
||||
@ -223,6 +260,11 @@ const app = {
|
||||
|
||||
// 1. Process recipe ingredients and calculate total based on per-recipe inputs
|
||||
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}"]`);
|
||||
if (!card) return;
|
||||
|
||||
@ -699,10 +741,34 @@ const app = {
|
||||
|
||||
app.dom.recipeCardsContainer.addEventListener('click', function(e) {
|
||||
const target = e.target.closest('button');
|
||||
const card = e.target.closest('.col-12[data-id]');
|
||||
if (!card) return;
|
||||
const checkbox = e.target.closest('.select-recipe');
|
||||
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 (confirm('Are you sure you want to delete this recipe?')) {
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
<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 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>
|
||||
<body>
|
||||
@ -380,7 +380,7 @@
|
||||
|
||||
<!-- Scripts -->
|
||||
<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 -->
|
||||
<div class="modal fade" id="confirmRemoveModal" tabindex="-1" aria-labelledby="confirmRemoveModalLabel" aria-hidden="true">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user