Skip to content

Commit f85905a

Browse files
kcdragonclaude
andauthored
Make scenario name inline-editable and restyle details card (#30)
Replace the scenario-name field with an inline, click-to-edit heading backed by a Turbo Frame and a new Scenarios::NamesController (show/edit/ update). Restyle the scenario details into a card matching the allocation panels, with the Total giving amount input and Save button on one row. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent bfc85d8 commit f85905a

8 files changed

Lines changed: 108 additions & 18 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
class Scenarios::NamesController < ApplicationController
2+
before_action :set_scenario
3+
4+
def show
5+
end
6+
7+
def edit
8+
end
9+
10+
def update
11+
if @scenario.update(name_params)
12+
render :show
13+
else
14+
render :edit, status: :unprocessable_entity
15+
end
16+
end
17+
18+
private
19+
20+
def set_scenario
21+
@scenario = Current.user.scenarios.where(organization: Current.organization).find(params[:scenario_id])
22+
end
23+
24+
def name_params
25+
params.require(:scenario).permit(:name)
26+
end
27+
end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<%= turbo_frame_tag dom_id(scenario, :name), class: "mt-6 block" do %>
2+
<%= form_with model: scenario, url: scenario_name_path(scenario), method: :patch, class: "flex items-center gap-2" do |form| %>
3+
<%= form.text_field :name, required: true, autofocus: true,
4+
class: "w-full max-w-md rounded-md border border-line bg-surface-soft px-3 py-1.5 font-serif text-3xl font-medium tracking-tight text-ink shadow-sm focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/10" %>
5+
6+
<button type="submit" aria-label="Save name"
7+
class="inline-flex h-9 w-9 items-center justify-center rounded-md bg-accent text-white transition hover:bg-[#444] cursor-pointer">
8+
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
9+
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" />
10+
</svg>
11+
</button>
12+
13+
<%= link_to scenario_name_path(scenario), aria: { label: "Cancel" },
14+
class: "inline-flex h-9 w-9 items-center justify-center rounded-md border border-line bg-surface text-ink-soft transition hover:bg-canvas cursor-pointer" do %>
15+
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">
16+
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
17+
</svg>
18+
<% end %>
19+
<% end %>
20+
<% end %>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<%= turbo_frame_tag dom_id(scenario, :name), class: "mt-6 block" do %>
2+
<%= link_to edit_scenario_name_path(scenario), class: "group inline-flex items-center gap-2" do %>
3+
<h1 class="font-serif font-medium text-3xl tracking-tight text-ink"><%= scenario.name %></h1>
4+
<span class="text-ink-faint opacity-0 transition group-hover:opacity-100" aria-hidden="true">
5+
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
6+
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931z" />
7+
</svg>
8+
</span>
9+
<% end %>
10+
<% end %>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<%= render "scenarios/names/form", scenario: @scenario %>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<%= render "scenarios/names/name", scenario: @scenario %>

app/views/scenarios/show.html.erb

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,20 @@
22
<%= link_to "← Back to explore options", scenarios_path,
33
class: "inline-block rounded-md border border-line bg-surface px-3 py-1.5 text-sm text-ink-soft hover:bg-canvas transition" %>
44

5-
<h1 class="mt-6 font-serif font-medium text-3xl tracking-tight text-ink"><%= @scenario.name %></h1>
5+
<%= render "scenarios/names/name", scenario: @scenario %>
66

77
<%= form_with model: @scenario, class: "contents" do |form| %>
8-
<div class="mt-6 flex items-center gap-4">
9-
<%= form.label :name, class: "w-40 text-ink-soft" %>
10-
<%= form.text_field :name, required: true,
11-
class: "max-w-md w-full block rounded-md border border-line bg-surface px-3 py-2 shadow-sm focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/10" %>
12-
</div>
13-
14-
<div class="mt-4 flex items-center gap-4">
15-
<%= form.label :total_giving_amount, "Total giving amount", class: "w-40 text-ink-soft" %>
16-
<div class="relative max-w-md w-full">
17-
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-ink-faint">$</span>
18-
<%= form.number_field :total_giving_amount, step: "1", min: 0, placeholder: "0",
19-
class: "block w-full rounded-md border border-line bg-surface pl-7 pr-3 py-2 shadow-sm focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/10" %>
8+
<div class="mt-6 rounded-2xl border border-line bg-surface p-6 shadow-sm">
9+
<div class="flex items-center gap-4">
10+
<%= form.label :total_giving_amount, "Total giving amount", class: "w-40 text-ink-soft" %>
11+
<div class="relative max-w-md w-full">
12+
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-ink-faint">$</span>
13+
<%= form.number_field :total_giving_amount, step: "1", min: 0, placeholder: "0",
14+
class: "block w-full rounded-md border border-line bg-surface-soft pl-7 pr-3 py-2 shadow-sm focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/10" %>
15+
</div>
16+
<%= form.submit "Save", class: "rounded-md px-3.5 py-2 bg-accent hover:bg-[#444] text-white text-sm font-medium cursor-pointer transition" %>
2017
</div>
2118
</div>
22-
23-
<div class="mt-4 flex items-center gap-4">
24-
<span class="w-40"></span>
25-
<%= form.submit "Save", class: "rounded-md px-3.5 py-2 bg-accent hover:bg-[#444] text-white text-sm font-medium cursor-pointer transition" %>
26-
</div>
2719
<% end %>
2820

2921
<div class="mt-8 grid gap-6 lg:grid-cols-2">

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
resource :session
88

99
resources :scenarios do
10+
resource :name, only: %i[ show edit update ], module: :scenarios
1011
resources :allocations, only: %i[ create update destroy ]
1112
end
1213

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
require "test_helper"
2+
3+
class Scenarios::NamesControllerTest < ActionDispatch::IntegrationTest
4+
setup do
5+
host! "arlington.localhost"
6+
sign_in_as users(:one)
7+
@scenario = scenarios(:one_arlington)
8+
end
9+
10+
test "show renders the inline name frame" do
11+
get scenario_name_url(@scenario)
12+
assert_response :success
13+
assert_select "turbo-frame##{dom_id(@scenario, :name)}"
14+
end
15+
16+
test "edit renders the inline name form" do
17+
get edit_scenario_name_url(@scenario)
18+
assert_response :success
19+
assert_select "turbo-frame##{dom_id(@scenario, :name)} form input[name=?]", "scenario[name]"
20+
end
21+
22+
test "update changes the name" do
23+
patch scenario_name_url(@scenario), params: { scenario: { name: "Renamed scenario" } }
24+
assert_response :success
25+
assert_equal "Renamed scenario", @scenario.reload.name
26+
end
27+
28+
test "update re-renders the form when name is blank" do
29+
patch scenario_name_url(@scenario), params: { scenario: { name: "" } }
30+
assert_response :unprocessable_entity
31+
assert_select "form input[name=?]", "scenario[name]"
32+
end
33+
34+
test "cannot edit a scenario owned by another user or org" do
35+
get edit_scenario_name_url(scenarios(:two_boston))
36+
assert_response :not_found
37+
end
38+
end

0 commit comments

Comments
 (0)