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