mitai-jinkendo/backend/routers/app_dashboard.py
Lars 141df021c1
All checks were successful
Deploy Development / deploy (push) Successful in 1m2s
Build Test / pytest-backend (push) Successful in 5s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
refactor: rename Dashboard-Lab-Widgets to Dashboard-Widgets and update related documentation
- Renamed references from "Dashboard-Lab-Widgets" to "Dashboard-Widgets" across documentation and codebase for consistency.
- Removed the deprecated Dashboard-Lab page and integrated its functionality into the new Dashboard-Widgets layout.
- Updated widget registration and configuration handling to reflect the new naming convention.
- Adjusted documentation in `.claude/docs/technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md` and other related files to ensure clarity on the updated structure.
- Bumped application version to reflect these changes.
2026-04-23 16:18:10 +02:00

119 lines
4.0 KiB
Python

"""
Geschützter App-Bereich: Dashboard-Layout und Widget-Katalog.
/api/app/dashboard-layout — nur mit Session + aktivem Profil (X-Profile-Id).
"""
from typing import Any, Optional
from fastapi import APIRouter, Depends, Header, HTTPException
from psycopg2.extras import Json
from auth import require_auth
from dashboard_layout_schema import (
DashboardLayoutPayload,
coalesce_effective_layout,
lab_default_layout_dict,
merge_missing_catalog_widgets,
)
from dashboard_widget_entitlements import apply_entitlements_to_layout_dict, widgets_catalog_payload
from db import get_cursor, get_db
from routers.profiles import get_pid
from system_dashboard_product_default import get_product_default_base_dict
router = APIRouter(prefix="/api/app", tags=["app-dashboard"])
@router.get("/widgets/catalog")
def get_widgets_catalog(
x_profile_id: Optional[str] = Header(default=None),
session: dict = Depends(require_auth),
) -> dict[str, Any]:
"""Katalog inkl. allowed pro Widget (Feature / Subscription, effektiver Tier)."""
_ = session
pid = get_pid(x_profile_id)
with get_db() as conn:
return widgets_catalog_payload(pid, conn)
@router.get("/dashboard-layout")
def get_dashboard_layout(
x_profile_id: Optional[str] = Header(default=None),
session: dict = Depends(require_auth),
) -> dict[str, Any]:
_ = session
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"SELECT dashboard_layout FROM profiles WHERE id = %s",
(pid,),
)
row = cur.fetchone()
raw = row["dashboard_layout"] if row else None
custom, effective = coalesce_effective_layout(raw)
with get_db() as conn:
base_product = merge_missing_catalog_widgets(get_product_default_base_dict(conn))
if not custom:
effective = base_product
else:
effective = merge_missing_catalog_widgets(effective)
effective = apply_entitlements_to_layout_dict(effective, pid, conn)
product_adj = apply_entitlements_to_layout_dict(base_product, pid, conn)
lab_adj = apply_entitlements_to_layout_dict(lab_default_layout_dict(), pid, conn)
return {
"custom": custom,
"layout": effective,
"product_default_layout": product_adj,
"lab_default_layout": lab_adj,
}
@router.put("/dashboard-layout")
def put_dashboard_layout(
body: dict[str, Any],
x_profile_id: Optional[str] = Header(default=None),
session: dict = Depends(require_auth),
) -> dict[str, Any]:
_ = session
pid = get_pid(x_profile_id)
try:
payload = DashboardLayoutPayload.model_validate(body)
except Exception as e:
raise HTTPException(422, str(e)) from e
with get_db() as conn:
adjusted = apply_entitlements_to_layout_dict(payload.to_stored_dict(), pid, conn)
try:
payload = DashboardLayoutPayload.model_validate(adjusted)
except Exception as e:
raise HTTPException(422, str(e)) from e
stored = payload.to_stored_dict()
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"UPDATE profiles SET dashboard_layout = %s WHERE id = %s",
(Json(stored), pid),
)
if cur.rowcount == 0:
raise HTTPException(404, "Profil nicht gefunden")
return {"ok": True, "layout": stored}
@router.post("/dashboard-layout/reset")
def reset_dashboard_layout(
x_profile_id: Optional[str] = Header(default=None),
session: dict = Depends(require_auth),
) -> dict[str, Any]:
_ = session
pid = get_pid(x_profile_id)
with get_db() as conn:
cur = get_cursor(conn)
cur.execute(
"UPDATE profiles SET dashboard_layout = NULL WHERE id = %s",
(pid,),
)
if cur.rowcount == 0:
raise HTTPException(404, "Profil nicht gefunden")
base = get_product_default_base_dict(conn)
cleared = apply_entitlements_to_layout_dict(base, pid, conn)
return {"ok": True, "layout": cleared}