diff --git a/app/controllers/workshop_variations_controller.rb b/app/controllers/workshop_variations_controller.rb index 3c55fa029..9cf864d48 100644 --- a/app/controllers/workshop_variations_controller.rb +++ b/app/controllers/workshop_variations_controller.rb @@ -102,16 +102,15 @@ def update # end def set_form_variables - @workshop = @workshop_variation.workshop || params[:workshop_id].present? && - Workshop.where(id: params[:workshop_id]).last - @workshop_variation_idea = params[:workshop_variation_idea_id].present? && - WorkshopVariationIdea.find_by(id: params[:workshop_variation_idea_id]) + @workshop = @workshop_variation.workshop || (Workshop.find_by(id: params[:workshop_id]) if params[:workshop_id].present?) + @workshop_variation_idea = WorkshopVariationIdea.find_by(id: params[:workshop_variation_idea_id]) if params[:workshop_variation_idea_id].present? end def workshop_variation_params params.require(:workshop_variation).permit( [ :name, :rhino_body, :published, :publicly_visible, :position, :youtube_url, :created_by_id, - :organization_id, :workshop_id, :workshop_variation_idea_id, :author_credit_preference + :organization_id, :workshop_id, :workshop_variation_idea_id, :author_credit_preference, + :windows_type_id ] ) end diff --git a/app/models/workshop_variation.rb b/app/models/workshop_variation.rb index 5cc80e187..15a253a67 100644 --- a/app/models/workshop_variation.rb +++ b/app/models/workshop_variation.rb @@ -37,13 +37,13 @@ def self.search_by_params(params) has_many :assets, as: :owner, dependent: :destroy validates :name, presence: true, uniqueness: { scope: :workshop_id, case_sensitive: false } + validates :windows_type_id, presence: true + validates :author_credit_preference, presence: true validates :rhino_body, presence: true accepts_nested_attributes_for :primary_asset, allow_destroy: true, reject_if: :all_blank accepts_nested_attributes_for :gallery_assets, allow_destroy: true, reject_if: :all_blank - delegate :windows_type, to: :workshop - # Scopes # See Publishable, Trendable diff --git a/app/services/workshop_variation_from_idea_service.rb b/app/services/workshop_variation_from_idea_service.rb index b84720353..52abdf576 100644 --- a/app/services/workshop_variation_from_idea_service.rb +++ b/app/services/workshop_variation_from_idea_service.rb @@ -18,7 +18,7 @@ def call def attributes_from_idea workshop_variation_idea.attributes.slice( - "name", "youtube_url", "position", "workshop_id", "author_credit_preference" + "name", "youtube_url", "position", "workshop_id", "windows_type_id", "organization_id", "author_credit_preference" ).merge( created_by_id: user.id, workshop_variation_idea_id: workshop_variation_idea.id, diff --git a/app/views/workshop_variation_ideas/_form.html.erb b/app/views/workshop_variation_ideas/_form.html.erb index c9ca35437..49b7bfc5d 100644 --- a/app/views/workshop_variation_ideas/_form.html.erb +++ b/app/views/workshop_variation_ideas/_form.html.erb @@ -22,43 +22,26 @@ <% end %> <%= render 'shared/errors', resource: workshop_variation_idea if workshop_variation_idea.errors.any? %> - -
+ +
- <%= f.input :name, - label: "Variation name", - required: true, - disabled: promoted_to_variation, - input_html: { class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 #{('readonly' if promoted_to_variation)}" } %> -
-
-
-
- <%= f.input :workshop_id, - collection: f.object.workshop.present? ? [[ f.object.workshop.remote_search_label[:label], f.object.workshop.id]] : [], - include_blank: true, - required: true, - input_html: { - data: { - controller: "remote-select", - remote_select_model_value: "workshop" - } - }, - prompt: "Search by title", - label: "Workshop", - label_html: { class: "block text-sm font-medium text-gray-700 mb-1 #{ 'readonly' if promoted_to_variation}" } %> -
-
- <%= f.input :windows_type_id, - as: :select, - required: true, - label: "Windows audience", - prompt: "Select audience", - collection: @windows_types, label_method: :short_name, value_method: :id, - disabled: promoted_to_variation, - input_html: { - class: "#{'readonly' if promoted_to_variation} block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" - } %> + <% if !promoted_to_variation && (params[:admin] == "true" || f.object.new_record?) %> + <%= f.input :workshop_id, + collection: f.object.workshop.present? ? [[ f.object.workshop.remote_search_label[:label], f.object.workshop.id]] : [], + include_blank: true, + required: true, + input_html: { + class: "h-10 rounded-md", + data: { + controller: "remote-select", + remote_select_model_value: "workshop" + } + }, + prompt: "Search by title", + label: "Workshop" %> + <% elsif f.object.workshop_id.present? %> + <%= f.hidden_field :workshop_id, value: f.object.workshop_id %> + <% end %>
<% if current_user.person&.affiliations&.count != 1 %> @@ -83,6 +66,29 @@
+ +
+
+ <%= f.input :name, + label: "Variation name", + required: true, + disabled: promoted_to_variation, + input_html: { class: "block w-full h-10 rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 #{('readonly' if promoted_to_variation)}" } %> +
+
+ <%= f.input :windows_type_id, + as: :select, + required: true, + label: "Windows audience", + prompt: "Select audience", + collection: @windows_types, label_method: :short_name, value_method: :id, + disabled: promoted_to_variation, + input_html: { + class: "#{'readonly' if promoted_to_variation} w-full h-10 rounded-md border border-gray-300 px-3 py-2 text-gray-800 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:outline-none" + } %> +
+
+ <% if promoted_to_variation %>
<%= workshop_variation_idea.rhino_body %>
<% else %> @@ -103,11 +109,11 @@ collection: AuthorCreditable::IDEA_FORM_OPTIONS, include_blank: "Select a preference", selected: f.object.author_credit_preference, - input_html: { class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" } %> + input_html: { class: "block w-full h-10 rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" } %> <%= f.input :youtube_url, as: :text, label: "YouTube link (optional)".html_safe, - hint: "If you already have a video on YouTube you’d like to share", + hint: "If you already have a video on YouTube you'd like to share", disabled: promoted_to_variation, input_html: { rows: 1, class: ("readonly" if promoted_to_variation) @@ -127,36 +133,11 @@ class: ("readonly" if promoted_to_variation) } %>
- <% if allowed_to?(:manage?, WorkshopVariationIdea) || @user %> -
-
- <% if promoted_to_variation %> -
- - <% f.object. workshop_variations.each do |workshop_variation| %> - <%= link_to workshop_variation.title, workshop_variation_path(workshop_variation), class: "btn btn-secondary-outline" %> - <% end %> -
- <% elsif allowed_to?(:manage?, WorkshopVariationIdea) && f.object.persisted? %> -
- <%= link_to "Promote to Workshop Variation", - new_workshop_variation_path(workshop_variation_idea_id: @workshop_variation_idea.id), - class: "btn btn-secondary-outline ms-2" %> -
- <% end %> -
-
- <% end %> <%= render "shared/form_image_fields", f: f, include_primary_asset: false unless promoted_to_variation %>
- <% if allowed_to?(:destroy?, f.object) && f.object. workshop_variations.none? && !promoted_to_variation %> - - <%= link_to "Promote to Workshop Variation", - new_workshop_variation_path(workshop_variation_idea_id: f.object.id), - class: "btn btn-secondary-outline" %> - <%= link_to "Delete", @workshop_variation_idea, class: "btn btn-danger-outline", - data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete?" } %> - + <% if allowed_to?(:destroy?, f.object) && f.object.workshop_variations.none? && !promoted_to_variation %> + <%= link_to "Delete", @workshop_variation_idea, class: "admin-only bg-blue-100 btn btn-danger-outline", + data: { turbo_method: :delete, turbo_confirm: "Are you sure you want to delete?" } %> <% end %> <% cancel_path = if params[:workshop_id].present? && @workshop diff --git a/app/views/workshop_variation_ideas/edit.html.erb b/app/views/workshop_variation_ideas/edit.html.erb index f76f0bc78..104b78ac0 100644 --- a/app/views/workshop_variation_ideas/edit.html.erb +++ b/app/views/workshop_variation_ideas/edit.html.erb @@ -1,23 +1,46 @@ <% content_for(:page_bg_class, "admin-only bg-blue-100") %>
- <%= link_to "Home", root_path, class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> <% if @workshop_variation_idea.workshop_variations.any? %> <% @workshop_variation_idea.workshop_variations.each do |workshop_variation| %> <%= link_to "View Variation", workshop_variation_path(workshop_variation), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> <% end %> - <% else %> - <% if allowed_to?(:manage?, @workshop_variation_idea) && @workshop_variation_idea.persisted? %> - <%= link_to "Promote to Workshop Variation", - new_workshop_variation_path(workshop_variation_idea_id: @workshop_variation_idea.id), - class: "admin-only bg-blue-100 btn btn-secondary-outline" %> - <% end %> <% end %> + <%= link_to "Home", root_path, class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> <%= link_to "View", workshop_variation_idea_path(@workshop_variation_idea), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %>
-

Edit workshop variation idea

+

Edit workshop variation idea: <%= @workshop_variation_idea.name %>

+ <% if @workshop_variation_idea.workshop.present? %> +

Workshop: <%= link_to("#{@workshop_variation_idea.workshop.title} (#{@workshop_variation_idea.workshop.windows_type&.short_name || 'Combined'})", + workshop_path(@workshop_variation_idea.workshop), class: "btn btn-secondary-outline") %>

+ <% end %> + + <% visible_variations = allowed_to?(:manage?, WorkshopVariation) ? @workshop_variation_idea.workshop_variations : @workshop_variation_idea.workshop_variations.where(published: true) %> + <% if visible_variations.any? %> +
+
+ + + Promoted to: + <% visible_variations.each do |variation| %> + <%= link_to variation.title, workshop_variation_path(variation), class: "underline hover:text-purple-600" %> + <% end %> + +
+
+ <% elsif @workshop_variation_idea.persisted? && allowed_to?(:manage?, WorkshopVariation) %> +
+
+ + This idea has not been promoted yet. + <%= link_to "Promote to Workshop Variation", + new_workshop_variation_path(workshop_variation_idea_id: @workshop_variation_idea.id), + class: "btn btn-secondary-outline text-sm" %> +
+
+ <% end %>
diff --git a/app/views/workshop_variation_ideas/index.html.erb b/app/views/workshop_variation_ideas/index.html.erb index e41042bdb..caed96eae 100644 --- a/app/views/workshop_variation_ideas/index.html.erb +++ b/app/views/workshop_variation_ideas/index.html.erb @@ -1,5 +1,5 @@ <% content_for(:page_bg_class, "admin-only bg-blue-100") %> -
+
@@ -20,42 +20,75 @@ - - - - - - + + + + + + + <% @workshop_variation_ideas.each do |workshop_variation_idea| %> - - - - - + + + - - - + <% end %> diff --git a/app/views/workshop_variation_ideas/show.html.erb b/app/views/workshop_variation_ideas/show.html.erb index 1bc1797c4..e0c616894 100644 --- a/app/views/workshop_variation_ideas/show.html.erb +++ b/app/views/workshop_variation_ideas/show.html.erb @@ -1,6 +1,12 @@ <% content_for(:page_bg_class, "admin-or-owner") %>
+ <% if @workshop_variation_idea.workshop_variations.any? %> + <% @workshop_variation_idea.workshop_variations.each do |workshop_variation| %> + <%= link_to "View Variation", workshop_variation_path(workshop_variation), + class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> + <% end %> + <% end %> <%= link_to "Home", root_path, class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> <% if allowed_to?(:index?, WorkshopVariationIdea) %> <%= link_to "Workshop Variation Ideas", workshop_variation_ideas_path, class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> @@ -16,15 +22,28 @@

<%= @workshop_variation_idea.name %>

- <% if @workshop_variation_idea.workshop_variations.any? %> -
- - - Promoted to: - <% @workshop_variation_idea.workshop_variations.each do |variation| %> - <%= link_to variation.title, workshop_variation_path(variation), class: "underline hover:text-blue-600" %> - <% end %> - + <% visible_variations = allowed_to?(:manage?, WorkshopVariation) ? @workshop_variation_idea.workshop_variations : @workshop_variation_idea.workshop_variations.where(published: true) %> + <% if visible_variations.any? %> +
+
+ + + Promoted to: + <% visible_variations.each do |variation| %> + <%= link_to variation.title, workshop_variation_path(variation), class: "underline hover:text-purple-600" %> + <% end %> + +
+
+ <% elsif @workshop_variation_idea.persisted? && allowed_to?(:manage?, WorkshopVariation) %> +
+
+ + This idea has not been promoted yet. + <%= link_to "Promote to Workshop Variation", + new_workshop_variation_path(workshop_variation_idea_id: @workshop_variation_idea.id), + class: "btn btn-secondary-outline text-sm" %> +
<% end %> @@ -56,9 +75,9 @@
Workshop: <% if @workshop_variation_idea.workshop && allowed_to?(:show?, @workshop_variation_idea.workshop) %> - <%= link_to @workshop_variation_idea.workshop.title, workshop_path(@workshop_variation_idea.workshop), - data: { turbo_frame: "_top" }, - class: "inline-block px-3 py-1 rounded-md text-sm font-medium #{DomainTheme.bg_class_for(:workshops, intensity: 100)} #{DomainTheme.text_class_for(:workshops)} #{DomainTheme.bg_class_for(:workshops, intensity: 100, hover: true)}" %> + <%= link_to "#{@workshop_variation_idea.workshop.title} (#{@workshop_variation_idea.workshop.windows_type&.short_name || 'Combined'})", + workshop_path(@workshop_variation_idea.workshop), + class: "btn btn-secondary-outline text-sm" %> <% else %> <%= @workshop_variation_idea.workshop&.title || "—" %> <% end %> diff --git a/app/views/workshop_variations/_form.html.erb b/app/views/workshop_variations/_form.html.erb index 601ab16bc..aafa9c92a 100644 --- a/app/views/workshop_variations/_form.html.erb +++ b/app/views/workshop_variations/_form.html.erb @@ -6,51 +6,71 @@
- <% if @workshop %> - <%= f.hidden_field :workshop_id, value: @workshop.id %> -
- Variant of workshop: - <%= link_to(@workshop.name, workshop_path(@workshop), class: "hover:underline") %> -
- <% else %> + <% if params[:admin] == "true" || (f.object.new_record? && !@workshop) %> <%= f.input :workshop_id, collection: f.object.workshop.present? ? [[ f.object.workshop.remote_search_label[:label], f.object.workshop.id]] : [], include_blank: true, required: true, input_html: { + class: "h-10 rounded-md", data: { controller: "remote-select", remote_select_model_value: "workshop" } }, prompt: "Search by title", - label: "Workshop", - label_html: { class: "block text-sm font-medium text-gray-700 mb-1 " } %> + label: "Workshop" %> + <% elsif @workshop %> + <%= f.hidden_field :workshop_id, value: @workshop.id %> + <% elsif f.object.workshop_id.present? %> + <%= f.hidden_field :workshop_id, value: f.object.workshop_id %> <% end %>
- <% if @workshop_variation_idea %> -
-
- Promoting from Variation Idea: + <% linked_idea = @workshop_variation_idea || f.object.workshop_variation_idea %> + <% if linked_idea %> +
+
+ + Promoted from: + <%= link_to linked_idea.name, + workshop_variation_idea_path(linked_idea), + class: "btn btn-secondary-outline text-sm" %>
+
+ <% end %> -
- <%= link_to @workshop_variation_idea.name, - workshop_variation_idea_path(@workshop_variation_idea), - class: "hover:underline" %> + +
+
+
+ <%= f.input :name, label: "Variation name", input_html: { class: "w-full h-10" } %> +
+
+ <%= f.input :windows_type_id, + as: :select, + required: true, + collection: WindowsType.all, + label_method: :short_name, + value_method: :id, + selected: f.object.windows_type_id || WindowsType.find_by(name: "Combined")&.id, + include_blank: "Select type", + label: "Windows type", + input_html: { class: "w-full h-10 rounded-md border border-gray-300 px-3 py-2 text-gray-800 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:outline-none" } %>
- - <%= f.hidden_field :workshop_variation_idea_id, - value: @workshop_variation_idea.id %>
- <% end %> + + <% if allowed_to?(:manage?, WorkshopVariation) %> +
+ <%= f.input :published, as: :boolean, input_html: { checked: f.object.id ? f.object.published? : false } %> + <%= f.input :publicly_visible, as: :boolean, input_html: { checked: f.object.id ? f.object.publicly_visible? : false } %> +
+ <% end %> +
- <%= f.input :name, label: "Variation name", input_html: { class: "w-full" } %> -
<%= rhino_editor(f, :body) %> @@ -59,39 +79,66 @@ <% end %>
- <%= f.input :youtube_url, - label: "YouTube link (optional)".html_safe, - hint: "If you already have a video on YouTube you’d like to share", - input_html: { class: "w-full" } %>
- <% if allowed_to?(:manage?, WorkshopVariation) %> -
- <%= f.input :published, as: :boolean, input_html: { checked: f.object.id ? f.object.published? : false } %> - <%= f.input :publicly_visible, as: :boolean, input_html: { checked: f.object.id ? f.object.publicly_visible? : false } %> - <%= f.input :position, as: :integer, label: "Ordering", input_html: { class: "w-24" } %> - -
- <%= f.input :created_by_id, - as: :select, - collection: User.includes(:person).references(:person).order(Arel.sql("LOWER(people.first_name), LOWER(people.last_name), LOWER(users.email), LOWER(people.email_2), LOWER(people.email)")), - label_method: :full_name_with_email, - value_method: :id, - selected: (f.object.created_by_id || current_user.id), - input_html: { class: "w-full rounded-lg px-3 py-2", - data: { searchable_select_target: "select" } } %> -
+
+
<%= f.input :author_credit_preference, as: :select, + required: true, collection: AuthorCreditable::ADMIN_FORM_OPTIONS, include_blank: "Select a preference", - selected: f.object.author_credit_preference, + selected: f.object.author_credit_preference || "full_name", label: "Author credit preference", - hint: "Controls how the author's name is displayed" %> + hint: "Controls how the author’s name is displayed", + input_html: { class: "h-10 rounded-md" } %> + + <% if allowed_to?(:manage?, WorkshopVariation) %> +
+ <% if params[:admin] == "true" %> + <%= f.input :position, as: :integer, label: "Ordering", input_html: { class: "w-24" } %> + <% end %> +
+ <%= f.input :created_by_id, + as: :select, + label: "Variation author", + collection: User.includes(:person).references(:person).order(Arel.sql("LOWER(people.first_name), LOWER(people.last_name), LOWER(users.email), LOWER(people.email_2), LOWER(people.email)")), + label_method: :full_name_with_email, + value_method: :id, + selected: (f.object.created_by_id || current_user.id), + input_html: { class: "w-full rounded-lg px-3 py-2", + data: { searchable_select_target: "select" } } %> +
+
+ <% else %> + <%= f.hidden_field :created_by_id, value: current_user.id %> + <% end %>
- <% else %> - <%= f.hidden_field :created_by_id, value: current_user.id %> - <% end %> + +
+ <%= f.input :youtube_url, + label: "YouTube link (optional)".html_safe, + hint: "If you already have a video on YouTube you’d like to share", + input_html: { class: "w-full" } %> + + <% if allowed_to?(:manage?, WorkshopVariation) %> +
+
+ <%= f.input :workshop_variation_idea_id, + as: :select, + label: "Variation idea", + collection: WorkshopVariationIdea.includes(:workshop, :windows_type, :created_by).order(:name), + label_method: ->(idea) { "#{idea.name} (#{idea.windows_type&.short_name&.first || 'C'}) by #{idea.created_by&.name || '?'} — #{idea.workshop&.title || 'No workshop'} (#{idea.workshop&.windows_type&.short_name&.first || 'C'})" }, + value_method: :id, + include_blank: "Select a variation idea", + selected: (f.object.workshop_variation_idea_id || @workshop_variation_idea&.id), + input_html: { class: "w-full rounded-lg px-3 py-2", + data: { searchable_select_target: "select" } } %> +
+
+ <% end %> +
+
diff --git a/app/views/workshop_variations/edit.html.erb b/app/views/workshop_variations/edit.html.erb index b036c4507..976600000 100644 --- a/app/views/workshop_variations/edit.html.erb +++ b/app/views/workshop_variations/edit.html.erb @@ -4,9 +4,11 @@ <%= link_to "Home", root_path, class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> <%= link_to "View", workshop_variation_path(@workshop_variation), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %>
-

Edit workshop variation

- -
+

Edit workshop variation: <%= @workshop_variation.name %>

+ <% if @workshop_variation.workshop.present? %> +

Workshop: <%= link_to("#{@workshop_variation.workshop.name} (#{@workshop_variation.workshop.windows_type&.short_name || 'Combined'})", + workshop_path(@workshop_variation.workshop), class: "btn btn-secondary-outline") %>

+ <% end %>
<%= render "form", workshop_variation: @workshop_variation %> diff --git a/app/views/workshop_variations/index.html.erb b/app/views/workshop_variations/index.html.erb index 4f17fbd8c..1c844e0ad 100644 --- a/app/views/workshop_variations/index.html.erb +++ b/app/views/workshop_variations/index.html.erb @@ -20,10 +20,11 @@
Main ImageNameWorkshopAuthorPromoted?ActionsNameDescriptionAuthorPromoted toUpdatedActions
-
+
+
<%= render "assets/display_image", resource: workshop_variation_idea, - width: 18, height: 14, - variant: :index, - link_to_object: true, - file: workshop_variation_idea.display_image %> + width: 12, height: 9, + link: false, + link_to_object: false, + file: workshop_variation_idea.display_image, + variant: :gallery %>
<%= workshop_variation_idea.name %><%= workshop_variation_idea.workshop.title %><%= workshop_variation_idea.created_by.name %> - <% if workshop_variation_idea.workshop_variations.any? %> - + +
+ <%= link_to truncate(workshop_variation_idea.name, length: 30), + workshop_variation_idea_path(workshop_variation_idea), + class: "hover:text-blue-600 hover:underline" %> +
+ <% if workshop_variation_idea.workshop %> +
+ <%= link_to truncate(strip_tags(workshop_variation_idea.workshop.name), length: 40), + workshop_path(workshop_variation_idea.workshop), + class: "hover:text-gray-600" %> +
+ <% end %> +
+ <% plain = workshop_variation_idea.rhino_body.to_plain_text %> + <% if plain.present? %> + + + + <% end %> + + <% display_name = workshop_variation_idea.author_credit.presence || workshop_variation_idea.created_by&.name %> + <% if workshop_variation_idea.created_by&.person %> + <%= link_to display_name, + person_path(workshop_variation_idea.created_by.person), + class: "text-gray-500 hover:text-gray-700" %> + <% elsif display_name %> + <%= display_name %> + <% end %> + + <% promoted_variation = workshop_variation_idea.workshop_variations.first %> + <% if promoted_variation %> + <%= link_to "Variation ##{promoted_variation.id}", + workshop_variation_path(promoted_variation), + class: "text-purple-600 hover:text-purple-800 hover:underline" %> <% else %> - -- + -- <% end %> - <%= link_to 'Edit', edit_workshop_variation_idea_path(workshop_variation_idea), class: "btn btn-secondary-outline" %> + <%= workshop_variation_idea.updated_at&.strftime("%m/%d/%y") %> + <%= link_to "View", workshop_variation_idea_path(workshop_variation_idea), class: "btn btn-secondary-outline" %>
- - + + + @@ -32,30 +33,62 @@ <% @workshop_variations.each do |workshop_variation| %> - + - +
WorkshopVariation nameName Description AuthorFrom idea Updated Edit
+ +
+ <%= render "assets/display_image", + resource: workshop_variation, + width: 12, height: 9, + link: false, + link_to_object: false, + file: workshop_variation.display_image, + variant: :gallery %> +
+
+
+ <%= link_to "#{truncate(workshop_variation.name, length: 30)} (#{workshop_variation.windows_type&.short_name || 'Combined'})", workshop_variation_path(workshop_variation), class: "hover:text-blue-600 hover:underline" %> + <% unless workshop_variation.published? %> + + + Unpublished + + <% end %> +
<% if workshop_variation.workshop %> - <%= link_to truncate(strip_tags(workshop_variation.workshop.name), length: 30), - workshop_path(workshop_variation.workshop), - class: "btn btn-secondary-outline" %> +
+ <%= link_to truncate(strip_tags(workshop_variation.workshop.name), length: 40), + workshop_path(workshop_variation.workshop), + class: "hover:text-gray-600" %> +
<% end %>
<%= truncate(workshop_variation.name, length: 30) %> <% plain = workshop_variation.rhino_body.to_plain_text %> - - <%= truncate(plain, length: 30) %> - + <% if plain.present? %> + + + + <% end %> <% display_name = workshop_variation.workshop_variation_idea&.author_credit || workshop_variation.created_by&.name %> <% if workshop_variation.created_by&.person %> <%= link_to display_name, person_path(workshop_variation.created_by.person), - class: "btn btn-secondary-outline" %> + class: "text-gray-500 hover:text-gray-700" %> <% elsif display_name %> <%= display_name %> <% end %> + <% if workshop_variation.workshop_variation_idea %> + <%= link_to "Idea ##{workshop_variation.workshop_variation_idea.id}", + workshop_variation_idea_path(workshop_variation.workshop_variation_idea), + class: "text-purple-600 hover:text-purple-800 hover:underline" %> + <% else %> + -- + <% end %> + <%= workshop_variation.updated_at&.strftime('%m/%d/%y') %> <%= link_to "Edit", diff --git a/app/views/workshop_variations/show.html.erb b/app/views/workshop_variations/show.html.erb index b4568decf..17b4d254c 100644 --- a/app/views/workshop_variations/show.html.erb +++ b/app/views/workshop_variations/show.html.erb @@ -15,35 +15,31 @@
- Workshop: <%= link_to(@workshop_variation.workshop.name, - workshop_path(@workshop_variation.workshop), class: "btn btn-secondary-outline") %> + Workshop: <%= link_to("#{@workshop_variation.workshop.name} (#{@workshop_variation.workshop.windows_type&.short_name || 'Combined'})", + workshop_path(@workshop_variation.workshop), class: "btn btn-secondary-outline") %>
+ <% if @workshop_variation.workshop_variation_idea.present? && allowed_to?(:edit?, @workshop_variation) %> +
+
+ + Promoted from: + <%= link_to @workshop_variation.workshop_variation_idea.name, + workshop_variation_idea_path(@workshop_variation.workshop_variation_idea), + class: "btn btn-secondary-outline text-sm" %> +
+
+ <% end %> + -

- <%= @workshop_variation.name %> - <% unless @workshop_variation.published? %> - - [UNPUBLISHED] - - <% end %> -

+ <%= title_with_badges(@workshop_variation, font_size: "text-3xl", + record_title: "#{@workshop_variation.name} (#{@workshop_variation.windows_type&.short_name || 'Combined'})") %>
- <% if @workshop_variation.author_credit_preference.present? %> - <%= @workshop_variation.author_credit %> - <% else %> - <% creator = @workshop_variation.created_by %> - <% display_name = @workshop_variation.workshop_variation_idea&.author_credit || creator&.name %> - <% if creator&.person && allowed_to?(:show?, creator.person, with: PersonPolicy) %> - <%= link_to display_name, person_path(creator.person), class: "text-blue-600 hover:underline" %> - <% else %> - <%= display_name %> - <% end %> - <% end %> + <%= @workshop_variation.author_credit %> "> <%= @workshop_variation.created_at.strftime('%B %e, %Y') %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 9d3c80238..4c5b68b82 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -32,6 +32,11 @@ en: hello: "Hello world" activerecord: + attributes: + workshop_variation: + rhino_body: "Details" + workshop_variation_idea: + rhino_body: "Details" models: tutorial: one: "Video" diff --git a/db/migrate/20260303180000_add_windows_type_id_to_workshop_variations.rb b/db/migrate/20260303180000_add_windows_type_id_to_workshop_variations.rb new file mode 100644 index 000000000..b47e30cf7 --- /dev/null +++ b/db/migrate/20260303180000_add_windows_type_id_to_workshop_variations.rb @@ -0,0 +1,5 @@ +class AddWindowsTypeIdToWorkshopVariations < ActiveRecord::Migration[8.0] + def change + add_reference :workshop_variations, :windows_type, type: :integer, foreign_key: true, null: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 1baa25275..77439b4b6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_03_02_150000) do +ActiveRecord::Schema[8.1].define(version: 2026_03_03_180000) do create_table "action_text_mentions", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.bigint "action_text_rich_text_id", null: false t.datetime "created_at", null: false @@ -1217,12 +1217,14 @@ t.boolean "published", default: false, null: false t.datetime "updated_at", precision: nil, null: false t.integer "variation_id" + t.integer "windows_type_id" t.integer "workshop_id", null: false t.bigint "workshop_variation_idea_id" t.string "youtube_url" t.index ["created_by_id"], name: "index_workshop_variations_on_created_by_id" t.index ["organization_id"], name: "index_workshop_variations_on_organization_id" t.index ["published"], name: "index_workshop_variations_on_published" + t.index ["windows_type_id"], name: "index_workshop_variations_on_windows_type_id" t.index ["workshop_id", "name"], name: "index_workshop_variations_on_workshop_id_and_name", unique: true t.index ["workshop_id"], name: "index_workshop_variations_on_workshop_id" t.index ["workshop_variation_idea_id"], name: "index_workshop_variations_on_workshop_variation_idea_id" @@ -1433,6 +1435,7 @@ add_foreign_key "workshop_variation_ideas", "workshops" add_foreign_key "workshop_variations", "organizations" add_foreign_key "workshop_variations", "users", column: "created_by_id" + add_foreign_key "workshop_variations", "windows_types" add_foreign_key "workshop_variations", "workshop_variation_ideas" add_foreign_key "workshop_variations", "workshops" add_foreign_key "workshops", "users", column: "created_by_id" diff --git a/db/seeds/dummy_dev_seeds.rb b/db/seeds/dummy_dev_seeds.rb index 16e07bd37..0fb7a2690 100644 --- a/db/seeds/dummy_dev_seeds.rb +++ b/db/seeds/dummy_dev_seeds.rb @@ -555,14 +555,15 @@ ] # rubocop:enable Style/PercentLiteralDelimiters -variations.each do |var_data| +variations.each_with_index do |var_data, i| workshop = Workshop.find_by(title: var_data[:workshop_title]) next unless workshop workshop.workshop_variations.where(name: var_data[:name]).first_or_create!( body: var_data[:rhino_body], rhino_body: var_data[:rhino_body], - position: var_data[:position] + position: var_data[:position], + published: i != variations.length - 1 ) end @@ -832,6 +833,14 @@ ) end +puts "Linking some WorkshopVariations to WorkshopVariationIdeas…" +WorkshopVariationIdea.all.sample(2).each do |idea| + variation = WorkshopVariation.where(workshop_variation_idea_id: nil).sample + next unless variation + + variation.update!(workshop_variation_idea_id: idea.id) +end + puts "Creating WorkshopLogs…" 5.times do workshop = Workshop.all.sample diff --git a/spec/factories/workshop_variations.rb b/spec/factories/workshop_variations.rb index 0fd3c3545..e75cd1be4 100644 --- a/spec/factories/workshop_variations.rb +++ b/spec/factories/workshop_variations.rb @@ -1,8 +1,10 @@ FactoryBot.define do factory :workshop_variation do association :workshop + association :windows_type sequence(:name) { |n| "Variation #{n}" } rhino_body { "

Variation details using CKEditor

" } + author_credit_preference { "full_name" } sequence(:position) { |n| n } published { false } diff --git a/spec/models/workshop_variation_spec.rb b/spec/models/workshop_variation_spec.rb index e45144b68..9e0f8fd5a 100644 --- a/spec/models/workshop_variation_spec.rb +++ b/spec/models/workshop_variation_spec.rb @@ -1,42 +1,42 @@ -require 'rails_helper' +require "rails_helper" RSpec.describe WorkshopVariation do it_behaves_like "author_creditable", factory: :workshop_variation - describe 'associations' do + describe "associations" do it { should belong_to(:workshop).optional } + it { should belong_to(:windows_type).optional } + it { should belong_to(:created_by).class_name("User").optional } + it { should belong_to(:workshop_variation_idea).optional } end - describe 'validations' do - # Add validation tests if any (e.g., presence of name, code?) - # subject { build(:workshop_variation) } # Requires workshop - # it { should validate_presence_of(:name) } - # it { should validate_presence_of(:code) } - end + describe "validations" do + subject { build(:workshop_variation) } - # it 'is valid with valid attributes' do - # # Note: Factory needs association uncommented for create - # # expect(build(:workshop_variation)).to be_valid - # pending("Requires functional workshop factory and association uncommented") - # end + it { should validate_presence_of(:name) } + it { should validate_presence_of(:rhino_body) } + it { should validate_presence_of(:windows_type_id) } + it { should validate_presence_of(:author_credit_preference) } + it { should validate_uniqueness_of(:name).scoped_to(:workshop_id).case_insensitive } + end - describe '.search_by_params' do - let!(:variation_a) { create(:workshop_variation, name: 'Watercolor Technique') } - let!(:variation_b) { create(:workshop_variation, name: 'Clay Sculpting') } + describe ".search_by_params" do + let!(:variation_a) { create(:workshop_variation, name: "Watercolor Technique") } + let!(:variation_b) { create(:workshop_variation, name: "Clay Sculpting") } - it 'returns all when no params' do + it "returns all when no params" do results = WorkshopVariation.search_by_params({}) expect(results).to include(variation_a, variation_b) end - it 'filters by query matching name' do - results = WorkshopVariation.search_by_params(query: 'Watercolor') + it "filters by query matching name" do + results = WorkshopVariation.search_by_params(query: "Watercolor") expect(results).to include(variation_a) expect(results).not_to include(variation_b) end - it 'returns empty for non-matching query' do - results = WorkshopVariation.search_by_params(query: 'nonexistent') + it "returns empty for non-matching query" do + results = WorkshopVariation.search_by_params(query: "nonexistent") expect(results).not_to include(variation_a, variation_b) end end diff --git a/spec/requests/workshop_variations_spec.rb b/spec/requests/workshop_variations_spec.rb index 1592761a1..dcc4ac1cd 100644 --- a/spec/requests/workshop_variations_spec.rb +++ b/spec/requests/workshop_variations_spec.rb @@ -4,6 +4,7 @@ let(:regular_user) { create(:user) } let(:admin) { create(:user, super_user: true) } let(:workshop) { create(:workshop, published: true) } + let(:windows_type) { create(:windows_type) } let(:valid_attributes) do { @@ -11,6 +12,8 @@ rhino_body: "

A therapeutic approach to art-making.

", youtube_url: "https://www.youtube.com/watch?v=example", workshop_id: workshop.id, + windows_type_id: windows_type.id, + author_credit_preference: "full_name", created_by_id: admin.id } end @@ -18,7 +21,9 @@ let(:invalid_attributes) do { name: "", - rhino_body: "" + rhino_body: "", + windows_type_id: nil, + author_credit_preference: "" } end @@ -51,19 +56,22 @@ end describe "POST /create from workshop_variation_idea" do + let(:promotion_params) do + { + name: workshop_variation_idea.name, + rhino_body: workshop_variation_idea.rhino_body.body, + youtube_url: workshop_variation_idea.youtube_url, + workshop_id: workshop_variation_idea.workshop_id, + windows_type_id: workshop_variation_idea.windows_type_id, + author_credit_preference: workshop_variation_idea.author_credit_preference, + workshop_variation_idea_id: workshop_variation_idea.id, + created_by_id: admin.id + } + end + it "creates a WorkshopVariation linked to the idea" do expect { - post workshop_variations_path, - params: { - workshop_variation: { - name: workshop_variation_idea.name, - rhino_body: workshop_variation_idea.rhino_body.body, - youtube_url: workshop_variation_idea.youtube_url, - workshop_id: workshop_variation_idea.workshop_id, - workshop_variation_idea_id: workshop_variation_idea.id, - created_by_id: admin.id - } - } + post workshop_variations_path, params: { workshop_variation: promotion_params } }.to change(WorkshopVariation, :count).by(1) new_variation = WorkshopVariation.last @@ -73,25 +81,25 @@ end it "attaches assets from idea when promote_idea_assets is true" do - # Create an idea with a primary asset idea_with_asset = create(:workshop_variation_idea, workshop: workshop) - primary_asset = PrimaryAsset.create!( + PrimaryAsset.create!( owner: idea_with_asset, file: fixture_file_upload("spec/fixtures/files/sample.png", "image/png") ) + idea_params = { + name: idea_with_asset.name, + rhino_body: idea_with_asset.rhino_body.body, + workshop_id: idea_with_asset.workshop_id, + windows_type_id: idea_with_asset.windows_type_id, + author_credit_preference: idea_with_asset.author_credit_preference, + workshop_variation_idea_id: idea_with_asset.id, + created_by_id: admin.id + } + expect { post workshop_variations_path, - params: { - workshop_variation: { - name: idea_with_asset.name, - rhino_body: idea_with_asset.rhino_body.body, - workshop_id: idea_with_asset.workshop_id, - workshop_variation_idea_id: idea_with_asset.id, - created_by_id: admin.id - }, - promote_idea_assets: "true" - } + params: { workshop_variation: idea_params, promote_idea_assets: "true" } }.to change(WorkshopVariation, :count).by(1) new_variation = WorkshopVariation.last @@ -100,21 +108,22 @@ it "does not duplicate assets if promote_idea_assets is false" do idea_with_asset = create(:workshop_variation_idea, workshop: workshop) - primary_asset = PrimaryAsset.create!( + PrimaryAsset.create!( owner: idea_with_asset, file: fixture_file_upload("spec/fixtures/files/sample.png", "image/png") ) - post workshop_variations_path, - params: { - workshop_variation: { - name: idea_with_asset.name, - rhino_body: idea_with_asset.rhino_body.body, - workshop_id: idea_with_asset.workshop_id, - workshop_variation_idea_id: idea_with_asset.id, - created_by_id: admin.id - } - } + idea_params = { + name: idea_with_asset.name, + rhino_body: idea_with_asset.rhino_body.body, + workshop_id: idea_with_asset.workshop_id, + windows_type_id: idea_with_asset.windows_type_id, + author_credit_preference: idea_with_asset.author_credit_preference, + workshop_variation_idea_id: idea_with_asset.id, + created_by_id: admin.id + } + + post workshop_variations_path, params: { workshop_variation: idea_params } new_variation = WorkshopVariation.last expect(new_variation.assets.count).to eq(0) @@ -126,9 +135,11 @@ post workshop_variations_path, params: { workshop_variation: { - name: workshop_variation_idea.name, - rhino_body: workshop_variation_idea.rhino_body.body, + name: workshop_variation_idea.name, + rhino_body: workshop_variation_idea.rhino_body.body, workshop_id: workshop_variation_idea.workshop_id, + windows_type_id: workshop_variation_idea.windows_type_id, + author_credit_preference: workshop_variation_idea.author_credit_preference, workshop_variation_idea_id: workshop_variation_idea.id, created_by_id: admin.id } @@ -186,12 +197,41 @@ end end + describe "GET /show" do + it "renders successfully" do + variation = create(:workshop_variation, valid_attributes) + get workshop_variation_path(variation) + expect(response).to have_http_status(:ok) + end + + it "shows promoted from banner when linked to an idea" do + idea = create(:workshop_variation_idea, workshop: workshop) + variation = create(:workshop_variation, valid_attributes.merge(workshop_variation_idea_id: idea.id)) + get workshop_variation_path(variation) + expect(response.body).to include("Promoted from:") + expect(response.body).to include(idea.name) + end + + it "does not show promoted from banner when not linked" do + variation = create(:workshop_variation, valid_attributes) + get workshop_variation_path(variation) + expect(response.body).not_to include("Promoted from:") + end + end + describe "GET /edit" do it "renders successfully" do variation = create(:workshop_variation, valid_attributes) get edit_workshop_variation_path(variation) expect(response).to have_http_status(:ok) end + + it "renders without error when no workshop_variation_idea_id param" do + variation = create(:workshop_variation, valid_attributes) + get edit_workshop_variation_path(variation) + expect(response).to have_http_status(:ok) + expect(response.body).not_to include("Promoted from:") + end end describe "PATCH /update" do diff --git a/spec/views/workshop_variations/show.html.erb_spec.rb b/spec/views/workshop_variations/show.html.erb_spec.rb index 5e7206d82..49e501eef 100644 --- a/spec/views/workshop_variations/show.html.erb_spec.rb +++ b/spec/views/workshop_variations/show.html.erb_spec.rb @@ -22,7 +22,7 @@ expect(rendered).to include(workshop_variation.name) expect(rendered).to include(workshop_variation.rhino_body.body.to_s) expect(rendered).to include(workshop.title) - expect(rendered).to include(user.name) + expect(rendered).to include(workshop_variation.author_credit) end context "when the workshop_variation has a youtube_url" do @@ -39,7 +39,7 @@ it "displays an unpublished indicator" do render - expect(rendered).to include("[UNPUBLISHED]") + expect(rendered).to include("Unpublished") end end end