Task 5.25: Dashboard-System — GET /api/v1/dashboard/widgets, DashboardWidgetLoader, DashboardGrid with drag-and-drop, 3 example widgets (RecentContacts, TasksSummary, CalendarUpcoming), updated Dashboard.tsx, i18n, 6 tests

This commit is contained in:
Agent Zero
2026-07-24 00:09:26 +02:00
parent 96e183bab2
commit 036e87a9ed
15 changed files with 544 additions and 6 deletions
+2
View File
@@ -30,6 +30,7 @@ from app.routes import (
auth,
contact_folders,
contacts,
dashboard,
entity_history,
groups,
health,
@@ -293,6 +294,7 @@ def create_app() -> FastAPI:
app.include_router(notifications.router)
app.include_router(contacts.router)
app.include_router(contact_folders.router)
app.include_router(dashboard.router)
app.include_router(entity_history.router)
app.include_router(import_export.router)
app.include_router(plugins.router)
+1
View File
@@ -6,6 +6,7 @@ from app.routes import (
audit, # noqa: F401
auth, # noqa: F401
contacts, # noqa: F401
dashboard, # noqa: F401
entity_history, # noqa: F401
currencies, # noqa: F401
taxes, # noqa: F401
+38
View File
@@ -0,0 +1,38 @@
"""Dashboard routes — list available widgets from active plugins (Task 5.25)."""
from __future__ import annotations
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.db import get_db
from app.deps import require_permission
from app.plugins.registry import get_registry
router = APIRouter(prefix="/api/v1/dashboard", tags=["dashboard"])
@router.get("/widgets")
async def list_dashboard_widgets(
db: AsyncSession = Depends(get_db),
current_user: dict = Depends(require_permission("dashboard:read")),
):
"""List all available dashboard widgets from active plugins.
Returns a flat list of widget definitions contributed by active plugins,
sorted by their order field. Each widget includes the contributing plugin name.
"""
registry = get_registry()
manifests = await registry.get_active_manifests(db)
widgets: list[dict] = []
for manifest in manifests:
for widget in manifest.get("dashboard_widgets", []):
widget_copy = dict(widget)
widget_copy["plugin_name"] = manifest["name"]
widgets.append(widget_copy)
# Sort by order field
widgets.sort(key=lambda w: w.get("order", 100))
return {"items": widgets, "total": len(widgets)}