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.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +23 -0
  3. data/Rakefile +2 -0
  4. data/app/assets/config/decidim_budgets_manifest.js +1 -0
  5. data/app/assets/images/decidim/budgets/icon.svg +4 -0
  6. data/app/assets/javascripts/decidim/budgets/projects.js.es6 +28 -0
  7. data/app/assets/stylesheets/decidim/budgets/_budgets.scss +1 -0
  8. data/app/assets/stylesheets/decidim/budgets/budget/_budget-list.scss +32 -0
  9. data/app/assets/stylesheets/decidim/budgets/budget/_budget-meter.scss +78 -0
  10. data/app/assets/stylesheets/decidim/budgets/budget/_progress.scss +17 -0
  11. data/app/commands/decidim/budgets/add_line_item.rb +43 -0
  12. data/app/commands/decidim/budgets/admin/create_project.rb +52 -0
  13. data/app/commands/decidim/budgets/admin/update_project.rb +56 -0
  14. data/app/commands/decidim/budgets/cancel_order.rb +36 -0
  15. data/app/commands/decidim/budgets/checkout.rb +34 -0
  16. data/app/commands/decidim/budgets/remove_line_item.rb +35 -0
  17. data/app/controllers/concerns/decidim/budgets/needs_current_order.rb +27 -0
  18. data/app/controllers/decidim/budgets/admin/application_controller.rb +14 -0
  19. data/app/controllers/decidim/budgets/admin/projects_controller.rb +69 -0
  20. data/app/controllers/decidim/budgets/application_controller.rb +13 -0
  21. data/app/controllers/decidim/budgets/line_items_controller.rb +43 -0
  22. data/app/controllers/decidim/budgets/orders_controller.rb +38 -0
  23. data/app/controllers/decidim/budgets/projects_controller.rb +44 -0
  24. data/app/forms/decidim/budgets/admin/project_form.rb +44 -0
  25. data/app/helpers/decidim/budgets/application_helper.rb +12 -0
  26. data/app/helpers/decidim/budgets/projects_helper.rb +29 -0
  27. data/app/models/decidim/budgets/application_record.rb +9 -0
  28. data/app/models/decidim/budgets/line_item.rb +20 -0
  29. data/app/models/decidim/budgets/order.rb +52 -0
  30. data/app/models/decidim/budgets/project.rb +15 -0
  31. data/app/services/decidim/budgets/project_search.rb +58 -0
  32. data/app/views/decidim/budgets/admin/projects/_form.html.erb +31 -0
  33. data/app/views/decidim/budgets/admin/projects/edit.html.erb +9 -0
  34. data/app/views/decidim/budgets/admin/projects/index.html.erb +27 -0
  35. data/app/views/decidim/budgets/admin/projects/new.html.erb +9 -0
  36. data/app/views/decidim/budgets/line_items/update_budget.js.erb +21 -0
  37. data/app/views/decidim/budgets/projects/_budget_confirm.html.erb +30 -0
  38. data/app/views/decidim/budgets/projects/_budget_excess.html.erb +14 -0
  39. data/app/views/decidim/budgets/projects/_budget_summary.html.erb +46 -0
  40. data/app/views/decidim/budgets/projects/_count.html.erb +1 -0
  41. data/app/views/decidim/budgets/projects/_filters.html.erb +24 -0
  42. data/app/views/decidim/budgets/projects/_linked_projects.html.erb +12 -0
  43. data/app/views/decidim/budgets/projects/_order_progress.html.erb +34 -0
  44. data/app/views/decidim/budgets/projects/_order_selected_projects.html.erb +25 -0
  45. data/app/views/decidim/budgets/projects/_order_total_budget.html.erb +1 -0
  46. data/app/views/decidim/budgets/projects/_project.html.erb +40 -0
  47. data/app/views/decidim/budgets/projects/_project_budget_button.html.erb +9 -0
  48. data/app/views/decidim/budgets/projects/_projects.html.erb +8 -0
  49. data/app/views/decidim/budgets/projects/_tags.html.erb +10 -0
  50. data/app/views/decidim/budgets/projects/index.html.erb +26 -0
  51. data/app/views/decidim/budgets/projects/index.js.erb +2 -0
  52. data/app/views/decidim/budgets/projects/show.html.erb +48 -0
  53. data/config/i18n-tasks.yml +4 -0
  54. data/config/locales/ca.yml +84 -0
  55. data/config/locales/en.yml +95 -0
  56. data/config/locales/es.yml +84 -0
  57. data/db/migrate/20170127114122_create_projects.rb +15 -0
  58. data/db/migrate/20170130095615_create_orders.rb +13 -0
  59. data/db/migrate/20170130101825_create_line_items.rb +10 -0
  60. data/lib/decidim/budgets/admin.rb +9 -0
  61. data/lib/decidim/budgets/admin_engine.rb +22 -0
  62. data/lib/decidim/budgets/feature.rb +59 -0
  63. data/lib/decidim/budgets/list_engine.rb +30 -0
  64. data/lib/decidim/budgets/test/factories.rb +43 -0
  65. data/lib/decidim/budgets.rb +11 -0
  66. 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,9 @@
1
+ # frozen_string_literal: true
2
+ module Decidim
3
+ module Budgets
4
+ # Abstract class from which all models in this engine inherit.
5
+ class ApplicationRecord < ActiveRecord::Base
6
+ self.abstract_class = true
7
+ end
8
+ end
9
+ 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,9 @@
1
+ <h3><%= t ".title" %></h3>
2
+
3
+ <%= form_for(@form) do |f| %>
4
+ <%= render partial: 'form', object: f %>
5
+
6
+ <div class="actions">
7
+ <%= f.submit t(".update") %>
8
+ </div>
9
+ <% 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,9 @@
1
+ <h3><%= t ".title" %></h3>
2
+
3
+ <%= form_for(@form) do |f| %>
4
+ <%= render partial: 'form', object: f %>
5
+
6
+ <div class="actions">
7
+ <%= f.submit t(".create") %>
8
+ </div>
9
+ <% end %>
@@ -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">&times;</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">&times;</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 %>