Skip to content

Commit c88c8f5

Browse files
feat: manage games with module usage and a delete warning
Adds a games admin section with module usage, an attempts counter cache, and a delete warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent c73e3df commit c88c8f5

16 files changed

Lines changed: 233 additions & 83 deletions

File tree

‎app/controllers/admin/games_controller.rb‎

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ class Admin::GamesController < AdminController
22
before_action :set_game, only: %i[ show edit update destroy ]
33

44
def index
5-
@games = Game.all
5+
@games = Game.includes(:content_modules).order(:title)
6+
end
7+
8+
def show
9+
@content_modules = @game.content_modules.order(:name)
610
end
711

812
def new
@@ -15,34 +19,24 @@ def edit
1519
def create
1620
@game = Game.new(game_params)
1721

18-
respond_to do |format|
19-
if @game.save
20-
format.html { redirect_back(fallback_location: admin_games_path, notice: "Game was successfully created.") }
21-
else
22-
format.html { render :new, status: :unprocessable_content }
23-
end
22+
if @game.save
23+
redirect_to [ :admin, @game ], notice: "Game was successfully created."
24+
else
25+
render :new, status: :unprocessable_content
2426
end
2527
end
2628

2729
def update
28-
respond_to do |format|
29-
if @game.update(game_params)
30-
format.html { redirect_to [ :admin, @game ], notice: "Game was successfully updated.", status: :see_other }
31-
format.json { render :show, status: :ok, location: [ :admin, @game ] }
32-
else
33-
format.html { render :edit, status: :unprocessable_content }
34-
format.json { render json: @game.errors, status: :unprocessable_content }
35-
end
30+
if @game.update(game_params)
31+
redirect_to [ :admin, @game ], notice: "Game was successfully updated."
32+
else
33+
render :edit, status: :unprocessable_content
3634
end
3735
end
3836

3937
def destroy
4038
@game.destroy!
41-
42-
respond_to do |format|
43-
format.html { redirect_to admin_games_path, notice: "Game was successfully destroyed.", status: :see_other }
44-
format.json { head :no_content }
45-
end
39+
redirect_to admin_games_path, notice: "Game was successfully deleted.", status: :see_other
4640
end
4741

4842
private

‎app/helpers/admin/games_helper.rb‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module Admin::GamesHelper
2+
def delete_game_confirmation(game)
3+
module_count = game.content_modules.size
4+
attempt_count = game.game_attempts_count
5+
6+
consequences = []
7+
consequences << "It will be removed from #{pluralize(module_count, "module")}" if module_count.positive?
8+
consequences << "#{pluralize(attempt_count, "student attempt")} will be deleted" if attempt_count.positive?
9+
return "Delete this game?" if consequences.empty?
10+
11+
"Delete this game? #{consequences.join(" and ")}."
12+
end
13+
end

‎app/models/game_attempt.rb‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
class GameAttempt < ApplicationRecord
22
belongs_to :student
3-
belongs_to :game
3+
belongs_to :game, counter_cache: true
44

55
validates :student_id, :game_id, presence: true
66
validates :token, presence: true, uniqueness: true

‎app/views/admin/games/_form.html.erb‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
<%= form.label :slug, "Slug", class: "label" %>
1818
<%= form.text_field :slug, class: "input w-full" %>
1919

20+
<%= form.label :description, class: "label" %>
21+
<%= form.text_area :description, rows: 4, class: "textarea w-full" %>
22+
2023
<%= form.submit class: "btn btn-primary" %>
2124
</div>
2225
<% end %>

‎app/views/admin/games/_game.html.erb‎

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
<% content_for :title, "Editing game" %>
1+
<% content_for :title, "Edit Game" %>
22

