Fixes #135
Adds a table with proposed actions in the dashboard. The user can mark an action as executed.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
class ProposalsDashboardController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
|
||||
helper_method :proposal
|
||||
helper_method :proposal, :proposed_actions
|
||||
respond_to :html
|
||||
layout 'proposals_dashboard'
|
||||
|
||||
@@ -19,9 +19,23 @@ class ProposalsDashboardController < ApplicationController
|
||||
redirect_to proposal_dashboard_index_path(proposal), notice: t('proposals.notice.published')
|
||||
end
|
||||
|
||||
def execute
|
||||
authorize! :dashboard, proposal
|
||||
ProposalExecutedDashboardAction.create(proposal: proposal, proposal_dashboard_action: proposal_dashboard_action, executed_at: Time.now)
|
||||
redirect_to proposal_dashboard_index_path(proposal.to_param)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def proposal_dashboard_action
|
||||
@proposal_dashboard_action ||= ProposalDashboardAction.find(params[:id])
|
||||
end
|
||||
|
||||
def proposal
|
||||
@proposal ||= Proposal.find(params[:proposal_id])
|
||||
end
|
||||
|
||||
def proposed_actions
|
||||
@proposed_actions ||= ProposalDashboardAction.proposed_actions.active_for(proposal)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,6 +31,8 @@ class Proposal < ActiveRecord::Base
|
||||
belongs_to :geozone
|
||||
has_many :comments, as: :commentable, dependent: :destroy
|
||||
has_many :proposal_notifications, dependent: :destroy
|
||||
has_many :proposal_executed_dashboard_actions, dependent: :destroy
|
||||
has_many :proposal_dashboard_actions, through: :proposal_executed_dashboard_actions
|
||||
|
||||
validates :title, presence: true
|
||||
validates :question, presence: true
|
||||
|
||||
@@ -4,6 +4,9 @@ class ProposalDashboardAction < ActiveRecord::Base
|
||||
acts_as_paranoid column: :hidden_at
|
||||
include ActsAsParanoidAliases
|
||||
|
||||
has_many :proposal_executed_dashboard_actions, dependent: :restrict_with_error
|
||||
has_many :proposals, through: :proposal_executed_dashboard_actions
|
||||
|
||||
enum action_type: %i[proposed_action resource]
|
||||
|
||||
validates :title,
|
||||
@@ -41,6 +44,17 @@ class ProposalDashboardAction < ActiveRecord::Base
|
||||
|
||||
scope :active, -> { where(active: true) }
|
||||
scope :inactive, -> { where(active: false) }
|
||||
scope :resources, -> { where(action_type: 'resource') }
|
||||
scope :proposed_actions, -> { where(action_type: 'proposed_action') }
|
||||
scope :active_for, ->(proposal) do
|
||||
published_at = proposal.published_at || Date.today
|
||||
|
||||
active
|
||||
.where('required_supports <= ?', proposal.votes_for.size)
|
||||
.where('day_offset <= ?', (Date.today - published_at).to_i)
|
||||
end
|
||||
|
||||
default_scope { order(order: :asc, title: :asc) }
|
||||
|
||||
def request_to_administrators?
|
||||
request_to_administrators || false
|
||||
|
||||
10
app/models/proposal_executed_dashboard_action.rb
Normal file
10
app/models/proposal_executed_dashboard_action.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ProposalExecutedDashboardAction < ActiveRecord::Base
|
||||
belongs_to :proposal
|
||||
belongs_to :proposal_dashboard_action
|
||||
|
||||
validates :proposal, presence: true, uniqueness: { scope: :proposal_dashboard_action }
|
||||
validates :proposal_dashboard_action, presence: true
|
||||
validates :executed_at, presence: true
|
||||
end
|
||||
25
app/views/proposals_dashboard/_proposed_action.html.erb
Normal file
25
app/views/proposals_dashboard/_proposed_action.html.erb
Normal file
@@ -0,0 +1,25 @@
|
||||
<tr>
|
||||
<td>
|
||||
<% if action.link.blank? %>
|
||||
<span data-tooltip title="<%= action.description %>">
|
||||
<%= action.title %>
|
||||
</span>
|
||||
<% else %>
|
||||
<%= link_to action.link, target: '_blank' do %>
|
||||
<span data-tooltip title="<%= action.description %>">
|
||||
<%= action.title %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
<% if action.proposals.where(id: proposal.id).any? %>
|
||||
<%=l action.proposal_executed_dashboard_actions.find_by(proposal: proposal).executed_at, format: :short %>
|
||||
<% else %>
|
||||
<%= link_to t('.execute'),
|
||||
execute_proposal_dashboard_path(proposal.to_param, action.to_param),
|
||||
method: 'post',
|
||||
data: { confirm: t('admin.actions.confirm') } %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -41,13 +41,12 @@
|
||||
<%= link_to t('.publish'), publish_proposal_dashboard_index_path(proposal), method: :patch, class: 'menu-entry' if can?(:publish, proposal) %>
|
||||
<% end %>
|
||||
|
||||
<!--
|
||||
<div class="menu-title"><span class="icon-user"></span> Progreso</div>
|
||||
<a href='#' class="menu-entry">Meta actual</a>
|
||||
<a href='#' class="menu-entry">Acciones</a>
|
||||
<a href='#' class="menu-entry">Feed</a>
|
||||
<a href='#' class="menu-entry">Ruta</a>
|
||||
|
||||
<div class="menu-title"><span class="icon-user"></span> <%= t '.progress' %></div>
|
||||
<!-- a href='#' class="menu-entry">Meta actual</a -->
|
||||
<%= link_to t('.actions'), proposal_dashboard_index_path(proposal.to_param), class: 'menu-entry' %>
|
||||
<!-- a href='#' class="menu-entry">Feed</a -->
|
||||
<!-- a href='#' class="menu-entry">Ruta</a -->
|
||||
<!--
|
||||
<div class="menu-title selected"><span class="icon-user"></span> Recursos</div>
|
||||
<a href='#' class="menu-entry selected">Kit de difusión</a>
|
||||
<a href='#' class="menu-entry">Encuestas</a>
|
||||
@@ -89,9 +88,16 @@
|
||||
<div class="caption">Notificaciones</div>
|
||||
<div class="value">3</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="body">body</div>
|
||||
</div>
|
||||
-->
|
||||
<div class="body">
|
||||
<table>
|
||||
<caption><%= t('.actions') %></caption>
|
||||
<% proposed_actions.each do |action| %>
|
||||
<%= render partial: 'proposed_action', locals: { action: action } %>
|
||||
<% end %>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -498,6 +498,10 @@ en:
|
||||
edit_proposal_link: Edit
|
||||
send_notification: Send notification
|
||||
retire: Retire
|
||||
progress: Progress
|
||||
actions: Actions
|
||||
proposed_action:
|
||||
execute: Execute
|
||||
polls:
|
||||
all: "All"
|
||||
no_dates: "no date assigned"
|
||||
|
||||
@@ -498,6 +498,10 @@ es:
|
||||
edit_proposal_link: Editar propuesta
|
||||
send_notification: Enviar notificación
|
||||
retire: Retirar
|
||||
progress: Progreso
|
||||
actions: Acciones
|
||||
proposed_action:
|
||||
execute: Ejecutar
|
||||
polls:
|
||||
all: "Todas"
|
||||
no_dates: "sin fecha asignada"
|
||||
|
||||
@@ -3,6 +3,10 @@ resources :proposals do
|
||||
collection do
|
||||
patch :publish
|
||||
end
|
||||
|
||||
member do
|
||||
post :execute
|
||||
end
|
||||
end
|
||||
|
||||
member do
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
class CreateProposalExecutedDashboardActions < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :proposal_executed_dashboard_actions do |t|
|
||||
t.references :proposal, index: true, foreign_key: true
|
||||
t.references :proposal_dashboard_action, index: { name: 'index_proposal_action' }, foreign_key: true
|
||||
t.datetime :executed_at
|
||||
t.text :comments
|
||||
|
||||
t.timestamps null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
14
db/schema.rb
14
db/schema.rb
@@ -891,6 +891,18 @@ ActiveRecord::Schema.define(version: 20180711224810) do
|
||||
t.integer "action_type", default: 0, null: false
|
||||
end
|
||||
|
||||
create_table "proposal_executed_dashboard_actions", force: :cascade do |t|
|
||||
t.integer "proposal_id"
|
||||
t.integer "proposal_dashboard_action_id"
|
||||
t.datetime "executed_at"
|
||||
t.text "comments"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
add_index "proposal_executed_dashboard_actions", ["proposal_dashboard_action_id"], name: "index_proposal_action", using: :btree
|
||||
add_index "proposal_executed_dashboard_actions", ["proposal_id"], name: "index_proposal_executed_dashboard_actions_on_proposal_id", using: :btree
|
||||
|
||||
create_table "proposal_notifications", force: :cascade do |t|
|
||||
t.string "title"
|
||||
t.text "body"
|
||||
@@ -1324,6 +1336,8 @@ ActiveRecord::Schema.define(version: 20180711224810) do
|
||||
add_foreign_key "poll_recounts", "poll_booth_assignments", column: "booth_assignment_id"
|
||||
add_foreign_key "poll_recounts", "poll_officer_assignments", column: "officer_assignment_id"
|
||||
add_foreign_key "poll_voters", "polls"
|
||||
add_foreign_key "proposal_executed_dashboard_actions", "proposal_dashboard_actions"
|
||||
add_foreign_key "proposal_executed_dashboard_actions", "proposals"
|
||||
add_foreign_key "proposals", "communities"
|
||||
add_foreign_key "related_content_scores", "related_contents"
|
||||
add_foreign_key "related_content_scores", "users"
|
||||
|
||||
@@ -1071,4 +1071,14 @@ LOREM_IPSUM
|
||||
action_type 'resource'
|
||||
end
|
||||
end
|
||||
|
||||
factory :proposal_executed_dashboard_action, class: 'ProposalExecutedDashboardAction' do
|
||||
proposal
|
||||
proposal_dashboard_action
|
||||
executed_at { Time.now }
|
||||
|
||||
trait :with_comments do
|
||||
comments { Faker::Lorem.sentence(10) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -161,6 +161,19 @@ describe Abilities::Common do
|
||||
it { should_not be_able_to(:destroy, proposal_document) }
|
||||
end
|
||||
|
||||
describe 'proposals dashboard' do
|
||||
it { is_expected.to be_able_to(:dashboard, own_proposal) }
|
||||
it { is_expected.not_to be_able_to(:dashboard, proposal) }
|
||||
end
|
||||
|
||||
describe 'publishing proposals' do
|
||||
let(:draft_own_proposal) { create(:proposal, :draft, author: user) }
|
||||
|
||||
it { is_expected.to be_able_to(:publish, draft_own_proposal) }
|
||||
it { is_expected.not_to be_able_to(:publish, own_proposal) }
|
||||
it { is_expected.not_to be_able_to(:publish, proposal) }
|
||||
end
|
||||
|
||||
describe "when level 2 verified" do
|
||||
let(:own_spending_proposal) { create(:spending_proposal, author: user) }
|
||||
|
||||
|
||||
41
spec/models/proposal_executed_dashboard_action_spec.rb
Normal file
41
spec/models/proposal_executed_dashboard_action_spec.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
require 'rails_helper'
|
||||
|
||||
describe ProposalExecutedDashboardAction do
|
||||
subject do
|
||||
build :proposal_executed_dashboard_action,
|
||||
proposal: proposal,
|
||||
proposal_dashboard_action: proposal_dashboard_action,
|
||||
executed_at: executed_at
|
||||
end
|
||||
|
||||
let(:proposal) { create :proposal }
|
||||
let(:proposal_dashboard_action) { create :proposal_dashboard_action }
|
||||
let(:executed_at) { Time.now }
|
||||
|
||||
it { is_expected.to be_valid }
|
||||
|
||||
context 'when proposal is nil' do
|
||||
let(:proposal) { nil }
|
||||
|
||||
it { is_expected.not_to be_valid }
|
||||
end
|
||||
|
||||
context 'when proposal_dashboard_action is nil' do
|
||||
let(:proposal_dashboard_action) { nil }
|
||||
|
||||
it { is_expected.not_to be_valid }
|
||||
end
|
||||
|
||||
context 'when executed_at is nil' do
|
||||
let(:executed_at) { nil }
|
||||
|
||||
it { is_expected.not_to be_valid }
|
||||
end
|
||||
|
||||
context 'when it has been already executed' do
|
||||
let!(:executed) { create(:proposal_executed_dashboard_action, proposal: proposal, proposal_dashboard_action: proposal_dashboard_action) }
|
||||
|
||||
it { is_expected.not_to be_valid }
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user