Invita lead

ma per ora non posso tenerlo così? tolgo solo il form… e mi generi il link per il referral e non puoi invitare più di 7 persone? e l’approve lo fa il tutor? e se non sei approved il link referral non funziona?

Sì, puoi tenerlo così e semplificare: togli il form, mostri solo il tuo link referral, metti limite 7 invitati, approva il Tutor, e fai sì che il link funzioni solo se l’invitante è “approved” e ha ancora slot disponibili.

Ti lascio tutto pronto (migrazioni + model + controller + view). Copia/incolla e vai.

1) Migrazione (limite inviti sul referrer)

rails g migration AddInvitesToUsers invites_count:integer invites_limit:integer referrer_id:integer

db/migrate/20251110_add_invites_to_users.rb

class AddInvitesToUsers < ActiveRecord::Migration[7.2] def change add_column :users, :invites_count, :integer, default: 0, null: false add_column :users, :invites_limit, :integer, default: 7, null: false add_column :users, :referrer_id, :bigint add_index :users, :referrer_id end end

Se referrer_id è già sul modello User, rimuovi quella riga.

2) User model (token referral, gating, contatore)

app/models/user.rb

class User < ApplicationRecord belongs_to :referrer, class_name: “User”, optional: true

# Sei “approved” per invitare? (adatta alla tua logica: ruolo Tutor/TeamManager o Lead approvato) def approved_referrer? has_role?(:tutor) || has_role?(:team_manager) || lead&.approved? rescue false end

def can_invite? approved_referrer? && invites_count < invites_limit end

def remaining_invites [invites_limit - invites_count, 0].max end

# Link referral firmato a scadenza (rilancia quando vuoi) def referral_token(expires_in: 14.days) return nil unless approved_referrer? && can_invite? signed_id(purpose: :referral, expires_in: expires_in) end

def referral_url tok = referral_token tok ? Rails.application.routes.url_helpers.new_registration_url(ref: tok) : nil end

# Conta un invito andato a buon fine def count_successful_invite! increment!(:invites_count) end end

3) RegistrationsController (accetta ref firmato, applica limiti, fallback legacy)

Manteniamo compatibilità col tuo referral_code (username) ma preferiamo ref firmato.

app/controllers/registrations_controller.rb

class RegistrationsController < ApplicationController allow_unauthenticated_access only: %i[new create] layout “posts”, only: %i[new create]

def new @referrer = locate_referrer(params[:ref], params.dig(:lead, :referral_code)) @user = User.new if params[:ref].present? && @referrer.nil? flash.now[:alert] = “Link invito non valido, scaduto o referrer non abilitato.” elsif @referrer && !@referrer.can_invite? flash.now[:alert] = “Questo invito non è più disponibile.” @referrer = nil end end

def create # preferisci token firmato se presente referrer = locate_referrer(params[:ref], signup_params[:referral_code])

email    = signup_params[:email].to_s.strip
password = signup_params[:password]

provisional_username = generate_username_from(email)

# Se il referrer non è valido/abilitato, ignoralo
referrer = nil unless referrer&.can_invite?

flow = LeadSignupFlow.new.call!(
  lead_params: { email: email, username: provisional_username },
  password: password,
  referral_lead_id: nil,      # non più da username, usiamo referrer se c'è
  auto_approve: false         # il Tutor approva
)

# collega il referrer (se valido)
if referrer
  flow.user.update!(referrer_id: referrer.id)
end

start_new_session_for(flow.user)

# conteggia solo se c'è referrer valido
referrer&.count_successful_invite!

redirect_to after_authentication_url, notice: "Benvenuto! Account creato."   rescue ActiveRecord::RecordInvalid => e
@lead = Lead.new(signup_params)
flash.now[:alert] = e.record.errors.full_messages.to_sentence
render "sessions/new", status: :unprocessable_entity   end

def edit @user = Current.user @lead = @user.lead || Lead.find_by(email: @user.email_address) || Lead.new(email: @user.email_address, username: default_username(@user)) end

def update @user = Current.user @lead = @user.lead || Lead.find_by(email: @user.email_address) || Lead.new(email: @user.email_address, username: default_username(@user))

