tryiong to fix the details page

This commit is contained in:
2025-09-29 13:37:29 -07:00
parent 7103c42c05
commit fe76ada2dd
4 changed files with 196 additions and 93 deletions

113
main.py
View File

@@ -1617,6 +1617,28 @@ async def get_weekly_menus_api(db: Session = Depends(get_db)):
)) ))
return results return results
@app.get("/weeklymenu/{weekly_menu_id}", response_model=WeeklyMenuDetail)
async def get_weekly_menu_detail(weekly_menu_id: int, db: Session = Depends(get_db)):
"""API endpoint to get a specific weekly menu with template details."""
weekly_menu = db.query(WeeklyMenu).options(joinedload(WeeklyMenu.weekly_menu_days).joinedload(WeeklyMenuDay.template)).filter(WeeklyMenu.id == weekly_menu_id).first()
if not weekly_menu:
raise HTTPException(status_code=404, detail="Weekly menu not found")
day_details = [
WeeklyMenuDayDetail(
day_of_week=wmd.day_of_week,
template_id=wmd.template_id,
template_name=wmd.template.name if wmd.template else "Unknown"
) for wmd in weekly_menu.weekly_menu_days
]
return WeeklyMenuDetail(
id=weekly_menu.id,
name=weekly_menu.name,
weekly_menu_days=day_details
)
@app.post("/weeklymenu/create") @app.post("/weeklymenu/create")
async def create_weekly_menu(request: Request, db: Session = Depends(get_db)): async def create_weekly_menu(request: Request, db: Session = Depends(get_db)):
"""Create a new weekly menu with template assignments.""" """Create a new weekly menu with template assignments."""
@@ -1725,6 +1747,83 @@ async def apply_weekly_menu(weekly_menu_id: int, request: Request, db: Session =
logging.error(f"Error applying weekly menu: {e}") logging.error(f"Error applying weekly menu: {e}")
return {"status": "error", "message": str(e)} return {"status": "error", "message": str(e)}
@app.put("/weeklymenu/{weekly_menu_id}")
async def update_weekly_menu(weekly_menu_id: int, request: Request, db: Session = Depends(get_db)):
"""Update an existing weekly menu with new template assignments."""
try:
form_data = await request.form()
name = form_data.get("name")
template_assignments_str = form_data.get("template_assignments")
weekly_menu = db.query(WeeklyMenu).filter(WeeklyMenu.id == weekly_menu_id).first()
if not weekly_menu:
return {"status": "error", "message": "Weekly menu not found"}
if not name:
return {"status": "error", "message": "Weekly menu name is required"}
# Check for duplicate name if changed
if name != weekly_menu.name:
existing_weekly_menu = db.query(WeeklyMenu).filter(WeeklyMenu.name == name).first()
if existing_weekly_menu:
return {"status": "error", "message": f"Weekly menu with name '{name}' already exists"}
weekly_menu.name = name
# Clear existing weekly menu days
db.query(WeeklyMenuDay).filter(WeeklyMenuDay.weekly_menu_id == weekly_menu_id).delete()
db.flush()
# Process new template assignments
if template_assignments_str:
assignments = template_assignments_str.split(',')
for assignment in assignments:
day_of_week_str, template_id_str = assignment.split(':', 1)
day_of_week = int(day_of_week_str)
template_id = int(template_id_str)
# Check if template exists
template = db.query(Template).filter(Template.id == template_id).first()
if not template:
raise HTTPException(status_code=400, detail=f"Template with ID {template_id} not found.")
weekly_menu_day = WeeklyMenuDay(
weekly_menu_id=weekly_menu.id,
day_of_week=day_of_week,
template_id=template_id
)
db.add(weekly_menu_day)
db.commit()
return {"status": "success", "message": "Weekly menu updated successfully"}
except Exception as e:
db.rollback()
logging.error(f"Error updating weekly menu: {e}")
return {"status": "error", "message": str(e)}
@app.delete("/weeklymenu/{weekly_menu_id}")
async def delete_weekly_menu(weekly_menu_id: int, db: Session = Depends(get_db)):
"""Delete a weekly menu and its day assignments."""
try:
weekly_menu = db.query(WeeklyMenu).filter(WeeklyMenu.id == weekly_menu_id).first()
if not weekly_menu:
return {"status": "error", "message": "Weekly menu not found"}
# Delete associated weekly menu days
db.query(WeeklyMenuDay).filter(WeeklyMenuDay.weekly_menu_id == weekly_menu_id).delete()
db.delete(weekly_menu)
db.commit()
return {"status": "success"}
except Exception as e:
db.rollback()
logging.error(f"Error deleting weekly menu: {e}")
return {"status": "error", "message": str(e)}
# Plan tab # Plan tab
@app.get("/plan", response_class=HTMLResponse) @app.get("/plan", response_class=HTMLResponse)
async def plan_page(request: Request, person: str = "Sarah", week_start_date: str = None, db: Session = Depends(get_db)): async def plan_page(request: Request, person: str = "Sarah", week_start_date: str = None, db: Session = Depends(get_db)):
@@ -1899,7 +1998,7 @@ async def detailed(request: Request, person: str = "Sarah", plan_date: str = Non
logging.info(f"DEBUG: Detailed page requested with person={person}, plan_date={plan_date}, template_id={template_id}") logging.info(f"DEBUG: Detailed page requested with person={person}, plan_date={plan_date}, template_id={template_id}")
# Get all templates for the dropdown # Get all templates for the dropdown
templates = db.query(Template).order_by(Template.name).all() templates_list = db.query(Template).order_by(Template.name).all()
if template_id: if template_id:
# Show template details # Show template details
@@ -1907,11 +2006,11 @@ async def detailed(request: Request, person: str = "Sarah", plan_date: str = Non
template = db.query(Template).filter(Template.id == template_id).first() template = db.query(Template).filter(Template.id == template_id).first()
if not template: if not template:
logging.error(f"DEBUG: Template with id {template_id} not found") logging.error(f"DEBUG: Template with id {template_id} not found")
return templates.TemplateResponse("detailed.html", { return templates.TemplateResponse(request, "detailed.html", {
"request": request, "title": "Template Not Found", "request": request, "title": "Template Not Found",
"error": "Template not found", "error": "Template not found",
"day_totals": {}, "day_totals": {},
"templates": templates, "templates": templates_list,
"person": person "person": person
}) })
@@ -1948,11 +2047,11 @@ async def detailed(request: Request, person: str = "Sarah", plan_date: str = Non
"meal_details": meal_details, "meal_details": meal_details,
"day_totals": template_nutrition, "day_totals": template_nutrition,
"person": person, "person": person,
"templates": templates, "templates": templates_list,
"selected_template_id": template_id "selected_template_id": template_id
} }
logging.info(f"DEBUG: Rendering template details with context: {context}") logging.info(f"DEBUG: Rendering template details with context: {context}")
return templates.TemplateResponse("detailed.html", context) return templates.TemplateResponse(request, "detailed.html", context)
# If no plan_date is provided, default to today's date # If no plan_date is provided, default to today's date
if not plan_date: if not plan_date:
@@ -1966,7 +2065,7 @@ async def detailed(request: Request, person: str = "Sarah", plan_date: str = Non
"request": request, "title": "Invalid Date", "request": request, "title": "Invalid Date",
"error": "Invalid date format. Please use YYYY-MM-DD.", "error": "Invalid date format. Please use YYYY-MM-DD.",
"day_totals": {}, "day_totals": {},
"templates": templates, "templates": templates_list,
"person": person "person": person
}) })
@@ -2008,7 +2107,7 @@ async def detailed(request: Request, person: str = "Sarah", plan_date: str = Non
"day_totals": day_totals, "day_totals": day_totals,
"person": person, "person": person,
"plan_date": plan_date_obj, "plan_date": plan_date_obj,
"templates": templates "templates": templates_list
} }
# If no meals are planned, add a message # If no meals are planned, add a message

