Files
foodplanner/templates/meals.html

309 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="col-md-12">
<button type="button" class="btn btn-primary btn-lg mb-4" data-bs-toggle="modal" data-bs-target="#addMealModal">
<i class="bi bi-plus-circle"></i> Create New Meal
</button>
<h3>Meals</h3>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th><input type="checkbox" id="select-all-meals" onclick="toggleAllMeals(this)"></th>
<th>Name</th>
<th>Food Items</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for meal in meals %}
<tr>
<td><input type="checkbox" name="selected_meals" value="{{ meal.id }}"></td>
<td>{{ meal.name }}</td>
<td>
{% if meal.meal_foods %}
<ul class="list-unstyled">
{% for meal_food in meal.meal_foods %}
<li>{{ meal_food.quantity }} × {{ meal_food.food.name }}</li>
{% endfor %}
</ul>
{% else %}
<em>No foods added</em>
{% endif %}
</td>
<td>
<button type="button" class="btn btn-sm btn-outline-primary"
onclick="manageMeal({{ meal.id }})">
<i class="bi bi-pencil-square"></i> Manage
</button>
<button type="button" class="btn btn-sm btn-outline-info"
onclick="cloneMeal({{ meal.id }})">
<i class="bi bi-front"></i> Clone
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<button class="btn btn-danger mt-3" onclick="deleteSelectedMeals()">
<i class="bi bi-trash"></i> Delete Selected Meals
</button>
</div>
</div>
<!-- Add Meal Modal -->
<div class="modal fade" id="addMealModal" tabindex="-1" aria-labelledby="addMealModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addMealModalLabel">Create New Meal</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="addMealForm" action="/meals/add" method="post">
<div class="mb-3">
<label class="form-label">Meal Name</label>
<input type="text" class="form-control" name="name" required>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="submitMealForm('add')">Create Meal</button>
</div>
</div>
</div>
</div>
{% include 'modals/manage_meal.html' %}
<script>
function toggleAllMeals(source) {
const checkboxes = document.querySelectorAll('input[name="selected_meals"]');
checkboxes.forEach(checkbox => checkbox.checked = source.checked);
}
// Manage meal function
async function manageMeal(mealId) {
document.getElementById('manage_meal_id').value = mealId;
document.getElementById('meal_id_for_food').value = mealId;
try {
const response = await fetch(`/meals/${mealId}`);
const meal = await response.json();
if (meal.status === 'success') {
document.getElementById('manage_meal_name').value = meal.name;
document.getElementById('manageMealModalLabel').textContent = `Manage Meal: ${meal.name}`;
await loadCurrentMealFoods(mealId);
new bootstrap.Modal(document.getElementById('manageMealModal')).show();
} else {
alert('Error fetching meal details: ' + meal.message);
}
} catch (error) {
alert('Error fetching meal details: ' + error.message);
}
}
// Load current meal foods
async function loadCurrentMealFoods(mealId) {
try {
const response = await fetch(`/meals/${mealId}/foods`);
const foods = await response.json();
const container = document.getElementById('currentMealFoods');
if (foods.length === 0) {
container.innerHTML = '<em>No foods added yet</em>';
} else {
container.innerHTML = foods.map(mf => `
<div class="d-flex justify-content-between align-items-center mb-2 p-2 bg-light rounded">
<div class="input-group">
<input type="number" step="0.01" class="form-control" value="${mf.quantity}" data-meal-food-id="${mf.id}" onchange="updateFoodQuantity(this)">
<span class="input-group-text">${mf.food_name}</span>
<button class="btn btn-outline-danger" onclick="removeFoodFromMeal(${mf.id})">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
`).join('');
}
} catch (error) {
console.error('Error loading meal foods:', error);
}
}
// Add food to meal
async function addFoodToMeal() {
const form = document.getElementById('addFoodToMealForm');
const formData = new FormData(form);
const mealId = formData.get('meal_id');
try {
const response = await fetch(`/meals/${mealId}/add_food`, {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.status === 'success') {
// Reset form and reload current foods
form.reset();
document.getElementById('meal_id_for_food').value = mealId;
await loadCurrentMealFoods(mealId);
} else {
alert('Error adding food: ' + result.message);
}
} catch (error) {
alert('Error adding food: ' + error.message);
}
}
// Remove food from meal
async function removeFoodFromMeal(mealFoodId) {
if (confirm('Remove this food from the meal?')) {
try {
const response = await fetch(`/meals/remove_food/${mealFoodId}`, {
method: 'DELETE'
});
const result = await response.json();
if (result.status === 'success') {
const mealId = document.getElementById('meal_id_for_food').value;
await loadCurrentMealFoods(mealId);
} else {
alert('Error removing food: ' + result.message);
}
} catch (error) {
alert('Error removing food: ' + error.message);
}
}
}
// Submit meal form (add or manage)
async function submitMealForm(action) {
const form = document.getElementById(action + 'MealForm');
if (action === 'manage') { // Handle name update separately
const mealId = document.getElementById('manage_meal_id').value;
const newName = document.getElementById('manage_meal_name').value;
const formData = new FormData();
formData.append('meal_id', mealId);
formData.append('name', newName);
try {
const response = await fetch('/meals/edit', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.status === 'success') {
document.getElementById('manageMealModalLabel').textContent = `Manage Meal: ${newName}`;
// No page reload, just confirmation
alert('Meal name updated successfully!');
} else {
alert('Error: ' + result.message);
}
} catch (error) {
alert('Error updating meal name: ' + error.message);
}
return;
}
const formData = new FormData(form);
try {
const response = await fetch(form.action, {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.status === 'success') {
// Close modal and reload page
bootstrap.Modal.getInstance(document.getElementById(action + 'MealModal')).hide();
location.reload();
} else {
alert('Error: ' + result.message);
}
} catch (error) {
alert('Error submitting form: ' + error.message);
}
}
// Delete selected meals
async function deleteSelectedMeals() {
const selected = Array.from(document.querySelectorAll('input[name="selected_meals"]:checked'))
.map(checkbox => checkbox.value);
if (selected.length === 0) {
alert('Please select meals to delete');
return;
}
if (confirm(`Delete ${selected.length} selected meals?`)) {
try {
const response = await fetch('/meals/delete', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({meal_ids: selected})
});
const result = await response.json();
if (response.ok) {
window.location.reload();
} else {
alert(`Delete failed: ${result.message || response.status}`);
}
} catch (error) {
alert('Delete failed: ' + error.message);
}
}
}
// Clone meal function
async function cloneMeal(mealId) {
if (confirm('Are you sure you want to clone this meal?')) {
try {
const response = await fetch(`/meals/clone/${mealId}`, {
method: 'POST'
});
const result = await response.json();
if (result.status === 'success') {
location.reload();
} else {
alert('Error cloning meal: ' + result.message);
}
} catch (error) {
alert('Error cloning meal: ' + error.message);
}
}
}
// Update food quantity
async function updateFoodQuantity(input) {
const mealFoodId = input.dataset.mealFoodId;
const quantity = input.value;
const formData = new FormData();
formData.append('meal_food_id', mealFoodId);
formData.append('quantity', quantity);
try {
const response = await fetch('/meals/update_food_quantity', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.status !== 'success') {
alert('Error updating quantity: ' + result.message);
}
} catch (error) {
alert('Error updating quantity: ' + error.message);
}
}
</script>
{% endblock %}