Club come entità principale, branding e fix infrastruttura streaming.
Introduce clubs con abbonamento, branding Active Storage e flusso registrazione società; corregge healthcheck MediaMTX (immagine distroless) e allinea dominio produzione matchlivetv.it. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
27
backend/app/models/club.rb
Normal file
27
backend/app/models/club.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
class Club < ApplicationRecord
|
||||
include Brandable
|
||||
|
||||
has_many :club_memberships, dependent: :destroy
|
||||
has_many :users, through: :club_memberships
|
||||
has_many :teams, dependent: :destroy
|
||||
has_one :subscription, dependent: :destroy
|
||||
|
||||
validates :name, presence: true
|
||||
validates :sport, presence: true
|
||||
validates :primary_color, presence: true
|
||||
validates :secondary_color, presence: true
|
||||
|
||||
def owner
|
||||
club_memberships.find_by(role: "owner")&.user
|
||||
end
|
||||
|
||||
def owned_by?(user)
|
||||
club_memberships.exists?(user: user, role: "owner")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def branding_parent
|
||||
nil
|
||||
end
|
||||
end
|
||||
9
backend/app/models/club_membership.rb
Normal file
9
backend/app/models/club_membership.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class ClubMembership < ApplicationRecord
|
||||
ROLES = %w[owner].freeze
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :club
|
||||
|
||||
validates :role, inclusion: { in: ROLES }
|
||||
validates :user_id, uniqueness: { scope: :club_id }
|
||||
end
|
||||
43
backend/app/models/concerns/brandable.rb
Normal file
43
backend/app/models/concerns/brandable.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
module Brandable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
HEX_COLOR = /\A#[0-9A-Fa-f]{6}\z/
|
||||
|
||||
included do
|
||||
has_one_attached :logo_file
|
||||
validates :primary_color, format: { with: HEX_COLOR }, allow_blank: true
|
||||
validates :secondary_color, format: { with: HEX_COLOR }, allow_blank: true
|
||||
end
|
||||
|
||||
def effective_primary_color
|
||||
primary_color.presence || default_primary_color
|
||||
end
|
||||
|
||||
def effective_secondary_color
|
||||
secondary_color.presence || default_secondary_color
|
||||
end
|
||||
|
||||
def effective_logo_url
|
||||
if logo_file.attached?
|
||||
Rails.application.routes.url_helpers.rails_blob_path(logo_file, only_path: true)
|
||||
elsif logo_url.present?
|
||||
logo_url
|
||||
else
|
||||
branding_parent&.effective_logo_url
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def default_primary_color
|
||||
"#e53935"
|
||||
end
|
||||
|
||||
def default_secondary_color
|
||||
"#ffffff"
|
||||
end
|
||||
|
||||
def branding_parent
|
||||
nil
|
||||
end
|
||||
end
|
||||
@@ -2,11 +2,11 @@ class Subscription < ApplicationRecord
|
||||
STATUSES = %w[active trialing past_due canceled incomplete].freeze
|
||||
ACTIVE_STATUSES = %w[active trialing].freeze
|
||||
|
||||
belongs_to :team
|
||||
belongs_to :club
|
||||
belongs_to :plan
|
||||
|
||||
validates :status, inclusion: { in: STATUSES }
|
||||
validates :team_id, uniqueness: true
|
||||
validates :club_id, uniqueness: true
|
||||
|
||||
scope :active, -> { where(status: ACTIVE_STATUSES) }
|
||||
|
||||
|
||||
@@ -1,19 +1,39 @@
|
||||
class Team < ApplicationRecord
|
||||
include Brandable
|
||||
|
||||
belongs_to :club
|
||||
has_many :user_teams, dependent: :destroy
|
||||
has_many :users, through: :user_teams
|
||||
has_many :matches, dependent: :destroy
|
||||
has_one :youtube_credential, dependent: :destroy
|
||||
has_one :subscription, dependent: :destroy
|
||||
has_many :recordings, dependent: :destroy
|
||||
has_many :team_invitations, dependent: :destroy
|
||||
|
||||
validates :name, presence: true
|
||||
|
||||
def subscription
|
||||
club.subscription
|
||||
end
|
||||
|
||||
def entitlements
|
||||
@entitlements ||= Teams::Entitlements.new(self)
|
||||
Teams::Entitlements.new(self)
|
||||
end
|
||||
|
||||
def owner
|
||||
user_teams.find_by(role: "owner")&.user
|
||||
club&.owner
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def branding_parent
|
||||
club
|
||||
end
|
||||
|
||||
def default_primary_color
|
||||
club&.primary_color.presence || super
|
||||
end
|
||||
|
||||
def default_secondary_color
|
||||
club&.secondary_color.presence || super
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,8 +5,21 @@ class User < ApplicationRecord
|
||||
|
||||
has_many :user_teams, dependent: :destroy
|
||||
has_many :teams, through: :user_teams
|
||||
has_many :club_memberships, dependent: :destroy
|
||||
has_many :clubs, through: :club_memberships
|
||||
has_many :owned_clubs, -> { where(club_memberships: { role: "owner" }) }, through: :club_memberships, source: :club
|
||||
has_many :stream_sessions, dependent: :nullify
|
||||
|
||||
def manageable_teams
|
||||
staff_ids = teams.select(:id)
|
||||
owner_ids = Team.where(club_id: owned_clubs.select(:id)).select(:id)
|
||||
Team.where(id: staff_ids).or(Team.where(id: owner_ids))
|
||||
end
|
||||
|
||||
def primary_club
|
||||
owned_clubs.order(:created_at).first
|
||||
end
|
||||
|
||||
validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
|
||||
validates :name, presence: true
|
||||
validates :role, inclusion: { in: ROLES }
|
||||
|
||||
Reference in New Issue
Block a user