Feature: Einstiegsseite, Einkaufsliste, Wochenplan-Live-Suche & Zufallsgenerator-Optimierung
This commit is contained in:
+134
-5
@@ -41,7 +41,15 @@ from .database import (
|
||||
get_meal_plan,
|
||||
set_meal_plan_slot,
|
||||
delete_meal_plan_slot,
|
||||
clear_meal_plan_week
|
||||
clear_meal_plan_week,
|
||||
get_shopping_list,
|
||||
add_shopping_item,
|
||||
add_shopping_items_bulk,
|
||||
toggle_shopping_item,
|
||||
delete_shopping_item,
|
||||
delete_checked_shopping_items,
|
||||
clear_shopping_list,
|
||||
get_meal_plan_ingredients
|
||||
)
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
@@ -172,6 +180,15 @@ class MealPlanSlotRequest(BaseModel):
|
||||
recipe_id: Optional[int] = None
|
||||
custom_text: Optional[str] = None
|
||||
|
||||
class ShoppingItemCreate(BaseModel):
|
||||
item_name: str
|
||||
amount: Optional[str] = ""
|
||||
unit: Optional[str] = ""
|
||||
source: Optional[str] = ""
|
||||
|
||||
class ShoppingBulkCreate(BaseModel):
|
||||
items: List[ShoppingItemCreate]
|
||||
|
||||
|
||||
# ==========================================
|
||||
# AUTH VIEWS
|
||||
@@ -238,10 +255,43 @@ async def logout():
|
||||
# VIEWS
|
||||
# ==========================================
|
||||
|
||||
@app.get("/")
|
||||
async def index_view():
|
||||
"""Hauptroute leitet direkt auf das Kochbuch um"""
|
||||
return RedirectResponse(url="/cookbook", status_code=302)
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def home_view(request: Request):
|
||||
"""Einstiegsseite: Ausgeklapptes Menü / Hub"""
|
||||
all_recipes = get_all_recipes()
|
||||
today = date.today()
|
||||
iso_year, iso_week, _ = today.isocalendar()
|
||||
shopping_items = get_shopping_list()
|
||||
open_shopping_count = sum(1 for it in shopping_items if not it.get("checked"))
|
||||
|
||||
return templates.TemplateResponse(
|
||||
request=request,
|
||||
name="home.html",
|
||||
context={
|
||||
"recipe_count": len(all_recipes),
|
||||
"current_year": iso_year,
|
||||
"current_week": iso_week,
|
||||
"open_shopping_count": open_shopping_count,
|
||||
"chapters": CHAPTERS
|
||||
}
|
||||
)
|
||||
|
||||
@app.get("/shopping-list", response_class=HTMLResponse)
|
||||
@app.get("/groceries", response_class=HTMLResponse)
|
||||
async def shopping_list_view(request: Request):
|
||||
"""Einkaufsliste Übersicht"""
|
||||
items = get_shopping_list()
|
||||
today = date.today()
|
||||
iso_year, iso_week, _ = today.isocalendar()
|
||||
return templates.TemplateResponse(
|
||||
request=request,
|
||||
name="shopping_list.html",
|
||||
context={
|
||||
"items": items,
|
||||
"current_year": iso_year,
|
||||
"current_week": iso_week
|
||||
}
|
||||
)
|
||||
|
||||
@app.get("/cookbook", response_class=HTMLResponse)
|
||||
async def cookbook_view(
|
||||
@@ -854,3 +904,82 @@ def api_clear_mealplan_week(year: int = Query(...), week: int = Query(...)):
|
||||
count = clear_meal_plan_week(year, week)
|
||||
return {"status": "ok", "cleared_count": count}
|
||||
|
||||
|
||||
# ==========================================
|
||||
# SHOPPING LIST API
|
||||
# ==========================================
|
||||
|
||||
@app.get("/api/shopping-list")
|
||||
def api_get_shopping_list():
|
||||
items = get_shopping_list()
|
||||
return {"status": "ok", "items": items}
|
||||
|
||||
|
||||
@app.post("/api/shopping-list")
|
||||
def api_add_shopping_item(payload: ShoppingItemCreate):
|
||||
if not payload.item_name.strip():
|
||||
raise HTTPException(status_code=400, detail="Name des Eintrags darf nicht leer sein.")
|
||||
item = add_shopping_item(
|
||||
item_name=payload.item_name,
|
||||
amount=payload.amount or "",
|
||||
unit=payload.unit or "",
|
||||
source=payload.source or ""
|
||||
)
|
||||
return {"status": "ok", "item": item}
|
||||
|
||||
|
||||
@app.post("/api/shopping-list/bulk")
|
||||
def api_add_shopping_bulk(payload: ShoppingBulkCreate):
|
||||
items_dicts = [it.model_dump() for it in payload.items]
|
||||
count = add_shopping_items_bulk(items_dicts)
|
||||
return {"status": "ok", "added_count": count}
|
||||
|
||||
|
||||
@app.post("/api/shopping-list/{item_id}/toggle")
|
||||
def api_toggle_shopping_item(item_id: int):
|
||||
updated = toggle_shopping_item(item_id)
|
||||
if not updated:
|
||||
raise HTTPException(status_code=404, detail="Eintrag nicht gefunden.")
|
||||
return {"status": "ok", "item": updated}
|
||||
|
||||
|
||||
@app.delete("/api/shopping-list/checked")
|
||||
def api_delete_checked_shopping():
|
||||
count = delete_checked_shopping_items()
|
||||
return {"status": "ok", "deleted_count": count}
|
||||
|
||||
|
||||
@app.delete("/api/shopping-list/all")
|
||||
def api_clear_shopping_list():
|
||||
count = clear_shopping_list()
|
||||
return {"status": "ok", "cleared_count": count}
|
||||
|
||||
|
||||
@app.delete("/api/shopping-list/{item_id}")
|
||||
def api_delete_shopping_item(item_id: int):
|
||||
success = delete_shopping_item(item_id)
|
||||
return {"status": "ok", "deleted": success}
|
||||
|
||||
|
||||
# ==========================================
|
||||
# MEAL PLAN INGREDIENTS API
|
||||
# ==========================================
|
||||
|
||||
@app.get("/api/mealplan/ingredients")
|
||||
def api_get_mealplan_ingredients(
|
||||
year: Optional[int] = Query(None),
|
||||
week: Optional[int] = Query(None)
|
||||
):
|
||||
today = date.today()
|
||||
iso_year, iso_week, _ = today.isocalendar()
|
||||
y = year or iso_year
|
||||
w = week or iso_week
|
||||
groups = get_meal_plan_ingredients(y, w)
|
||||
return {
|
||||
"status": "ok",
|
||||
"year": y,
|
||||
"week": w,
|
||||
"groups": groups
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user