decidim-budgets 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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 %>
|