ActiveRecord::Base.transaction do
  @user.update!(user_params)
  if @lead.new_record?
    @lead.assign_attributes(lead_params)
    @lead.user_id ||= @user.id if @lead.respond_to?(:user_id)
    @lead.save!
  else
    @lead.update!(lead_params)
  end
end

redirect_to after_authentication_url, notice: "Profilo aggiornato!"   rescue ActiveRecord::RecordInvalid => e
flash.now[:alert] = e.record.errors.full_messages.to_sentence
render :edit, status: :unprocessable_entity   end

private

def locate_referrer(signed_ref, legacy_ref_code) # 1) token firmato if signed_ref.present? user = User.find_signed(signed_ref, purpose: :referral) rescue nil return (user if user&.approved_referrer?) end

# 2) fallback legacy: username nel Lead (come prima)
if legacy_ref_code.present?
  lead = Lead.find_by(username: legacy_ref_code)
  user = lead&.user
  return (user if user&.approved_referrer?)
end

nil   end

def user_params params.require(:user).permit(:name, :surname, :phone, :email_address, :password, :password_confirmation) end

def lead_params params.fetch(:lead, {}).permit(:username, :phone, :notes) end

def default_username(user) user.email_address.to_s.split(“@”).first.to_s.parameterize.presence || “utente” end

def signup_params params.require(:lead).permit(:email, :password, :referral_code) end

def generate_username_from(email) base = email.to_s.split(“@”).first.to_s.parameterize.presence || “user” uname = base i = 1 while Lead.exists?(username: uname) i += 1 uname = “#{base}-#{i}” break if i > 1000 end uname end end

4) Solo il Tutor approva

Se usi già l’approve sui Lead, tieni questo guard-rail:

app/controllers/account/leads_controller.rb

class Account::LeadsController < ApplicationController before_action :require_tutor!

def approve lead = Lead.find(params[:id]) # … la tua logica di approve (crea user o setta stato) … redirect_to account_leads_path, notice: “Lead approvato.” end

private

def require_tutor! unless Current.user&.has_role?(:tutor) || Current.user&.has_role?(:team_manager) redirect_to account_leads_path, alert: “Non autorizzato.” end end end

5) View “I miei invitati” (niente form; mostra link + quota)

Sostituisci il blocco del form con questo:

<% content_for :title, “I miei invitati” %>

I miei invitati

<% if flash[:notice] %>

<%= flash[:notice] %>

<% end %> <% if flash[:alert] %>

<%= flash[:alert] %>

<% end %>

Il tuo link referral

<% if Current.user.approved_referrer? %> <% if Current.user.can_invite? %> <% link = Current.user.referral_url %>

Inviti rimasti: <%= Current.user.remaining_invites %> su <%= Current.user.invites_limit %>. Il link scade tra 14 giorni.

<% else %>

Hai raggiunto il limite massimo di inviti ( <%= Current.user.invites_limit %> ).

<% end %> <% else %>

Il referral è attivo solo per utenti approvati. Contatta un Tutor per l’abilitazione.

<% end %>

Invitati (referral)

<% @invited_leads.each do |lead| %>
EmailUsernameNomeCognome
<%= lead.email %><%= lead.username %><%= lead.name %><%= lead.surname %> <% if Current.user.has_role?(:tutor) || Current.user.has_role?(:team_manager) %> <%= button_to "Approva", approve_account_lead_path(lead), method: :post, form: { data: { turbo_confirm: "Approvare questo invitato?" } }, class: "text-blue-600 hover:underline mr-3" %> <% end %> <%= button_to "Rimuovi", account_lead_path(lead), method: :delete, form: { data: { turbo_confirm: "Rimuovere questo invitato?" } }, class: "text-red-600 hover:underline" %> <% end %>

6) Risposte ai tuoi punti (sintesi)

“Per ora posso tenerlo così?” Sì: togli il form; usa link referral firmato; password nel form resta ok.

“Generi il link e max 7 inviti?” Sì: invites_limit=7, can_invite? e count_successful_invite!.

“Approve lo fa il Tutor?” Sì: require_tutor! su approve.

“Se non sei approved il link non funziona?” Esatto: approved_referrer? viene controllato in locate_referrer e nella view; il link non si genera / non viene accettato.