Limita una diretta attiva per account e valorizza la copertina Premium Full.
Blocca la seconda diretta sullo stesso utente, registra gli abusi in admin e presenta la copertina come grafica caricata dalla società, con demo Team MLTV. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -46,3 +46,14 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<% end %>
|
||||
|
||||
<h3 style="font-size:1rem;margin-top:28px"><%= t("admin.clubs.show.concurrency_title") %></h3>
|
||||
<p class="muted"><%= t("admin.clubs.show.concurrency_lead") %></p>
|
||||
<%= render "admin/stream_concurrency_violations/table",
|
||||
violations: @concurrency_violations,
|
||||
empty_key: "admin.clubs.show.concurrency_none" %>
|
||||
<% if @concurrency_violations.any? %>
|
||||
<p class="kpi-sub" style="margin-top:0.75rem">
|
||||
<%= link_to t("admin.clubs.show.concurrency_all"), admin_stream_concurrency_violations_path(q: @club.name) %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
@@ -67,6 +67,24 @@
|
||||
<% end %>
|
||||
</section>
|
||||
|
||||
<section class="panel" style="margin-bottom:1.25rem">
|
||||
<h2><%= t("admin.dashboard.concurrency_panel.title") %></h2>
|
||||
<% if @concurrency_violation_lookback.positive? %>
|
||||
<p class="kpi-sub" style="margin-bottom:0.75rem">
|
||||
<%= t("admin.dashboard.concurrency_panel.count", count: @concurrency_violation_lookback) %>
|
||||
— <%= link_to t("admin.dashboard.concurrency_panel.view_all"), admin_stream_concurrency_violations_path %>
|
||||
</p>
|
||||
<%= render "admin/stream_concurrency_violations/table",
|
||||
violations: @recent_concurrency_violations,
|
||||
empty_key: "admin.dashboard.concurrency_panel.none" %>
|
||||
<% else %>
|
||||
<p class="empty" style="margin:0">
|
||||
<%= t("admin.dashboard.concurrency_panel.none_html",
|
||||
link: link_to(t("admin.dashboard.concurrency_panel.view_all"), admin_stream_concurrency_violations_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>
|
||||
|
||||
@@ -258,6 +258,16 @@
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
<% if @concurrency_violations.any? %>
|
||||
<section class="panel">
|
||||
<h3><%= t("admin.sessions.show.concurrency_title") %></h3>
|
||||
<p class="muted"><%= t("admin.sessions.show.concurrency_lead") %></p>
|
||||
<%= render "admin/stream_concurrency_violations/table",
|
||||
violations: @concurrency_violations,
|
||||
empty_key: "admin.sessions.show.concurrency_none" %>
|
||||
</section>
|
||||
<% end %>
|
||||
|
||||
<section class="panel">
|
||||
<h3><%= t("admin.sessions.show.events_title") %></h3>
|
||||
<% if @events.any? %>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<% if violations.any? %>
|
||||
<div class="admin-table-wrap">
|
||||
<table class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= t("admin.stream_concurrency.table.when") %></th>
|
||||
<th><%= t("admin.stream_concurrency.table.account") %></th>
|
||||
<th><%= t("admin.stream_concurrency.table.club") %></th>
|
||||
<th><%= t("admin.stream_concurrency.table.occupying") %></th>
|
||||
<th><%= t("admin.stream_concurrency.table.attempted") %></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% violations.each do |row| %>
|
||||
<tr class="<%= 'admin-row--two-devices' if row.devices_differ? %>">
|
||||
<td class="muted"><%= admin_datetime(row.created_at, with_seconds: true) %></td>
|
||||
<td>
|
||||
<strong><%= row.user_name.presence || t("admin.common.dash") %></strong>
|
||||
<div class="muted" style="font-size:0.85rem"><%= row.user_email %></div>
|
||||
</td>
|
||||
<td>
|
||||
<% if row.attempted_club %>
|
||||
<%= link_to row.club_name, admin_club_path(row.attempted_club) %>
|
||||
<% elsif row.occupying_club %>
|
||||
<%= link_to row.club_name, admin_club_path(row.occupying_club) %>
|
||||
<% else %>
|
||||
<%= row.club_name.presence || t("admin.common.dash") %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
<% if row.occupying_session %>
|
||||
<%= link_to row.occupying_match_label, admin_session_path(row.occupying_session) %>
|
||||
<% else %>
|
||||
<%= row.occupying_match_label %>
|
||||
<% end %>
|
||||
<div class="muted" style="font-size:0.85rem;margin-top:0.2rem">
|
||||
<span class="badge <%= admin_session_status_badge_class(row.occupying_status) %>"><%= row.occupying_status %></span>
|
||||
<%= row.occupying_device.presence || t("admin.common.dash") %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<% if row.attempted_session %>
|
||||
<%= link_to row.attempted_match_label, admin_session_path(row.attempted_session) %>
|
||||
<% else %>
|
||||
<%= row.attempted_match_label %>
|
||||
<% end %>
|
||||
<div class="muted" style="font-size:0.85rem;margin-top:0.2rem">
|
||||
<%= t("admin.stream_concurrency.action.#{row.attempt_action}") %>
|
||||
· <%= row.attempted_device.presence || t("admin.common.dash") %>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<% if row.devices_differ? %>
|
||||
<span class="badge badge--abuse"><%= t("admin.stream_concurrency.badge.two_devices") %></span>
|
||||
<% else %>
|
||||
<span class="muted"><%= t("admin.stream_concurrency.badge.same_or_unknown") %></span>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<% else %>
|
||||
<p class="empty"><%= t(empty_key) %></p>
|
||||
<% end %>
|
||||
@@ -0,0 +1,48 @@
|
||||
<% content_for :body_class, "admin-body" %>
|
||||
|
||||
<div class="admin-page-head">
|
||||
<h2 class="admin-page-title"><%= t("admin.stream_concurrency.index.title") %></h2>
|
||||
<p class="muted admin-page-sub"><%= t("admin.stream_concurrency.index.lead") %></p>
|
||||
</div>
|
||||
|
||||
<section class="kpi-grid">
|
||||
<div class="kpi-card <%= @lookback_count.positive? ? 'kpi-card--accent' : '' %>">
|
||||
<div class="kpi-label"><%= t("admin.stream_concurrency.kpi.lookback") %></div>
|
||||
<div class="kpi-value"><%= @lookback_count %></div>
|
||||
<div class="kpi-sub"><%= t("admin.stream_concurrency.kpi.lookback_sub") %></div>
|
||||
</div>
|
||||
<div class="kpi-card <%= @two_devices_count.positive? ? 'kpi-card--danger' : '' %>">
|
||||
<div class="kpi-label"><%= t("admin.stream_concurrency.kpi.two_devices") %></div>
|
||||
<div class="kpi-value"><%= @two_devices_count %></div>
|
||||
<div class="kpi-sub"><%= t("admin.stream_concurrency.kpi.two_devices_sub") %></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="panel admin-sessions-filters">
|
||||
<%= form_with url: admin_stream_concurrency_violations_path, method: :get, local: true, class: "admin-filter-form" do %>
|
||||
<div class="admin-filter-grid">
|
||||
<label class="admin-filter-field">
|
||||
<span><%= t("admin.stream_concurrency.filters.q") %></span>
|
||||
<%= text_field_tag :q, @filters[:q], placeholder: t("admin.stream_concurrency.filters.q_placeholder") %>
|
||||
</label>
|
||||
<label class="admin-filter-field admin-filter-field--check">
|
||||
<span><%= t("admin.stream_concurrency.filters.two_devices") %></span>
|
||||
<label class="admin-checkbox">
|
||||
<%= check_box_tag :devices_differ, "1", @filters[:devices_differ] %>
|
||||
<%= t("admin.stream_concurrency.filters.two_devices_hint") %>
|
||||
</label>
|
||||
</label>
|
||||
</div>
|
||||
<div class="admin-filter-actions">
|
||||
<%= submit_tag t("admin.stream_concurrency.filters.apply"), class: "admin-btn admin-btn--primary admin-btn--sm" %>
|
||||
<%= link_to t("admin.stream_concurrency.filters.reset"), admin_stream_concurrency_violations_path, class: "admin-btn admin-btn--outline admin-btn--sm" %>
|
||||
<span class="muted admin-filter-count">
|
||||
<%= t("admin.stream_concurrency.index.results", shown: @violations.size, total: @total_count) %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<%= render "admin/stream_concurrency_violations/table", violations: @violations, empty_key: "admin.stream_concurrency.index.none" %>
|
||||
</div>
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<%= csrf_meta_tags %>
|
||||
<link rel="stylesheet" href="/admin.css?v=15">
|
||||
<link rel="stylesheet" href="/admin.css?v=16">
|
||||
<%= yield :head %>
|
||||
<% if content_for?(:replay_archive_styles) %>
|
||||
<link rel="stylesheet" href="/marketing.css?v=42">
|
||||
@@ -32,6 +32,10 @@
|
||||
<%= link_to t("admin.layout.nav.billing"), admin_billing_path, class: ("active" if controller_name.in?(%w[billing billing_invoices])) %>
|
||||
<%= link_to t("admin.layout.nav.youtube"), admin_youtube_platform_path, class: ("active" if controller_name == "youtube") %>
|
||||
<%= link_to t("admin.layout.nav.sessions"), admin_sessions_path, class: ("active" if controller_name == "sessions") %>
|
||||
<% abuse_count = admin_concurrency_violation_lookback_count %>
|
||||
<%= link_to admin_stream_concurrency_violations_path, class: ("active" if controller_name == "stream_concurrency_violations") do %>
|
||||
<%= t("admin.layout.nav.stream_concurrency") %><% if abuse_count.positive? %> <span class="admin-nav-badge"><%= abuse_count %></span><% end %>
|
||||
<% end %>
|
||||
<%= link_to t("admin.layout.nav.analytics"), admin_analytics_path, class: ("active" if controller_name == "analytics") %>
|
||||
<%= link_to t("admin.layout.nav.costs"), admin_costs_path, class: ("active" if controller_name.in?(%w[costs cost_entries])) %>
|
||||
<%= link_to t("admin.layout.nav.stream_nodes"), admin_stream_nodes_path, class: ("active" if controller_name == "stream_nodes") %>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<%= render "shared/analytics_suppress" %>
|
||||
<%= yield :head %>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" integrity="sha512-SnH5WK+bZxgPHs44uWIX+LLJAJ9/2PkPKZ5QiAj6Ta86w+fsb2TkcmfRyVX3pBnMFcV7oQPJkl9QevSCWr3W6A==" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||
<link rel="stylesheet" href="/marketing.css?v=79">
|
||||
<link rel="stylesheet" href="/marketing.css?v=81">
|
||||
</head>
|
||||
<body data-confirm-i18n='<%= raw confirm_dialog_i18n_json %>'<% if MatchLiveTv.google_analytics_configured? %> data-ga-id="<%= MatchLiveTv.google_analytics_measurement_id %>"<% end %>>
|
||||
<%= render "shared/cookie_banner" %>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<%= render "shared/meta_tags" %>
|
||||
<%= render "shared/analytics_suppress" %>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" integrity="sha512-SnH5WK+bZxgPHs44uWIX+LLJAJ9/2PkPKZ5QiAj6Ta86w+fsb2TkcmfRyVX3pBnMFcV7oQPJkl9QevSCWr3W6A==" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||
<link rel="stylesheet" href="/marketing.css?v=79">
|
||||
<link rel="stylesheet" href="/marketing.css?v=81">
|
||||
<link rel="stylesheet" href="/live.css?v=26">
|
||||
<%= yield :head %>
|
||||
</head>
|
||||
|
||||
@@ -164,8 +164,8 @@
|
||||
<aside class="demo-live-card" aria-label="<%= t("live.index.demo_aria_label") %>">
|
||||
<p class="demo-label"><%= t("live.index.demo_label") %></p>
|
||||
<article class="live-card live-card--demo">
|
||||
<h3>Tigers Volley vs ASD Eagles</h3>
|
||||
<p class="meta"><%= t("live.index.demo_meta") %></p>
|
||||
<h3><%= MatchLiveTv::Demo.match_title %></h3>
|
||||
<p class="meta"><%= MatchLiveTv::Demo.live_meta %></p>
|
||||
<p class="card-score">
|
||||
<span class="card-sets"><%= t("live.index.demo_sets") %></span>
|
||||
<span class="card-points">18 - 16</span>
|
||||
|
||||
@@ -80,6 +80,13 @@
|
||||
) %>
|
||||
</p>
|
||||
</details>
|
||||
|
||||
<details class="faq-item">
|
||||
<summary><%= t("pages.faq.q10_question") %></summary>
|
||||
<p>
|
||||
<%= t("pages.faq.q10_answer") %>
|
||||
</p>
|
||||
</details>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
<div class="features-yt-mock__meta">
|
||||
<span class="features-yt-mock__avatar"><i class="fa-solid fa-shield-halved"></i></span>
|
||||
<div class="features-yt-mock__text">
|
||||
<strong><%= t("pages.features.youtube_mock_channel") %></strong>
|
||||
<strong><%= MatchLiveTv::Demo.home_team %></strong>
|
||||
<span><%= t("pages.features.youtube_mock_subs") %></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -196,6 +196,8 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%= render "shared/sponsor_cover_promo" %>
|
||||
|
||||
<section class="section wrap features-replay" aria-labelledby="features-replay-title">
|
||||
<div class="features-split features-split--reverse">
|
||||
<div class="features-split__copy">
|
||||
@@ -218,7 +220,7 @@
|
||||
<li>
|
||||
<span class="features-archive-mock__thumb features-archive-mock__thumb--a"></span>
|
||||
<span class="features-archive-mock__info">
|
||||
<strong><%= t("pages.features.replay_mock_1_title") %></strong>
|
||||
<strong><%= MatchLiveTv::Demo.match_title %></strong>
|
||||
<em><%= t("pages.features.replay_mock_1_meta") %></em>
|
||||
</span>
|
||||
<i class="fa-solid fa-download" aria-hidden="true"></i>
|
||||
@@ -226,7 +228,7 @@
|
||||
<li>
|
||||
<span class="features-archive-mock__thumb features-archive-mock__thumb--b"></span>
|
||||
<span class="features-archive-mock__info">
|
||||
<strong><%= t("pages.features.replay_mock_2_title") %></strong>
|
||||
<strong><%= MatchLiveTv::Demo.match_title %></strong>
|
||||
<em><%= t("pages.features.replay_mock_2_meta") %></em>
|
||||
</span>
|
||||
<i class="fa-solid fa-download" aria-hidden="true"></i>
|
||||
@@ -234,7 +236,7 @@
|
||||
<li>
|
||||
<span class="features-archive-mock__thumb features-archive-mock__thumb--c"></span>
|
||||
<span class="features-archive-mock__info">
|
||||
<strong><%= t("pages.features.replay_mock_3_title") %></strong>
|
||||
<strong><%= MatchLiveTv::Demo.match_title %></strong>
|
||||
<em><%= t("pages.features.replay_mock_3_meta") %></em>
|
||||
</span>
|
||||
<i class="fa-solid fa-download" aria-hidden="true"></i>
|
||||
|
||||
@@ -94,6 +94,8 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<%= render "shared/sponsor_cover_promo", variant: :compact %>
|
||||
|
||||
<section class="section wrap plans-teaser">
|
||||
<h2><%= t("home.plans_title") %></h2>
|
||||
<p class="plans-teaser-lead"><%= t("home.plans_lead") %></p>
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
<%= render "shared/plan_cards" %>
|
||||
|
||||
<%= render "shared/sponsor_cover_promo", variant: :compact, show_cta: false, nested: true %>
|
||||
|
||||
<div class="table-scroll compare-table-wrap">
|
||||
<table class="compare-table">
|
||||
<colgroup>
|
||||
@@ -50,6 +52,12 @@
|
||||
<tr><td><%= t("pages.pricing.table_youtube") %></td><td><%= t("pages.pricing.table_no") %></td><td>Match Live TV</td><td><%= t("pages.pricing.table_youtube_club") %></td></tr>
|
||||
<tr><td><%= t("pages.pricing.table_replay") %></td><td><%= t("pages.pricing.table_no") %></td><td><%= t("pages.plans.replay_days", count: 30) %></td><td><%= t("pages.plans.replay_days", count: 90) %></td></tr>
|
||||
<tr><td><%= t("pages.pricing.table_download") %></td><td><%= t("pages.pricing.table_no") %></td><td><%= t("pages.pricing.table_yes") %></td><td><%= t("pages.pricing.table_yes") %></td></tr>
|
||||
<tr>
|
||||
<td><%= t("pages.pricing.table_cover_sponsor") %></td>
|
||||
<td><%= t("pages.pricing.table_no") %></td>
|
||||
<td><%= t("pages.pricing.table_no") %></td>
|
||||
<td><%= t("pages.pricing.table_yes") %></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><%= t("pages.pricing.table_price") %></td>
|
||||
<td><%= t("pages.pricing.table_price_free") %></td>
|
||||
|
||||
@@ -65,6 +65,9 @@
|
||||
t("pages.plans.youtube_none")
|
||||
end
|
||||
) %></li>
|
||||
<% if plan.slug == "premium_full" %>
|
||||
<li><%= raw t("pages.plans.cover_sponsor_html") %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% if plan.slug == "premium_full" %>
|
||||
<p class="plan-staff-note"><%= t("pages.plans.staff_note_full") %></p>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<figure class="cover-mock" aria-hidden="true" inert>
|
||||
<div class="cover-mock__chrome">
|
||||
<span class="cover-mock__brand">MATCH <em>LIVE</em> TV</span>
|
||||
<span class="cover-mock__state"><%= t("pages.sponsor_cover.mock.state") %></span>
|
||||
</div>
|
||||
<div class="cover-mock__stage">
|
||||
<p class="cover-mock__cat"><%= MatchLiveTv::Demo.category %></p>
|
||||
<p class="cover-mock__home"><%= MatchLiveTv::Demo.home_team %></p>
|
||||
<p class="cover-mock__vs">vs</p>
|
||||
<p class="cover-mock__away"><%= MatchLiveTv::Demo.away_team %></p>
|
||||
<p class="cover-mock__when"><%= MatchLiveTv::Demo.when_label %></p>
|
||||
<p class="cover-mock__art-label"><%= t("pages.sponsor_cover.mock.art_label") %></p>
|
||||
<div class="cover-mock__art">
|
||||
<div class="cover-mock__crest">
|
||||
<span class="cover-mock__crest-mark">M</span>
|
||||
<span class="cover-mock__crest-name"><%= MatchLiveTv::Demo.home_team %></span>
|
||||
</div>
|
||||
<div class="cover-mock__marks">
|
||||
<span class="cover-mock__mark cover-mock__mark--a"></span>
|
||||
<span class="cover-mock__mark cover-mock__mark--b"></span>
|
||||
<span class="cover-mock__mark cover-mock__mark--c"></span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="cover-mock__soon"><%= t("pages.sponsor_cover.mock.soon") %></p>
|
||||
</div>
|
||||
</figure>
|
||||
@@ -0,0 +1,30 @@
|
||||
<%# locals: (variant: :full, show_cta: nil, nested: false) %>
|
||||
<% variant = (local_assigns[:variant] || :full).to_sym %>
|
||||
<% compact = variant == :compact %>
|
||||
<% nested = local_assigns[:nested] %>
|
||||
<% show_cta = local_assigns.fetch(:show_cta, true) %>
|
||||
<% title_id = compact ? "sponsor-cover-title-compact" : "sponsor-cover-title" %>
|
||||
|
||||
<section class="section<%= " wrap" unless nested %> sponsor-cover<%= " sponsor-cover--compact" if compact %>" aria-labelledby="<%= title_id %>">
|
||||
<div class="sponsor-cover__panel">
|
||||
<div class="sponsor-cover__copy">
|
||||
<p class="feature-card__badge feature-card__badge--gold"><%= t("pages.sponsor_cover.eyebrow") %></p>
|
||||
<h2 id="<%= title_id %>"><%= t("pages.sponsor_cover.title") %></h2>
|
||||
<p class="sponsor-cover__lead"><%= t("pages.sponsor_cover.body") %></p>
|
||||
<% unless compact %>
|
||||
<ul class="features-checklist">
|
||||
<li><%= t("pages.sponsor_cover.item_cover") %></li>
|
||||
<li><%= t("pages.sponsor_cover.item_before") %></li>
|
||||
<li><%= t("pages.sponsor_cover.item_club") %></li>
|
||||
</ul>
|
||||
<p class="sponsor-cover__claim"><%= t("pages.sponsor_cover.claim") %></p>
|
||||
<% end %>
|
||||
<% if show_cta %>
|
||||
<%= link_to t("pages.sponsor_cover.cta"), public_prezzi_path, class: "btn btn-outline" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="sponsor-cover__visual">
|
||||
<%= render "shared/sponsor_cover_mock" %>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
Reference in New Issue
Block a user