diff --git a/app/assets/stylesheets/characters.css b/app/assets/stylesheets/characters.css
index 23d2f9a..10dace6 100644
--- a/app/assets/stylesheets/characters.css
+++ b/app/assets/stylesheets/characters.css
@@ -50,14 +50,14 @@
}
}
-.stats {
+.stats, .counters {
display: flex;
flex-wrap: wrap;
gap: .2em;
justify-content: center;
}
-.stat {
+.stat, .counter {
display: flex;
flex-direction: column;
border-radius: var(--border-radius);
diff --git a/app/assets/stylesheets/forms.css b/app/assets/stylesheets/forms.css
index d8b7dbc..ad08a17 100644
--- a/app/assets/stylesheets/forms.css
+++ b/app/assets/stylesheets/forms.css
@@ -41,7 +41,7 @@ form, fieldset {
}
}
-form.stat-form {
+form.stat-form, form.counter-form {
display: flex;
flex-direction: column;
diff --git a/app/controllers/counters_controller.rb b/app/controllers/counters_controller.rb
new file mode 100644
index 0000000..2f8cb73
--- /dev/null
+++ b/app/controllers/counters_controller.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+class CountersController < ApplicationController
+ before_action :set_section, only: [ :new, :create ]
+ before_action :set_character, only: [ :new, :create ]
+ before_action :set_counter, only: [ :update, :destroy ]
+
+ def new
+ @counter = @section.counters.new
+ end
+
+ def create
+ @counter = @section.counters.new(counter_params)
+ unless @counter.save
+ render :new, status: :unprocessable_entity
+ end
+ end
+
+ def update
+ @counter.update(counter_params)
+ end
+
+ def destroy
+ @id = helpers.dom_id(@counter)
+ @counter.destroy
+ end
+
+ private
+ def set_character
+ @character = @section.character
+ end
+
+ def set_section
+ @section = Current.user.character_sheet_sections.find(params[:character_sheet_section_id])
+ end
+
+ def set_counter
+ @counter = Current.user.counters.find(params[:id])
+ end
+
+ def counter_params
+ params.require(:counter).permit(
+ :name,
+ :value,
+ :character_sheet_section_id,
+ )
+ end
+end
diff --git a/app/javascript/controllers/stat_update_controller.js b/app/javascript/controllers/auto_update_controller.js
similarity index 100%
rename from app/javascript/controllers/stat_update_controller.js
rename to app/javascript/controllers/auto_update_controller.js
diff --git a/app/models/user.rb b/app/models/user.rb
index 97a6154..fec6849 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -9,6 +9,7 @@ class User < ApplicationRecord
has_many :character_sheet_sections, through: :characters
has_many :owned_tables, foreign_key: :owner_id, class_name: "Table"
has_many :players, dependent: :destroy
+ has_many :counters, through: :character_sheet_sections
has_many :stats, through: :character_sheet_sections
has_many :tables, through: :players
has_rich_text :profile
diff --git a/app/views/character_sheet_sections/_character_sheet_section.html.erb b/app/views/character_sheet_sections/_character_sheet_section.html.erb
index f32486b..eb6e28c 100644
--- a/app/views/character_sheet_sections/_character_sheet_section.html.erb
+++ b/app/views/character_sheet_sections/_character_sheet_section.html.erb
@@ -7,6 +7,9 @@
<%= render character_sheet_section.stats %>
+
+ <%= render character_sheet_section.counters %>
+
<%= render character_sheet_section.character_sheet_subsections %>
diff --git a/app/views/character_sheet_sections/_edit_links.html.erb b/app/views/character_sheet_sections/_edit_links.html.erb
index 28eac2f..1d3b43a 100644
--- a/app/views/character_sheet_sections/_edit_links.html.erb
+++ b/app/views/character_sheet_sections/_edit_links.html.erb
@@ -5,6 +5,8 @@
<% if section.present? %>
<%= link_to t(".add_stat"),
new_character_sheet_section_stat_path(section), data: { turbo_stream: true }, class: "add-stat" %>
+ <%= link_to t(".add_counter"),
+ new_character_sheet_section_counter_path(section), data: { turbo_stream: true }, class: "add-counter" %>
<% end %>
<% unless id == "character_sheet_add_section" %>
<%= link_to t(".delete_section", name: section.name), character_sheet_section_path(section),
diff --git a/app/views/counters/_counter.html.erb b/app/views/counters/_counter.html.erb
new file mode 100644
index 0000000..847980a
--- /dev/null
+++ b/app/views/counters/_counter.html.erb
@@ -0,0 +1,11 @@
+
+ <% if @editable %>
+ <%= link_to(t(".delete"), counter,
+ data: { turbo_method: :delete, turbo_confirm: t(".confirm_delete", name: counter.name) }) %>
+ <% end %>
+
<%= counter.name %>
+ <%= form_with model: counter, class: "counter-form",
+ data: { controller: "auto-update", auto_update_update_url_value: counter_path(counter) } do |f| %>
+ <%= f.number_field :value %>
+ <% end %>
+
diff --git a/app/views/counters/_form.html.erb b/app/views/counters/_form.html.erb
new file mode 100644
index 0000000..ddd1fd2
--- /dev/null
+++ b/app/views/counters/_form.html.erb
@@ -0,0 +1,11 @@
+
+ <%= form_with model: @counter, url: character_sheet_section_counters_path(@section) do |f| %>
+ <%= f.hidden_field :character_sheet_section_id, value: @section.id %>
+
+ <%= f.label :name %>
+ <%= f.text_field :name %>
+ <%= display_form_errors(@section, :name) %>
+
+ <%= f.submit button_text %>
+ <% end %>
+
diff --git a/app/views/counters/create.turbo_stream.erb b/app/views/counters/create.turbo_stream.erb
new file mode 100644
index 0000000..68cd0ea
--- /dev/null
+++ b/app/views/counters/create.turbo_stream.erb
@@ -0,0 +1,11 @@
+<%= content_target = "#{dom_id(@section)}_counters" %>
+<%= turbo_stream.append(content_target) do %>
+ <%= render @counter %>
+<% end %>
+
+<%= form_target = "#{dom_id(@section)}_add_section" %>
+<%= turbo_stream.replace(form_target) do %>
+
+ <%= render partial: "character_sheet_sections/edit_links", locals: { section: @section, parent: @section.parent_section, id: nil} %>
+
+<% end %>
diff --git a/app/views/counters/destroy.turbo_stream.erb b/app/views/counters/destroy.turbo_stream.erb
new file mode 100644
index 0000000..f50ff4a
--- /dev/null
+++ b/app/views/counters/destroy.turbo_stream.erb
@@ -0,0 +1 @@
+<%= turbo_stream.remove @id %>
diff --git a/app/views/counters/new.turbo_stream.erb b/app/views/counters/new.turbo_stream.erb
new file mode 100644
index 0000000..c56aa03
--- /dev/null
+++ b/app/views/counters/new.turbo_stream.erb
@@ -0,0 +1,6 @@
+<% target = "#{dom_id(@counter.character_sheet_section)}_add_section" %>
+<%= turbo_stream.replace(target) do %>
+
+ <%= render partial: "form", locals: { button_text: t(".create_counter") } %>
+
+<% end %>
diff --git a/app/views/counters/update.turbo_stream.erb b/app/views/counters/update.turbo_stream.erb
new file mode 100644
index 0000000..92a6c47
--- /dev/null
+++ b/app/views/counters/update.turbo_stream.erb
@@ -0,0 +1,3 @@
+<%= turbo_stream.replace dom_id(@counter) do %>
+ <%= render @counter %>
+<% end %>
diff --git a/app/views/stats/_stat.html.erb b/app/views/stats/_stat.html.erb
index 6528816..507a78e 100644
--- a/app/views/stats/_stat.html.erb
+++ b/app/views/stats/_stat.html.erb
@@ -5,7 +5,7 @@
<% end %>
<%= stat.name %>
<%= form_with model: stat, class: "stat-form",
- data: { controller: "stat-update", stat_update_update_url_value: stat_path(stat) } do |f| %>
+ data: { controller: "auto-update", auto_update_update_url_value: stat_path(stat) } do |f| %>
<%= f.number_field :value, disabled: !@editable %>
<% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 6230b67..db67e7b 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -61,6 +61,7 @@ en:
delete_section: Delete %{name}
confirm_delete: Are you sure you want to delete this section?
add_stat: Add stat
+ add_counter: Add counter
index:
edit: Edit character sheet
stop_editing: Stop editing
@@ -97,6 +98,12 @@ en:
destroy:
success: Deleted “%{name}”
error: Could not delete your character
+ counters:
+ counter:
+ delete: Delete counter
+ confirm_delete: Are you sure you want to delete %{name}?
+ new:
+ create_counter: Create counter
password_resets:
new:
reset_password: Reset your password
diff --git a/config/routes.rb b/config/routes.rb
index 2caeadc..f0a1ba9 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -16,11 +16,13 @@ Rails.application.routes.draw do
resources :sessions, only: [ :new, :create, :destroy ]
resources :character_sheet_sections, only: [ :destroy ] do
+ resources :counters, only: [ :new, :create ]
resources :stats, only: [ :new, :create ]
end
resources :characters do
resources :character_sheet_sections, only: [ :index, :new, :create ]
end
+ resources :counters, only: [ :update, :destroy ]
resources :stats, only: [ :update, :destroy ]
resources :table_invites, only: [ :index, :edit, :update ]
resources :tables do
diff --git a/test/controllers/counters_controller_test.rb b/test/controllers/counters_controller_test.rb
new file mode 100644
index 0000000..b994630
--- /dev/null
+++ b/test/controllers/counters_controller_test.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require "test_helper"
+
+class CountersControllerTest < ActionDispatch::IntegrationTest
+ test "should render new turbo stream" do
+ sign_in users(:trevor)
+ get new_character_sheet_section_counter_url(character_sheet_sections(:counters)), as: :turbo_stream
+ assert_response :success
+ end
+
+ test "should create counter" do
+ sign_in users(:trevor)
+ assert_difference "Counter.count", 1 do
+ post character_sheet_section_counters_url(character_sheet_sections(:counters)),
+ params: { counter: { name: "Ammo", character_sheet_section_id: character_sheet_sections(:counters).id } },
+ as: :turbo_stream
+ end
+ end
+
+ test "should delete counter" do
+ sign_in users(:trevor)
+ assert_difference "Counter.count", -1 do
+ delete counter_url(Counter.first), as: :turbo_stream
+ assert_response :success
+ end
+ end
+end
diff --git a/todo.md b/todo.md
index c811f1c..cb8beba 100644
--- a/todo.md
+++ b/todo.md
@@ -7,3 +7,4 @@
- notifications
- chat
- maps
+- show errors from invalid sheet bits