Files
MatchLiveTv/backend/app/views/admin/dashboard/index.html.erb
T
eminuxandCursor 60dfe7a75b Allinea verticalmente le righe della tabella sessioni admin.
Il flex era sulla td e spezzava vertical-align; ora sta nel contenuto e l'ingest è su una riga.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-20 16:21:03 +02:00

164 lines
8.5 KiB
ERB

<% content_for :body_class, "admin-body" %>
<section class="kpi-grid">
<div class="kpi-card kpi-card--accent">
<div class="kpi-label"><%= t("admin.dashboard.kpi.active_sessions") %></div>
<div class="kpi-value kpi-value--live" id="kpi-active-sessions"><%= @stats[:active_sessions] %></div>
<div class="kpi-sub"><%= t("admin.dashboard.kpi.active_sessions_sub") %></div>
</div>
<div class="kpi-card">
<div class="kpi-label"><%= t("admin.dashboard.kpi.today") %></div>
<div class="kpi-value" id="kpi-sessions-today"><%= @stats[:sessions_today] %></div>
<div class="kpi-sub"><%= t("admin.dashboard.kpi.today_sub", duration: format_duration_minutes(@stats[:stream_minutes_today])) %></div>
</div>
<div class="kpi-card">
<div class="kpi-label"><%= t("admin.dashboard.kpi.month") %></div>
<div class="kpi-value" id="kpi-sessions-month"><%= @stats[:sessions_month] %></div>
<div class="kpi-sub"><%= t("admin.dashboard.kpi.month_sub", duration: format_duration_minutes(@stats[:stream_minutes_month])) %></div>
</div>
<div class="kpi-card">
<div class="kpi-label"><%= t("admin.dashboard.kpi.teams") %></div>
<div class="kpi-value"><%= @stats[:teams_count] %></div>
<div class="kpi-sub"><%= t("admin.dashboard.kpi.teams_sub", count: @stats[:users_count]) %></div>
</div>
<div class="kpi-card">
<div class="kpi-label"><%= t("admin.dashboard.kpi.cpu") %></div>
<div class="kpi-value" id="kpi-cpu"><%= @host[:cpu] %>%</div>
<div class="kpi-sub"><%= t("admin.dashboard.kpi.cpu_sub", one: @host[:load_avg]["1m"], five: @host[:load_avg]["5m"], fifteen: @host[:load_avg]["15m"]) %></div>
</div>
<div class="kpi-card">
<div class="kpi-label"><%= t("admin.dashboard.kpi.ram") %></div>
<div class="kpi-value" id="kpi-mem"><%= @host[:memory][:used_percent] %>%</div>
<div class="kpi-sub"><%= t("admin.dashboard.kpi.ram_sub", used: format_bytes(@host[:memory][:used_bytes]), total: format_bytes(@host[:memory][:total_bytes])) %></div>
</div>
<div class="kpi-card">
<div class="kpi-label"><%= t("admin.dashboard.kpi.network") %></div>
<div class="kpi-value" style="font-size:1.2rem" id="kpi-network"><%= format_bytes(@host[:network][:total_bytes]) %></div>
<div class="kpi-sub"><%= t("admin.dashboard.kpi.network_sub", tx: format_bytes(@host[:network][:tx_bytes_total]), rx: format_bytes(@host[:network][:rx_bytes_total])) %></div>
</div>
<div class="kpi-card">
<div class="kpi-label"><%= t("admin.dashboard.kpi.recordings") %></div>
<div class="kpi-value"><%= @stats[:recordings_ready] %></div>
<div class="kpi-sub"><%= t("admin.dashboard.kpi.recordings_sub", count: @stats[:recordings_count]) %></div>
</div>
</section>
<section class="charts-grid">
<div class="chart-card">
<h3><%= t("admin.dashboard.charts.cpu") %></h3>
<div class="chart-wrap"><canvas id="chart-cpu"></canvas></div>
</div>
<div class="chart-card">
<h3><%= t("admin.dashboard.charts.ram") %></h3>
<div class="chart-wrap"><canvas id="chart-mem"></canvas></div>
</div>
</section>
<section class="panel" style="margin-bottom:1.25rem">
<h2><%= t("admin.dashboard.ops_panel.title") %></h2>
<% critical = Ops::Incident.critical_open.count %>
<% warnings = Ops::Incident.open.where(severity: "warning").count %>
<% if critical.positive? %>
<p class="admin-flash" style="margin-bottom:0.75rem"><%= t("admin.dashboard.ops_panel.critical", count: critical) %><%= link_to t("admin.dashboard.ops_panel.view_dashboard"), admin_ops_path %></p>
<% elsif warnings.positive? %>
<p class="kpi-sub" style="margin-bottom:0.75rem"><%= t("admin.dashboard.ops_panel.warnings", count: warnings) %><%= link_to t("admin.dashboard.ops_panel.dashboard_link"), admin_ops_path %></p>
<% else %>
<p class="empty" style="margin:0"><%= t("admin.dashboard.ops_panel.none_html", link: link_to(t("admin.dashboard.ops_panel.dashboard_link"), admin_ops_path)) %></p>
<% end %>
</section>
<section class="charts-grid" style="grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));">
<div class="chart-card">
<h3><%= t("admin.dashboard.disk.system_title") %></h3>
<% root = @host[:disk][:root] %>
<% if root[:total_bytes] %>
<div class="disk-row">
<header><span><%= root[:path] %></span><span><%= format_bytes(root[:used_bytes]) %> / <%= format_bytes(root[:total_bytes]) %></span></header>
<div class="progress-bar <%= 'progress-bar--ok' if root[:used_percent].to_f < 80 %>"><span style="width:<%= root[:used_percent] %>%"></span></div>
<p class="kpi-sub" style="margin-top:0.35rem"><%= t("admin.dashboard.disk.free_label", free: format_bytes(root[:free_bytes]), percent: root[:used_percent]) %></p>
</div>
<% else %>
<p class="empty"><%= t("admin.dashboard.disk.not_available") %></p>
<% end %>
</div>
<div class="chart-card">
<h3><%= t("admin.dashboard.disk.recordings_title") %></h3>
<% rec = @host[:disk][:recordings] %>
<% if rec[:total_bytes] %>
<div class="disk-row">
<header><span><%= rec[:path] %></span><span><%= format_bytes(rec[:used_bytes]) %> / <%= format_bytes(rec[:total_bytes]) %></span></header>
<div class="progress-bar <%= 'progress-bar--ok' if rec[:used_percent].to_f < 80 %>"><span style="width:<%= rec[:used_percent] %>%"></span></div>
<% if rec[:dir_size_bytes] %>
<p class="kpi-sub" style="margin-top:0.35rem"><%= t("admin.dashboard.disk.recordings_content", size: format_bytes(rec[:dir_size_bytes])) %></p>
<% end %>
</div>
<% else %>
<p class="empty"><%= t("admin.dashboard.disk.recordings_not_mounted", path: Admin::HostMetrics::RECORDINGS_PATH) %></p>
<% end %>
</div>
</section>
<section class="admin-panels">
<div class="panel">
<h2><%= t("admin.dashboard.sessions.title") %></h2>
<% if @active_sessions.any? %>
<table class="admin-table">
<thead>
<tr>
<th><%= t("admin.dashboard.sessions.table.match") %></th>
<th><%= t("admin.dashboard.sessions.table.status") %></th>
<th><%= t("admin.dashboard.sessions.table.ingest") %></th>
<th><%= t("admin.dashboard.sessions.table.start") %></th>
<th><%= t("admin.dashboard.sessions.table.link") %></th>
<th></th>
</tr>
</thead>
<tbody id="active-sessions-body">
<% @active_sessions.each do |s| %>
<tr>
<td><%= s.match.team.name %> vs <%= s.match.opponent_name %></td>
<td><span class="badge badge--<%= s.status == 'live' ? 'live' : (s.status == 'paused' ? 'paused' : 'connecting') %>"><%= s.status %></span></td>
<td><%= render "admin/sessions/ingest_cell", session: s %></td>
<td class="muted"><%= s.started_at&.strftime("%d/%m %H:%M") || t("admin.common.dash") %></td>
<td>
<div class="admin-link-compact">
<% admin_session_watch_links(s).each do |link| %>
<%= link_to link[:label], link[:url], target: "_blank", rel: "noopener", class: "admin-link-chip" %>
<% end %>
<%= link_to t("admin.dashboard.sessions.regia"), admin_session_path(s), class: "admin-link-chip admin-link-chip--regia" %>
</div>
</td>
<td class="admin-actions">
<%= link_to t("admin.dashboard.sessions.detail"), admin_session_path(s) %>
<%= button_to t("admin.dashboard.sessions.stop"), stop_admin_session_path(s), method: :post, class: "admin-btn admin-btn--danger admin-btn--sm", form: { data: { turbo_confirm: t("admin.dashboard.sessions.stop_confirm") } } %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<p class="empty"><%= t("admin.dashboard.sessions.none") %></p>
<% end %>
</div>
<div class="panel">
<h2><%= t("admin.dashboard.teams.title") %></h2>
<ul class="team-list">
<% @teams.each do |t_| %>
<li>
<%= link_to t_.name, admin_team_path(t_) %>
<span class="muted"><%= t("admin.dashboard.teams.matches_count", count: t_.matches.count) %></span>
</li>
<% end %>
</ul>
<% if @stats[:teams_count] > @teams.size %>
<p class="kpi-sub" style="margin-top:0.75rem"><%= link_to t("admin.dashboard.teams.view_all", count: @stats[:teams_count]), admin_clubs_path %></p>
<% end %>
</div>
</section>
<script>
window.adminMetricsUrl = "<%= admin_metrics_path %>";
window.adminInitialHistory = <%= raw @host[:history].to_json %>;
</script>