Dashboard: add Playlist Overview card and compact the sidebar
Restructure the dashboard into a two-column layout: a wide main column
holding the new Playlist Overview card, and a narrow (220px) right
sidebar with the compacted Quick Actions and Workflow Guide cards.
The Overview card answers "which playlist should I add media to, and
who will see it?" without opening each playlist. Per playlist it shows:
- name, linked to that playlist's content manager
- item count, total duration and current version
- "N/M online" coverage, or a "no players" warning
- one chip per assigned player, with an online/offline dot, linking
to that player's manage page
- an explicit empty state when no player is assigned
Players with no playlist are surfaced in a separate warning, since they
silently display nothing.
main.py builds this in a fixed number of queries: players are fetched
once and grouped by playlist_id, rather than calling
Playlist.players per row (which would be one query per playlist).
Layout notes:
- minmax(0, 1fr) on the main column so long names cannot widen the grid
- sidebar is sticky on tall screens and collapses below the main column
under 900px, where the two small cards sit side by side
- sidebar cards used the global .card hover lift; suppressed for these
Also fixes a corrupted emoji (U+FFFD) in the System Status card.
Verified at 1600/1200/950/850/600px: no overflow, no button label
wrapping, and correct dark-mode colours.
This commit is contained in:
+32
-2
@@ -34,14 +34,44 @@ def dashboard():
|
||||
storage_mb = round(storage_mb / (1024 * 1024), 2) # Convert to MB
|
||||
|
||||
server_logs = get_recent_logs(20)
|
||||
|
||||
|
||||
# Per-playlist overview for the dashboard: which playlist holds what, and
|
||||
# which players consume it. This answers "where should I add media / which
|
||||
# playlist needs editing?" without opening each playlist.
|
||||
#
|
||||
# Players are fetched in a single query and grouped in Python rather than
|
||||
# relying on Playlist.players per row, to avoid one query per playlist.
|
||||
playlists = Playlist.query.order_by(Playlist.name).all()
|
||||
players_by_playlist = {}
|
||||
for player in Player.query.order_by(Player.name).all():
|
||||
if player.playlist_id is not None:
|
||||
players_by_playlist.setdefault(player.playlist_id, []).append(player)
|
||||
|
||||
playlist_overview = []
|
||||
for playlist in playlists:
|
||||
assigned = players_by_playlist.get(playlist.id, [])
|
||||
playlist_overview.append({
|
||||
'playlist': playlist,
|
||||
'content_count': playlist.contents.count(),
|
||||
'total_duration': playlist.total_duration,
|
||||
'players': assigned,
|
||||
'player_count': len(assigned),
|
||||
'online_count': sum(1 for p in assigned if p.is_online),
|
||||
})
|
||||
|
||||
# Players with no playlist produce no content on screen, so surface them.
|
||||
unassigned_players = Player.query.filter(Player.playlist_id.is_(None))\
|
||||
.order_by(Player.name).all()
|
||||
|
||||
return render_template(
|
||||
'dashboard.html',
|
||||
total_players=total_players,
|
||||
total_playlists=total_playlists,
|
||||
total_content=total_content,
|
||||
storage_mb=storage_mb,
|
||||
recent_logs=server_logs
|
||||
recent_logs=server_logs,
|
||||
playlist_overview=playlist_overview,
|
||||
unassigned_players=unassigned_players
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user