diff --git a/app/assets/stylesheets/characters.css b/app/assets/stylesheets/characters.css
index 1ca07f4..23d2f9a 100644
--- a/app/assets/stylesheets/characters.css
+++ b/app/assets/stylesheets/characters.css
@@ -49,3 +49,29 @@
padding: 1em;
}
}
+
+.stats {
+ display: flex;
+ flex-wrap: wrap;
+ gap: .2em;
+ justify-content: center;
+}
+
+.stat {
+ display: flex;
+ flex-direction: column;
+ border-radius: var(--border-radius);
+ h6 {
+ font-size: .8em;
+ text-align: center;
+ background-color: var(--background-color);
+ color: var(--header-text-color);
+ margin: 0;
+ padding: .5em;
+ }
+ input {
+ text-align: center;
+ font-size: 3em;
+ width: 2.5em;
+ }
+}
diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb
new file mode 100644
index 0000000..2b5fb50
--- /dev/null
+++ b/app/controllers/stats_controller.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+class StatsController < ApplicationController
+ before_action :set_section, only: [ :new, :create ]
+ before_action :set_character, only: [ :new, :create ]
+
+ def new
+ @stat = @section.stats.new
+ end
+
+ def create
+ @stat = @section.stats.new(stat_params)
+ unless @stat.save
+ render :new, status: :unprocessable_entity
+ end
+ 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 stat_params
+ params.require(:stat).permit(
+ :name,
+ :value,
+ :roll_command,
+ :character_sheet_section_id,
+ )
+ end
+end
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 d6f19dd..ab2cb4a 100644
--- a/app/views/character_sheet_sections/_character_sheet_section.html.erb
+++ b/app/views/character_sheet_sections/_character_sheet_section.html.erb
@@ -4,6 +4,9 @@
+
+ <%= render character_sheet_section.stats %>
+
<%= 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 1eb97d7..74cb69c 100644
--- a/app/views/character_sheet_sections/_edit_links.html.erb
+++ b/app/views/character_sheet_sections/_edit_links.html.erb
@@ -1,8 +1,12 @@
<%# locals: (section:, parent:, id:) -%>
<%= link_to t(".add_subsection"),
- new_character_character_sheet_section_path(@character, parent_section_id: parent&.id),
- data: { turbo_stream: true } %>
-<% unless id == "character_sheet_add_section" %>
-<%= link_to t(".delete_section", name: section.name), character_sheet_section_path(section),
- data: { turbo_method: :delete, turbo_confirm: t(".confirm_delete", name: section.name) } %>
+ new_character_character_sheet_section_path(@character, parent_section_id: parent&.id),
+ data: { turbo_stream: true } %>
+<% if section.present? %>
+ <%= link_to t(".add_stat"),
+ new_character_sheet_section_stat_path(section), data: { turbo_stream: true } %>
+<% end %>
+<% unless id == "character_sheet_add_section" %>
+ <%= link_to t(".delete_section", name: section.name), character_sheet_section_path(section),
+ data: { turbo_method: :delete, turbo_confirm: t(".confirm_delete", name: section.name) } %>
<% end %>
diff --git a/app/views/stats/_form.html.erb b/app/views/stats/_form.html.erb
new file mode 100644
index 0000000..fd5838a
--- /dev/null
+++ b/app/views/stats/_form.html.erb
@@ -0,0 +1,15 @@
+
+ <%= form_with model: @stat, url: character_sheet_section_stats_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.label :roll_command %>
+ <%= f.text_field :roll_command %>
+ <%= display_form_errors(@section, :roll_command) %>
+
+ <%= f.submit button_text %>
+ <% end %>
+
diff --git a/app/views/stats/_stat.html.erb b/app/views/stats/_stat.html.erb
new file mode 100644
index 0000000..0175048
--- /dev/null
+++ b/app/views/stats/_stat.html.erb
@@ -0,0 +1,4 @@
+
+
<%= stat.name %>
+
+
diff --git a/app/views/stats/create.turbo_stream.erb b/app/views/stats/create.turbo_stream.erb
new file mode 100644
index 0000000..f9d4f97
--- /dev/null
+++ b/app/views/stats/create.turbo_stream.erb
@@ -0,0 +1,11 @@
+<%= content_target = "#{dom_id(@section)}_stats" %>
+<%= turbo_stream.append(content_target) do %>
+ <%= render @stat %>
+<% 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/stats/new.turbo_stream.erb b/app/views/stats/new.turbo_stream.erb
new file mode 100644
index 0000000..5fc376d
--- /dev/null
+++ b/app/views/stats/new.turbo_stream.erb
@@ -0,0 +1,6 @@
+<% target = "#{dom_id(@stat.character_sheet_section)}_add_section" %>
+<%= turbo_stream.replace(target) do %>
+
+ <%= render partial: "form", locals: { button_text: t(".create_stat") } %>
+
+<% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 9cb8277..77d26b5 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -60,6 +60,7 @@ en:
add_subsection: Add subsection
delete_section: Delete %{name}
confirm_delete: Are you sure you want to delete this section?
+ add_stat: Add stat
index:
character_sheet: "%{name}’s character sheet"
no_sections: This character sheet has no content
@@ -129,6 +130,9 @@ en:
destroy:
log_out: Log out
success: "You have signed out."
+ stats:
+ new:
+ create_stat: Create stat
table_invite_mailer:
invite_new_user:
subject: You’ve been invited to join a game on Tabletop Companion!
diff --git a/config/routes.rb b/config/routes.rb
index af74565..21eda7b 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -15,7 +15,9 @@ Rails.application.routes.draw do
resources :password_resets, only: [ :new, :create, :edit, :update ]
resources :sessions, only: [ :new, :create, :destroy ]
- resources :character_sheet_sections, only: [ :destroy ]
+ resources :character_sheet_sections, only: [ :destroy ] do
+ resources :stats, only: [ :new, :create ]
+ end
resources :characters do
resources :character_sheet_sections, only: [ :index, :new, :create ]
end
diff --git a/db/migrate/20240607091417_create_stats.rb b/db/migrate/20240607091417_create_stats.rb
index 1368dc6..3713928 100644
--- a/db/migrate/20240607091417_create_stats.rb
+++ b/db/migrate/20240607091417_create_stats.rb
@@ -6,7 +6,7 @@ class CreateStats < ActiveRecord::Migration[7.1]
t.string :name, null: false
t.string :slug, null: false
t.belongs_to :character_sheet_section, null: false, foreign_key: true
- t.decimal :value, null: false, default: 0
+ t.integer :value, null: false, default: 0
t.string :roll_command
t.timestamps
diff --git a/test/controllers/stats_controller_test.rb b/test/controllers/stats_controller_test.rb
new file mode 100644
index 0000000..3414204
--- /dev/null
+++ b/test/controllers/stats_controller_test.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require "test_helper"
+
+class StatsControllerTest < ActionDispatch::IntegrationTest
+ test "should render new turbo stream" do
+ sign_in users(:trevor)
+ get new_character_sheet_section_stat_url(character_sheet_sections(:stats)), as: :turbo_stream
+ assert_response :success
+ end
+
+ test "should create stat" do
+ sign_in users(:trevor)
+ assert_difference "Stat.count", 1 do
+ post character_sheet_section_stats_url(character_sheet_sections(:stats)),
+ params: { stat: { name: "Wisdom", character_sheet_section_id: character_sheet_sections(:stats).id } },
+ as: :turbo_stream
+ end
+ end
+end
diff --git a/todo.md b/todo.md
index b997165..59d3bbc 100644
--- a/todo.md
+++ b/todo.md
@@ -1,3 +1,5 @@
+- fix inconsistencies in nested create: do we need to submit the id or use in url?
+- add stat stimulus controller to update stats
- default avatars
- add uuid/slug to characters and any other url-visible ids
- shared/private notes