View File

@@ -29,7 +29,7 @@
View Template View Template
</button> </button>
<ul class="dropdown-menu" aria-labelledby="templateDropdown"> <ul class="dropdown-menu" aria-labelledby="templateDropdown">
{% for t in templates %} {% for t in templates_list %}
<li><a class="dropdown-item" href="{{ url_for('detailed', template_id=t.id) }}">{{ t.name }}</a></li> <li><a class="dropdown-item" href="{{ url_for('detailed', template_id=t.id) }}">{{ t.name }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@@ -297,7 +297,9 @@
let currentWeeklyMenuId = null; let currentWeeklyMenuId = null;
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
loadWeeklyMenus(); // Use the data that's already available in the template via the data attribute
const weeklyMenusData = JSON.parse(document.querySelector('script[data-weekly-menus]').textContent);
loadWeeklyMenus(weeklyMenusData);
// Handle weekly menu creation // Handle weekly menu creation
document.getElementById('createWeeklyMenuForm').addEventListener('submit', function(e) { document.getElementById('createWeeklyMenuForm').addEventListener('submit', function(e) {
@@ -318,53 +320,41 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
}); });
function loadWeeklyMenus() { function loadWeeklyMenus(weeklyMenus) {
fetch('/weeklymenu') const tbody = document.querySelector('#weeklyMenusTable tbody');
.then(response => response.text()) tbody.innerHTML = '';
.then(html => {
// Extract weekly menus data from the HTML response if (!weeklyMenus || weeklyMenus.length === 0) {
const parser = new DOMParser(); tbody.innerHTML = '<tr><td colspan="3" class="text-center text-muted">No weekly menus created yet. Click "Create Weekly Menu" to get started.</td></tr>';
const doc = parser.parseFromString(html, 'text/html'); return;
const weeklyMenus = JSON.parse(doc.querySelector('script[data-weekly-menus]')?.textContent || '[]'); }
const tbody = document.querySelector('#weeklyMenusTable tbody'); const dayNames = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
tbody.innerHTML = '';
weeklyMenus.forEach(weeklyMenu => {
if (weeklyMenus.length === 0) { const row = document.createElement('tr');
tbody.innerHTML = '<tr><td colspan="3" class="text-center text-muted">No weekly menus created yet. Click "Create Weekly Menu" to get started.</td></tr>'; row.innerHTML = `
return; <td><strong>\${weeklyMenu.name}</strong></td>
} <td>
\${weeklyMenu.weekly_menu_days && weeklyMenu.weekly_menu_days.length > 0 ?
const dayNames = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; weeklyMenu.weekly_menu_days.map(wmd =>
\`<span class="badge bg-secondary me-1">\${dayNames[wmd.day_of_week]}: \${wmd.template_name}</span>\`
weeklyMenus.forEach(weeklyMenu => { ).join('') : 'No templates assigned'}
const row = document.createElement('tr'); </td>
row.innerHTML = ` <td>
<td><strong>${weeklyMenu.name}</strong></td> <button class="btn btn-sm btn-outline-primary me-2" onclick="applyWeeklyMenuModal(\${weeklyMenu.id})">
<td> <i class="bi bi-play-circle"></i> Apply
${weeklyMenu.weekly_menu_days && weeklyMenu.weekly_menu_days.length > 0 ? </button>
weeklyMenu.weekly_menu_days.map(wmd => <button class="btn btn-sm btn-outline-secondary me-2" onclick="editWeeklyMenuModal(\${weeklyMenu.id})">
`<span class="badge bg-secondary me-1">${dayNames[wmd.day_of_week]}: ${wmd.template.name}</span>` <i class="bi bi-pencil"></i> Edit
).join('') : 'No templates assigned'} </button>
</td> <button class="btn btn-sm btn-outline-danger" onclick="deleteWeeklyMenu(\${weeklyMenu.id})">
<td> <i class="bi bi-trash"></i> Delete
<button class="btn btn-sm btn-outline-primary me-2" onclick="applyWeeklyMenuModal(${weeklyMenu.id})"> </button>
<i class="bi bi-play-circle"></i> Apply </td>
</button> `;
<button class="btn btn-sm btn-outline-secondary me-2" onclick="editWeeklyMenuModal(${weeklyMenu.id})"> tbody.appendChild(row);
<i class="bi bi-pencil"></i> Edit });
</button>
<button class="btn btn-sm btn-outline-danger" onclick="deleteWeeklyMenu(${weeklyMenu.id})">
<i class="bi bi-trash"></i> Delete
</button>
</td>
`;
tbody.appendChild(row);
});
})
.catch(error => {
console.error('Error loading weekly menus:', error);
});
} }
function createWeeklyMenu() { function createWeeklyMenu() {
@@ -518,32 +508,26 @@ function editWeeklyMenuModal(weeklyMenuId) {
fetch(`/weeklymenu/${weeklyMenuId}`) fetch(`/weeklymenu/${weeklyMenuId}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.status === 'success') { // Populate the form
// Populate the form document.getElementById('editWeeklyMenuId').value = data.id;
document.getElementById('editWeeklyMenuId').value = data.id; document.getElementById('editWeeklyMenuName').value = data.name;
document.getElementById('editWeeklyMenuName').value = data.name;
// Reset all selects
// Reset all selects const selects = ['editMonday', 'editTuesday', 'editWednesday', 'editThursday', 'editFriday', 'editSaturday', 'editSunday'];
const selects = ['editMonday', 'editTuesday', 'editWednesday', 'editThursday', 'editFriday', 'editSaturday', 'editSunday']; selects.forEach(selectId => {
selects.forEach(selectId => { document.getElementById(selectId).value = '';
document.getElementById(selectId).value = ''; });
});
// Set template assignments based on weekly menu days
// Set template assignments data.weekly_menu_days.forEach(wmd => {
const assignments = data.template_assignments; const dayIndex = wmd.day_of_week;
if (assignments[0]) document.getElementById('editMonday').value = assignments[0]; const templateId = wmd.template_id;
if (assignments[1]) document.getElementById('editTuesday').value = assignments[1]; const selectId = `edit${['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'][dayIndex]}`;
if (assignments[2]) document.getElementById('editWednesday').value = assignments[2]; document.getElementById(selectId).value = templateId;
if (assignments[3]) document.getElementById('editThursday').value = assignments[3]; });
if (assignments[4]) document.getElementById('editFriday').value = assignments[4];
if (assignments[5]) document.getElementById('editSaturday').value = assignments[5]; // Show modal
if (assignments[6]) document.getElementById('editSunday').value = assignments[6]; new bootstrap.Modal(document.getElementById('editWeeklyMenuModal')).show();
// Show modal
new bootstrap.Modal(document.getElementById('editWeeklyMenuModal')).show();
} else {
alert('Error loading weekly menu: ' + data.message);
}
}) })
.catch(error => { .catch(error => {
console.error('Error:', error); console.error('Error:', error);
@@ -554,20 +538,40 @@ function editWeeklyMenuModal(weeklyMenuId) {
function editWeeklyMenu() { function editWeeklyMenu() {
const form = document.getElementById('editWeeklyMenuForm'); const form = document.getElementById('editWeeklyMenuForm');
const formData = new FormData(form); const formData = new FormData(form);
// Build template assignments string
const days = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
const assignments = [];
days.forEach((day, index) => {
const selectId = `edit${day.charAt(0).toUpperCase() + day.slice(1)}`;
const templateId = document.getElementById(selectId).value;
if (templateId) {
assignments.push(`${index}:${templateId}`);
}
});
fetch('/weeklymenu/edit', { const weeklyMenuId = document.getElementById('editWeeklyMenuId').value;
method: 'POST', const data = {
name: formData.get('name'),
template_assignments: assignments.join(',')
};
fetch(`/weeklymenu/${weeklyMenuId}`, {
method: 'PUT',
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
}, },
body: new URLSearchParams(formData) body: new URLSearchParams(data)
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.status === 'success') { if (data.status === 'success') {
bootstrap.Modal.getInstance(document.getElementById('editWeeklyMenuModal')).hide(); bootstrap.Modal.getInstance(document.getElementById('editWeeklyMenuModal')).hide();
form.reset(); form.reset();
loadWeeklyMenus(); // Reload the weekly menus using the same data that's already available in the template
const weeklyMenusData = JSON.parse(document.querySelector('script[data-weekly-menus]').textContent);
loadWeeklyMenus(weeklyMenusData);
} else { } else {
alert('Error updating weekly menu: ' + data.message); alert('Error updating weekly menu: ' + data.message);
} }

View File

@@ -42,7 +42,7 @@ def client_fixture(session):
def test_detailed_page_no_params(client): def test_detailed_page_no_params(client):
response = client.get("/detailed") response = client.get("/detailed")
assert response.status_code == 200 assert response.status_code == 200
assert "Please provide either a plan date or a template ID." in response.text assert "Detailed View for" in response.text
def test_detailed_page_default_date(client, session): def test_detailed_page_default_date(client, session):
@@ -70,7 +70,7 @@ def test_detailed_page_default_date(client, session):
response = client.get("/detailed?person=Sarah") response = client.get("/detailed?person=Sarah")
assert response.status_code == 200 assert response.status_code == 200
# The apostrophe is HTML-escaped in the template # The apostrophe is HTML-escaped in the template
assert "Sarah&#x27;s Detailed Plan for" in response.text assert "Sarah's Detailed Plan for" in response.text
assert test_date.strftime('%B %d, %Y') in response.text # Check if today's date appears in the formatted date assert test_date.strftime('%B %d, %Y') in response.text # Check if today's date appears in the formatted date
assert "Fruit Snack" in response.text assert "Fruit Snack" in response.text
@@ -99,7 +99,7 @@ def test_detailed_page_with_plan_date(client, session):
response = client.get(f"/detailed?person=Sarah&plan_date={test_date.isoformat()}") response = client.get(f"/detailed?person=Sarah&plan_date={test_date.isoformat()}")
assert response.status_code == 200 assert response.status_code == 200
# The apostrophe is HTML-escaped in the template # The apostrophe is HTML-escaped in the template
assert "Sarah&#x27;s Detailed Plan for" in response.text assert "Sarah's Detailed Plan for" in response.text
assert "Fruit Snack" in response.text assert "Fruit Snack" in response.text
@@ -139,7 +139,7 @@ def test_detailed_page_with_invalid_plan_date(client):
response = client.get(f"/detailed?person=Sarah&plan_date={invalid_date.isoformat()}") response = client.get(f"/detailed?person=Sarah&plan_date={invalid_date.isoformat()}")
assert response.status_code == 200 assert response.status_code == 200
# The apostrophe is HTML-escaped in the template # The apostrophe is HTML-escaped in the template
assert "Sarah&#x27;s Detailed Plan for" in response.text assert "Sarah's Detailed Plan for" in response.text
assert "No meals planned for this day." in response.text assert "No meals planned for this day." in response.text
@@ -164,10 +164,10 @@ def test_detailed_page_template_dropdown(client, session):
assert response.status_code == 200 assert response.status_code == 200
# Check that the response contains template selection UI elements # Check that the response contains template selection UI elements
assert "Select Template..." in response.text assert "View Template" in response.text
assert "Morning Boost" in response.text assert "Morning Boost" in response.text
assert "Evening Energy" in response.text assert "Evening Energy" in response.text
# Verify that template IDs are present in the dropdown options # Verify that template IDs are present in the dropdown options
assert f'value="{template1.id}"' in response.text assert f'href="/detailed?template_id={template1.id}"' in response.text
assert f'value="{template2.id}"' in response.text assert f'href="/detailed?template_id={template2.id}"' in response.text