3-
<div class="md:w-2/3 w-full">
4-
<h1 class="font-bold text-4xl">Editing game</h1>
5-
6-
<%= render "form", game: @game %>
7-
8-
<%= link_to "Show this game", [:admin, @game], class: "w-full sm:w-auto text-center mt-2 sm:mt-0 sm:ml-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
9-
<%= link_to "Back to games", admin_games_path, class: "w-full sm:w-auto text-center mt-2 sm:mt-0 sm:ml-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
10-
</div>
3+
<nav class="breadcrumbs text-sm">
4+
<ul>
5+
<li><%= link_to "Games", admin_games_path %></li>
6+
<li><%= link_to @game.title, [:admin, @game] %></li>
7+
<li>Edit</li>
8+
</ul>
9+
</nav>
10+
<section class="py-10 flex items-center justify-center">
11+
<div class="card card-border bg-base-100 shadow-md w-96">
12+
<div class="card-body">
13+
<h1 class="card-title">Edit Game</h1>
14+
<%= render "form", game: @game %>
15+
</div>
16+
</div>
17+
</section>
Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,43 @@
11
<% content_for :title, "Games" %>
22

3-
<div class="w-full">
4-
<% if notice.present? %>
5-
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-md inline-block" id="notice"><%= notice %></p>
6-
<% end %>
3+
<p style="color: green"><%= notice %></p>
4+
<p style="color: red"><%= alert %></p>
75

8-
<div class="flex justify-between items-center">
9-
<h1 class="font-bold text-4xl">Games</h1>
10-
<%= link_to "New game", new_admin_game_path, class: "rounded-md px-3.5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white block font-medium" %>
11-
</div>
6+
<header class="flex justify-between items-center mb-4">
7+
<h1 class="text-2xl">Games</h1>
8+
<%= link_to "New game", new_admin_game_path, class: "btn btn-primary" %>
9+
</header>
1210

13-
<div id="games" class="min-w-full divide-y divide-gray-200 space-y-5">
14-
<% if @games.any? %>
15-
<% @games.each do |game| %>
16-
<div class="flex flex-col sm:flex-row justify-between items-center pb-5 sm:pb-0">
17-
<%= render game %>
18-
<div class="w-full sm:w-auto flex flex-col sm:flex-row space-x-2 space-y-2">
19-
<%= link_to "Show", [:admin, game], class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
20-
<%= link_to "Edit", edit_admin_game_path(game), class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
21-
<%= button_to "Destroy", [:admin, game], method: :delete, class: "w-full sm:w-auto rounded-md px-3.5 py-2.5 text-white bg-red-600 hover:bg-red-500 font-medium cursor-pointer", data: { turbo_confirm: "Are you sure?" } %>
22-
</div>
23-
</div>
24-
<% end %>
25-
<% else %>
26-
<p class="text-center my-10">No games found.</p>
27-
<% end %>
11+
<% if @games.any? %>
12+
<div class="overflow-x-auto rounded-box border border-base-content/5 bg-base-100">
13+
<table class="table">
14+
<thead>
15+
<tr>
16+
<th>Title</th>
17+
<th>Slug</th>
18+
<th>Modules</th>
19+
<th>Actions</th>
20+
</tr>
21+
</thead>
22+
<tbody>
23+
<% @games.each do |game| %>
24+
<tr>
25+
<td><%= link_to game.title, [:admin, game], class: "link" %></td>
26+
<td><%= game.slug %></td>
27+
<td><%= game.content_modules.size %></td>
28+
<td>
29+
<div class="flex gap-2">
30+
<%= link_to "Edit", edit_admin_game_path(game), class: "btn btn-sm btn-primary" %>
31+
<%= button_to "Delete", [:admin, game], method: :delete,
32+
data: { "turbo-confirm": delete_game_confirmation(game) },
33+
class: "btn btn-sm btn-error" %>
34+
</div>
35+
</td>
36+
</tr>
37+
<% end %>
38+
</tbody>
39+
</table>
2840
</div>
29-
</div>
41+
<% else %>
42+
<p class="text-sm opacity-60">No games yet.</p>
43+
<% end %>

