diff --git a/app/controllers/stories_controller.rb b/app/controllers/stories_controller.rb index 0e6b8007e..1daed96ba 100644 --- a/app/controllers/stories_controller.rb +++ b/app/controllers/stories_controller.rb @@ -28,7 +28,12 @@ def show end def new - @story = Story.new.decorate + if params[:story_idea_id].present? + @story_idea = StoryIdea.find(params[:story_idea_id]) + @story = StoryFromIdeaService.new(@story_idea, user: current_user).call + else + @story = Story.new + end @story = @story.decorate set_form_variables end diff --git a/app/services/story_from_idea_service.rb b/app/services/story_from_idea_service.rb new file mode 100644 index 000000000..8b1dea621 --- /dev/null +++ b/app/services/story_from_idea_service.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class StoryFromIdeaService + def initialize(story_idea, user:) + @story_idea = story_idea + @user = user + end + + def call + Story.new(attributes_from_idea).tap do |story| + duplicate_assets(story) + end + end + + private + + attr_reader :story_idea, :user + + def attributes_from_idea + story_idea.attributes.slice( + "title", "body", "youtube_url", + "windows_type_id", "project_id", "workshop_id", "external_workshop_title" + ).merge( + created_by_id: user.id, + updated_by_id: user.id, + story_idea_id: story_idea.id, + published: false + ) + end + + def duplicate_assets(story) + # Duplicate primary asset with correct type + if story_idea.primary_asset&.file&.attached? + story.build_primary_asset(file: story_idea.primary_asset.file.blob) + end + + # Duplicate gallery assets with correct type + story_idea.gallery_assets.each do |gallery_asset| + next unless gallery_asset.file&.attached? + + story.gallery_assets.build(file: gallery_asset.file.blob) + end + end +end diff --git a/app/services/workshop_from_idea_service.rb b/app/services/workshop_from_idea_service.rb index 04d68b1c4..5e89337fa 100644 --- a/app/services/workshop_from_idea_service.rb +++ b/app/services/workshop_from_idea_service.rb @@ -63,8 +63,16 @@ def duplicate_series_children(workshop) end def duplicate_assets(workshop) - workshop_idea.assets.each do |image| - workshop.assets.build(file: image.file.blob) + # Duplicate primary asset with correct type + if workshop_idea.primary_asset&.file&.attached? + workshop.build_primary_asset(file: workshop_idea.primary_asset.file.blob) + end + + # Duplicate gallery assets with correct type + workshop_idea.gallery_assets.each do |gallery_asset| + next unless gallery_asset.file&.attached? + + workshop.gallery_assets.build(file: gallery_asset.file.blob) end end end diff --git a/app/views/workshop_ideas/edit.html.erb b/app/views/workshop_ideas/edit.html.erb index c4e5d622d..525762989 100644 --- a/app/views/workshop_ideas/edit.html.erb +++ b/app/views/workshop_ideas/edit.html.erb @@ -3,8 +3,21 @@

Edit <%= @workshop_idea.class.model_name.human %>

