Billing Stripe, link regia mobile e staff solo trasmissione.
Aggiunge fatturazione club, pagina regia condivisibile senza account, roster squadre e rimuove la modalità controller dall'app (v1.1.0). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
70
backend/app/controllers/public/club_billing_controller.rb
Normal file
70
backend/app/controllers/public/club_billing_controller.rb
Normal file
@@ -0,0 +1,70 @@
|
||||
module Public
|
||||
class ClubBillingController < WebBaseController
|
||||
before_action :require_login!
|
||||
before_action :set_club
|
||||
before_action :require_club_owner_for_club!
|
||||
|
||||
def profile
|
||||
end
|
||||
|
||||
def update_profile
|
||||
@club.assign_attributes(billing_profile_params)
|
||||
if @club.save(context: :billing_profile)
|
||||
redirect_to public_club_billing_path(@club), notice: "Dati di fatturazione aggiornati."
|
||||
else
|
||||
flash.now[:alert] = @club.errors.full_messages.join(", ")
|
||||
render :profile, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def sync_payments
|
||||
unless MatchLiveTv.stripe_enabled?
|
||||
redirect_to public_club_billing_path(@club), alert: "Stripe non configurato."
|
||||
return
|
||||
end
|
||||
|
||||
count = Billing::Stripe::SyncPayments.call(club: @club)
|
||||
redirect_to public_club_billing_path(@club), notice: "Sincronizzati #{count} pagamenti da Stripe."
|
||||
rescue ::Stripe::StripeError => e
|
||||
redirect_to public_club_billing_path(@club), alert: "Errore Stripe: #{e.message}"
|
||||
end
|
||||
|
||||
def download_invoice
|
||||
invoice = @club.billing_invoices.find(params[:invoice_id])
|
||||
unless invoice.pdf.attached?
|
||||
redirect_to public_club_billing_path(@club), alert: "PDF fattura non disponibile."
|
||||
return
|
||||
end
|
||||
|
||||
redirect_to rails_blob_path(invoice.pdf, disposition: "attachment"), allow_other_host: true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_club
|
||||
@club = current_user.owned_clubs.find(params[:id])
|
||||
end
|
||||
|
||||
def require_club_owner_for_club!
|
||||
require_club_owner!(@club)
|
||||
end
|
||||
|
||||
def billing_profile_params
|
||||
params.require(:club).permit(
|
||||
:billing_entity_type,
|
||||
:billing_legal_name,
|
||||
:billing_vat_number,
|
||||
:billing_fiscal_code,
|
||||
:billing_email,
|
||||
:billing_phone,
|
||||
:billing_address_line,
|
||||
:billing_city,
|
||||
:billing_province,
|
||||
:billing_postal_code,
|
||||
:billing_country,
|
||||
:billing_recipient_code,
|
||||
:billing_pec
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
85
backend/app/controllers/public/club_matches_controller.rb
Normal file
85
backend/app/controllers/public/club_matches_controller.rb
Normal file
@@ -0,0 +1,85 @@
|
||||
module Public
|
||||
class ClubMatchesController < WebBaseController
|
||||
before_action :require_login!
|
||||
before_action :set_club
|
||||
before_action :load_schedulable_teams
|
||||
before_action :require_any_schedulable_team!
|
||||
|
||||
def new
|
||||
@match = default_match_for_form
|
||||
@selected_team = resolve_selected_team
|
||||
end
|
||||
|
||||
def create
|
||||
@selected_team = @schedulable_teams.find { |t| t.id.to_s == params[:team_id].to_s }
|
||||
unless @selected_team
|
||||
@match = default_match_for_form
|
||||
flash.now[:alert] = "Seleziona una squadra valida."
|
||||
return render :new, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
@match = @selected_team.matches.create!(
|
||||
match_params.merge(sport: @selected_team.sport, sets_to_win: 3)
|
||||
)
|
||||
redirect_to public_live_index_path(club_id: @club.id),
|
||||
notice: "Partita programmata: #{@selected_team.name} vs #{@match.opponent_name}."
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
@match = @selected_team.matches.build(match_params)
|
||||
flash.now[:alert] = e.record.errors.full_messages.join(", ")
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_club
|
||||
@club = Club.find(params[:club_id])
|
||||
end
|
||||
|
||||
def load_schedulable_teams
|
||||
@schedulable_teams = current_user.schedulable_teams_for(@club)
|
||||
@club_admin = @club.owned_by?(current_user)
|
||||
end
|
||||
|
||||
def require_any_schedulable_team!
|
||||
return if @schedulable_teams.any?
|
||||
|
||||
redirect_to public_live_index_path(club_id: @club.id),
|
||||
alert: "Non hai permessi per programmare partite in questa società."
|
||||
end
|
||||
|
||||
def resolve_selected_team
|
||||
if params[:team_id].present?
|
||||
@schedulable_teams.find { |t| t.id.to_s == params[:team_id].to_s }
|
||||
end || @schedulable_teams.first
|
||||
end
|
||||
|
||||
def default_match_for_form
|
||||
team = resolve_selected_team
|
||||
Match.new(
|
||||
sport: team&.sport || @club.sport,
|
||||
sets_to_win: 3,
|
||||
scheduled_at: default_scheduled_at
|
||||
)
|
||||
end
|
||||
|
||||
def match_params
|
||||
p = params.require(:match).permit(:opponent_name, :location, :scheduled_at)
|
||||
p[:scheduled_at] = parse_scheduled_at(p[:scheduled_at]) if p[:scheduled_at].present?
|
||||
p
|
||||
end
|
||||
|
||||
def parse_scheduled_at(value)
|
||||
raw = value.to_s.strip
|
||||
return nil if raw.blank?
|
||||
|
||||
Time.zone.strptime(raw, "%Y-%m-%dT%H:%M")
|
||||
rescue ArgumentError
|
||||
Time.zone.parse(raw)
|
||||
end
|
||||
|
||||
def default_scheduled_at
|
||||
t = Time.current + 2.hours
|
||||
Time.zone.local(t.year, t.month, t.day, t.hour, 0, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,6 +4,7 @@ module Public
|
||||
|
||||
before_action :require_login!
|
||||
before_action :set_club, only: %i[show edit update billing checkout portal]
|
||||
before_action :require_billing_profile_for_premium_checkout!, only: :checkout
|
||||
|
||||
def new
|
||||
redirect_to public_club_path(current_user.primary_club) if current_user.primary_club
|
||||
@@ -42,6 +43,7 @@ module Public
|
||||
|
||||
def show
|
||||
require_club_owner!(@club)
|
||||
apply_checkout_flash!
|
||||
@entitlements_team = @club.teams.first
|
||||
@entitlements = @entitlements_team&.entitlements
|
||||
@teams = @club.teams.order(:name)
|
||||
@@ -64,9 +66,13 @@ module Public
|
||||
|
||||
def billing
|
||||
require_club_owner!(@club)
|
||||
apply_checkout_flash!
|
||||
@team = @club.teams.first!
|
||||
@entitlements = @team.entitlements
|
||||
@subscription = @club.subscription
|
||||
@plans = Plan.ordered
|
||||
@payments = @club.billing_payments.recent.limit(50)
|
||||
@invoices = @club.billing_invoices.recent.limit(50)
|
||||
end
|
||||
|
||||
def checkout
|
||||
@@ -77,8 +83,20 @@ module Public
|
||||
end
|
||||
|
||||
plan_slug = params[:plan].presence_in(%w[premium_light premium_full]) || "premium_light"
|
||||
url = Billing::Stripe::CheckoutSession.new(club: @club, user: current_user, plan_slug: plan_slug).url
|
||||
redirect_to url, allow_other_host: true
|
||||
target_plan = Plan[plan_slug]
|
||||
sub = @club.subscription
|
||||
|
||||
if sub&.stripe_subscription_id.present? && sub.active? && sub.plan.slug != plan_slug
|
||||
Billing::Stripe::ChangePlan.call(club: @club, plan_slug: plan_slug)
|
||||
redirect_to public_club_billing_path(@club),
|
||||
notice: "Piano aggiornato a #{target_plan.name}. L'eventuale differenza verrà addebitata o accreditata da Stripe."
|
||||
else
|
||||
url = Billing::Stripe::CheckoutSession.new(club: @club, user: current_user, plan_slug: plan_slug).url
|
||||
redirect_to url, allow_other_host: true
|
||||
end
|
||||
rescue ::Stripe::StripeError => e
|
||||
Rails.logger.warn("[Stripe checkout] #{e.message}")
|
||||
redirect_to public_club_billing_path(@club), alert: "Errore Stripe: #{e.message}"
|
||||
end
|
||||
|
||||
def portal
|
||||
@@ -99,10 +117,34 @@ module Public
|
||||
end
|
||||
|
||||
def club_params
|
||||
p = params.require(:club).permit(:name, :sport, :logo_url, :primary_color, :secondary_color)
|
||||
p = params.require(:club).permit(
|
||||
:name, :sport, :logo_url, :primary_color, :secondary_color,
|
||||
:billing_entity_type, :billing_legal_name, :billing_vat_number, :billing_fiscal_code,
|
||||
:billing_email, :billing_phone, :billing_address_line, :billing_city, :billing_province,
|
||||
:billing_postal_code, :billing_country, :billing_recipient_code, :billing_pec
|
||||
)
|
||||
p[:primary_color] = normalize_hex_color(p[:primary_color], "#e53935")
|
||||
p[:secondary_color] = normalize_hex_color(p[:secondary_color], "#ffffff")
|
||||
p
|
||||
end
|
||||
|
||||
def require_billing_profile_for_premium_checkout!
|
||||
require_club_owner!(@club)
|
||||
plan_slug = params[:plan].presence_in(%w[premium_light premium_full])
|
||||
return if plan_slug.blank?
|
||||
return if @club.billing_profile_complete?
|
||||
|
||||
redirect_to public_club_billing_profile_path(@club),
|
||||
alert: "Completa i dati di fatturazione prima di attivare un piano premium."
|
||||
end
|
||||
|
||||
def apply_checkout_flash!
|
||||
case params[:checkout]
|
||||
when "success"
|
||||
flash.now[:notice] = "Pagamento completato! Il piano premium è attivo per la società."
|
||||
when "canceled"
|
||||
flash.now[:alert] = "Pagamento annullato. Nessun addebito è stato effettuato."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -18,7 +18,7 @@ module Public
|
||||
return
|
||||
end
|
||||
invitation.accept!(current_user)
|
||||
redirect_to public_team_dashboard_path(invitation.team), notice: "Sei entrato nella squadra!"
|
||||
redirect_to public_team_details_path(invitation.team), notice: "Sei entrato nella squadra!"
|
||||
else
|
||||
session[:pending_invite_token] = params[:token]
|
||||
redirect_to public_signup_path, notice: "Registrati con #{invitation.email} per accettare l'invito"
|
||||
|
||||
@@ -6,33 +6,46 @@ module Public
|
||||
|
||||
def index
|
||||
@query = params[:q].to_s.strip
|
||||
@sessions = StreamSession
|
||||
@club = Club.find_by(id: params[:club_id]) if params[:club_id].present?
|
||||
team_ids = @club&.teams&.pluck(:id)
|
||||
|
||||
sessions = StreamSession
|
||||
.broadcasting
|
||||
.includes(:score_state, match: :team)
|
||||
.includes(:score_state, match: { team: :club })
|
||||
sessions = sessions.joins(:match).where(matches: { team_id: team_ids }) if team_ids.present?
|
||||
|
||||
@sessions = sessions
|
||||
.search_by_team_or_opponent(@query)
|
||||
.order(Arel.sql("started_at DESC NULLS LAST"), created_at: :desc)
|
||||
|
||||
busy_match_ids = StreamSession.where.not(status: %w[ended error]).select(:match_id)
|
||||
@upcoming_matches = Match
|
||||
.scheduled_upcoming
|
||||
.includes(:team)
|
||||
# Solo dirette già in onda: le partite «programmate» restano visibili anche se c’è sessione idle.
|
||||
broadcasting_match_ids = StreamSession.broadcasting.select(:match_id)
|
||||
upcoming = Match.scheduled_for_live.includes(team: :club)
|
||||
upcoming = upcoming.where(team_id: team_ids) if team_ids.present?
|
||||
|
||||
@upcoming_matches = upcoming
|
||||
.search_teams_or_opponents(@query)
|
||||
.where.not(id: busy_match_ids)
|
||||
.where.not(id: broadcasting_match_ids)
|
||||
.order(scheduled_at: :asc)
|
||||
.limit(30)
|
||||
.limit(50)
|
||||
|
||||
@online_paths = Mediamtx::Client.new.online_path_names
|
||||
|
||||
return unless logged_in? && @club
|
||||
|
||||
@schedulable_teams = current_user.schedulable_teams_for(@club)
|
||||
@can_schedule_match = @schedulable_teams.any?
|
||||
end
|
||||
|
||||
def show
|
||||
@session = StreamSession.includes(match: :team).find(params[:id])
|
||||
@session = StreamSession.includes(match: { team: :club }).find(params[:id])
|
||||
@match = @session.match
|
||||
@stream_closed = @session.terminal?
|
||||
@on_air = !@stream_closed && mediamtx_online?(@session)
|
||||
end
|
||||
|
||||
def status
|
||||
session = StreamSession.includes(match: :team).find(params[:id])
|
||||
session = StreamSession.includes(match: { team: :club }).find(params[:id])
|
||||
closed = session.terminal?
|
||||
render json: {
|
||||
status: session.status,
|
||||
|
||||
102
backend/app/controllers/public/matches_controller.rb
Normal file
102
backend/app/controllers/public/matches_controller.rb
Normal file
@@ -0,0 +1,102 @@
|
||||
module Public
|
||||
class MatchesController < WebBaseController
|
||||
before_action :require_login!
|
||||
before_action :set_team
|
||||
before_action :require_can_schedule!
|
||||
before_action :set_match, only: %i[edit update destroy]
|
||||
|
||||
def index
|
||||
@matches = @team.matches
|
||||
.includes(:stream_sessions)
|
||||
.order(Arel.sql("scheduled_at ASC NULLS LAST"), created_at: :desc)
|
||||
@can_manage = @team.club.owned_by?(current_user)
|
||||
end
|
||||
|
||||
def new
|
||||
@match = @team.matches.build(
|
||||
sport: @team.sport,
|
||||
sets_to_win: 3,
|
||||
scheduled_at: default_scheduled_at
|
||||
)
|
||||
end
|
||||
|
||||
def create
|
||||
@match = @team.matches.create!(
|
||||
match_params.merge(sport: @team.sport, sets_to_win: match_params[:sets_to_win].presence || 3)
|
||||
)
|
||||
redirect_to public_team_matches_path(@team),
|
||||
notice: "Partita programmata: #{@match.opponent_name}. Avvia lo streaming dall’app quando sei in palestra."
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
@match = @team.matches.build(match_params)
|
||||
flash.now[:alert] = e.record.errors.full_messages.join(", ")
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def edit
|
||||
end
|
||||
|
||||
def update
|
||||
@match.update!(match_params)
|
||||
redirect_to public_team_matches_path(@team), notice: "Partita aggiornata."
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
flash.now[:alert] = e.record.errors.full_messages.join(", ")
|
||||
render :edit, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def destroy
|
||||
active = active_session_for(@match)
|
||||
if active&.resumable?
|
||||
redirect_to public_team_matches_path(@team),
|
||||
alert: "Chiudi la diretta prima di eliminare questa partita."
|
||||
return
|
||||
end
|
||||
|
||||
@match.destroy!
|
||||
redirect_to public_team_matches_path(@team), notice: "Partita eliminata."
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_team
|
||||
@team = current_user.manageable_teams.find(params[:team_id])
|
||||
@club = @team.club
|
||||
end
|
||||
|
||||
def require_can_schedule!
|
||||
return if current_user.can_schedule_for?(@team)
|
||||
|
||||
redirect_to public_team_details_path(@team),
|
||||
alert: "Solo un responsabile trasmissione o il titolare della società possono programmare le partite."
|
||||
end
|
||||
|
||||
def set_match
|
||||
@match = @team.matches.find(params[:id])
|
||||
end
|
||||
|
||||
def match_params
|
||||
p = params.require(:match).permit(:opponent_name, :location, :scheduled_at, :sport, :sets_to_win)
|
||||
p[:scheduled_at] = parse_scheduled_at(p[:scheduled_at]) if p[:scheduled_at].present?
|
||||
p
|
||||
end
|
||||
|
||||
def parse_scheduled_at(value)
|
||||
raw = value.to_s.strip
|
||||
return nil if raw.blank?
|
||||
|
||||
Time.zone.strptime(raw, "%Y-%m-%dT%H:%M")
|
||||
rescue ArgumentError
|
||||
Time.zone.parse(raw)
|
||||
end
|
||||
|
||||
def default_scheduled_at
|
||||
t = Time.current + 2.hours
|
||||
Time.zone.local(t.year, t.month, t.day, t.hour, 0, 0)
|
||||
end
|
||||
|
||||
def active_session_for(match)
|
||||
match.stream_sessions
|
||||
.order(created_at: :desc)
|
||||
.find { |s| !%w[ended error].include?(s.status) }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,6 +8,20 @@ module Public
|
||||
|
||||
def pricing
|
||||
@plans = Plan.ordered
|
||||
load_club_billing_context_for_pricing
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_club_billing_context_for_pricing
|
||||
return unless logged_in?
|
||||
|
||||
@club = current_user.primary_club
|
||||
return unless @club
|
||||
|
||||
@subscription = @club.subscription
|
||||
@team = @club.teams.order(:name).first
|
||||
@entitlements = @team&.entitlements
|
||||
end
|
||||
|
||||
def privacy
|
||||
|
||||
82
backend/app/controllers/public/regia_controller.rb
Normal file
82
backend/app/controllers/public/regia_controller.rb
Normal file
@@ -0,0 +1,82 @@
|
||||
module Public
|
||||
class RegiaController < SiteBaseController
|
||||
include Public::LiveHelper
|
||||
|
||||
layout "regia"
|
||||
|
||||
protect_from_forgery with: :null_session
|
||||
skip_before_action :verify_authenticity_token, only: %i[score pause stop]
|
||||
|
||||
before_action :set_session_from_token
|
||||
|
||||
def show
|
||||
@match = @session.match
|
||||
@team = @match.team
|
||||
@score = @session.score_state || @session.create_score_state!(
|
||||
home_sets: 0, away_sets: 0, home_points: 0, away_points: 0, current_set: 1, set_partials: []
|
||||
)
|
||||
@rules = Scoring::Rules.from_match(@match)
|
||||
@stream_closed = @session.terminal?
|
||||
@on_air = !@stream_closed && mediamtx_online?(@session)
|
||||
@share_url = request.original_url
|
||||
@share_title = "Regia — #{@team.name} vs #{@match.opponent_name}"
|
||||
end
|
||||
|
||||
def status
|
||||
render json: status_payload
|
||||
end
|
||||
|
||||
def score
|
||||
cmd = params[:cmd].presence || params[:score_action].presence
|
||||
result = Scoring::ApplyAction.new(session: @session, action: cmd).call
|
||||
render json: status_payload.merge(
|
||||
set_won: result.set_won,
|
||||
match_won: result.match_won,
|
||||
winner: result.winner&.to_s
|
||||
)
|
||||
rescue ArgumentError => e
|
||||
render json: { error: e.message }, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def pause
|
||||
Sessions::Pause.new(@session).call
|
||||
render json: status_payload
|
||||
end
|
||||
|
||||
def stop
|
||||
Sessions::Stop.new(@session).call
|
||||
render json: status_payload
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_session_from_token
|
||||
@session = Sessions::RegiaAccess.find_session_by_token(params[:token])
|
||||
return if @session
|
||||
|
||||
render file: Rails.root.join("public/404.html"), status: :not_found, layout: false
|
||||
throw :abort
|
||||
end
|
||||
|
||||
def status_payload
|
||||
score = @session.score_state
|
||||
closed = @session.terminal?
|
||||
{
|
||||
status: @session.status,
|
||||
stream_closed: closed,
|
||||
live: !closed && (@session.live? || mediamtx_online?(@session)),
|
||||
on_air: !closed && mediamtx_online?(@session),
|
||||
ended_at: @session.ended_at,
|
||||
home_name: @session.match.team.name,
|
||||
away_name: @session.match.opponent_name,
|
||||
points_target: Scoring::Rules.from_match(@session.match).points_target(score&.current_set || 1),
|
||||
score: score&.as_cable_payload
|
||||
}
|
||||
end
|
||||
|
||||
def mediamtx_online?(session)
|
||||
@online_paths_cache ||= Mediamtx::Client.new.online_path_names
|
||||
@online_paths_cache.include?(session.mediamtx_path_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -20,7 +20,7 @@ module Public
|
||||
invitation = TeamInvitation.pending.find_by(token_digest: Digest::SHA256.hexdigest(token))
|
||||
if invitation && invitation.email.downcase == @user.email.downcase
|
||||
invitation.accept!(@user)
|
||||
return redirect_to public_team_dashboard_path(invitation.team), notice: "Benvenuto nella squadra!"
|
||||
return redirect_to public_team_details_path(invitation.team), notice: "Benvenuto nella squadra!"
|
||||
end
|
||||
end
|
||||
redirect_to public_new_club_path, notice: "Account creato. Ora registra la tua società."
|
||||
|
||||
@@ -13,7 +13,7 @@ module Public
|
||||
dest = if user.primary_club
|
||||
public_club_path(user.primary_club)
|
||||
elsif user.manageable_teams.first
|
||||
public_team_dashboard_path(user.manageable_teams.first)
|
||||
public_team_details_path(user.manageable_teams.first)
|
||||
else
|
||||
public_new_club_path
|
||||
end
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
module Public
|
||||
class TeamRosterMembersController < WebBaseController
|
||||
before_action :require_login!
|
||||
before_action :set_team
|
||||
before_action :require_team_owner!
|
||||
before_action :set_member, only: %i[edit update destroy]
|
||||
|
||||
def create
|
||||
member = @team.roster_members.build(member_params)
|
||||
attach_photo(member)
|
||||
member.save!
|
||||
redirect_to public_team_details_path(@team), notice: "#{member.full_name} aggiunto all'organico."
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
redirect_to public_team_details_path(@team), alert: e.record.errors.full_messages.join(", ")
|
||||
end
|
||||
|
||||
def edit
|
||||
@club = @team.club
|
||||
end
|
||||
|
||||
def update
|
||||
@member.assign_attributes(member_params)
|
||||
attach_photo(@member)
|
||||
@member.save!
|
||||
redirect_to public_team_details_path(@team), notice: "Scheda aggiornata."
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
@club = @team.club
|
||||
flash.now[:alert] = e.record.errors.full_messages.join(", ")
|
||||
render :edit, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def destroy
|
||||
name = @member.full_name
|
||||
@member.destroy!
|
||||
redirect_to public_team_details_path(@team), notice: "#{name} rimosso dall'organico."
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_team
|
||||
@team = current_user.manageable_teams.find(params[:team_id])
|
||||
end
|
||||
|
||||
def require_team_owner!
|
||||
require_club_owner_for_team!(@team)
|
||||
end
|
||||
|
||||
def set_member
|
||||
@member = @team.roster_members.find(params[:id])
|
||||
end
|
||||
|
||||
def member_params
|
||||
params.require(:team_roster_member).permit(
|
||||
:category, :full_name, :role_label, :jersey_number, :bio, :position
|
||||
)
|
||||
end
|
||||
|
||||
def attach_photo(member)
|
||||
file = params.dig(:team_roster_member, :photo_file)
|
||||
member.photo_file.attach(file) if file.present?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,7 +5,7 @@ module Public
|
||||
before_action :require_login!
|
||||
before_action :set_club, only: %i[new create]
|
||||
before_action :set_team, only: %i[
|
||||
dashboard edit update invite create_invitation
|
||||
details dashboard roster edit update invite create_invitation
|
||||
assign_self_staff clear_self_staff remove_member destroy_invitation
|
||||
]
|
||||
|
||||
@@ -24,15 +24,17 @@ module Public
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def details
|
||||
load_team_details!
|
||||
render :details
|
||||
end
|
||||
|
||||
def dashboard
|
||||
require_club_owner_for_team!(@team)
|
||||
@club = @team.club
|
||||
@entitlements = @team.entitlements
|
||||
@recordings = @team.recordings.ready.includes(stream_session: :match).order(created_at: :desc).limit(10)
|
||||
@owner_membership = current_user.user_teams.find_by(team: @team)
|
||||
@staff_memberships = @team.user_teams.includes(:user)
|
||||
.where("user_teams.role = 'member' OR (user_teams.staff_kind IS NOT NULL)")
|
||||
@pending_invitations = @team.team_invitations.pending.order(created_at: :desc)
|
||||
redirect_to public_team_details_path(params[:id])
|
||||
end
|
||||
|
||||
def roster
|
||||
redirect_to public_team_details_path(params[:id])
|
||||
end
|
||||
|
||||
def edit
|
||||
@@ -44,8 +46,9 @@ module Public
|
||||
require_club_owner_for_team!(@team)
|
||||
@team.assign_attributes(team_params)
|
||||
attach_branding_logo(@team)
|
||||
attach_team_photo(@team)
|
||||
@team.save!
|
||||
redirect_to public_team_dashboard_path(@team), notice: "Squadra aggiornata."
|
||||
redirect_to public_team_details_path(@team), notice: "Squadra aggiornata."
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
@club = @team.club
|
||||
flash.now[:alert] = e.record.errors.full_messages.join(", ")
|
||||
@@ -59,27 +62,25 @@ module Public
|
||||
|
||||
def assign_self_staff
|
||||
require_club_owner_for_team!(@team)
|
||||
staff_kind = params[:staff_kind].presence_in(TeamInvitation::STAFF_KINDS) || "transmission"
|
||||
membership = current_user.user_teams.find_or_initialize_by(team: @team)
|
||||
membership.role = "member" if membership.new_record?
|
||||
Teams::StaffAssignment.call(team: @team, user: current_user, staff_kind: staff_kind, membership: membership)
|
||||
label = staff_kind == "regia" ? "Regia" : "Trasmissione"
|
||||
redirect_to public_team_dashboard_path(@team), notice: "Il tuo account è ora staff #{label}."
|
||||
Teams::StaffAssignment.call(team: @team, user: current_user, membership: membership)
|
||||
redirect_to public_team_details_path(@team), notice: "Il tuo account è ora responsabile trasmissione."
|
||||
rescue Teams::StaffAssignmentError, Teams::EntitlementError => e
|
||||
redirect_to public_team_dashboard_path(@team), alert: e.message
|
||||
redirect_to public_team_details_path(@team), alert: e.message
|
||||
end
|
||||
|
||||
def clear_self_staff
|
||||
require_club_owner_for_team!(@team)
|
||||
membership = current_user.user_teams.find_by(team: @team)
|
||||
membership&.update!(staff_kind: nil)
|
||||
redirect_to public_team_dashboard_path(@team), notice: "Ruolo staff rimosso dal tuo account."
|
||||
redirect_to public_team_details_path(@team), notice: "Ruolo staff rimosso dal tuo account."
|
||||
end
|
||||
|
||||
def create_invitation
|
||||
require_club_owner_for_team!(@team)
|
||||
@entitlements = @team.entitlements
|
||||
staff_kind = params[:staff_kind].presence_in(TeamInvitation::STAFF_KINDS) || "transmission"
|
||||
staff_kind = "transmission"
|
||||
@entitlements.assert_can_invite!(staff_kind: staff_kind)
|
||||
|
||||
email = params[:email]&.downcase&.strip
|
||||
@@ -103,18 +104,35 @@ module Public
|
||||
require_club_owner_for_team!(@team)
|
||||
ut = @team.user_teams.find_by!(user_id: params[:user_id], role: "member")
|
||||
ut.destroy!
|
||||
redirect_to public_team_dashboard_path(@team), notice: "Accesso revocato"
|
||||
redirect_to public_team_details_path(@team), notice: "Accesso revocato"
|
||||
end
|
||||
|
||||
def destroy_invitation
|
||||
require_club_owner_for_team!(@team)
|
||||
inv = @team.team_invitations.pending.find(params[:invitation_id])
|
||||
inv.destroy!
|
||||
redirect_to public_team_dashboard_path(@team), notice: "Invito annullato"
|
||||
redirect_to public_team_details_path(@team), notice: "Invito annullato"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_team_details!
|
||||
@club = @team.club
|
||||
@can_manage = @club.owned_by?(current_user)
|
||||
@entitlements = @team.entitlements
|
||||
@roster_by_category = @team.roster_by_category
|
||||
if @can_manage
|
||||
@roster_member = @team.roster_members.build(
|
||||
category: params[:category].presence_in(TeamRosterMember::CATEGORIES) || "player"
|
||||
)
|
||||
end
|
||||
@recordings = @team.recordings.ready.includes(stream_session: :match).order(created_at: :desc).limit(10)
|
||||
@owner_membership = current_user.user_teams.find_by(team: @team)
|
||||
@staff_memberships = @team.user_teams.includes(:user)
|
||||
.where("user_teams.role = 'member' OR (user_teams.staff_kind IS NOT NULL)")
|
||||
@pending_invitations = @team.team_invitations.pending.order(created_at: :desc)
|
||||
end
|
||||
|
||||
def set_club
|
||||
@club = current_user.owned_clubs.find(params[:club_id])
|
||||
end
|
||||
@@ -123,8 +141,13 @@ module Public
|
||||
@team = current_user.manageable_teams.find(params[:id])
|
||||
end
|
||||
|
||||
def attach_team_photo(team)
|
||||
file = params.dig(:team, :photo_file)
|
||||
team.photo_file.attach(file) if file.present?
|
||||
end
|
||||
|
||||
def team_params
|
||||
p = params.require(:team).permit(:name, :sport, :logo_url, :primary_color, :secondary_color)
|
||||
p = params.require(:team).permit(:name, :sport, :description, :logo_url, :primary_color, :secondary_color)
|
||||
p[:primary_color] = p[:primary_color].presence
|
||||
p[:secondary_color] = p[:secondary_color].presence
|
||||
if p[:primary_color].present?
|
||||
|
||||
Reference in New Issue
Block a user