‎app/views/admin/games/new.html.erb‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
<% content_for :title, "New game" %>
1+
<% content_for :title, "New Game" %>
22

33
<nav class="breadcrumbs text-sm">
44
<ul>
5-
<li><%= link_to "Content Modules", admin_content_modules_path %></li>
5+
<li><%= link_to "Games", admin_games_path %></li>
66
<li>New Game</li>
77
</ul>
88
</nav>
Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,45 @@
1-
<% content_for :title, "Showing game" %>
1+
<% content_for :title, @game.title %>
22

3-
<div class="md:w-2/3 w-full">
4-
<% if notice.present? %>
5-
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-md inline-block" id="notice"><%= notice %></p>
6-
<% end %>
3+
<p style="color: green"><%= notice %></p>
4+
<p style="color: red"><%= alert %></p>
5+
6+
<nav class="breadcrumbs text-sm">
7+
<ul>
8+
<li><%= link_to "Games", admin_games_path %></li>
9+
<li><%= @game.title %></li>
10+
</ul>
11+
</nav>
712

8-
<h1 class="font-bold text-4xl">Showing game</h1>
13+
<header class="flex items-center justify-between mb-4">
14+
<h1 class="text-2xl"><%= @game.title %></h1>
15+
<div class="flex gap-2">
16+
<%= link_to "Edit this game", edit_admin_game_path(@game), class: "btn btn-primary" %>
17+
<%= button_to "Delete this game", [:admin, @game], method: :delete,
18+
data: { "turbo-confirm": delete_game_confirmation(@game) },
19+
class: "btn btn-error" %>
20+
</div>
21+
</header>
922

10-
<%= render @game %>
23+
<dl class="mb-8 space-y-4">
24+
<div>
25+
<dt class="font-semibold">Slug</dt>
26+
<dd><%= @game.slug %></dd>
27+
</div>
28+
<div>
29+
<dt class="font-semibold">Description</dt>
30+
<dd><%= @game.description.presence || "No description." %></dd>
31+
</div>
32+
</dl>
1133

12-
<%= link_to "Edit this game", edit_admin_game_path(@game), class: "w-full sm:w-auto text-center rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
13-
<%= link_to "Back to games", admin_games_path, class: "w-full sm:w-auto text-center mt-2 sm:mt-0 sm:ml-2 rounded-md px-3.5 py-2.5 bg-gray-100 hover:bg-gray-50 inline-block font-medium" %>
14-
<%= button_to "Destroy this game", [:admin, @game], method: :delete, form_class: "sm:inline-block mt-2 sm:mt-0 sm:ml-2", class: "w-full rounded-md px-3.5 py-2.5 text-white bg-red-600 hover:bg-red-500 font-medium cursor-pointer", data: { turbo_confirm: "Are you sure?" } %>
15-
</div>
34+
<section>
35+
<h2 class="text-lg font-semibold mb-2">Modules</h2>
36+
<% if @content_modules.any? %>
37+
<ul class="list-disc pl-6">
38+
<% @content_modules.each do |content_module| %>
39+
<li><%= link_to content_module.name, edit_admin_content_module_path(content_module), class: "link" %></li>
40+
<% end %>
41+
</ul>
42+
<% else %>
43+
<p class="text-sm opacity-60">Not in any module yet.</p>
44+
<% end %>
45+
</section>

‎app/views/shared/_admin_nav.html.erb‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
<%= link_to "Content Modules", admin_content_modules_path,
1414
class: "#{controller_path.start_with?("admin/content_modules", "admin/links") ? "active" : ""}" %>
1515
</li>
16+
<li>
17+
<%= link_to "Games", admin_games_path,
18+
class: "#{controller_path.start_with?("admin/games") ? "active" : ""}" %>
19+
</li>
1620
</ul>
1721
<%= button_to "Log out", admin_session_path, method: :delete, class: "btn btn-sm btn-ghost ml-2" %>
1822
</div>

0 commit comments

Comments
 (0)