diff --git a/app/controllers/admin/crops_controller.rb b/app/controllers/admin/crops_controller.rb index 596071cf6..8a498db62 100644 --- a/app/controllers/admin/crops_controller.rb +++ b/app/controllers/admin/crops_controller.rb @@ -8,6 +8,7 @@ class Admin::CropsController < ApplicationController @versions = PaperTrail::Version.where(item_type: 'Crop').order(created_at: :desc).limit(100) member_ids = @versions.map(&:whodunnit).compact.map(&:to_i) @members = Member.where(id: member_ids).index_by(&:id) + @crop_wranglers = Role.crop_wranglers end private diff --git a/app/controllers/admin/versions_controller.rb b/app/controllers/admin/versions_controller.rb new file mode 100644 index 000000000..ff2691642 --- /dev/null +++ b/app/controllers/admin/versions_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Admin + class VersionsController < ApplicationController + before_action :authenticate_member! + before_action :authorize_admin! + + def revert + @version = PaperTrail::Version.find(params[:id]) + @object = @version.reify + if @object.save + redirect_to admin_crops_path, notice: "Reverted to version from #{@version.created_at.strftime('%B %d, %Y')}" + else + redirect_to admin_crops_path, alert: "Could not revert to version from #{@version.created_at.strftime('%B %d, %Y')}. Errors: #{@object.errors.full_messages.to_sentence}" + end + end + + private + + def authorize_admin! + authorize! :wrangle, Crop + end + end +end diff --git a/app/views/admin/crops/index.html.haml b/app/views/admin/crops/index.html.haml index a62cda0ae..ce410b93b 100644 --- a/app/views/admin/crops/index.html.haml +++ b/app/views/admin/crops/index.html.haml @@ -1,41 +1,56 @@ -- content_for :title, "Crop Wrangler Admin" -- content_for :breadcrumbs do - %li.breadcrumb-item= link_to 'Admin', '#' - %li.breadcrumb-item.active Crop Wrangler +- content_for :title, "Crop Wrangling" -%h1 Crop Wrangler Admin +%h1 Crop Wrangling -%ul#myTab.nav.nav-tabs{role: "tablist"} - %li.nav-item - %a#home-tab.nav-link{ href: admin_crops_path, role: "tab", class: 'active'} - Recently edited - %li.nav-item - %a#home-tab.nav-link{ href: wrangle_crops_path, role: "tab"} - Recently added - %li.nav-item - %a#profile-tab.nav-link{ href: wrangle_crops_path(approval_status: "pending"), role: "tab"} - Pending approval - %li.nav-item - %a#contact-tab.nav-link{ href: wrangle_crops_path(approval_status: "rejected"), role: "tab"} - Rejected +%nav.nav + = link_to "Full crop hierarchy", hierarchy_crops_path, class: 'nav-link' + = link_to "Add Crop", new_crop_path, class: 'btn' -.card - %ul.list-group.list-group-flush - - @versions.each do |version| - - crop = version.item || version.reify - - if crop - %li.list-group-item - .d-flex.w-100.justify-content-between - %h5.mb-1 - - if version.event == "destroy" - = crop.name - - else - = link_to crop.name, crop - %small.text-muted= "was #{version.event}d" - %small= time_ago_in_words(version.created_at) + " ago" - - member = @members[version.whodunnit.to_i] - - if member - %p.mb-1 - Made by - = link_to member.name, member - = render 'shared/version_changeset', version: version +%section.crop_wranglers + %h2 Crop Wranglers + - @crop_wranglers.each do |crop_wrangler| + = render 'members/tiny', member: crop_wrangler + +%hr/ + +%section + %h2 Crops + + + %ul#myTab.nav.nav-tabs{role: "tablist"} + %li.nav-item + %a#home-tab.nav-link{ href: admin_crops_path, role: "tab", class: 'active'} + Recently edited + %li.nav-item + %a#home-tab.nav-link{ href: wrangle_crops_path, role: "tab"} + Recently added + %li.nav-item + %a#profile-tab.nav-link{ href: wrangle_crops_path(approval_status: "pending"), role: "tab"} + Pending approval + %li.nav-item + %a#contact-tab.nav-link{ href: wrangle_crops_path(approval_status: "rejected"), role: "tab"} + Rejected + + .card + %ul.list-group.list-group-flush + - @versions.each do |version| + - crop = version.item || version.reify + - if crop + %li.list-group-item + .d-flex.w-100.justify-content-between + %h5.mb-1 + - if version.event == "destroy" + = crop.name + - else + = link_to crop.name, crop + %small.text-muted= "was #{version.event}d" + .d-inline-block + %small.mr-2= time_ago_in_words(version.created_at) + " ago" + - if can?(:wrangle, Crop) + = link_to "Revert", revert_admin_version_path(version), method: :post, class: "btn btn-sm btn-outline-danger" + - member = @members[version.whodunnit.to_i] + - if member + %p.mb-1 + Made by + = link_to member.name, member + = render 'shared/version_changeset', version: version diff --git a/config/routes.rb b/config/routes.rb index edac51fdb..93d4d4b35 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -94,6 +94,9 @@ Rails.application.routes.draw do namespace :admin do resources :crops, only: [:index] + resources :versions, only: [] do + post :revert, on: :member, as: :revert + end end resources :comments diff --git a/spec/features/admin/reverting_crops_spec.rb b/spec/features/admin/reverting_crops_spec.rb new file mode 100644 index 000000000..f6f436f1b --- /dev/null +++ b/spec/features/admin/reverting_crops_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.feature 'Reverting crops' do + let(:wrangler) { create(:crop_wrangling_member) } + let(:member) { create(:member) } + let!(:crop) { create(:crop, name: 'Initial Name') } + + before do + crop.update(name: 'Updated Name') + end + + context 'when logged in as an wrangler' do + before do + login_as(wrangler, scope: :member) + end + + scenario 'Admin reverts a crop' do + visit admin_crops_path + click_link 'Revert', match: :first + expect(page).to have_content('Reverted to version from') + crop.reload + expect(crop.name).to eq('Initial Name') + end + end + + context 'when logged in as a regular member' do + before do + login_as(member, scope: :member) + end + + scenario 'Member cannot revert a crop' do + visit admin_crops_path + expect(page).not_to have_link('Revert') + end + end +end