From 565e32866c749417ed07107acd662a0039a0256a Mon Sep 17 00:00:00 2001 From: Francois Buys Date: Mon, 24 May 2021 16:38:08 +0200 Subject: [PATCH 1/4] Add tool-versions to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5c351d67..8cc396a2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ config/database.yml coverage .env .byebug_history +.tool-versions From 5f7a938ccc99743a1dba7bf029d079629b105e5b Mon Sep 17 00:00:00 2001 From: Francois Buys Date: Mon, 24 May 2021 16:44:25 +0200 Subject: [PATCH 2/4] Show that CSV import/export is clickable This commit will fix: https://github.com/fastruby/points/issues/35 A chevron icon will indicate that a panel will expand downward. Minimal transition will attract attention visually. Also updated the cursor to be a pointer when hovered over the header. --- app/assets/javascripts/project.js | 4 ++ app/assets/stylesheets/2-quarks/_icons.scss | 10 ++++- app/assets/stylesheets/project.scss | 46 +++++++++++++++------ app/views/projects/_import_export.html.erb | 4 +- 4 files changed, 48 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js index 0766c2cb..de48242d 100644 --- a/app/assets/javascripts/project.js +++ b/app/assets/javascripts/project.js @@ -16,6 +16,10 @@ document.addEventListener("turbolinks:load", function() { } }) + $(".import-export-header").click(function () { + $(this).children(".rotate").toggleClass("left"); + }) + $("#bulk_delete").click((event) => { let stories_ids = [] $("input[name='stories[]']:checked").each((_, checkbox) => { diff --git a/app/assets/stylesheets/2-quarks/_icons.scss b/app/assets/stylesheets/2-quarks/_icons.scss index 7de517b7..e79a450f 100644 --- a/app/assets/stylesheets/2-quarks/_icons.scss +++ b/app/assets/stylesheets/2-quarks/_icons.scss @@ -1,7 +1,13 @@ /* === ICONS */ /* ========================== */ +.rotate { + transition: all 0.1s linear; +} +.rotate.left { + transform: rotate(180deg); +} .features .fa { - color: #333; -} \ No newline at end of file + color: #333; +} diff --git a/app/assets/stylesheets/project.scss b/app/assets/stylesheets/project.scss index a185f74c..2da5762f 100644 --- a/app/assets/stylesheets/project.scss +++ b/app/assets/stylesheets/project.scss @@ -2,13 +2,13 @@ // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ -.projects-controller{ - &.index-action{ - .btn-group{ +.projects-controller { + &.index-action { + .btn-group { display: flex; justify-content: center; margin-top: 20px; - .button{ + .button { margin: 0 5px; } } @@ -19,24 +19,44 @@ font-weight: bold; } -#import-export{ +#import-export { margin: 0 0 50px 0; - summary{ - h4{ + summary { + h4 { display: inline-block; } } - section{ + section { background-color: $white; - padding:15px; + padding: 15px; margin: 5px 0 60px 0; - input{ - display: inline-block; + input { + display: inline-block; margin: 0 0 10px 0; } } } +.import-export-header { + transition: all 0.2s linear; + cursor: pointer; + outline: transparent; + + & &__text { + padding-left: 1rem; + vertical-align: bottom; + } + + &:hover { + transform: translateY(-2px); + } + + &::-webkit-details-marker { + // Remove Safari/Webkit summary marker + display: none; + } +} + .card-deck { -webkit-box-orient: horizontal; -webkit-box-direction: normal; @@ -49,8 +69,8 @@ display: flex; .card { padding-right: 40px; - .card-header{ + .card-header { margin-bottom: 10px; } } -} \ No newline at end of file +} diff --git a/app/views/projects/_import_export.html.erb b/app/views/projects/_import_export.html.erb index a206bf8b..48a739dc 100644 --- a/app/views/projects/_import_export.html.erb +++ b/app/views/projects/_import_export.html.erb @@ -1,5 +1,7 @@
-

CSV Import / Export

+ + +

CSV Import / Export