- <%= link_to 'View', workshop_idea_path(@workshop_idea), - class: "btn btn-secondary-outline" %> +
+ <% if @workshop_idea.workshops.any? %> + <% @workshop_idea.workshops.each do |workshop| %> + <%= link_to "View Workshop", workshop_path(workshop), + class: "btn btn-secondary-outline" %> + <% end %> + <% else %> + <% if current_user.super_user && @workshop_idea.persisted? %> + <%= link_to "Promote to Workshop", new_workshop_path(workshop_idea_id: @workshop_idea.id), + class: "admin-only bg-blue-100 btn btn-secondary-outline ms-2" %> + <% end %> + <% end %> + <%= link_to "View", workshop_idea_path(@workshop_idea), + class: "btn btn-secondary-outline" %> +
diff --git a/spec/fixtures/test_image.jpg b/spec/fixtures/test_image.jpg new file mode 100644 index 000000000..ea680ea0d Binary files /dev/null and b/spec/fixtures/test_image.jpg differ diff --git a/spec/services/story_from_idea_service_spec.rb b/spec/services/story_from_idea_service_spec.rb new file mode 100644 index 000000000..15d8e7878 --- /dev/null +++ b/spec/services/story_from_idea_service_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe StoryFromIdeaService do + subject(:service) { described_class.new(story_idea, user: user) } + + let(:user) { create(:user) } + let(:story_idea) { create(:story_idea, created_by: user, updated_by: user) } + + describe "#call" do + let(:story) { service.call } + + it "returns a new Story" do + expect(story).to be_a(Story) + expect(story).to be_new_record + end + + it "copies basic attributes from story_idea" do + expect(story).to have_attributes( + title: story_idea.title, + body: story_idea.body, + youtube_url: story_idea.youtube_url, + windows_type_id: story_idea.windows_type_id, + project_id: story_idea.project_id, + workshop_id: story_idea.workshop_id, + story_idea_id: story_idea.id, + created_by_id: user.id, + updated_by_id: user.id + ) + end + + it "sets the story as unpublished by default" do + expect(story.published).to be false + end + + context "when story_idea has a primary_asset" do + let(:story_idea) do + create(:story_idea, created_by: user, updated_by: user).tap do |si| + si.create_primary_asset!(file: fixture_file_upload("spec/fixtures/test_image.jpg", "image/jpeg")) + end + end + + it "duplicates the primary_asset to the story" do + expect(story.primary_asset).to be_present + expect(story.primary_asset).to be_a(PrimaryAsset) + expect(story.primary_asset.file).to be_attached + end + end + + context "when story_idea has gallery_assets" do + let(:story_idea) do + create(:story_idea, created_by: user, updated_by: user).tap do |si| + si.gallery_assets.create!(file: fixture_file_upload("spec/fixtures/test_image.jpg", "image/jpeg")) + si.gallery_assets.create!(file: fixture_file_upload("spec/fixtures/test_image.jpg", "image/jpeg")) + end + end + + it "duplicates all gallery_assets to the story" do + expect(story.gallery_assets.size).to eq(2) + story.gallery_assets.each do |asset| + expect(asset).to be_a(GalleryAsset) + expect(asset.file).to be_attached + end + end + end + + context "when story_idea has both primary and gallery assets" do + let(:story_idea) do + create(:story_idea, created_by: user, updated_by: user).tap do |si| + si.create_primary_asset!(file: fixture_file_upload("spec/fixtures/test_image.jpg", "image/jpeg")) + si.gallery_assets.create!(file: fixture_file_upload("spec/fixtures/test_image.jpg", "image/jpeg")) + end + end + + it "duplicates both asset types correctly" do + expect(story.primary_asset).to be_a(PrimaryAsset) + expect(story.gallery_assets.size).to eq(1) + expect(story.gallery_assets.first).to be_a(GalleryAsset) + end + end + end +end diff --git a/spec/services/workshop_from_idea_service_spec.rb b/spec/services/workshop_from_idea_service_spec.rb new file mode 100644 index 000000000..835d268e2 --- /dev/null +++ b/spec/services/workshop_from_idea_service_spec.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe WorkshopFromIdeaService do + subject(:service) { described_class.new(workshop_idea, user: user) } + + let(:user) { create(:user) } + let(:workshop_idea) { create(:workshop_idea, created_by: user, updated_by: user) } + + describe "#call" do + let(:workshop) { service.call } + + it "returns a new Workshop" do + expect(workshop).to be_a(Workshop) + expect(workshop).to be_new_record + end + + it "copies basic attributes from workshop_idea" do + expect(workshop).to have_attributes( + title: workshop_idea.title, + objective: workshop_idea.objective, + materials: workshop_idea.materials, + windows_type_id: workshop_idea.windows_type_id, + workshop_idea_id: workshop_idea.id, + user_id: user.id + ) + end + + it "sets the workshop as inactive by default" do + expect(workshop.inactive).to be true + end + + it "sets the workshop as not featured by default" do + expect(workshop.featured).to be false + end + + context "when workshop_idea has a primary_asset" do + let(:workshop_idea) do + create(:workshop_idea, created_by: user, updated_by: user).tap do |wi| + wi.create_primary_asset!(file: fixture_file_upload("spec/fixtures/test_image.jpg", "image/jpeg")) + end + end + + it "duplicates the primary_asset to the workshop" do + expect(workshop.primary_asset).to be_present + expect(workshop.primary_asset).to be_a(PrimaryAsset) + expect(workshop.primary_asset.file).to be_attached + end + end + + context "when workshop_idea has gallery_assets" do + let(:workshop_idea) do + create(:workshop_idea, created_by: user, updated_by: user).tap do |wi| + wi.gallery_assets.create!(file: fixture_file_upload("spec/fixtures/test_image.jpg", "image/jpeg")) + wi.gallery_assets.create!(file: fixture_file_upload("spec/fixtures/test_image.jpg", "image/jpeg")) + end + end + + it "duplicates all gallery_assets to the workshop" do + expect(workshop.gallery_assets.size).to eq(2) + workshop.gallery_assets.each do |asset| + expect(asset).to be_a(GalleryAsset) + expect(asset.file).to be_attached + end + end + end + + context "when workshop_idea has both primary and gallery assets" do + let(:workshop_idea) do + create(:workshop_idea, created_by: user, updated_by: user).tap do |wi| + wi.create_primary_asset!(file: fixture_file_upload("spec/fixtures/test_image.jpg", "image/jpeg")) + wi.gallery_assets.create!(file: fixture_file_upload("spec/fixtures/test_image.jpg", "image/jpeg")) + end + end + + it "duplicates both asset types correctly" do + expect(workshop.primary_asset).to be_a(PrimaryAsset) + expect(workshop.gallery_assets.size).to eq(1) + expect(workshop.gallery_assets.first).to be_a(GalleryAsset) + end + end + end +end