feat(dashboard): a curated default layout, merged duplicate widgets, a labelled Overview (#5213)
- New installs start from widgets.RECOMMENDED, packed row by row for the enabled plugins; the edit drawer can reset a dashboard to it. - Host Agent — Resources folds into Hosts — Overview (which gains load 1m), UniFi — Devices into UniFi — Overview; migration 0032 moves existing dashboards onto the survivors. - The Overview labels what it counts (hosts, monitors up/down/pending, alerts firing) and lists firing alerts and down monitors with since-times. - Uptime polls every 5 minutes (poll_seconds) instead of every cycle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ from steward.models.users import UserRole
|
||||
from steward.models.hosts import Host
|
||||
from steward.models.dashboard import Dashboard, DashboardWidget, DashboardShareToken
|
||||
from steward.core.settings import public_base_url
|
||||
from steward.core.widgets import get_available_widgets, WIDGET_REGISTRY
|
||||
from steward.core.widgets import get_available_widgets, recommended_layout, WIDGET_REGISTRY
|
||||
|
||||
dashboard_bp = Blueprint("dashboard", __name__)
|
||||
|
||||
@@ -92,16 +92,20 @@ async def _get_or_create_system_default(db) -> Dashboard:
|
||||
dash = Dashboard(name="Default", owner_id=None, is_default=True, is_shared=True)
|
||||
db.add(dash)
|
||||
await db.flush()
|
||||
plugins_cfg = current_app.config.get("PLUGINS", {})
|
||||
# Seed a 3-up grid (each widget 4 cols wide x 4 cells tall on the 12-col grid).
|
||||
for pos, w in enumerate(get_available_widgets(plugins_cfg)):
|
||||
db.add(DashboardWidget(
|
||||
dashboard_id=dash.id, widget_key=w["key"],
|
||||
grid_x=(pos % 3) * 4, grid_y=(pos // 3) * 4, grid_w=4, grid_h=4,
|
||||
))
|
||||
_seed_recommended(db, dash.id)
|
||||
return dash
|
||||
|
||||
|
||||
def _seed_recommended(db, dash_id: int) -> None:
|
||||
"""Place the recommended layout (widgets.RECOMMENDED) on a dashboard."""
|
||||
for item in recommended_layout(current_app.config.get("PLUGINS", {})):
|
||||
db.add(DashboardWidget(
|
||||
dashboard_id=dash_id, widget_key=item["key"],
|
||||
grid_x=item["x"], grid_y=item["y"], grid_w=item["w"], grid_h=item["h"],
|
||||
config_json=json.dumps(item["config"]) if item["config"] else None,
|
||||
))
|
||||
|
||||
|
||||
def _resolve_widgets(widgets: list[DashboardWidget]) -> list[dict]:
|
||||
plugins_cfg = current_app.config.get("PLUGINS", {})
|
||||
out = []
|
||||
@@ -384,6 +388,25 @@ async def remove_widget(dash_id: int, widget_id: int):
|
||||
return ("", 204)
|
||||
|
||||
|
||||
@dashboard_bp.post("/d/<int:dash_id>/edit/reset")
|
||||
@require_role(UserRole.viewer)
|
||||
async def reset_layout(dash_id: int):
|
||||
"""Replace a dashboard's widgets with the recommended layout (#5213). The
|
||||
edit drawer asks first: widgets added by hand and their titles go."""
|
||||
user_id = current_user_id()
|
||||
user_role = session.get("user_role", "viewer")
|
||||
async with current_app.db_sessionmaker() as db:
|
||||
async with db.begin():
|
||||
dash = (await db.execute(select(Dashboard).where(Dashboard.id == dash_id))).scalar_one_or_none()
|
||||
if dash is None or not _can_edit(dash, user_id, user_role):
|
||||
abort(403)
|
||||
for w in await _get_widgets(db, dash_id):
|
||||
await db.delete(w)
|
||||
await db.flush()
|
||||
_seed_recommended(db, dash_id)
|
||||
return redirect(f"/d/{dash_id}")
|
||||
|
||||
|
||||
@dashboard_bp.post("/d/<int:dash_id>/edit/layout")
|
||||
@require_role(UserRole.viewer)
|
||||
async def save_layout(dash_id: int):
|
||||
|
||||
Reference in New Issue
Block a user