diff --git a/app/assets/stylesheets/stories.scss b/app/assets/stylesheets/stories.scss index a37e7e97..503a5812 100644 --- a/app/assets/stylesheets/stories.scss +++ b/app/assets/stylesheets/stories.scss @@ -60,6 +60,10 @@ padding-bottom: 1.3em; } +.modal strong { + font-weight: bold; +} + .new_story, .edit_story { display: grid; diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 84537228..b9c352bd 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,6 +1,6 @@ class ProjectsController < ApplicationController before_action :authenticate_user! - before_action :find_project, only: [:show, :edit, :update, :sort, :sort_stories, :destroy, :new_sub_project, :toggle_archive, :toggle_locked] + before_action :find_project, only: [:show, :edit, :update, :sort, :sort_stories, :destroy, :new_sub_project, :toggle_archive, :toggle_locked, :open_delete_modal] before_action :ensure_unarchived!, only: [:edit, :new_sub_project, :update] def index @@ -70,9 +70,14 @@ def create end def destroy - @project.destroy respond_to do |format| - format.html { redirect_to projects_path, notice: "Project was successfully destroyed." } + if @project.title.strip.eql?(params.dig(:project, :title)&.strip) + @project.destroy + flash[:success] = "Project was successfully destroyed." + else + flash[:error] = "Make sure you added the correct project's title" + end + format.html { redirect_to projects_path } end end @@ -104,6 +109,10 @@ def new_sub_project @sub = Project.new(parent_id: @project) end + # GET /projects/1/open_delete_modal.js + def open_delete_modal + end + private def find_project diff --git a/app/views/projects/_delete_form.html.erb b/app/views/projects/_delete_form.html.erb new file mode 100644 index 00000000..ec2694bd --- /dev/null +++ b/app/views/projects/_delete_form.html.erb @@ -0,0 +1,12 @@ +<%= form_with(model: project, method: :delete) do |f| %> + This action cannot be undone. + This will permanently delete the <%= project.title %> project, + stories, and associated estimations. + +
+ <%= f.label :title, raw("Please type #{project.title} to confirm.") %> + <%= f.text_field :title, value: "", placeholder: "Project's title", autofocus: true, required: true %> +
+ + <%= f.submit "I understand the consequences, delete this project", class: "button magenta" %> +<% end %> diff --git a/app/views/projects/open_delete_modal.js.erb b/app/views/projects/open_delete_modal.js.erb new file mode 100644 index 00000000..a2887254 --- /dev/null +++ b/app/views/projects/open_delete_modal.js.erb @@ -0,0 +1,3 @@ +(function(){ + showModal("Are you absolutely sure?", "<%= j(render('delete_form', project: @project)) %>") +})() diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb index 1bb4c6bb..79402b60 100644 --- a/app/views/projects/show.html.erb +++ b/app/views/projects/show.html.erb @@ -112,7 +112,7 @@ <% if is_unlocked?(@project) %> <%= link_unless_archived(@project, "Add Sub-Project", project_new_sub_project_path(@project), classes: :green) unless @project.parent_id.present? %> - <%= link_unless_archived(@project, "Delete Project", project_path(@project.id), classes: "delete magenta", method: :delete, remote: true, data_attr: { confirm: 'Are you sure?' }, id: "delete") %> + <%= link_unless_archived(@project, "Delete Project", open_delete_modal_project_path(@project.id), classes: "delete magenta", remote: true) %> <% end %> <% unless @project.parent_id %> diff --git a/config/routes.rb b/config/routes.rb index 86c0cca9..0608541a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,6 +24,7 @@ patch :toggle_locked get :new_clone post :clone + get :open_delete_modal end get :new_sub_project diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index ffe9b248..c2b8d54e 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -124,9 +124,29 @@ describe "#destroy" do it "deletes the project" do expect { - delete :destroy, params: {id: project.id} + delete :destroy, params: {id: project.id, project: {title: project.title}} }.to change(Project, :count).by(-1) end + + it "deletes stripped project's title" do + project.update(title: " foo bar ") + expect { + delete :destroy, params: {id: project.id, project: {title: "foo bar"}} + }.to change(Project, :count).by(-1) + end + + it "deletes stripped project's params" do + project.update(title: "foo bar") + expect { + delete :destroy, params: {id: project.id, project: {title: "foo bar "}} + }.to change(Project, :count).by(-1) + end + + it "does not delete the project" do + expect { + delete :destroy, params: {id: project.id} + }.not_to change(Project, :count) + end end describe "#show" do diff --git a/spec/features/projects_manage_spec.rb b/spec/features/projects_manage_spec.rb index ac4bb97f..78ce59ec 100644 --- a/spec/features/projects_manage_spec.rb +++ b/spec/features/projects_manage_spec.rb @@ -50,19 +50,27 @@ end context "when the project is unarchived" do - it "allows me to delete a project", js: false do + it "does not delete a project" do + project.update(title: "Awesome Project's Title") visit project_path(id: project.id) + click_link "Delete Project" - expect(Project.count).to eq 0 + expect(page).to have_content "Are you absolutely sure?" + fill_in "project_title", with: "Random Project's Title" + click_button "I understand the consequences, delete this project" + + expect(page).to have_content "Make sure you added the correct project's title" end it "allows me to delete a project" do visit project_path(id: project.id) - accept_confirm do - click_link "Delete Project" - end - expect(page).not_to have_content "Delete Project" - expect(Project.count).to eq 0 + + click_link "Delete Project" + expect(page).to have_content "Are you absolutely sure?" + fill_in "project_title", with: project.title + click_button "I understand the consequences, delete this project" + + expect(page).to have_content "Project was successfully destroyed." end it "allows editing the project's title inline" do