feat: add training constructor and drop score stats from trainings - #153
Conversation
Тренировка отличается от игры не только тренером: счёт там не ведут, а само занятие складывается из блоков. Блоки живут в библиотеке автора, поэтому заполняются один раз, а дальше просто отмечаются в тренировке. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a0747656ba
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| owner_ids = ([ current_user&.id ] + Array(@game&.assigned_coach_ids)).compact.uniq | ||
| @training_blocks = TrainingBlock.where(user_id: owner_ids).includes(:user).ordered |
There was a problem hiding this comment.
Load selected coaches' blocks during creation
On both new and create, this before-action runs before the action initializes @game, so @game is nil and owner_ids contains only the current user. Selecting a coach on the new-training form therefore never exposes that coach's library; those blocks become available only after saving and reopening the game for editing. Build the game from submitted/default attributes before preparing this collection, or refresh the collection when the coach selection changes.
Useful? React with 👍 / 👎.
| def create_training_blocks(new_blocks) | ||
| new_blocks.filter_map { |attrs| TrainingBlock.upsert_for(current_user, attrs)&.id } |
There was a problem hiding this comment.
Reject invalid inline blocks before saving the game
When an inline block fails validation—for example, a duration above 600, a title over 100 characters, or a description over 500 characters—upsert_for returns nil and this filter_map silently discards it. The game has already been saved, so the request redirects with a success notice while the entered block and its plan position are lost; surface these validation errors and avoid completing the game save as successful.
Useful? React with 👍 / 👎.
| # На тренировке счёт не ведут, поэтому матчи из формы туда не попадают. | ||
| matches_input = game.training? ? [] : matches_params |
There was a problem hiding this comment.
Remove or prohibit scores when converting to training
If an ordinary game already has saved Match rows and is later edited to kind: "training", this condition only ignores future score submissions; the existing matches remain linked to the game and continue appearing in player histories and contributing to accumulated statistics. Because the training UI now hides all match controls, users cannot see or correct that retained score from the game page, so the kind transition must either be blocked or explicitly reconcile the existing matches and statistics.
Useful? React with 👍 / 👎.
| <div class="mt-3 space-y-2"> | ||
| <% @training_blocks.each do |block| %> | ||
| <label class="flex cursor-pointer items-start gap-2 rounded-md border border-gray-300 px-3 py-2 hover:bg-gray-50 dark:border-white/15 dark:hover:bg-white/5"> | ||
| <%= check_box_tag "game[training_block_ids][]", block.id, selected_block_ids.include?(block.id), |
There was a problem hiding this comment.
Submit an explicit training-plan order
For multiple existing library blocks, checkbox values are submitted in this DOM order, not in the order the user clicks them, while @training_blocks is alphabetically ordered by title. replace_training_plan! then treats that submitted sequence as the plan order and the show page numbers it, so organizers cannot construct a non-alphabetical session sequence; provide ordering controls and submit explicit positions.
Useful? React with 👍 / 👎.
| <p class="mt-3 text-xs text-gray-500 dark:text-slate-400"><%= t("games.form.training_plan_empty") %></p> | ||
| <% end %> | ||
|
|
||
| <div class="mt-3 space-y-3" data-training-plan-target="container"></div> |
There was a problem hiding this comment.
Repopulate inline blocks after validation errors
When game validation fails after the user has added inline blocks, create renders the form again but this container is always empty and the submitted new_training_blocks values are never rendered. The user consequently loses every title, duration, and description they entered merely because an unrelated game field was invalid; rebuild these rows from params on the error render.
Useful? React with 👍 / 👎.
Codex указал на четыре дыры: библиотека выбранного тренера не подтягивалась в форме создания, блок с ошибкой терялся вместе с успешным сохранением игры, порядок блоков задавался алфавитом, а игра с записанным счётом могла молча стать тренировкой и спрятать этот счёт. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Тренировка отличается от игры не только тренером: счёт там не ведут, а само занятие складывается из блоков. Блоки живут в библиотеке автора, поэтому заполняются один раз, а дальше просто отмечаются в тренировке.