mitai-jinkendo/backend/dashboard_widget_entitlements.py
Lars 9bc0cf70da
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
feat: Update widget catalog and enhance dashboard layout features
- Added new "Dashboard-Lab-Widgets" entry to the documentation for better guidance on widget configuration.
- Updated the app_dashboard version to 1.8.0 to reflect the introduction of widget catalog features and layout entitlements.
- Enhanced widget catalog entries to include optional feature requirements for better visibility and access control.
- Improved the DashboardLabPage to manage widget visibility based on feature entitlements, ensuring a more tailored user experience.
2026-04-08 07:21:49 +02:00

80 lines
2.5 KiB
Python
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.

"""
Dashboard-Widgets × Feature-System: Sichtbarkeit aus check_feature_access.
Katalog-Einträge optional `requires_feature` (features.id). Fehlt der Key → immer erlaubt.
"""
from __future__ import annotations
import copy
from typing import Any
from widget_catalog import WIDGET_CATALOG
def _check_feature_access(profile_id: str, feature_id: str, conn) -> dict:
"""Indirection für Tests (monkeypatch) und spätes Laden von auth (bcrypt)."""
from auth import check_feature_access
return check_feature_access(profile_id, feature_id, conn)
_WIDGET_ENTRY_BY_ID: dict[str, dict[str, Any]] = {e["id"]: e for e in WIDGET_CATALOG}
def widget_id_allowed(widget_id: str, profile_id: str, conn) -> bool:
entry = _WIDGET_ENTRY_BY_ID.get(widget_id)
if entry is None:
return False
fid = entry.get("requires_feature")
if not fid:
return True
return bool(_check_feature_access(profile_id, fid, conn)["allowed"])
def _public_row(entry: dict[str, Any], *, allowed: bool) -> dict[str, Any]:
return {
"id": entry["id"],
"title": entry["title"],
"description": entry["description"],
"allowed": allowed,
}
def widgets_catalog_for_profile(profile_id: str, conn) -> list[dict[str, Any]]:
"""Zeilen für GET /api/app/widgets/catalog (ohne internes requires_feature-Feld)."""
out: list[dict[str, Any]] = []
for e in WIDGET_CATALOG:
fid = e.get("requires_feature")
allowed = True
if fid:
allowed = bool(_check_feature_access(profile_id, fid, conn)["allowed"])
out.append(_public_row(e, allowed=allowed))
return out
def widgets_catalog_payload(profile_id: str, conn) -> dict[str, Any]:
return {
"catalog_version": 1,
"widgets": widgets_catalog_for_profile(profile_id, conn),
}
def apply_entitlements_to_layout_dict(layout: dict[str, Any], profile_id: str, conn) -> dict[str, Any]:
"""
Setzt enabled=False für Widgets ohne Berechtigung. Mindestens ein Widget bleibt aktiv (welcome).
"""
out = copy.deepcopy(layout)
widgets = out.get("widgets") or []
for w in widgets:
wid = w.get("id")
if not wid:
continue
if w.get("enabled") and not widget_id_allowed(wid, profile_id, conn):
w["enabled"] = False
if not any(w.get("enabled") for w in widgets):
for w in widgets:
if w.get("id") == "welcome":
w["enabled"] = True
break
return out