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 @@

<%= character_sheet_section.name %>

+
+ <%= 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