diff --git a/app/assets/stylesheets/proposal.scss b/app/assets/stylesheets/proposal.scss index a01026d48..a1a440b43 100644 --- a/app/assets/stylesheets/proposal.scss +++ b/app/assets/stylesheets/proposal.scss @@ -284,10 +284,10 @@ i { display: inline-block; - width: rem-calc(20); - height: rem-calc(20); + width: rem-calc(24); + height: rem-calc(24); border-radius: rem-calc(10); - padding-right: rem-calc(4); + padding-right: rem-calc(5); } } diff --git a/app/controllers/admin/dashboard/actions_controller.rb b/app/controllers/admin/dashboard/actions_controller.rb new file mode 100644 index 000000000..cd1e2fd9c --- /dev/null +++ b/app/controllers/admin/dashboard/actions_controller.rb @@ -0,0 +1,67 @@ +class Admin::Dashboard::ActionsController < Admin::Dashboard::BaseController + helper_method :dashboard_action, :resource + + def index + @dashboard_actions = ::Dashboard::Action.order(required_supports: :asc) + end + + def new + @dashboard_action = ::Dashboard::Action.new( + active: true, + day_offset: 0, + required_supports: 0, + request_to_administrators: true, + action_type: 'proposed_action' + ) + end + + def create + @dashboard_action = ::Dashboard::Action.new(dashboard_action_params) + if @dashboard_action.save + redirect_to admin_dashboard_actions_path, notice: t('admin.dashboard.actions.create.notice') + else + render :new + end + end + + def edit; end + + def update + if dashboard_action.update(dashboard_action_params) + redirect_to admin_dashboard_actions_path + else + render :edit + end + end + + def destroy + if dashboard_action.destroy + flash[:notice] = t('admin.dashboard.actions.delete.success') + else + flash[:error] = dashboard_action.errors.full_messages.join(',') + end + + redirect_to admin_dashboard_actions_path + end + + private + + def resource + @dashboard_action + end + + def dashboard_action_params + params + .require(:dashboard_action) + .permit( + :title, :description, :short_description, :request_to_administrators, :day_offset, + :required_supports, :order, :active, :action_type, + documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy], + links_attributes: [:id, :label, :url, :open_in_new_tab, :_destroy] + ) + end + + def dashboard_action + @dashboard_action ||= ::Dashboard::Action.find(params[:id]) + end +end diff --git a/app/controllers/admin/dashboard/administrator_tasks_controller.rb b/app/controllers/admin/dashboard/administrator_tasks_controller.rb new file mode 100644 index 000000000..a67679a7d --- /dev/null +++ b/app/controllers/admin/dashboard/administrator_tasks_controller.rb @@ -0,0 +1,26 @@ +class Admin::Dashboard::AdministratorTasksController < Admin::Dashboard::BaseController + helper_method :administrator_task + + def index + authorize! :index, ::Dashboard::AdministratorTask + @administrator_tasks = ::Dashboard::AdministratorTask.pending + end + + def edit + authorize! :edit, administrator_task + end + + def update + authorize! :update, administrator_task + + administrator_task.update(user: current_user, executed_at: Time.now) + redirect_to admin_dashboard_administrator_tasks_path, + { flash: { notice: t("admin.dashboard.administrator_tasks.update.success") } } + end + + private + + def administrator_task + @administrator_task ||= ::Dashboard::AdministratorTask.find(params[:id]) + end +end diff --git a/app/controllers/admin/proposal_dashboard/base_controller.rb b/app/controllers/admin/dashboard/base_controller.rb similarity index 52% rename from app/controllers/admin/proposal_dashboard/base_controller.rb rename to app/controllers/admin/dashboard/base_controller.rb index 959ad20e7..891bc49f5 100644 --- a/app/controllers/admin/proposal_dashboard/base_controller.rb +++ b/app/controllers/admin/dashboard/base_controller.rb @@ -1,4 +1,4 @@ -class Admin::ProposalDashboard::BaseController < Admin::BaseController +class Admin::Dashboard::BaseController < Admin::BaseController helper_method :namespace private diff --git a/app/controllers/admin/proposal_dashboard/actions_controller.rb b/app/controllers/admin/proposal_dashboard/actions_controller.rb deleted file mode 100644 index 1e4c5ec0f..000000000 --- a/app/controllers/admin/proposal_dashboard/actions_controller.rb +++ /dev/null @@ -1,67 +0,0 @@ -class Admin::ProposalDashboard::ActionsController < Admin::ProposalDashboard::BaseController - helper_method :proposal_dashboard_action, :resource - - def index - @proposal_dashboard_actions = ProposalDashboardAction.order(required_supports: :asc) - end - - def new - @proposal_dashboard_action = ProposalDashboardAction.new( - active: true, - day_offset: 0, - required_supports: 0, - request_to_administrators: true, - action_type: 'proposed_action' - ) - end - - def create - @proposal_dashboard_action = ProposalDashboardAction.new(proposal_dashboard_action_params) - if @proposal_dashboard_action.save - redirect_to admin_proposal_dashboard_actions_path, notice: t('admin.proposal_dashboard_actions.create.notice') - else - render :new - end - end - - def edit; end - - def update - if proposal_dashboard_action.update(proposal_dashboard_action_params) - redirect_to admin_proposal_dashboard_actions_path - else - render :edit - end - end - - def destroy - if proposal_dashboard_action.destroy - flash[:notice] = t('admin.proposal_dashboard_actions.delete.success') - else - flash[:error] = proposal_dashboard_action.errors.full_messages.join(',') - end - - redirect_to admin_proposal_dashboard_actions_path - end - - private - - def resource - @proposal_dashboard_action - end - - def proposal_dashboard_action_params - params - .require(:proposal_dashboard_action) - .permit( - :title, :description, :short_description, :request_to_administrators, :day_offset, :required_supports, :order, :active, :action_type, - documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy], - links_attributes: [:id, :label, :url, :open_in_new_tab, :_destroy] - ) - end - - - def proposal_dashboard_action - @proposal_dashboard_action ||= ProposalDashboardAction.find(params[:id]) - end -end diff --git a/app/controllers/admin/proposal_dashboard/administrator_tasks_controller.rb b/app/controllers/admin/proposal_dashboard/administrator_tasks_controller.rb deleted file mode 100644 index f6ff2e6cc..000000000 --- a/app/controllers/admin/proposal_dashboard/administrator_tasks_controller.rb +++ /dev/null @@ -1,25 +0,0 @@ -class Admin::ProposalDashboard::AdministratorTasksController < Admin::ProposalDashboard::BaseController - helper_method :administrator_task - - def index - authorize! :index, AdministratorTask - @administrator_tasks = AdministratorTask.pending - end - - def edit - authorize! :edit, administrator_task - end - - def update - authorize! :update, administrator_task - - administrator_task.update(user: current_user, executed_at: Time.now) - redirect_to admin_proposal_dashboard_administrator_tasks_path, { flash: { notice: t('.success') } } - end - - private - - def administrator_task - @administrator_task ||= AdministratorTask.find(params[:id]) - end -end diff --git a/app/controllers/dashboard/base_controller.rb b/app/controllers/dashboard/base_controller.rb index f2db2ba33..260e06de5 100644 --- a/app/controllers/dashboard/base_controller.rb +++ b/app/controllers/dashboard/base_controller.rb @@ -4,7 +4,7 @@ class Dashboard::BaseController < ApplicationController helper_method :proposal, :proposed_actions, :resource, :resources, :next_goal, :next_goal_supports, :next_goal_progress, :community_members_count respond_to :html - layout 'proposals_dashboard' + layout 'dashboard' private @@ -13,11 +13,11 @@ class Dashboard::BaseController < ApplicationController end def proposed_actions - @proposed_actions ||= ProposalDashboardAction.proposed_actions.active_for(proposal).order(order: :asc) + @proposed_actions ||= Dashboard::Action.proposed_actions.active_for(proposal).order(order: :asc) end def resources - @resources ||= ProposalDashboardAction.resources.active_for(proposal).order(order: :asc) + @resources ||= Dashboard::Action.resources.active_for(proposal).order(order: :asc) end def next_goal_supports @@ -35,6 +35,6 @@ class Dashboard::BaseController < ApplicationController end def next_goal - @next_goal ||= ProposalDashboardAction.next_goal_for(proposal) + @next_goal ||= Dashboard::Action.next_goal_for(proposal) end end diff --git a/app/controllers/dashboard/resources_controller.rb b/app/controllers/dashboard/resources_controller.rb index 42154d55c..3c9e767c7 100644 --- a/app/controllers/dashboard/resources_controller.rb +++ b/app/controllers/dashboard/resources_controller.rb @@ -2,7 +2,7 @@ class Dashboard::ResourcesController < Dashboard::BaseController skip_authorization_check def index - @resources = ProposalDashboardAction + @resources = Dashboard::Action .active .resources .where('required_supports > 0') diff --git a/app/controllers/proposals_dashboard_controller.rb b/app/controllers/dashboard_controller.rb similarity index 55% rename from app/controllers/proposals_dashboard_controller.rb rename to app/controllers/dashboard_controller.rb index d023eb7bd..e6e0de1fe 100644 --- a/app/controllers/proposals_dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -1,5 +1,5 @@ -class ProposalsDashboardController < Dashboard::BaseController - helper_method :proposal_dashboard_action, :active_resources, :course +class DashboardController < Dashboard::BaseController + helper_method :dashboard_action, :active_resources, :course def index authorize! :dashboard, proposal @@ -15,13 +15,13 @@ class ProposalsDashboardController < Dashboard::BaseController def execute authorize! :dashboard, proposal - ProposalExecutedDashboardAction.create(proposal: proposal, proposal_dashboard_action: proposal_dashboard_action, executed_at: Time.now) + Dashboard::ExecutedAction.create(proposal: proposal, action: dashboard_action, executed_at: Time.now) redirect_to progress_proposal_dashboard_index_path(proposal.to_param) end def new_request authorize! :dashboard, proposal - @proposal_executed_dashboard_action = ProposalExecutedDashboardAction.new + @dashboard_executed_action = Dashboard::ExecutedAction.new end def create_request @@ -29,17 +29,17 @@ class ProposalsDashboardController < Dashboard::BaseController source_params = { proposal: proposal, - proposal_dashboard_action: proposal_dashboard_action, + action: dashboard_action, executed_at: Time.now } - @proposal_executed_dashboard_action = ProposalExecutedDashboardAction.new(source_params) - if @proposal_executed_dashboard_action.save - AdministratorTask.create(source: @proposal_executed_dashboard_action) + @dashboard_executed_action = Dashboard::ExecutedAction.new(source_params) + if @dashboard_executed_action.save + Dashboard::AdministratorTask.create(source: @dashboard_executed_action) redirect_to progress_proposal_dashboard_index_path(proposal.to_param), { flash: { info: t('.success') } } else - flash.now[:alert] = @proposal_executed_dashboard_action.errors.full_messages.join('
') + flash.now[:alert] = @dashboard_executed_action.errors.full_messages.join('
') render :new_request end end @@ -69,15 +69,15 @@ class ProposalsDashboardController < Dashboard::BaseController private - def proposal_dashboard_action - @proposal_dashboard_action ||= ProposalDashboardAction.find(params[:id]) + def dashboard_action + @dashboard_action ||= Dashboard::Action.find(params[:id]) end def active_resources - @active_resources ||= ProposalDashboardAction.active.resources.order(required_supports: :asc, day_offset: :asc) + @active_resources ||= Dashboard::Action.active.resources.order(required_supports: :asc, day_offset: :asc) end def course - @course ||= ProposalDashboardAction.course_for(proposal) + @course ||= Dashboard::Action.course_for(proposal) end end diff --git a/app/helpers/admin/proposal_dashboard_actions_helper.rb b/app/helpers/admin/proposal_dashboard_actions_helper.rb index a2d0f1be9..ceb8e6ba2 100644 --- a/app/helpers/admin/proposal_dashboard_actions_helper.rb +++ b/app/helpers/admin/proposal_dashboard_actions_helper.rb @@ -1,6 +1,6 @@ module Admin::ProposalDashboardActionsHelper def active_human_readable(active) - return t('admin.proposal_dashboard_actions.index.active') if active - t('admin.proposal_dashboard_actions.index.inactive') + return t('admin.dashboard.actions.index.active') if active + t('admin.dashboard.actions.index.inactive') end end diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb index ec6c7f778..038725e49 100644 --- a/app/helpers/admin_helper.rb +++ b/app/helpers/admin_helper.rb @@ -44,7 +44,7 @@ module AdminHelper ["homepage", "cards"].include?(controller_name) end - def menu_proposals_dashboard? + def menu_dashboard? ["actions", "administrator_tasks"].include?(controller_name) end diff --git a/app/helpers/proposals_dashboard_helper.rb b/app/helpers/proposals_dashboard_helper.rb index ace936650..a4ec06311 100644 --- a/app/helpers/proposals_dashboard_helper.rb +++ b/app/helpers/proposals_dashboard_helper.rb @@ -1,6 +1,6 @@ module ProposalsDashboardHelper def my_proposal_menu_class - return 'is-active' if controller_name == 'proposals_dashboard' && action_name == 'index' + return 'is-active' if controller_name == 'dashboard' && action_name == 'index' nil end @@ -10,12 +10,12 @@ module ProposalsDashboardHelper end def community_menu_class - return 'is-active' if controller_name == 'proposals_dashboard' && action_name == 'community' + return 'is-active' if controller_name == 'dashboard' && action_name == 'community' nil end def progress_menu_active? - is_proposed_action_request? || (controller_name == 'proposals_dashboard' && action_name == 'progress') + is_proposed_action_request? || (controller_name == 'dashboard' && action_name == 'progress') end def resources_menu_visible?(proposal, resources) @@ -56,24 +56,24 @@ module ProposalsDashboardHelper end def is_resource_request? - controller_name == 'proposals_dashboard' && action_name == 'new_request' && proposal_dashboard_action&.resource? + controller_name == 'dashboard' && action_name == 'new_request' && dashboard_action&.resource? end def is_proposed_action_request? - controller_name == 'proposals_dashboard' && action_name == 'new_request' && proposal_dashboard_action&.proposed_action? + controller_name == 'dashboard' && action_name == 'new_request' && dashboard_action&.proposed_action? end def is_request_active(id) - controller_name == 'proposals_dashboard' && action_name == 'new_request' && proposal_dashboard_action&.id == id + controller_name == 'dashboard' && action_name == 'new_request' && dashboard_action&.id == id end def resoure_availability_label(resource) label = [] - label << t('proposals_dashboard.resource.required_days', days: resource.day_offset) if resource.day_offset > 0 - label << t('proposals_dashboard.resource.required_supports', supports: number_with_delimiter(resource.required_supports, delimiter: '.')) if resource.required_supports > 0 + label << t("dashboard.resource.required_days", days: resource.day_offset) if resource.day_offset > 0 + label << t("dashboard.resource.required_supports", supports: number_with_delimiter(resource.required_supports, delimiter: '.')) if resource.required_supports > 0 - label.join(" #{t('proposals_dashboard.resource.and')}
") + label.join(" #{t("dashboard.resource.and")}
") end def daily_selected_class @@ -98,9 +98,9 @@ module ProposalsDashboardHelper end def resource_tooltip(resource, proposal) - return t('proposals_dashboard.resource.resource_locked') unless resource.active_for?(proposal) - return t('proposals_dashboard.resource.view_resource') if resource.executed_for?(proposal) - return t('proposals_dashboard.resource.resource_requested') if resource.requested_for?(proposal) - t('proposals_dashboard.resource.request_resource') + return t("dashboard.resource.resource_locked") unless resource.active_for?(proposal) + return t("dashboard.resource.view_resource") if resource.executed_for?(proposal) + return t("dashboard.resource.resource_requested") if resource.requested_for?(proposal) + t("dashboard.resource.request_resource") end end diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb index 071eab471..40793f14c 100644 --- a/app/models/abilities/administrator.rb +++ b/app/models/abilities/administrator.rb @@ -48,7 +48,7 @@ module Abilities can [:search, :index], ::User can :manage, Annotation - can :manage, ProposalDashboardAction + can :manage, Dashboard::Action can [:read, :update, :valuate, :destroy, :summary], SpendingProposal @@ -85,6 +85,7 @@ module Abilities can [:create, :destroy], DirectUpload can [:deliver], Newsletter, hidden_at: nil + can [:manage], Dashboard::AdministratorTask end end end diff --git a/app/models/abilities/moderation.rb b/app/models/abilities/moderation.rb index 1f26ff00a..801e752ed 100644 --- a/app/models/abilities/moderation.rb +++ b/app/models/abilities/moderation.rb @@ -63,7 +63,6 @@ module Abilities cannot :moderate, ProposalNotification, author_id: user.id can :index, ProposalNotification - can :manage, AdministratorTask end end end diff --git a/app/models/dashboard.rb b/app/models/dashboard.rb new file mode 100644 index 000000000..5b9104e4d --- /dev/null +++ b/app/models/dashboard.rb @@ -0,0 +1,5 @@ +module Dashboard + def self.table_name_prefix + 'dashboard_' + end +end diff --git a/app/models/dashboard/action.rb b/app/models/dashboard/action.rb new file mode 100644 index 000000000..45818c23b --- /dev/null +++ b/app/models/dashboard/action.rb @@ -0,0 +1,84 @@ +class Dashboard::Action < ActiveRecord::Base + include Documentable + documentable max_documents_allowed: 3, + max_file_size: 3.megabytes, + accepted_content_types: [ 'application/pdf' ] + + include Linkable + + acts_as_paranoid column: :hidden_at + include ActsAsParanoidAliases + + has_many :executed_actions, dependent: :restrict_with_error, class_name: 'Dashboard::ExecutedAction' + has_many :proposals, through: :executed_actions + + enum action_type: [:proposed_action, :resource] + + validates :title, + presence: true, + allow_blank: false, + length: { in: 4..80 } + + validates :action_type, presence: true + + validates :day_offset, + presence: true, + numericality: { + only_integer: true, + greater_than_or_equal_to: 0 + } + + validates :required_supports, + presence: true, + numericality: { + only_integer: true, + greater_than_or_equal_to: 0 + } + + scope :active, -> { where(active: true) } + scope :inactive, -> { where(active: false) } + scope :resources, -> { where(action_type: 1) } + scope :proposed_actions, -> { where(action_type: 0) } + scope :active_for, ->(proposal) do + published_at = proposal.published_at&.to_date || Date.today + + active + .where('required_supports <= ?', proposal.cached_votes_up) + .where('day_offset <= ?', (Date.today - published_at).to_i) + end + + scope :course_for, lambda { |proposal| + active + .resources + .where('required_supports > ?', proposal.cached_votes_up) + .order(required_supports: :asc) + } + + def active_for?(proposal) + published_at = proposal.published_at&.to_date || Date.today + + required_supports <= proposal.cached_votes_up && day_offset <= (Date.today - published_at).to_i + end + + def requested_for?(proposal) + executed_action = executed_actions.find_by(proposal: proposal) + return false if executed_action.nil? + + executed_action.administrator_tasks.any? + end + + def executed_for?(proposal) + executed_action = executed_actions.find_by(proposal: proposal) + return false if executed_action.nil? + + executed_action.administrator_tasks.where.not(executed_at: nil).any? + end + + def self.next_goal_for(proposal) + course_for(proposal).first + end + + def request_to_administrators? + request_to_administrators || false + end +end diff --git a/app/models/dashboard/administrator_task.rb b/app/models/dashboard/administrator_task.rb new file mode 100644 index 000000000..334b6e4fc --- /dev/null +++ b/app/models/dashboard/administrator_task.rb @@ -0,0 +1,11 @@ +class Dashboard::AdministratorTask < ActiveRecord::Base + belongs_to :source, polymorphic: true + belongs_to :user + + validates :source, presence: true + + default_scope { order(created_at: :asc) } + + scope :pending, -> { where(executed_at: nil) } + scope :done, -> { where.not(executed_at: nil) } +end diff --git a/app/models/dashboard/executed_action.rb b/app/models/dashboard/executed_action.rb new file mode 100644 index 000000000..f41b2481d --- /dev/null +++ b/app/models/dashboard/executed_action.rb @@ -0,0 +1,10 @@ +class Dashboard::ExecutedAction < ActiveRecord::Base + belongs_to :proposal + belongs_to :action, class_name: 'Dashboard::Action' + + has_many :administrator_tasks, as: :source, dependent: :destroy, class_name: 'Dashboard::AdministratorTask' + + validates :proposal, presence: true, uniqueness: { scope: :action } + validates :action, presence: true + validates :executed_at, presence: true +end diff --git a/app/models/proposal.rb b/app/models/proposal.rb index 9593e0d82..32dec90df 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -31,8 +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 + has_many :dashboard_executed_actions, dependent: :destroy, class_name: 'Dashboard::ExecutedAction' + has_many :dashboard_actions, through: :dashboard_executed_actions, class_name: 'Dashboard::Action' has_many :polls, as: :related validates :title, presence: true diff --git a/app/services/proposal_achievements_query.rb b/app/services/proposal_achievements_query.rb index 09789b70b..ace1113e8 100644 --- a/app/services/proposal_achievements_query.rb +++ b/app/services/proposal_achievements_query.rb @@ -18,7 +18,7 @@ class ProposalAchievementsQuery achievements.each do |achievement| grouped_results[key] << { executed_at: achievements.last.executed_at, - title: achievements.last.proposal_dashboard_action.title + title: achievements.last.action.title } end end @@ -36,11 +36,11 @@ class ProposalAchievementsQuery end def achievements - ProposalExecutedDashboardAction - .joins(:proposal_dashboard_action) - .includes(:proposal_dashboard_action) + Dashboard::ExecutedAction + .joins(:action) + .includes(:action) .where(proposal: proposal, executed_at: start_date.beginning_of_day..end_date.end_of_day) - .where(proposal_dashboard_actions: { action_type: 0 }) + .where(dashboard_actions: { action_type: 0 }) .order(executed_at: :asc) end diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb index 473bce948..aae27ac38 100644 --- a/app/views/admin/_menu.html.erb +++ b/app/views/admin/_menu.html.erb @@ -237,15 +237,15 @@
  • - <%= t("admin.menu.proposals_dashboard") %> + <%= t("admin.menu.dashboard") %> -