mirror of
https://github.com/sstent/foodplanner.git
synced 2026-01-25 03:01:35 +00:00
starting templates
This commit is contained in:
Binary file not shown.
41
main.py
41
main.py
@@ -192,6 +192,11 @@ def calculate_day_nutrition(plans, db: Session):
|
||||
async def root(request: Request):
|
||||
return templates.TemplateResponse("index.html", {"request": request})
|
||||
|
||||
# Imports tab
|
||||
@app.get("/imports", response_class=HTMLResponse)
|
||||
async def imports_page(request: Request):
|
||||
return templates.TemplateResponse("imports.html", {"request": request})
|
||||
|
||||
# Foods tab
|
||||
@app.get("/foods", response_class=HTMLResponse)
|
||||
async def foods_page(request: Request, db: Session = Depends(get_db)):
|
||||
@@ -601,37 +606,13 @@ async def remove_from_plan(plan_id: int, db: Session = Depends(get_db)):
|
||||
db.rollback()
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
# Detailed planner tab
|
||||
@app.get("/detailed", response_class=HTMLResponse)
|
||||
async def detailed_page(request: Request, person: str = "Person A",
|
||||
plan_day: str = None, db: Session = Depends(get_db)):
|
||||
|
||||
if not plan_day:
|
||||
plan_day = "Day1"
|
||||
|
||||
# Get all plans for the selected day
|
||||
plans = db.query(Plan).filter(Plan.person == person, Plan.date == plan_day).all()
|
||||
|
||||
# Group by meal type and calculate nutrition
|
||||
meal_details = []
|
||||
for plan in plans:
|
||||
meal_nutrition = calculate_meal_nutrition(plan.meal, db)
|
||||
meal_details.append({
|
||||
'plan': plan,
|
||||
'nutrition': meal_nutrition,
|
||||
'foods': plan.meal.meal_foods
|
||||
})
|
||||
|
||||
# Calculate day totals
|
||||
day_totals = calculate_day_nutrition(plans, db)
|
||||
|
||||
# Create list of all days for the selector
|
||||
days = [f"Day{i}" for i in range(1, 15)]
|
||||
|
||||
return templates.TemplateResponse("detailed.html", {
|
||||
"request": request, "person": person, "selected_day": plan_day,
|
||||
"meal_details": meal_details, "day_totals": day_totals, "days": days
|
||||
})
|
||||
async def detailed(request: Request):
|
||||
return templates.TemplateResponse("detailed.html", {"request": request, "title": "Detailed"})
|
||||
|
||||
@app.get("/templates", response_class=HTMLResponse)
|
||||
async def templates_page(request: Request):
|
||||
return templates.TemplateResponse("plans.html", {"request": request, "title": "Templates"})
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
@@ -67,15 +67,21 @@
|
||||
<i class="bi bi-egg-fried"></i> Meals
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" onclick="location.href='/imports'">
|
||||
<i class="bi bi-upload"></i> Imports
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" onclick="location.href='/plan'">
|
||||
<i class="bi bi-calendar-week"></i> Plan
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" onclick="location.href='/detailed'">
|
||||
<i class="bi bi-calendar-day"></i> Detailed
|
||||
</button>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/detailed">Detailed</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/templates">Templates</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -1,31 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h3>Bulk Import</h3>
|
||||
<form action="/foods/upload" method="post" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">CSV File</label>
|
||||
<input type="file" class="form-control" name="file" accept=".csv" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary mb-4">Upload CSV</button>
|
||||
</form>
|
||||
|
||||
<button type="button" class="btn btn-primary btn-lg" data-bs-toggle="modal" data-bs-target="#addFoodModal">
|
||||
<div class="col-md-12">
|
||||
<button type="button" class="btn btn-primary btn-lg mb-4" data-bs-toggle="modal" data-bs-target="#addFoodModal">
|
||||
<i class="bi bi-plus-circle"></i> Add New Food
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8" id="upload-results" style="display: none;">
|
||||
<div class="alert alert-success">
|
||||
<strong>Upload Results:</strong>
|
||||
<span id="created-count"></span> created,
|
||||
<span id="updated-count"></span> updated
|
||||
<div id="error-list" class="mt-2 text-danger"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
|
||||
<h3>Foods Database</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
@@ -59,7 +39,7 @@
|
||||
<td>{{ "%.2f"|format(food.calcium) }}mg</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary"
|
||||
onclick="editFood({{ food.id }}, '{{ food.name }}', '{{ food.serving_size }}', '{{ food.serving_unit }}', {{ food.calories }}, {{ food.protein }}, {{ food.carbs }}, {{ food.fat }}, {{ food.fiber }}, {{ food.sugar }}, {{ food.sodium }}, {{ food.calcium }})">
|
||||
onclick="editFood({{ food.id }})">
|
||||
<i class="bi bi-pencil"></i> Edit
|
||||
</button>
|
||||
</td>
|
||||
@@ -229,21 +209,10 @@
|
||||
|
||||
<script>
|
||||
// Edit food function
|
||||
function editFood(id, name, serving_size, serving_unit, calories, protein, carbs, fat, fiber, sugar, sodium, calcium) {
|
||||
document.getElementById('edit_food_id').value = id;
|
||||
document.getElementById('edit_name').value = name;
|
||||
document.getElementById('edit_serving_size').value = serving_size;
|
||||
document.getElementById('edit_serving_unit').value = serving_unit;
|
||||
document.getElementById('edit_calories').value = calories;
|
||||
document.getElementById('edit_protein').value = protein;
|
||||
document.getElementById('edit_carbs').value = carbs;
|
||||
document.getElementById('edit_fat').value = fat;
|
||||
document.getElementById('edit_fiber').value = fiber;
|
||||
document.getElementById('edit_sugar').value = sugar;
|
||||
document.getElementById('edit_sodium').value = sodium;
|
||||
document.getElementById('edit_calcium').value = calcium;
|
||||
|
||||
new bootstrap.Modal(document.getElementById('editFoodModal')).show();
|
||||
function editFood(foodId) {
|
||||
// This function will be implemented after page load using the food data
|
||||
// For now, we'll use a placeholder that will be replaced by the actual implementation
|
||||
console.log("Edit food called for ID:", foodId);
|
||||
}
|
||||
|
||||
// Submit food form (add or edit)
|
||||
@@ -302,50 +271,46 @@ async function deleteSelectedFoods() {
|
||||
}
|
||||
}
|
||||
|
||||
// CSV upload handling
|
||||
document.querySelector('form[action="/foods/upload"]').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const form = e.target;
|
||||
const submitBtn = form.querySelector('button[type="submit"]');
|
||||
const resultsDiv = document.getElementById('upload-results');
|
||||
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status"></span> Uploading...';
|
||||
|
||||
try {
|
||||
const formData = new FormData(form);
|
||||
const response = await fetch('/foods/upload', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
// Initialize editFood function after page load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const foodsData = {
|
||||
{% for food in foods %}
|
||||
{{ food.id }}: {
|
||||
name: '{{ food.name | replace("'", "\\'") }}',
|
||||
serving_size: '{{ food.serving_size | replace("'", "\\'") }}',
|
||||
serving_unit: '{{ food.serving_unit | replace("'", "\\'") }}',
|
||||
calories: {{ food.calories }},
|
||||
protein: {{ food.protein }},
|
||||
carbs: {{ food.carbs }},
|
||||
fat: {{ food.fat }},
|
||||
fiber: {{ food.fiber }},
|
||||
sugar: {{ food.sugar }},
|
||||
sodium: {{ food.sodium }},
|
||||
calcium: {{ food.calcium }}
|
||||
},
|
||||
{% endfor %}
|
||||
};
|
||||
|
||||
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
||||
// Implement the actual editFood function
|
||||
window.editFood = function(foodId) {
|
||||
const food = foodsData[foodId];
|
||||
if (!food) return;
|
||||
|
||||
const results = await response.json();
|
||||
|
||||
resultsDiv.style.display = 'block';
|
||||
document.getElementById('created-count').textContent = results.created || 0;
|
||||
document.getElementById('updated-count').textContent = results.updated || 0;
|
||||
|
||||
if (results.errors?.length > 0) {
|
||||
document.getElementById('error-list').innerHTML =
|
||||
`<strong>Errors (${results.errors.length}):</strong><br>` + results.errors.join('<br>');
|
||||
} else {
|
||||
document.getElementById('error-list').innerHTML = '';
|
||||
}
|
||||
|
||||
if (results.created || results.updated) {
|
||||
setTimeout(() => window.location.reload(), 2000);
|
||||
}
|
||||
} catch (error) {
|
||||
resultsDiv.style.display = 'block';
|
||||
resultsDiv.querySelector('.alert').className = 'alert alert-danger';
|
||||
document.getElementById('error-list').innerHTML =
|
||||
`<strong>Upload Failed:</strong> ${error.message}`;
|
||||
} finally {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Upload CSV';
|
||||
}
|
||||
document.getElementById('edit_food_id').value = foodId;
|
||||
document.getElementById('edit_name').value = food.name;
|
||||
document.getElementById('edit_serving_size').value = food.serving_size;
|
||||
document.getElementById('edit_serving_unit').value = food.serving_unit;
|
||||
document.getElementById('edit_calories').value = food.calories;
|
||||
document.getElementById('edit_protein').value = food.protein;
|
||||
document.getElementById('edit_carbs').value = food.carbs;
|
||||
document.getElementById('edit_fat').value = food.fat;
|
||||
document.getElementById('edit_fiber').value = food.fiber;
|
||||
document.getElementById('edit_sugar').value = food.sugar;
|
||||
document.getElementById('edit_sodium').value = food.sodium;
|
||||
document.getElementById('edit_calcium').value = food.calcium;
|
||||
|
||||
new bootstrap.Modal(document.getElementById('editFoodModal')).show();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -1,22 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h3>Bulk Import Meals</h3>
|
||||
<form action="/meals/upload" method="post" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">CSV File</label>
|
||||
<input type="file" class="form-control" name="file" accept=".csv" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary mb-4">Upload CSV</button>
|
||||
</form>
|
||||
|
||||
<button type="button" class="btn btn-primary btn-lg" data-bs-toggle="modal" data-bs-target="#addMealModal">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
|
||||
<h3>Meals</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
@@ -336,46 +325,5 @@ async function deleteSelectedMeals() {
|
||||
}
|
||||
}
|
||||
|
||||
// Meal CSV upload handling
|
||||
document.querySelector('form[action="/meals/upload"]').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const form = e.target;
|
||||
const submitBtn = form.querySelector('button[type="submit"]');
|
||||
const resultsDiv = document.createElement('div');
|
||||
resultsDiv.className = 'alert alert-info mt-3';
|
||||
form.parentNode.insertBefore(resultsDiv, form.nextSibling);
|
||||
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm"></span> Uploading...';
|
||||
|
||||
try {
|
||||
const formData = new FormData(form);
|
||||
const response = await fetch('/meals/upload', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const results = await response.json();
|
||||
|
||||
if (results.errors?.length > 0) {
|
||||
resultsDiv.className = 'alert alert-danger';
|
||||
resultsDiv.innerHTML = `<strong>Errors (${results.errors.length}):</strong><br>` +
|
||||
results.errors.join('<br>');
|
||||
} else {
|
||||
resultsDiv.className = 'alert alert-success';
|
||||
resultsDiv.innerHTML = `Successfully created ${results.created} meals, updated ${results.updated}`;
|
||||
}
|
||||
|
||||
if (results.created || results.updated) {
|
||||
setTimeout(() => window.location.reload(), 2000);
|
||||
}
|
||||
} catch (error) {
|
||||
resultsDiv.className = 'alert alert-danger';
|
||||
resultsDiv.textContent = `Upload failed: ${error.message}`;
|
||||
} finally {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Upload CSV';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user