Permette agli admin di prolungare la scadenza dei replay.
Aggiunge data personalizzata e pulsanti rapidi nell'archivio admin così il job di purge non elimina i video fino alla nuova scadenza. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -13,7 +13,12 @@ module Recordings
|
||||
end
|
||||
|
||||
def update
|
||||
permitted = params.require(:recording).permit(:privacy_status, :title)
|
||||
if admin_expiry_update_requested?
|
||||
apply_admin_expiry_update!
|
||||
return
|
||||
end
|
||||
|
||||
permitted = recording_update_params
|
||||
attrs = {}
|
||||
privacy = permitted[:privacy_status]
|
||||
attrs[:privacy_status] = privacy if privacy.in?(Recording::PRIVACY_STATUSES)
|
||||
@@ -25,6 +30,8 @@ module Recordings
|
||||
redirect_to archive_index_path, notice: "Replay aggiornato"
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
redirect_to archive_index_path, alert: e.record.errors.full_messages.join(", ")
|
||||
rescue Recordings::ExtendExpiry::Error => e
|
||||
redirect_to archive_index_path, alert: e.message
|
||||
end
|
||||
|
||||
def destroy
|
||||
@@ -87,5 +94,30 @@ module Recordings
|
||||
def archive_namespace
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def recording_update_params
|
||||
params.require(:recording).permit(:privacy_status, :title, :expires_at, :extend_days)
|
||||
end
|
||||
|
||||
def admin_expiry_update_requested?
|
||||
return false unless archive_namespace == :admin
|
||||
|
||||
permitted = recording_update_params
|
||||
permitted[:extend_days].present? || permitted[:expires_at].present?
|
||||
end
|
||||
|
||||
def apply_admin_expiry_update!
|
||||
permitted = recording_update_params
|
||||
extender = Recordings::ExtendExpiry.new(@recording)
|
||||
|
||||
if permitted[:extend_days].present?
|
||||
extender.by_days(permitted[:extend_days])
|
||||
redirect_to archive_index_path, notice: "Scadenza replay prolungata di #{permitted[:extend_days].to_i} giorni"
|
||||
else
|
||||
expires_at = Time.zone.parse(permitted[:expires_at].to_s)&.end_of_day
|
||||
extender.to_time(expires_at)
|
||||
redirect_to archive_index_path, notice: "Scadenza replay aggiornata"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
39
backend/app/services/recordings/extend_expiry.rb
Normal file
39
backend/app/services/recordings/extend_expiry.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
module Recordings
|
||||
class ExtendExpiry
|
||||
class Error < StandardError; end
|
||||
|
||||
def initialize(recording)
|
||||
@recording = recording
|
||||
end
|
||||
|
||||
def to_time(expires_at)
|
||||
raise Error, "Data scadenza obbligatoria" if expires_at.blank?
|
||||
|
||||
parsed = expires_at.is_a?(Time) || expires_at.is_a?(ActiveSupport::TimeWithZone) ? expires_at : Time.zone.parse(expires_at.to_s)
|
||||
raise Error, "Data non valida" unless parsed
|
||||
|
||||
apply(parsed)
|
||||
end
|
||||
|
||||
def by_days(days)
|
||||
count = days.to_i
|
||||
raise Error, "Giorni non validi" if count <= 0
|
||||
|
||||
base = [@recording.expires_at, Time.current].compact.max
|
||||
apply(base + count.days)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def apply(new_expires_at)
|
||||
raise Error, "Replay già eliminato" if @recording.deleted?
|
||||
raise Error, "La scadenza deve essere nel futuro" unless new_expires_at > Time.current
|
||||
|
||||
@recording.update!(
|
||||
expires_at: new_expires_at,
|
||||
expiry_warning_sent_at: nil
|
||||
)
|
||||
@recording
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -112,6 +112,26 @@
|
||||
Nessuna scadenza
|
||||
<% end %>
|
||||
</span>
|
||||
<% if local_assigns[:admin_mode] && !rec.deleted? && rec.expires_at.present? %>
|
||||
<%= form_with url: paths.update.call(rec), method: :patch, local: true, class: "replay-archive__expiry-form" do %>
|
||||
<% filter_params.each { |key, value| concat hidden_field_tag(key, value) } %>
|
||||
<%= date_field_tag "recording[expires_at]",
|
||||
rec.expires_at.to_date,
|
||||
min: Date.tomorrow,
|
||||
class: "replay-archive__expiry-input",
|
||||
title: "Nuova data di scadenza" %>
|
||||
<%= submit_tag "Salva", class: "replay-archive__expiry-save" %>
|
||||
<% end %>
|
||||
<div class="replay-archive__expiry-quick">
|
||||
<% [[30, "+30 gg"], [90, "+90 gg"], [180, "+180 gg"]].each do |days, label| %>
|
||||
<%= button_to label, paths.update.call(rec),
|
||||
method: :patch,
|
||||
params: filter_params.merge(recording: { extend_days: days }),
|
||||
class: "replay-archive__expiry-extend",
|
||||
title: "Prolunga la scadenza di #{days} giorni" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</td>
|
||||
<td class="replay-archive-table__col-status">
|
||||
<span class="replay-archive__status replay-archive__status--<%= rec.status %>"><%= rec.status_label %></span>
|
||||
|
||||
@@ -1593,6 +1593,55 @@ a.replay-archive__thumb:hover {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.replay-archive__expiry-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.replay-archive__expiry-input {
|
||||
font-size: 0.8rem;
|
||||
padding: 4px 6px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #444;
|
||||
background: #1a1a1a;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.replay-archive__expiry-save {
|
||||
font-size: 0.75rem;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #555;
|
||||
background: #2a2a2a;
|
||||
color: #ddd;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.replay-archive__expiry-quick {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.replay-archive__expiry-extend {
|
||||
font-size: 0.7rem;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #444;
|
||||
background: #222;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.replay-archive__expiry-extend:hover {
|
||||
color: #fff;
|
||||
border-color: #666;
|
||||
}
|
||||
|
||||
.visually-hidden {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
|
||||
@@ -47,4 +47,29 @@ RSpec.describe "Admin club replays", type: :request do
|
||||
expect(recording.reload.status).to eq("expired")
|
||||
expect(recording.deleted_at).to be_present
|
||||
end
|
||||
|
||||
it "prolunga la scadenza con giorni aggiuntivi" do
|
||||
original = recording.expires_at
|
||||
recording.update!(expires_at: 1.hour.ago, expiry_warning_sent_at: Time.current)
|
||||
|
||||
patch admin_club_recording_path(club, recording), params: { recording: { extend_days: 30 } }
|
||||
|
||||
expect(response).to redirect_to(admin_club_recordings_path(club))
|
||||
recording.reload
|
||||
expect(recording.expires_at).to be > Time.current
|
||||
expect(recording.expires_at).to be_within(2.seconds).of(30.days.from_now)
|
||||
expect(recording.expiry_warning_sent_at).to be_nil
|
||||
expect(Recording.expired_pending_purge).not_to include(recording)
|
||||
end
|
||||
|
||||
it "imposta una nuova data di scadenza" do
|
||||
new_date = 60.days.from_now.to_date
|
||||
|
||||
patch admin_club_recording_path(club, recording), params: { recording: { expires_at: new_date } }
|
||||
|
||||
expect(response).to redirect_to(admin_club_recordings_path(club))
|
||||
recording.reload
|
||||
expect(recording.expires_at.to_date).to eq(new_date)
|
||||
expect(recording.expiry_warning_sent_at).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
64
backend/spec/services/recordings/extend_expiry_spec.rb
Normal file
64
backend/spec/services/recordings/extend_expiry_spec.rb
Normal file
@@ -0,0 +1,64 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe Recordings::ExtendExpiry do
|
||||
let!(:club) { Club.create!(name: "Club", sport: "volleyball", primary_color: "#e53935", secondary_color: "#ffffff") }
|
||||
let!(:team) { club.teams.create!(name: "Tigers", sport: "volleyball") }
|
||||
let!(:user) { User.create!(email: "coach@test.com", name: "Coach", password: "password123", role: "coach") }
|
||||
let!(:match) { team.matches.create!(opponent_name: "Rival", scheduled_at: 1.day.ago) }
|
||||
let!(:session) do
|
||||
StreamSession.create!(match: match, user: user, platform: "matchlivetv", status: "ended", ended_at: 1.hour.ago)
|
||||
end
|
||||
let!(:recording) do
|
||||
Recording.create!(
|
||||
stream_session: session, team: team, status: "ready", privacy_status: "public",
|
||||
storage_key: "teams/#{team.id}/sessions/#{session.id}/replay.mp4",
|
||||
expires_at: 2.days.from_now,
|
||||
expiry_warning_sent_at: 1.day.ago
|
||||
)
|
||||
end
|
||||
|
||||
it "imposta una nuova data di scadenza e resetta l'avviso" do
|
||||
new_expiry = 45.days.from_now
|
||||
|
||||
described_class.new(recording).to_time(new_expiry)
|
||||
|
||||
recording.reload
|
||||
expect(recording.expires_at).to be_within(1.second).of(new_expiry)
|
||||
expect(recording.expiry_warning_sent_at).to be_nil
|
||||
end
|
||||
|
||||
it "prolunga dalla scadenza corrente" do
|
||||
original = recording.expires_at
|
||||
|
||||
described_class.new(recording).by_days(30)
|
||||
|
||||
recording.reload
|
||||
expect(recording.expires_at).to be_within(1.second).of(original + 30.days)
|
||||
expect(recording.expiry_warning_sent_at).to be_nil
|
||||
end
|
||||
|
||||
it "prolunga da oggi se la scadenza è già passata" do
|
||||
recording.update!(expires_at: 2.days.ago)
|
||||
|
||||
described_class.new(recording).by_days(10)
|
||||
|
||||
recording.reload
|
||||
expect(recording.expires_at).to be_within(1.second).of(10.days.from_now)
|
||||
end
|
||||
|
||||
it "esclude il replay dalla coda di purge dopo il prolungamento" do
|
||||
recording.update!(expires_at: 1.hour.ago)
|
||||
|
||||
expect(Recording.expired_pending_purge).to include(recording)
|
||||
|
||||
described_class.new(recording).by_days(30)
|
||||
|
||||
expect(Recording.expired_pending_purge).not_to include(recording.reload)
|
||||
end
|
||||
|
||||
it "rifiuta date nel passato" do
|
||||
expect do
|
||||
described_class.new(recording).to_time(1.day.ago)
|
||||
end.to raise_error(Recordings::ExtendExpiry::Error, /futuro/)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user