Skip to content

Commit 2101f5a

Browse files
authored
Merge pull request #35 from fastruby/feat/edit-puzzles
Add ability to update puzzles from the admin table (Issue #30)
2 parents ee08116 + 69b57de commit 2101f5a

File tree

9 files changed

+159
-5
lines changed

9 files changed

+159
-5
lines changed

app/assets/stylesheets/application.css

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ th {
7272
background-color: #4CAF50;
7373
}
7474

75-
.reject-btn {
75+
.reject-btn, .close-btn {
7676
background-color: #f44336;
7777
}
7878

79-
.pending-btn {
79+
.pending-btn, .edit-btn {
8080
background-color: yellow;
8181
color: black;
8282
}
@@ -109,4 +109,48 @@ th {
109109
.banner-message.alert {
110110
background-color: #fff3cd;
111111
color: #856404;
112-
}
112+
}
113+
114+
/* full screen overlay */
115+
.modal-overlay {
116+
position: fixed;
117+
top: 0;
118+
left: 0;
119+
width: 100vw;
120+
height: 100vh;
121+
background-color: rgba(0, 0, 0, 0.5); /* semi-transparent black */
122+
display: flex;
123+
align-items: center;
124+
justify-content: center;
125+
z-index: 9999;
126+
}
127+
128+
/* modal content box */
129+
.modal-content {
130+
background-color: white;
131+
padding: 1.5rem;
132+
border-radius: 0.5rem;
133+
min-width: 300px;
134+
max-width: 600px;
135+
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
136+
}
137+
138+
.modal-area {
139+
display: flex;
140+
flex-direction: column;
141+
margin-bottom: 1rem;
142+
}
143+
144+
/* Horizontal radio buttons for puzzle answer */
145+
.answer-radio-group {
146+
display: flex;
147+
gap: 1.5em;
148+
align-items: center;
149+
}
150+
.answer-radio-group label {
151+
display: flex;
152+
align-items: center;
153+
gap: 0.3em;
154+
}
155+
156+

app/controllers/puzzles_controller.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,31 @@ def index
99
@rejected_puzzles = Puzzle.rejected
1010
@archived_puzzles = Puzzle.archived
1111
end
12+
13+
def edit
14+
@puzzle = Puzzle.find(params[:id])
15+
end
16+
17+
def update
18+
@puzzle = Puzzle.find(params[:id])
19+
if @puzzle.update(puzzle_params)
20+
respond_to do |format|
21+
format.turbo_stream
22+
format.html { redirect_to puzzles_path, notice: "Puzzle updated." }
23+
format.json { render json: { success: true, puzzle: @puzzle } }
24+
end
25+
else
26+
respond_to do |format|
27+
format.turbo_stream { render turbo_stream: turbo_stream.replace(@puzzle, partial: "puzzles/form", locals: { puzzle: @puzzle }) }
28+
format.html { render :edit, status: :unprocessable_entity }
29+
format.json { render json: { success: false, errors: @puzzle.errors.full_messages }, status: :unprocessable_entity }
30+
end
31+
end
32+
end
33+
34+
private
35+
36+
def puzzle_params
37+
params.require(:puzzle).permit(:question, :answer, :explanation, :link)
38+
end
1239
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Controller } from "@hotwired/stimulus"
2+
3+
export default class extends Controller {
4+
static targets = ["content"]
5+
6+
close(event) {
7+
this.element.remove()
8+
}
9+
}

app/views/layouts/application.html.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@
3333
<% end %>
3434

3535
<%= yield %>
36+
<turbo-frame id="modal"></turbo-frame>
3637
</body>
3738
</html>

app/views/puzzles/_form.html.erb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<%=form_with model: @puzzle do |f| %>
2+
<div class="modal-area">
3+
<%= f.label :question %>
4+
<%= f.text_area :question %>
5+
</div>
6+
7+
<div class="modal-area">
8+
<%= f.label :answer %><br>
9+
<div class="answer-radio-group">
10+
<label>
11+
<%= f.radio_button :answer, "ruby" %> Ruby
12+
</label>
13+
<label>
14+
<%= f.radio_button :answer, "rails" %> Rails
15+
</label>
16+
</div>
17+
</div>
18+
19+
<div class="modal-area">
20+
<%= f.label :explanation %>
21+
<%= f.text_area :explanation %>
22+
</div>
23+
24+
<div class="modal-area">
25+
<%= f.label :link %>
26+
<%= f.text_field :link %>
27+
</div>
28+
29+
<%= f.submit "Save", class: "btn approve-btn" %>
30+
<% end %>

app/views/puzzles/_puzzles_table.html.erb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
</thead>
1414
<tbody>
1515
<% puzzles.each do |puzzle| %>
16-
<tr>
16+
<tr id="<%= dom_id(puzzle) %>">
1717
<td><%= puzzle.question %></td>
1818
<td><%= puzzle.answer %></td>
1919
<td><%= puzzle.explanation %></td>
@@ -30,6 +30,10 @@
3030
<% if actions == :pending %>
3131
<%= button_to 'Approve', puzzle_state_path(puzzle, state: :approved), method: :patch, form_class: 'inline-form', class: 'btn approve-btn' %>
3232
<%= button_to 'Reject', puzzle_state_path(puzzle, state: :rejected), method: :patch, form_class: 'inline-form', class: 'btn reject-btn' %>
33+
<%= link_to "Edit",
34+
edit_puzzle_path(puzzle),
35+
data: { turbo_frame: "modal" },
36+
class: "btn edit-btn" %>
3337
<% elsif actions == :approved %>
3438
<%= button_to 'Reject', puzzle_state_path(puzzle, state: :rejected), method: :patch, form_class: 'inline-form', class: 'btn reject-btn' %>
3539
<%= button_to 'Pending', puzzle_state_path(puzzle, state: :pending), method: :patch, form_class: 'inline-form', class: 'btn pending-btn' %>

app/views/puzzles/edit.html.erb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<turbo-frame id="modal">
2+
<div data-controller="modal" data-action="click->modal#close"class="modal-overlay">
3+
<div data-modal-target="content"
4+
class="modal-content"
5+
onclick="event.stopPropagation()">
6+
<h2 class="text-xl mb-4">Edit Puzzle</h2>
7+
8+
<%= render "form", puzzle: @puzzle %>
9+
10+
<button data-action="modal#close"
11+
class="btn close-btn">Close</button>
12+
</div>
13+
</div>
14+
</turbo-frame>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<turbo-stream action="replace" target="<%= dom_id(@puzzle) %>">
2+
<template>
3+
<tr id="<%= dom_id(@puzzle) %>">
4+
<td><%= @puzzle.question %></td>
5+
<td><%= @puzzle.answer %></td>
6+
<td><%= @puzzle.explanation %></td>
7+
<td>
8+
<% if @puzzle.link.present? %>
9+
<%= link_to 'View', @puzzle.link, target: '_blank' %>
10+
<% end %>
11+
</td>
12+
<td>
13+
<%= button_to 'Approve', puzzle_state_path(@puzzle, state: :approved), method: :patch, form_class: 'inline-form', class: 'btn approve-btn' %>
14+
<%= button_to 'Reject', puzzle_state_path(@puzzle, state: :rejected), method: :patch, form_class: 'inline-form', class: 'btn reject-btn' %>
15+
<%= link_to "Edit",
16+
edit_puzzle_path(@puzzle),
17+
data: { turbo_frame: "modal" },
18+
class: "btn edit-btn" %>
19+
</td>
20+
</tr>
21+
</template>
22+
</turbo-stream>
23+
<turbo-stream action="update" target="modal">
24+
<template></template>
25+
</turbo-stream>

config/routes.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Rails.application.routes.draw do
2-
resources :puzzles, only: [ :index ] do
2+
resources :puzzles, only: [ :index, :edit, :update ] do
33
resource :state, only: [ :update ], module: :puzzles
44
end
55
resources :sessions, only: [ :create, :destroy ]

0 commit comments

Comments
 (0)