Adds a table with proposed actions in the dashboard. The user can mark
an action as executed.
This commit is contained in:
Juan Salvador Pérez García
2018-06-15 11:55:36 +02:00
parent 9f27d73240
commit 83f78b1940
14 changed files with 183 additions and 10 deletions

View File

@@ -4,7 +4,7 @@
class ProposalsDashboardController < ApplicationController class ProposalsDashboardController < ApplicationController
before_action :authenticate_user! before_action :authenticate_user!
helper_method :proposal helper_method :proposal, :proposed_actions
respond_to :html respond_to :html
layout 'proposals_dashboard' layout 'proposals_dashboard'
@@ -19,9 +19,23 @@ class ProposalsDashboardController < ApplicationController
redirect_to proposal_dashboard_index_path(proposal), notice: t('proposals.notice.published') redirect_to proposal_dashboard_index_path(proposal), notice: t('proposals.notice.published')
end 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 private
def proposal_dashboard_action
@proposal_dashboard_action ||= ProposalDashboardAction.find(params[:id])
end
def proposal def proposal
@proposal ||= Proposal.find(params[:proposal_id]) @proposal ||= Proposal.find(params[:proposal_id])
end end
def proposed_actions
@proposed_actions ||= ProposalDashboardAction.proposed_actions.active_for(proposal)
end
end end

View File

@@ -31,6 +31,8 @@ class Proposal < ActiveRecord::Base
belongs_to :geozone belongs_to :geozone
has_many :comments, as: :commentable, dependent: :destroy has_many :comments, as: :commentable, dependent: :destroy
has_many :proposal_notifications, 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 :title, presence: true
validates :question, presence: true validates :question, presence: true

View File

@@ -4,6 +4,9 @@ class ProposalDashboardAction < ActiveRecord::Base
acts_as_paranoid column: :hidden_at acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases 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] enum action_type: %i[proposed_action resource]
validates :title, validates :title,
@@ -41,6 +44,17 @@ class ProposalDashboardAction < ActiveRecord::Base
scope :active, -> { where(active: true) } scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) } 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? def request_to_administrators?
request_to_administrators || false request_to_administrators || false

View 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

View 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>

View File

@@ -41,13 +41,12 @@
<%= link_to t('.publish'), publish_proposal_dashboard_index_path(proposal), method: :patch, class: 'menu-entry' if can?(:publish, proposal) %> <%= link_to t('.publish'), publish_proposal_dashboard_index_path(proposal), method: :patch, class: 'menu-entry' if can?(:publish, proposal) %>
<% end %> <% end %>
<!-- <div class="menu-title"><span class="icon-user"></span> <%= t '.progress' %></div>
<div class="menu-title"><span class="icon-user"></span> Progreso</div> <!-- a href='#' class="menu-entry">Meta actual</a -->
<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">Acciones</a> <!-- a href='#' class="menu-entry">Feed</a -->
<a href='#' class="menu-entry">Feed</a> <!-- a href='#' class="menu-entry">Ruta</a -->
<a href='#' class="menu-entry">Ruta</a> <!--
<div class="menu-title selected"><span class="icon-user"></span> Recursos</div> <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 selected">Kit de difusión</a>
<a href='#' class="menu-entry">Encuestas</a> <a href='#' class="menu-entry">Encuestas</a>
@@ -89,9 +88,16 @@
<div class="caption">Notificaciones</div> <div class="caption">Notificaciones</div>
<div class="value">3</div> <div class="value">3</div>
</div> </div>
</div> </div>
<div class="body">body</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> </div>
</div> </div>

View File

@@ -498,6 +498,10 @@ en:
edit_proposal_link: Edit edit_proposal_link: Edit
send_notification: Send notification send_notification: Send notification
retire: Retire retire: Retire
progress: Progress
actions: Actions
proposed_action:
execute: Execute
polls: polls:
all: "All" all: "All"
no_dates: "no date assigned" no_dates: "no date assigned"

View File

@@ -498,6 +498,10 @@ es:
edit_proposal_link: Editar propuesta edit_proposal_link: Editar propuesta
send_notification: Enviar notificación send_notification: Enviar notificación
retire: Retirar retire: Retirar
progress: Progreso
actions: Acciones
proposed_action:
execute: Ejecutar
polls: polls:
all: "Todas" all: "Todas"
no_dates: "sin fecha asignada" no_dates: "sin fecha asignada"

View File

@@ -3,6 +3,10 @@ resources :proposals do
collection do collection do
patch :publish patch :publish
end end
member do
post :execute
end
end end
member do member do

View File

@@ -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

View File

@@ -891,6 +891,18 @@ ActiveRecord::Schema.define(version: 20180711224810) do
t.integer "action_type", default: 0, null: false t.integer "action_type", default: 0, null: false
end 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| create_table "proposal_notifications", force: :cascade do |t|
t.string "title" t.string "title"
t.text "body" 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_booth_assignments", column: "booth_assignment_id"
add_foreign_key "poll_recounts", "poll_officer_assignments", column: "officer_assignment_id" add_foreign_key "poll_recounts", "poll_officer_assignments", column: "officer_assignment_id"
add_foreign_key "poll_voters", "polls" 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 "proposals", "communities"
add_foreign_key "related_content_scores", "related_contents" add_foreign_key "related_content_scores", "related_contents"
add_foreign_key "related_content_scores", "users" add_foreign_key "related_content_scores", "users"

View File

@@ -1071,4 +1071,14 @@ LOREM_IPSUM
action_type 'resource' action_type 'resource'
end end
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 end

View File

@@ -160,6 +160,19 @@ describe Abilities::Common do
it { should_not be_able_to(:destroy, proposal_image) } it { should_not be_able_to(:destroy, proposal_image) }
it { should_not be_able_to(:destroy, proposal_document) } it { should_not be_able_to(:destroy, proposal_document) }
end 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 describe "when level 2 verified" do
let(:own_spending_proposal) { create(:spending_proposal, author: user) } let(:own_spending_proposal) { create(:spending_proposal, author: user) }

View 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