decidim-budgets 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +23 -0
- data/Rakefile +2 -0
- data/app/assets/config/decidim_budgets_manifest.js +1 -0
- data/app/assets/images/decidim/budgets/icon.svg +4 -0
- data/app/assets/javascripts/decidim/budgets/projects.js.es6 +28 -0
- data/app/assets/stylesheets/decidim/budgets/_budgets.scss +1 -0
- data/app/assets/stylesheets/decidim/budgets/budget/_budget-list.scss +32 -0
- data/app/assets/stylesheets/decidim/budgets/budget/_budget-meter.scss +78 -0
- data/app/assets/stylesheets/decidim/budgets/budget/_progress.scss +17 -0
- data/app/commands/decidim/budgets/add_line_item.rb +43 -0
- data/app/commands/decidim/budgets/admin/create_project.rb +52 -0
- data/app/commands/decidim/budgets/admin/update_project.rb +56 -0
- data/app/commands/decidim/budgets/cancel_order.rb +36 -0
- data/app/commands/decidim/budgets/checkout.rb +34 -0
- data/app/commands/decidim/budgets/remove_line_item.rb +35 -0
- data/app/controllers/concerns/decidim/budgets/needs_current_order.rb +27 -0
- data/app/controllers/decidim/budgets/admin/application_controller.rb +14 -0
- data/app/controllers/decidim/budgets/admin/projects_controller.rb +69 -0
- data/app/controllers/decidim/budgets/application_controller.rb +13 -0
- data/app/controllers/decidim/budgets/line_items_controller.rb +43 -0
- data/app/controllers/decidim/budgets/orders_controller.rb +38 -0
- data/app/controllers/decidim/budgets/projects_controller.rb +44 -0
- data/app/forms/decidim/budgets/admin/project_form.rb +44 -0
- data/app/helpers/decidim/budgets/application_helper.rb +12 -0
- data/app/helpers/decidim/budgets/projects_helper.rb +29 -0
- data/app/models/decidim/budgets/application_record.rb +9 -0
- data/app/models/decidim/budgets/line_item.rb +20 -0
- data/app/models/decidim/budgets/order.rb +52 -0
- data/app/models/decidim/budgets/project.rb +15 -0
- data/app/services/decidim/budgets/project_search.rb +58 -0
- data/app/views/decidim/budgets/admin/projects/_form.html.erb +31 -0
- data/app/views/decidim/budgets/admin/projects/edit.html.erb +9 -0
- data/app/views/decidim/budgets/admin/projects/index.html.erb +27 -0
- data/app/views/decidim/budgets/admin/projects/new.html.erb +9 -0
- data/app/views/decidim/budgets/line_items/update_budget.js.erb +21 -0
- data/app/views/decidim/budgets/projects/_budget_confirm.html.erb +30 -0
- data/app/views/decidim/budgets/projects/_budget_excess.html.erb +14 -0
- data/app/views/decidim/budgets/projects/_budget_summary.html.erb +46 -0
- data/app/views/decidim/budgets/projects/_count.html.erb +1 -0
- data/app/views/decidim/budgets/projects/_filters.html.erb +24 -0
- data/app/views/decidim/budgets/projects/_linked_projects.html.erb +12 -0
- data/app/views/decidim/budgets/projects/_order_progress.html.erb +34 -0
- data/app/views/decidim/budgets/projects/_order_selected_projects.html.erb +25 -0
- data/app/views/decidim/budgets/projects/_order_total_budget.html.erb +1 -0
- data/app/views/decidim/budgets/projects/_project.html.erb +40 -0
- data/app/views/decidim/budgets/projects/_project_budget_button.html.erb +9 -0
- data/app/views/decidim/budgets/projects/_projects.html.erb +8 -0
- data/app/views/decidim/budgets/projects/_tags.html.erb +10 -0
- data/app/views/decidim/budgets/projects/index.html.erb +26 -0
- data/app/views/decidim/budgets/projects/index.js.erb +2 -0
- data/app/views/decidim/budgets/projects/show.html.erb +48 -0
- data/config/i18n-tasks.yml +4 -0
- data/config/locales/ca.yml +84 -0
- data/config/locales/en.yml +95 -0
- data/config/locales/es.yml +84 -0
- data/db/migrate/20170127114122_create_projects.rb +15 -0
- data/db/migrate/20170130095615_create_orders.rb +13 -0
- data/db/migrate/20170130101825_create_line_items.rb +10 -0
- data/lib/decidim/budgets/admin.rb +9 -0
- data/lib/decidim/budgets/admin_engine.rb +22 -0
- data/lib/decidim/budgets/feature.rb +59 -0
- data/lib/decidim/budgets/list_engine.rb +30 -0
- data/lib/decidim/budgets/test/factories.rb +43 -0
- data/lib/decidim/budgets.rb +11 -0
- metadata +210 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Decidim
|
4
|
+
module Budgets
|
5
|
+
# Exposes the project resource so users can view them
|
6
|
+
class ProjectsController < Decidim::Budgets::ApplicationController
|
7
|
+
include FilterResource
|
8
|
+
include NeedsCurrentOrder
|
9
|
+
|
10
|
+
helper_method :projects, :random_seed, :project
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def projects
|
15
|
+
@projects ||= search.results.page(params[:page]).per(12)
|
16
|
+
end
|
17
|
+
|
18
|
+
def random_seed
|
19
|
+
@random_seed ||= search.random_seed
|
20
|
+
end
|
21
|
+
|
22
|
+
def project
|
23
|
+
@project ||= projects.find(params[:id])
|
24
|
+
end
|
25
|
+
|
26
|
+
def search_klass
|
27
|
+
ProjectSearch
|
28
|
+
end
|
29
|
+
|
30
|
+
def default_filter_params
|
31
|
+
{
|
32
|
+
search_text: "",
|
33
|
+
scope_id: "",
|
34
|
+
category_id: "",
|
35
|
+
random_seed: params[:random_seed]
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def context_params
|
40
|
+
{ feature: current_feature, organization: current_organization }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Budgets
|
4
|
+
module Admin
|
5
|
+
# This class holds a Form to create/update projects from Decidim's admin panel.
|
6
|
+
class ProjectForm < Decidim::Form
|
7
|
+
include TranslatableAttributes
|
8
|
+
include TranslationsHelper
|
9
|
+
|
10
|
+
translatable_attribute :title, String
|
11
|
+
translatable_attribute :short_description, String
|
12
|
+
translatable_attribute :description, String
|
13
|
+
attribute :budget, Integer
|
14
|
+
attribute :decidim_scope_id, Integer
|
15
|
+
attribute :decidim_category_id, Integer
|
16
|
+
attribute :proposal_ids, Array[Integer]
|
17
|
+
|
18
|
+
validates :title, translatable_presence: true
|
19
|
+
validates :short_description, translatable_presence: true
|
20
|
+
validates :description, translatable_presence: true
|
21
|
+
validates :budget, presence: true, numericality: { greater_than: 0 }
|
22
|
+
|
23
|
+
validates :scope, presence: true, if: ->(form) { form.decidim_scope_id.present? }
|
24
|
+
validates :category, presence: true, if: ->(form) { form.decidim_category_id.present? }
|
25
|
+
|
26
|
+
def map_model(model)
|
27
|
+
self.proposal_ids = model.linked_resources(:proposals, "included_proposals").pluck(:id)
|
28
|
+
end
|
29
|
+
|
30
|
+
def proposals
|
31
|
+
@proposals ||= Decidim.find_resource_manifest(:proposals).try(:resource_scope, context.current_feature)&.order(title: :asc)&.pluck(:title, :id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def scope
|
35
|
+
@scope ||= context.current_feature.scopes.where(id: decidim_scope_id).first
|
36
|
+
end
|
37
|
+
|
38
|
+
def category
|
39
|
+
@category ||= context.current_feature.categories.where(id: decidim_category_id).first
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Budgets
|
4
|
+
# Custom helpers, scoped to the budgets engine.
|
5
|
+
#
|
6
|
+
module ApplicationHelper
|
7
|
+
include PaginateHelper
|
8
|
+
include Decidim::Comments::CommentsHelper
|
9
|
+
include ProjectsHelper
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Budgets
|
4
|
+
# A helper to render order and budgets actions
|
5
|
+
module ProjectsHelper
|
6
|
+
# Render a budget as a currency
|
7
|
+
#
|
8
|
+
# budget - A integer to represent a budget
|
9
|
+
def budget_to_currency(budget)
|
10
|
+
number_to_currency budget, unit: Decidim.currency_unit, precision: 0
|
11
|
+
end
|
12
|
+
|
13
|
+
# Return a percentage of the current order budget from the total budget
|
14
|
+
def current_order_budget_percent
|
15
|
+
current_order&.budget_percent.to_f.floor
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return true if the current order is checked out
|
19
|
+
def current_order_checked_out?
|
20
|
+
current_order&.checked_out?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return true if the user can continue to the checkout process
|
24
|
+
def current_order_can_be_checked_out?
|
25
|
+
current_order&.can_checkout?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Budgets
|
4
|
+
# The data store for a LineItem in the Decidim::Budgets component. It describes an
|
5
|
+
# association between an order and a project.
|
6
|
+
class LineItem < Budgets::ApplicationRecord
|
7
|
+
belongs_to :order, class_name: Decidim::Budgets::Order, foreign_key: "decidim_order_id"
|
8
|
+
belongs_to :project, class_name: Decidim::Budgets::Project, foreign_key: "decidim_project_id"
|
9
|
+
|
10
|
+
validates :order, presence: true, uniqueness: { scope: :project }
|
11
|
+
validates :project, presence: true
|
12
|
+
validate :same_feature
|
13
|
+
|
14
|
+
def same_feature
|
15
|
+
return unless order && project
|
16
|
+
errors.add(:order, :invalid) unless order.feature == project.feature
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Budgets
|
4
|
+
# The data store for a Order in the Decidim::Budgets component. It is unique for each
|
5
|
+
# user and feature and contains a collection of projects
|
6
|
+
class Order < Budgets::ApplicationRecord
|
7
|
+
include Decidim::HasFeature
|
8
|
+
|
9
|
+
feature_manifest_name "budgets"
|
10
|
+
|
11
|
+
belongs_to :user, class_name: Decidim::User, foreign_key: "decidim_user_id"
|
12
|
+
|
13
|
+
has_many :projects, through: :line_items, class_name: Decidim::Budgets::Project, foreign_key: "decidim_project_id"
|
14
|
+
has_many :line_items, class_name: Decidim::Budgets::LineItem, foreign_key: "decidim_order_id", dependent: :destroy
|
15
|
+
|
16
|
+
validates :user, presence: true, uniqueness: { scope: :feature }
|
17
|
+
validate :user_belongs_to_organization
|
18
|
+
|
19
|
+
# Public: Returns the sum of project budgets
|
20
|
+
def total_budget
|
21
|
+
@total_budget ||= projects.sum(&:budget)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public: Returns true if the order has been checked out
|
25
|
+
def checked_out?
|
26
|
+
checked_out_at.present?
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public: Check if the order total budget is enough to checkout
|
30
|
+
def can_checkout?
|
31
|
+
total_budget.to_f >= minimum_budget
|
32
|
+
end
|
33
|
+
|
34
|
+
# Public: Returns the order budget percent from the settings total budget
|
35
|
+
def budget_percent
|
36
|
+
(total_budget.to_f / feature.settings.total_budget.to_f) * 100
|
37
|
+
end
|
38
|
+
|
39
|
+
# Public: Returns the required minimum budget to checkout
|
40
|
+
def minimum_budget
|
41
|
+
feature.settings.total_budget.to_f * (feature.settings.vote_threshold_percent.to_f / 100)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def user_belongs_to_organization
|
47
|
+
return if !user || !organization
|
48
|
+
errors.add(:user, :invalid) unless user.organization == organization
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Budgets
|
4
|
+
# The data store for a Project in the Decidim::Budgets component. It stores a
|
5
|
+
# title, description and any other useful information to render a custom project.
|
6
|
+
class Project < Budgets::ApplicationRecord
|
7
|
+
include Decidim::Resourceable
|
8
|
+
include Decidim::HasFeature
|
9
|
+
include Decidim::HasScope
|
10
|
+
include Decidim::HasCategory
|
11
|
+
|
12
|
+
feature_manifest_name "budgets"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Decidim
|
3
|
+
module Budgets
|
4
|
+
# This class handles search and filtering of projects. Needs a
|
5
|
+
# `current_feature` param with a `Decidim::Feature` in order to
|
6
|
+
# find the projects.
|
7
|
+
class ProjectSearch < ResourceSearch
|
8
|
+
# Public: Initializes the service.
|
9
|
+
# feature - A Decidim::Feature to get the projects from.
|
10
|
+
def initialize(options = {})
|
11
|
+
super(Project.all, options)
|
12
|
+
@random_seed = options[:random_seed].to_f
|
13
|
+
end
|
14
|
+
|
15
|
+
# Handle the search_text filter
|
16
|
+
def search_search_text
|
17
|
+
query
|
18
|
+
.where(localized_search_text_in(:title), text: "%#{search_text}%")
|
19
|
+
.or(query.where(localized_search_text_in(:description), text: "%#{search_text}%"))
|
20
|
+
.or(query.where(localized_search_text_in(:short_description), text: "%#{search_text}%"))
|
21
|
+
end
|
22
|
+
|
23
|
+
# Handle the scope_id filter
|
24
|
+
def search_scope_id
|
25
|
+
query.where(decidim_scope_id: scope_id)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the random projects for the current page.
|
29
|
+
def results
|
30
|
+
@projects ||= Project.transaction do
|
31
|
+
Project.connection.execute("SELECT setseed(#{Project.connection.quote(random_seed)})")
|
32
|
+
super.reorder("RANDOM()").load
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the random seed used to randomize the proposals.
|
37
|
+
def random_seed
|
38
|
+
@random_seed = (rand * 2 - 1) if @random_seed == 0.0 || @random_seed > 1 || @random_seed < -1
|
39
|
+
@random_seed
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Internal: builds the needed query to search for a text in the organization's
|
45
|
+
# available locales. Note that it is intended to be used as follows:
|
46
|
+
#
|
47
|
+
# Example:
|
48
|
+
# Resource.where(localized_search_text_for(:title, text: "my_query"))
|
49
|
+
#
|
50
|
+
# The Hash with the `:text` key is required or it won't work.
|
51
|
+
def localized_search_text_in(field)
|
52
|
+
options[:organization].available_locales.map do |l|
|
53
|
+
"#{field} ->> '#{l}' ILIKE :text"
|
54
|
+
end.join(" OR ")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<div class="field" >
|
2
|
+
<%= form.translated :text_field, :title, autofocus: true %>
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<div class="field" >
|
6
|
+
<%= form.translated :editor, :short_description %>
|
7
|
+
</div>
|
8
|
+
|
9
|
+
<div class="field" >
|
10
|
+
<%= form.translated :editor, :description %>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<div class="field" >
|
14
|
+
<%= form.number_field :budget %>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<div class="field" >
|
18
|
+
<%= form.collection_select :decidim_scope_id, current_organization.scopes, :id, :name, include_blank: true %>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<div class="field" >
|
22
|
+
<%= form.categories_select :decidim_category_id, current_participatory_process.categories, include_blank: true, disable_parents: false %>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<% if @form.proposals %>
|
26
|
+
<%= form.select :proposal_ids,
|
27
|
+
@form.proposals,
|
28
|
+
{},
|
29
|
+
{ multiple: true, class: "chosen-select" }
|
30
|
+
%>
|
31
|
+
<% end %>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<h2><%= t(".title") %></h2>
|
2
|
+
|
3
|
+
<div class="actions title">
|
4
|
+
<%= link_to t("actions.new", scope: "decidim.budgets", name: t("models.project.name", scope: "decidim.budgets.admin")), new_project_path, class: 'new' if can? :manage, current_feature %>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<table class="stack">
|
8
|
+
<thead>
|
9
|
+
<tr>
|
10
|
+
<th><%= t("models.project.fields.title", scope: "decidim.budgets") %></th>
|
11
|
+
<th class="actions"><%= t("actions.title", scope: "decidim.budgets") %></th>
|
12
|
+
</tr>
|
13
|
+
</thead>
|
14
|
+
<tbody>
|
15
|
+
<% projects.each do |project| %>
|
16
|
+
<tr data-id="<%= project.id %>">
|
17
|
+
<td>
|
18
|
+
<%= link_to translated_attribute(project.title), decidim_budgets.project_path(id: project, feature_id: current_feature, participatory_process_id: current_participatory_process), target: :blank %><br />
|
19
|
+
</td>
|
20
|
+
<td class="actions">
|
21
|
+
<%= link_to t("actions.edit", scope: "decidim.budgets"), edit_project_path(project) if can? :update, current_feature %>
|
22
|
+
<%= link_to t("actions.destroy", scope: "decidim.budgets"), project_path(project), method: :delete, class: "small alert button", data: { confirm: t("actions.confirm_destroy", scope: "decidim.budgets") } if can? :destroy, current_feature %>
|
23
|
+
</td>
|
24
|
+
</tr>
|
25
|
+
<% end %>
|
26
|
+
</tbody>
|
27
|
+
</table>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
var $orderTotalBudget = $('#order-total-budget');
|
2
|
+
var $orderSelectedProjects = $('#order-selected-projects');
|
3
|
+
var $orderProgress = $('#order-progress');
|
4
|
+
var $projectItem = $('#project-<%= project.id %>-item');
|
5
|
+
var $projectBudgetButton = $('#project-<%= project.id %>-budget-button');
|
6
|
+
var $budgetConfirm = $('#budget-confirm');
|
7
|
+
|
8
|
+
$orderTotalBudget.html('<%= j(render partial: 'decidim/budgets/projects/order_total_budget') %>');
|
9
|
+
$orderSelectedProjects.html('<%= j(render partial: 'decidim/budgets/projects/order_selected_projects') %>');
|
10
|
+
$orderProgress.html('<%= j(render partial: 'decidim/budgets/projects/order_progress') %>');
|
11
|
+
$budgetConfirm.html('<%= j(render partial: 'decidim/budgets/projects/budget_confirm') %>')
|
12
|
+
|
13
|
+
$(document).foundation();
|
14
|
+
|
15
|
+
if ($projectItem.length > 0) {
|
16
|
+
$projectItem.html('<%= j(render partial: 'decidim/budgets/projects/project', locals: { project: project }) %>');
|
17
|
+
}
|
18
|
+
|
19
|
+
if ($projectBudgetButton.length > 0) {
|
20
|
+
$projectBudgetButton.html('<%= j(render partial: 'decidim/budgets/projects/project_budget_button', locals: { project: project }) %>');
|
21
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<% if current_order.present? %>
|
2
|
+
<div class="reveal__header">
|
3
|
+
<h3 class="reveal__title"><%= t('.title') %></h3>
|
4
|
+
<button class="close-button" data-close aria-label="<%= t('.cancel') %>" type="button">
|
5
|
+
<span aria-hidden="true">×</span>
|
6
|
+
</button>
|
7
|
+
</div>
|
8
|
+
<p><%= t('.description') %></p>
|
9
|
+
<div class="card card--secondary">
|
10
|
+
<ul class="card__content">
|
11
|
+
<% current_order.projects.each do |project| %>
|
12
|
+
<li class="budget-summary__selected-item">
|
13
|
+
<%= link_to translated_attribute(project.title), project %>
|
14
|
+
<strong class="budget-summary__selected-number">
|
15
|
+
<%= budget_to_currency project.budget %>
|
16
|
+
</strong>
|
17
|
+
</li>
|
18
|
+
<% end %>
|
19
|
+
</ul>
|
20
|
+
</div>
|
21
|
+
<p class="text-center"><%= t('.are_you_sure') %></p>
|
22
|
+
<div class="row">
|
23
|
+
<div class="columns medium-8 medium-offset-2">
|
24
|
+
<%= button_to t('.confirm'), checkout_order_path, class: "button expanded" %>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
<div class="text-center">
|
28
|
+
<button class="link" data-close><%= t('.cancel') %></button>
|
29
|
+
</div>
|
30
|
+
<% end %>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div class="reveal" data-reveal id="budget-excess">
|
2
|
+
<div class="reveal__header">
|
3
|
+
<h3 class="reveal__title"><%= t('.title') %></h3>
|
4
|
+
<button class="close-button" data-close aria-label="<%= t('.close') %>" type="button">
|
5
|
+
<span aria-hidden="true">×</span>
|
6
|
+
</button>
|
7
|
+
</div>
|
8
|
+
<p><%= t('.description') %></p>
|
9
|
+
<div class="row">
|
10
|
+
<div class="columns medium-8 medium-offset-2">
|
11
|
+
<button data-close class="button expanded"><%= t('.ok') %></button>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
</div>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<div class="card budget-summary" data-progress-reference>
|
2
|
+
<div class="card__content">
|
3
|
+
<% if include_heading %>
|
4
|
+
<% if current_order_checked_out? %>
|
5
|
+
<h3 class="heading3"><%= t('.checked_out.title') %></h3>
|
6
|
+
<p>
|
7
|
+
<%= raw t('.checked_out.description', cancel_link: link_to(t('.cancel_order'), order_path, method: :delete, class: 'cancel-order', data: { confirm: t('.are_you_sure') })) %>
|
8
|
+
</p>
|
9
|
+
<% else %>
|
10
|
+
<h3 class="heading3"><%= t('.title') %></h3>
|
11
|
+
<p><%= t('.description', minimum_budget: budget_to_currency(current_order&.minimum_budget)) %></p>
|
12
|
+
<% end %>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<div class="budget-summary__total" data-total-budget="<%= feature_settings.total_budget %>">
|
16
|
+
<span class="mini-title"><%= t('total_budget') %>
|
17
|
+
<strong class="mini-title__strong mini-title__strong--highlight">
|
18
|
+
<%= budget_to_currency(feature_settings.total_budget) %>
|
19
|
+
</strong>
|
20
|
+
</span>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div id="order-progress">
|
24
|
+
<%= render partial: 'order_progress' %>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<div>
|
28
|
+
<span class="mini-title">
|
29
|
+
<%= t('.assigned') %>
|
30
|
+
<strong id="order-total-budget" class="mini-title__strong">
|
31
|
+
<%= render partial: 'order_total_budget' %>
|
32
|
+
</strong>
|
33
|
+
</span>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
|
37
|
+
<div id="order-selected-projects">
|
38
|
+
<%= render partial: 'order_selected_projects' %>
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
|
42
|
+
<%= render partial: "budget_excess" %>
|
43
|
+
|
44
|
+
<div class="reveal" data-reveal id="budget-confirm">
|
45
|
+
<%= render partial: "budget_confirm" %>
|
46
|
+
</div>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= t(".projects_count", count: projects.total_count) %>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<%= filter_form_for filter do |form| %>
|
2
|
+
<div class="filters__section">
|
3
|
+
<div class="filters__search">
|
4
|
+
<div class="input-group">
|
5
|
+
<%= form.search_field :search_text, label: false, class: "input-group-field", placeholder: t('.search') %>
|
6
|
+
<div class="input-group-button">
|
7
|
+
<button type="submit" class="button button--muted">
|
8
|
+
<%= icon "magnifying-glass", aria_label: t('.search') %>
|
9
|
+
</button>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<% if current_organization.scopes.any? %>
|
16
|
+
<%= form.collection_check_boxes :scope_id, current_organization.scopes, lambda {|scope| scope.id.to_s}, :name, legend_title: t('.scopes') %>
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<% if current_feature.categories.any? %>
|
20
|
+
<%= form.categories_select :category_id, current_feature.categories, legend_title: t('.category'), disable_parents: false, label: false, include_blank: true %>
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
<%= form.hidden_field :random_seed %>
|
24
|
+
<% end %>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<div class="card card--action card--list">
|
2
|
+
<% resources.each do |project| %>
|
3
|
+
<div class="card--list__item">
|
4
|
+
<%= icon "actions", class: "card--list__icon", remove_icon_class: true %>
|
5
|
+
<%= link_to decidim_resource_path(project), class: "card--list__text card__link card__link--block" do %>
|
6
|
+
<h5 class="card--list__heading">
|
7
|
+
<%= translated_attribute(project.title) %>
|
8
|
+
</h5>
|
9
|
+
<% end %>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
12
|
+
</div>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<div class="budget-summary__progressbox" data-current-budget="<%= current_order ? current_order.total_budget : 0 %>">
|
2
|
+
<div class="progress budget-progress" role="progressbar" tabindex="0" aria-valuenow="<%= current_order_budget_percent %>" aria-valuemin="<%= feature_settings.vote_threshold_percent %>" aria-valuetext="<%= current_order_budget_percent %>%" aria-valuemax="100">
|
3
|
+
<div class="progress-meter progress-meter--minimum" style="width: <%= 100 - current_order_budget_percent %>%"></div>
|
4
|
+
<!--Change width and text dynamically.-->
|
5
|
+
<div class="progress-meter budget-progress__meter" style="width: <%= current_order_budget_percent %>%">
|
6
|
+
<p class="progress-meter-text progress-meter-text--right"><%= current_order_budget_percent %>%</p>
|
7
|
+
</div>
|
8
|
+
</div>
|
9
|
+
<% unless current_order_checked_out? %>
|
10
|
+
<% if current_order_can_be_checked_out? %>
|
11
|
+
<button class="button small button--sc" data-toggle="budget-confirm"><%= t('.vote') %></button>
|
12
|
+
<% else %>
|
13
|
+
<button class="button small button--sc" data-toggle="budget-confirm" disabled><%= t('.vote') %></button>
|
14
|
+
<% end %>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<div class="progressbox-fixed-wrapper" data-progressbox-fixed>
|
19
|
+
<div class="budget-summary__progressbox budget-summary__progressbox--fixed">
|
20
|
+
<div class="progress budget-progress budget-progress--fixed" role="progressbar" tabindex="0" aria-valuenow="<%= current_order_budget_percent %>" aria-valuemin="<%= feature_settings.vote_threshold_percent %>" aria-valuetext="<%= current_order_budget_percent %>%" aria-valuemax="100">
|
21
|
+
<div class="progress-meter progress-meter--minimum" style="width: <%= 100 - current_order_budget_percent %>%"></div>
|
22
|
+
<div class="progress-meter budget-progress__meter" style="width: <%= current_order_budget_percent %>%">
|
23
|
+
<p class="progress-meter-text progress-meter-text--right"><%= current_order_budget_percent %>%</p>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
<% unless current_order_checked_out? %>
|
27
|
+
<% if current_order_can_be_checked_out? %>
|
28
|
+
<button class="button small button--sc" data-toggle="budget-confirm"><%= t('.vote') %></button>
|
29
|
+
<% else %>
|
30
|
+
<button class="button small button--sc" data-toggle="budget-confirm" disabled><%= t('.vote') %></button>
|
31
|
+
<% end %>
|
32
|
+
<% end %>
|
33
|
+
</div>
|
34
|
+
</div>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<% if current_order&.projects&.any? %>
|
2
|
+
<div class="card__content budget-summary__selected">
|
3
|
+
<button data-toggle="reveal-selected">
|
4
|
+
<strong><%= current_order.projects.size %> </strong><%= t('.selected_projects', count: current_order.projects.size) %>
|
5
|
+
<%= icon("caret-bottom", class:"icon--small", aria_label: t('.view'), role: "img") %>
|
6
|
+
</button>
|
7
|
+
<div id="reveal-selected" class="hide" data-toggler=".hide">
|
8
|
+
<ul class="budget-summary__selected-list">
|
9
|
+
<% current_order.projects.each do |project| %>
|
10
|
+
<li class="budget-summary__selected-item">
|
11
|
+
<%= link_to translated_attribute(project.title), project %>
|
12
|
+
<strong class="budget-summary__selected-number">
|
13
|
+
<%= budget_to_currency project.budget %>
|
14
|
+
</strong>
|
15
|
+
<% unless current_order_checked_out? %>
|
16
|
+
<%= button_to order_line_item_path(project_id: project), method: :delete, remote: true, data: { disable: true }, form: { style: "display: inline" } do %>
|
17
|
+
<%= icon("trash", aria_label: t('.remove'), role: "img") %>
|
18
|
+
<% end %>
|
19
|
+
<% end %>
|
20
|
+
</li>
|
21
|
+
<% end %>
|
22
|
+
</ul>
|
23
|
+
</div>
|
24
|
+
</div>
|
25
|
+
<% end %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= budget_to_currency current_order&.total_budget.to_f %>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<div class="card--list__text">
|
2
|
+
<div>
|
3
|
+
<%= link_to project, class: "card__link" do %>
|
4
|
+
<h5 class="card__title budget-list__title">
|
5
|
+
<%= translated_attribute project.title %>
|
6
|
+
</h5>
|
7
|
+
<% end %>
|
8
|
+
<%= render partial: "decidim/budgets/projects/tags", locals: { project: project } %>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
<% if current_user.present? %>
|
12
|
+
<% if current_order && current_order.projects.include?(project) %>
|
13
|
+
<div class="card--list__data budget-list__data budget-list__data--added">
|
14
|
+
<span class="card--list__data__number budget-list__number">
|
15
|
+
<%= budget_to_currency(project.budget) %>
|
16
|
+
</span>
|
17
|
+
<%= button_to order_line_item_path(project_id: project), method: :delete, remote: true, data: { disable: true, budget: project.budget }, disabled: current_order_checked_out?, class: "button tiny budget--list__action success" do %>
|
18
|
+
<%= icon("check", class: "icon--small", aria_label: t('.remove'), role: "img") %>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
21
|
+
<% else %>
|
22
|
+
<div class="card--list__data budget-list__data">
|
23
|
+
<span class="card--list__data__number budget-list__number">
|
24
|
+
<%= budget_to_currency(project.budget) %>
|
25
|
+
</span>
|
26
|
+
<%= button_to order_line_item_path(project_id: project), method: :post, remote: true, data: { disable: true, budget: project.budget, add: true }, disabled: current_order_checked_out?, class: "button tiny hollow budget--list__action" do %>
|
27
|
+
<%= icon("check", class: "icon--small", aria_label: t('.add'), role: "img") %>
|
28
|
+
<% end %>
|
29
|
+
</div>
|
30
|
+
<% end %>
|
31
|
+
<% else %>
|
32
|
+
<div class="card--list__data budget-list__data">
|
33
|
+
<span class="card--list__data__number budget-list__number">
|
34
|
+
<%= budget_to_currency(project.budget) %>
|
35
|
+
</span>
|
36
|
+
<a href="#" class="button tiny hollow budget--list__action disabled" data-toggle="loginModal">
|
37
|
+
<%= icon("check", class: "icon--small", aria_label: t('.add'), role: "img") %>
|
38
|
+
</a>
|
39
|
+
</div>
|
40
|
+
<% end %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<% if current_user.present? %>
|
2
|
+
<% if current_order && current_order.projects.include?(project) %>
|
3
|
+
<%= button_to t('.added'), order_line_item_path(project_id: project), method: :delete, remote: true, data: { disable: true, budget: project.budget }, disabled: current_order_checked_out?, class: "button expanded button--sc success budget--list__action" %>
|
4
|
+
<% else %>
|
5
|
+
<%= button_to t('.add'), order_line_item_path(project_id: project), method: :post, remote: true, data: { disable: true, budget: project.budget, add: true }, disabled: current_order_checked_out?, class: "button expanded button--sc budget--list__action" %>
|
6
|
+
<% end %>
|
7
|
+
<% else %>
|
8
|
+
<button class="button expanded button--sc disabled" data-toggle="loginModal"><%= t('.add') %></button>
|
9
|
+
<% end %>
|