@@ -5,6 +5,7 @@ class GamesController < ApplicationController
55 before_action :set_game , only : %i[ show edit update destroy toggle_urgent_player_search accept_coach_invitation decline_coach_invitation ]
66 before_action :authorize_manage_game! , only : %i[ edit update destroy toggle_urgent_player_search ]
77 before_action :prepare_coaches , only : %i[ new create edit update ]
8+ before_action :prepare_training_blocks , only : %i[ new create edit update ]
89 skip_before_action :authenticate_user! , only : %i[ index show ]
910
1011 helper_method :display_date , :display_time , :game_badges
@@ -73,12 +74,16 @@ def new
7374 def create
7475 gp = sanitized_game_params
7576 @game = Game . new ( gp . merge ( user : current_user ) )
77+ @new_training_blocks = build_inline_training_blocks
7678
77- if @game . save
79+ if inline_training_blocks_valid? && @game . save
80+ save_inline_training_blocks
7881 @game . ensure_prebookings_for_next_weeks if @game . prebooking_enabled?
82+ apply_training_plan
7983 coaches_awaiting_invitation . each { |coach | deliver_coach_invitation ( coach ) }
8084 redirect_to @game , notice : "Game was successfully created."
8185 else
86+ @game . validate
8287 Rails . logger . warn "Game save failed: #{ @game . errors . full_messages . join ( '; ' ) } "
8388 render :new , status : :unprocessable_entity
8489 end
@@ -88,14 +93,19 @@ def edit
8893 end
8994
9095 def update
91- gp = sanitized_game_params
92- if @game . update ( gp )
96+ @game . assign_attributes ( sanitized_game_params )
97+ @new_training_blocks = build_inline_training_blocks
98+
99+ if inline_training_blocks_valid? && @game . save
100+ save_inline_training_blocks
93101 @game . ensure_prebookings_for_next_weeks if @game . prebooking_enabled?
102+ apply_training_plan
94103 coaches_awaiting_invitation . each { |coach | deliver_coach_invitation ( coach ) }
95104 # The web form saves every field at once, so one message covers the whole edit.
96105 GameChangeNotifier . notify ( game : @game , actor : current_user , changes : @game . saved_changes )
97106 redirect_to @game , notice : "Game was successfully updated."
98107 else
108+ @game . validate
99109 Rails . logger . warn "Game update failed: #{ @game . errors . full_messages . join ( '; ' ) } "
100110 render :edit , status : :unprocessable_entity
101111 end
@@ -128,6 +138,18 @@ def toggle_urgent_player_search
128138 redirect_to @game , notice : "Players search #{ state } ."
129139 end
130140
141+ # GET /games/training_plan_fragment
142+ # Библиотека зависит от выбранных тренеров, а выбирают их прямо в форме,
143+ # поэтому список блоков перезагружаем без перезагрузки страницы.
144+ def training_plan_fragment
145+ owner_ids = ( [ current_user . id ] + Array ( params [ :coach_ids ] ) . map ( &:to_i ) ) . reject ( &:zero? ) . uniq
146+
147+ render partial : "games/training_plan_library" , locals : {
148+ blocks : TrainingBlock . where ( user_id : owner_ids ) . ordered ,
149+ selected_ids : Array ( params [ :training_block_ids ] ) . map ( &:to_i )
150+ }
151+ end
152+
131153 # GET /games/prebooking_fragment
132154 def prebooking_fragment
133155 if params [ :game_id ] . present?
@@ -153,6 +175,66 @@ def prepare_coaches
153175 @coaches = User . not_merged . where ( coach : true ) . order ( :name )
154176 end
155177
178+ # В форме показываем свою библиотеку и библиотеки тренеров этой тренировки:
179+ # чужие блоки организатору ни к чему.
180+ def prepare_training_blocks
181+ @training_blocks = TrainingBlock . where ( user_id : training_library_owner_ids ) . ordered
182+ end
183+
184+ # Тренеров берём и из формы: на создании игры @game ещё нет, а библиотека
185+ # выбранного тренера нужна уже там.
186+ def training_library_owner_ids
187+ submitted = [ params . dig ( :game , :coach_id ) , params . dig ( :game , :second_coach_id ) ]
188+
189+ ( [ current_user &.id ] + Array ( @game &.assigned_coach_ids ) + submitted ) . map ( &:to_i ) . reject ( &:zero? ) . uniq
190+ end
191+
192+ def apply_training_plan
193+ # План приходит только из формы игры, поэтому пустой запрос его не стирает.
194+ return unless params [ :game ] . respond_to? ( :key? ) && params [ :game ] . key? ( :training_block_ids )
195+ return unless @game . training?
196+
197+ ids = training_plan_params [ :ids ] + @new_training_blocks . map ( &:id )
198+ # Блок из чужой библиотеки в план не попадает, даже если его id прислали в форме.
199+ allowed_ids = TrainingBlock . where ( id : ids , user_id : training_plan_owner_ids ) . pluck ( :id )
200+
201+ @game . replace_training_plan! ( ids . uniq . select { |id | allowed_ids . include? ( id ) } )
202+ end
203+
204+ def training_plan_params
205+ raw = params . fetch ( :game , ActionController ::Parameters . new )
206+ submitted = raw [ :new_training_blocks ]
207+ submitted = submitted . values if submitted . respond_to? ( :values )
208+
209+ @training_plan_params ||= {
210+ ids : Array ( raw [ :training_block_ids ] ) . map ( &:to_i ) ,
211+ new_blocks : Array ( submitted ) . filter_map { |block | block . permit ( :title , :description , :duration_minutes ) if block . respond_to? ( :permit ) }
212+ }
213+ end
214+
215+ # Блоки собираем до сохранения игры: иначе игра сохранится, а блок с опечаткой
216+ # молча потеряется вместе со своим местом в плане.
217+ def build_inline_training_blocks
218+ return [ ] unless @game . training? || @game . with_coach?
219+
220+ training_plan_params [ :new_blocks ]
221+ . reject { |attrs | attrs . values . all? ( &:blank? ) }
222+ . uniq { |attrs | attrs [ :title ] . to_s . strip . downcase }
223+ . map { |attrs | TrainingBlock . build_for ( current_user , attrs ) }
224+ end
225+
226+ def inline_training_blocks_valid?
227+ @new_training_blocks . map ( &:valid? ) . all?
228+ end
229+
230+ def save_inline_training_blocks
231+ @new_training_blocks . each ( &:save! )
232+ end
233+
234+ def training_plan_owner_ids
235+ ( [ current_user . id ] + @game . assigned_coach_ids ) . uniq
236+ end
237+
156238 # Приглашение уходит на смену слота, а не набора тренеров: если тренеров
157239 # поменять местами, модель сбросит оба статуса в pending, и по одному только
158240 # набору id никто бы приглашения не получил.
0 commit comments