From e8abe737fc626c82784f0379b7818f5ad95f403d Mon Sep 17 00:00:00 2001 From: Francois Buys Date: Tue, 25 May 2021 19:29:08 +0200 Subject: [PATCH 3/4] Add duplicate project action and route --- app/controllers/projects_controller.rb | 12 ++++++ config/routes.rb | 1 + spec/controllers/projects_controller_spec.rb | 40 ++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 7fe00190..494922b3 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -26,6 +26,18 @@ def toggle_archive Project.find(params[:id]).toggle_archived! end + def duplicate + original = Project.includes(stories: :estimates).find(params[:id]) + duplicate = original.dup + duplicate.title = "Copy of #{original.title}" + duplicate.save + + original.stories.each { |x| duplicate.stories.create(x.dup.attributes) } + + flash[:success] = 'Project created!' + redirect_to "/projects/#{duplicate.id}" + end + def create @project = Project.new(projects_params) if @project.save diff --git a/config/routes.rb b/config/routes.rb index 487cf9e5..719fa012 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -17,6 +17,7 @@ patch :sort, on: :member patch :toggle_archive, on: :member get :new_sub_project + post :duplicate, on: :member resource :report do get "download", to: "reports#download" diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 7ce31eb9..45ac36f0 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -144,4 +144,44 @@ expect(project.reload.title).to eq "New Project Title" end end + + describe 'duplicate' do + context 'with a project' do + before do + post :duplicate, params: { id: project.id } + end + + it 'creates a duplicate project' do + expect(Project.last.title).to eq "Copy of #{project.title}" + end + + it 'redirects to new project' do + expect(response).to redirect_to "/projects/#{Project.last.id}" + end + + it 'adds a success message' do + expect(flash[:success]).to be_present + end + end + + context 'with stories' do + it 'creates a duplicate project with matching stories' do + story = project.stories.create({ title: 'Story 1' }) + + post :duplicate, params: { id: project.id } + + expect(Project.last.stories.first.id).not_to eq story.id + expect(Project.last.stories.first.title).to eq story.title + end + + it 'creates stories without estimates' do + story = project.stories.create({ title: 'Story 1' }) + story.estimates.create({ best_case_points: 1, worst_case_points: 3 }) + + post :duplicate, params: { id: project.id } + + expect(Project.last.stories.first.estimates).to be_empty + end + end + end end From c6cb03cc45b1e38a7e4a70211748b46c096997c7 Mon Sep 17 00:00:00 2001 From: Francois Buys Date: Tue, 25 May 2021 19:51:23 +0200 Subject: [PATCH 4/4] Add Clone Project button --- app/views/projects/show.html.erb | 1 + spec/features/projects_manage_spec.rb | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb index 73e2096c..e6779b68 100644 --- a/app/views/projects/show.html.erb +++ b/app/views/projects/show.html.erb @@ -64,6 +64,7 @@ <% unless @project.parent_id %> <%= link_to 'Add Sub-Project', project_new_sub_project_path(@project.id), id:"subproject", class: "button magenta" %> <% end %> + <%= link_to 'Clone Project', duplicate_project_path(@project.id), method: :post, disable_with: 'Cloning...', class: "button magenta" %> <%= link_to "Archive Project", toggle_archive_project_path(@project.id), method: "patch", id: "toggle_archive", class: "button magenta", remote: true %> <%= link_to "Generate Action Plan", project_action_plan_path(@project.id), class: "button" %> <% if current_user.admin? %> diff --git a/spec/features/projects_manage_spec.rb b/spec/features/projects_manage_spec.rb index ccf28c27..f849abba 100644 --- a/spec/features/projects_manage_spec.rb +++ b/spec/features/projects_manage_spec.rb @@ -22,6 +22,13 @@ expect(Project.count).to eq 1 end + it "allows me to clone a project" do + visit project_path(id: project.id) + click_link "Clone Project" + expect(Project.count).to eq 2 + expect(Project.last.title).to eq "Copy of #{project.title}" + end + it "allows me to edit a project" do visit project_path(id: project.id) click_link "Edit or Delete Project"