@@ -36,12 +36,21 @@ body.admin {
|
|||||||
input[type="text"], textarea {
|
input[type="text"], textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-group input[type="text"] {
|
||||||
|
border-radius: 0;
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
|
|
||||||
th {
|
th {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
|
&.with-button {
|
||||||
|
line-height: $line-height*2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tr {
|
tr {
|
||||||
|
|||||||
15
app/controllers/admin/budget_groups_controller.rb
Normal file
15
app/controllers/admin/budget_groups_controller.rb
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
class Admin::BudgetGroupsController < Admin::BaseController
|
||||||
|
|
||||||
|
def create
|
||||||
|
@budget = Budget.find params[:budget_id]
|
||||||
|
@budget.groups.create(budget_group_params)
|
||||||
|
@groups = @budget.groups.includes(:headings)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def budget_group_params
|
||||||
|
params.require(:budget_group).permit(:name)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
16
app/controllers/admin/budget_headings_controller.rb
Normal file
16
app/controllers/admin/budget_headings_controller.rb
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
class Admin::BudgetHeadingsController < Admin::BaseController
|
||||||
|
|
||||||
|
def create
|
||||||
|
@budget = Budget.find params[:budget_id]
|
||||||
|
@budget_group = @budget.groups.find params[:budget_group_id]
|
||||||
|
@budget_group.headings.create(budget_heading_params)
|
||||||
|
@headings = @budget_group.headings
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def budget_heading_params
|
||||||
|
params.require(:budget_heading).permit(:name, :price, :geozone_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
34
app/controllers/admin/budgets_controller.rb
Normal file
34
app/controllers/admin/budgets_controller.rb
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
class Admin::BudgetsController < Admin::BaseController
|
||||||
|
|
||||||
|
has_filters %w{open finished}, only: :index
|
||||||
|
|
||||||
|
load_and_authorize_resource
|
||||||
|
|
||||||
|
def index
|
||||||
|
@budgets = Budget.send(@current_filter).order(created_at: :desc).page(params[:page])
|
||||||
|
end
|
||||||
|
|
||||||
|
def show
|
||||||
|
@budget = Budget.includes(groups: :headings).find(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@budget = Budget.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@budget = Budget.new(budget_params)
|
||||||
|
if @budget.save
|
||||||
|
redirect_to admin_budget_path(@budget), notice: t('admin.budgets.create.notice')
|
||||||
|
else
|
||||||
|
render :new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def budget_params
|
||||||
|
params.require(:budget).permit(:name, :description, :phase, :currency_symbol)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
11
app/helpers/budgets_helper.rb
Normal file
11
app/helpers/budgets_helper.rb
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
module BudgetsHelper
|
||||||
|
|
||||||
|
def budget_phases_select_options
|
||||||
|
Budget::VALID_PHASES.map { |ph| [ t("budget.phase.#{ph}"), ph ] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def budget_currency_symbol_select_options
|
||||||
|
Budget::CURRENCY_SYMBOLS.map { |cs| [ cs, cs ] }
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -8,4 +8,9 @@ module GeozonesHelper
|
|||||||
Geozone.all.order(name: :asc).collect { |g| [ g.name, g.id ] }
|
Geozone.all.order(name: :asc).collect { |g| [ g.name, g.id ] }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def geozone_name_from_id(g_id)
|
||||||
|
@all_geozones ||= Geozone.all.collect{ |g| [ g.id, g.name ] }.to_h
|
||||||
|
@all_geozones[g_id] || t("geozones.none")
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ module Abilities
|
|||||||
|
|
||||||
can [:read, :update, :valuate, :destroy, :summary], SpendingProposal
|
can [:read, :update, :valuate, :destroy, :summary], SpendingProposal
|
||||||
|
|
||||||
can [:create, :update], Budget
|
can [:index, :read, :new, :create, :update, :destroy], Budget
|
||||||
|
can [:read, :create, :update, :destroy], Budget::Group
|
||||||
|
can [:read, :create, :update, :destroy], Budget::Heading
|
||||||
can [:hide, :update], Budget::Investment
|
can [:hide, :update], Budget::Investment
|
||||||
can :valuate, Budget::Investment, budget: { valuating: true }
|
can :valuate, Budget::Investment, budget: { valuating: true }
|
||||||
can :create, Budget::ValuatorAssignment
|
can :create, Budget::ValuatorAssignment
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
class Budget < ActiveRecord::Base
|
class Budget < ActiveRecord::Base
|
||||||
|
|
||||||
VALID_PHASES = %W{on_hold accepting selecting balloting finished}
|
VALID_PHASES = %W{on_hold accepting selecting balloting finished}
|
||||||
|
CURRENCY_SYMBOLS = %W{€ $ £ ¥}
|
||||||
|
|
||||||
|
validates :name, presence: true
|
||||||
validates :phase, inclusion: { in: VALID_PHASES }
|
validates :phase, inclusion: { in: VALID_PHASES }
|
||||||
|
|
||||||
has_many :investments, dependent: :destroy
|
has_many :investments, dependent: :destroy
|
||||||
@@ -10,6 +12,9 @@ class Budget < ActiveRecord::Base
|
|||||||
has_many :headings, through: :groups
|
has_many :headings, through: :groups
|
||||||
has_many :investments, through: :headings
|
has_many :investments, through: :headings
|
||||||
|
|
||||||
|
scope :open, -> { where.not(phase: "finished") }
|
||||||
|
scope :finished, -> { where(phase: "finished") }
|
||||||
|
|
||||||
def on_hold?
|
def on_hold?
|
||||||
phase == "on_hold"
|
phase == "on_hold"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -35,6 +35,14 @@
|
|||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<%# if feature?(:budgets) %>
|
||||||
|
<li <%= "class=active" if controller_name == "budgets" %>>
|
||||||
|
<%= link_to admin_budgets_path do %>
|
||||||
|
<span class="icon-budget"></span><%= t("admin.menu.budgets") %>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<%# end %>
|
||||||
|
|
||||||
<li <%= "class=active" if controller_name == "banners" %>>
|
<li <%= "class=active" if controller_name == "banners" %>>
|
||||||
<%= link_to admin_banners_path do %>
|
<%= link_to admin_banners_path do %>
|
||||||
<span class="icon-eye"></span><%= t("admin.menu.banner") %>
|
<span class="icon-eye"></span><%= t("admin.menu.banner") %>
|
||||||
|
|||||||
2
app/views/admin/budget_groups/create.js.erb
Normal file
2
app/views/admin/budget_groups/create.js.erb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
$("#<%= dom_id(@budget) %>_groups").html('<%= j render("admin/budgets/groups", groups: @groups) %>');
|
||||||
|
App.Forms.toggleLink();
|
||||||
2
app/views/admin/budget_headings/create.js.erb
Normal file
2
app/views/admin/budget_headings/create.js.erb
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
$("#<%= dom_id(@budget_group) %>").html('<%= j render("admin/budgets/group", group: @budget_group, headings: @headings) %>');
|
||||||
|
App.Forms.toggleLink();
|
||||||
76
app/views/admin/budgets/_group.html.erb
Normal file
76
app/views/admin/budgets/_group.html.erb
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<div class="small-12 column">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="3" class="with-button">
|
||||||
|
<%= group.name %>
|
||||||
|
<%= link_to t("admin.budgets.form.add_heading"), "#", class: "button float-right js-toggle-link", data: { "toggle-selector" => "#group-#{group.id}-new-heading-form" } %>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<% if headings.blank? %>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
<div class="callout primary">
|
||||||
|
<%= t("admin.budgets.form.no_heading") %>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% else %>
|
||||||
|
<tr>
|
||||||
|
<th><%= t("admin.budgets.form.table_heading") %></th>
|
||||||
|
<th><%= t("admin.budgets.form.table_amount") %></th>
|
||||||
|
<th><%= t("admin.budgets.form.table_geozone") %></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<!-- new heading form -->
|
||||||
|
<tr id="group-<%= group.id %>-new-heading-form" style="display:none">
|
||||||
|
<td colspan="3">
|
||||||
|
<%= form_for [:admin, @budget, group, Budget::Heading.new], remote: true do |f| %>
|
||||||
|
<label><%= t("admin.budgets.form.heading") %></label>
|
||||||
|
<%= f.text_field :name,
|
||||||
|
label: false,
|
||||||
|
maxlength: 50,
|
||||||
|
placeholder: t("admin.budgets.form.heading") %>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="small-12 medium-6 column">
|
||||||
|
<label><%= t("admin.budgets.form.amount") %></label>
|
||||||
|
<%= f.text_field :price,
|
||||||
|
label: false,
|
||||||
|
maxlength: 8,
|
||||||
|
placeholder: t("admin.budgets.form.amount") %>
|
||||||
|
</div>
|
||||||
|
<div class="small-12 medium-6 column">
|
||||||
|
<label><%= t("admin.budgets.form.geozone") %></label>
|
||||||
|
<%= f.select :geozone_id, geozone_select_options, {include_blank: t("geozones.none"), label: false} %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= f.submit t("admin.budgets.form.save_heading"), class: "button success" %>
|
||||||
|
<% end %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- /. new heading form -->
|
||||||
|
<!-- headings list -->
|
||||||
|
<% headings.each do |heading| %>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<%= heading.name %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= heading.price %>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<%= geozone_name_from_id heading.geozone_id %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
<!-- /. headings list -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
34
app/views/admin/budgets/_groups.html.erb
Normal file
34
app/views/admin/budgets/_groups.html.erb
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<div class="small-12 column">
|
||||||
|
<h3 class="inline-block"><%= t('admin.budgets.show.groups') %></h3>
|
||||||
|
<% if groups.blank? %>
|
||||||
|
<div class="callout primary">
|
||||||
|
<%= t("admin.budgets.form.no_groups") %>
|
||||||
|
<strong><%= link_to t("admin.budgets.form.add_group"), "#",
|
||||||
|
class: "js-toggle-link",
|
||||||
|
data: { "toggle-selector" => "#new-group-form" } %></strong>
|
||||||
|
</div>
|
||||||
|
<% else %>
|
||||||
|
<%= link_to t("admin.budgets.form.add_group"), "#", class: "button float-right js-toggle-link", data: { "toggle-selector" => "#new-group-form" } %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= form_for [:admin, @budget, Budget::Group.new], html: {id: "new-group-form", style: "display:none"}, remote: true do |f| %>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-label">
|
||||||
|
<label><%= f.label :name,t("admin.budgets.form.group") %></label>
|
||||||
|
</span>
|
||||||
|
<%= f.text_field :name,
|
||||||
|
label: false,
|
||||||
|
maxlength: 50,
|
||||||
|
placeholder: t("admin.budgets.form.group") %>
|
||||||
|
<div class="input-group-button">
|
||||||
|
<%= f.submit t("admin.budgets.form.create_group"), class: "button success" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% groups.each do |group| %>
|
||||||
|
<div class="row" id="<%= dom_id(group) %>">
|
||||||
|
<%= render "admin/budgets/group", group: group, headings: group.headings %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
25
app/views/admin/budgets/index.html.erb
Normal file
25
app/views/admin/budgets/index.html.erb
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<h2 class="inline-block"><%= t("admin.budgets.index.title") %></h2>
|
||||||
|
|
||||||
|
<%= link_to t("admin.budgets.index.new_link"),
|
||||||
|
new_admin_budget_path,
|
||||||
|
class: "button float-right margin-right" %>
|
||||||
|
|
||||||
|
<%= render 'shared/filter_subnav', i18n_namespace: "admin.budgets.index" %>
|
||||||
|
|
||||||
|
|
||||||
|
<h3><%= page_entries_info @budgets %></h3>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<% @budgets.each do |budget| %>
|
||||||
|
<tr id="<%= dom_id(budget) %>" class="budget">
|
||||||
|
<td>
|
||||||
|
<%= link_to budget.name, admin_budget_path(budget) %>
|
||||||
|
</td>
|
||||||
|
<td class="small">
|
||||||
|
<%= t("budget.phase.#{budget.phase}") %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<%= paginate @budgets %>
|
||||||
29
app/views/admin/budgets/new.html.erb
Normal file
29
app/views/admin/budgets/new.html.erb
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="small-12 medium-9 column">
|
||||||
|
<h2><%= t("admin.budgets.new.title") %></h2>
|
||||||
|
|
||||||
|
<%= form_for [:admin, @budget] do |f| %>
|
||||||
|
|
||||||
|
<%= f.label :name, t("admin.budgets.new.name") %>
|
||||||
|
<%= f.text_field :name,
|
||||||
|
label: false,
|
||||||
|
maxlength: 30,
|
||||||
|
placeholder: t("admin.budgets.new.name") %>
|
||||||
|
|
||||||
|
<%= f.label :description, t("admin.budgets.new.description") %>
|
||||||
|
<%= f.text_area :description, rows: 3, maxlength: 6000, label: false, placeholder: t("admin.budgets.new.description") %>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="small-12 medium-9 column">
|
||||||
|
<%= f.label :description, t("admin.budgets.new.phase") %>
|
||||||
|
<%= f.select :phase, budget_phases_select_options, {label: false} %>
|
||||||
|
</div>
|
||||||
|
<div class="small-12 medium-3 column">
|
||||||
|
<%= f.label :description, t("admin.budgets.new.currency") %>
|
||||||
|
<%= f.select :currency_symbol, budget_currency_symbol_select_options, {label: false} %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<%= f.submit t("admin.budgets.new.create"), class: "button success" %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
16
app/views/admin/budgets/show.html.erb
Normal file
16
app/views/admin/budgets/show.html.erb
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="small-12 medium-9 column">
|
||||||
|
<h2><%= @budget.name %></h2>
|
||||||
|
|
||||||
|
<%= simple_format(text_with_links(@budget.description), {}, sanitize: false) %>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong><%= t('admin.budgets.show.phase') %>:</strong> <%= t("budget.phase.#{@budget.phase}") %> |
|
||||||
|
<strong><%= t('admin.budgets.show.currency') %>:</strong> <%= @budget.currency_symbol %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="<%= dom_id @budget %>_groups" class="row">
|
||||||
|
<%= render "groups", groups: @budget.groups %>
|
||||||
|
</div>
|
||||||
@@ -5,13 +5,13 @@
|
|||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if current_user.moderator? || current_user.administrator? %>
|
<% if current_user.administrator? || current_user.moderator? %>
|
||||||
<li>
|
<li>
|
||||||
<%= link_to t("layouts.header.moderation"), moderation_root_path %>
|
<%= link_to t("layouts.header.moderation"), moderation_root_path %>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if feature?(:spending_proposals) && (current_user.valuator? || current_user.administrator?) %>
|
<% if feature?(:spending_proposals) && (current_user.administrator? || current_user.valuator?) %>
|
||||||
<li>
|
<li>
|
||||||
<%= link_to t("layouts.header.valuation"), valuation_root_path %>
|
<%= link_to t("layouts.header.valuation"), valuation_root_path %>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ ignore_unused:
|
|||||||
- 'admin.banners.index.filters.*'
|
- 'admin.banners.index.filters.*'
|
||||||
- 'admin.debates.index.filter*'
|
- 'admin.debates.index.filter*'
|
||||||
- 'admin.proposals.index.filter*'
|
- 'admin.proposals.index.filter*'
|
||||||
|
- 'admin.budgets.index.filter*'
|
||||||
- 'admin.spending_proposals.index.filter*'
|
- 'admin.spending_proposals.index.filter*'
|
||||||
- 'admin.organizations.index.filter*'
|
- 'admin.organizations.index.filter*'
|
||||||
- 'admin.users.index.filter*'
|
- 'admin.users.index.filter*'
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ en:
|
|||||||
activity:
|
activity:
|
||||||
one: "activity"
|
one: "activity"
|
||||||
other: "activities"
|
other: "activities"
|
||||||
|
budget:
|
||||||
|
one: "Participatory budget"
|
||||||
|
other: "Participatory budgets"
|
||||||
comment:
|
comment:
|
||||||
one: "Comment"
|
one: "Comment"
|
||||||
other: "Comments"
|
other: "Comments"
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ es:
|
|||||||
activity:
|
activity:
|
||||||
one: "actividad"
|
one: "actividad"
|
||||||
other: "actividades"
|
other: "actividades"
|
||||||
|
budget:
|
||||||
|
one: "Presupuesto participativo"
|
||||||
|
other: "Presupuestos participativos"
|
||||||
comment:
|
comment:
|
||||||
one: "Comentario"
|
one: "Comentario"
|
||||||
other: "Comentarios"
|
other: "Comentarios"
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ en:
|
|||||||
editing: Edit banner
|
editing: Edit banner
|
||||||
form:
|
form:
|
||||||
submit_button: Save changes
|
submit_button: Save changes
|
||||||
errors:
|
|
||||||
form:
|
|
||||||
errors:
|
errors:
|
||||||
form:
|
form:
|
||||||
error:
|
error:
|
||||||
@@ -60,6 +58,40 @@ en:
|
|||||||
on_users: Users
|
on_users: Users
|
||||||
title: Moderator activity
|
title: Moderator activity
|
||||||
type: Type
|
type: Type
|
||||||
|
budgets:
|
||||||
|
index:
|
||||||
|
title: Participatory budgets
|
||||||
|
new_link: Create new
|
||||||
|
filters:
|
||||||
|
open: Open
|
||||||
|
finished: Finished
|
||||||
|
create:
|
||||||
|
notice: New participatory budget created successfully!
|
||||||
|
new:
|
||||||
|
title: New participatory budget
|
||||||
|
create: Create budget
|
||||||
|
name: Budget's name
|
||||||
|
description: Description
|
||||||
|
phase: Phase
|
||||||
|
currency: Currency
|
||||||
|
show:
|
||||||
|
phase: Current phase
|
||||||
|
currency: Currency
|
||||||
|
groups: Groups of budget headings
|
||||||
|
form:
|
||||||
|
group: Group's name
|
||||||
|
no_groups: No groups created yet. Each user will be able to vote in only one heading per group.
|
||||||
|
add_group: Add new group
|
||||||
|
create_group: Create group
|
||||||
|
heading: Heading's name
|
||||||
|
add_heading: Add heading
|
||||||
|
amount: Amount
|
||||||
|
save_heading: Save heading
|
||||||
|
no_heading: This group has no assigned heading.
|
||||||
|
geozone: Scope of operation
|
||||||
|
table_heading: Heading
|
||||||
|
table_amount: Amount
|
||||||
|
table_geozone: Scope of operation
|
||||||
comments:
|
comments:
|
||||||
index:
|
index:
|
||||||
filter: Filter
|
filter: Filter
|
||||||
@@ -96,6 +128,7 @@ en:
|
|||||||
activity: Moderator activity
|
activity: Moderator activity
|
||||||
admin: Admin menu
|
admin: Admin menu
|
||||||
banner: Manage banners
|
banner: Manage banners
|
||||||
|
budgets: Participatory budgets
|
||||||
debate_topics: Debate topics
|
debate_topics: Debate topics
|
||||||
hidden_comments: Hidden comments
|
hidden_comments: Hidden comments
|
||||||
hidden_debates: Hidden debates
|
hidden_debates: Hidden debates
|
||||||
|
|||||||
@@ -58,6 +58,40 @@ es:
|
|||||||
on_users: Usuarios
|
on_users: Usuarios
|
||||||
title: Actividad de los Moderadores
|
title: Actividad de los Moderadores
|
||||||
type: Tipo
|
type: Tipo
|
||||||
|
budgets:
|
||||||
|
index:
|
||||||
|
title: Presupuestos participativos
|
||||||
|
new_link: Crear nuevo
|
||||||
|
filters:
|
||||||
|
open: Abiertos
|
||||||
|
finished: Terminados
|
||||||
|
create:
|
||||||
|
notice: ¡Nueva campaña de presupuestos participativos creada con éxito!
|
||||||
|
new:
|
||||||
|
title: Nuevo presupuesto ciudadano
|
||||||
|
create: Crear presupuesto
|
||||||
|
name: Nombre del presupuesto
|
||||||
|
description: Descripción
|
||||||
|
phase: Fase
|
||||||
|
currency: Divisa
|
||||||
|
show:
|
||||||
|
phase: Fase actual
|
||||||
|
currency: Divisa
|
||||||
|
groups: Grupos de partidas presupuestarias
|
||||||
|
form:
|
||||||
|
group: Nombre del grupo
|
||||||
|
no_groups: No hay grupos creados todavía. Cada usuario podrá votar en una sola partida de cada grupo.
|
||||||
|
add_group: Añadir nuevo grupo
|
||||||
|
create_group: Crear grupo
|
||||||
|
heading: Nombre de la partida
|
||||||
|
add_heading: Añadir partida
|
||||||
|
amount: Cantidad
|
||||||
|
save_heading: Guardar partida
|
||||||
|
no_heading: Este grupo no tiene ninguna partida asignada.
|
||||||
|
geozone: Ámbito de actuación
|
||||||
|
table_heading: Partida
|
||||||
|
table_amount: Cantidad
|
||||||
|
table_geozone: Ámbito de actuación
|
||||||
comments:
|
comments:
|
||||||
index:
|
index:
|
||||||
filter: Filtro
|
filter: Filtro
|
||||||
@@ -94,6 +128,7 @@ es:
|
|||||||
activity: Actividad de moderadores
|
activity: Actividad de moderadores
|
||||||
admin: Menú de administración
|
admin: Menú de administración
|
||||||
banner: Gestionar banners
|
banner: Gestionar banners
|
||||||
|
budgets: Presupuestos participativos
|
||||||
debate_topics: Temas de debate
|
debate_topics: Temas de debate
|
||||||
hidden_comments: Comentarios ocultos
|
hidden_comments: Comentarios ocultos
|
||||||
hidden_debates: Debates ocultos
|
hidden_debates: Debates ocultos
|
||||||
|
|||||||
@@ -33,6 +33,13 @@ en:
|
|||||||
application:
|
application:
|
||||||
close: Close
|
close: Close
|
||||||
menu: Menu
|
menu: Menu
|
||||||
|
budget:
|
||||||
|
phase:
|
||||||
|
on_hold: On hold
|
||||||
|
accepting: Accepting proposals
|
||||||
|
selecting: Selecting
|
||||||
|
balloting: Balloting
|
||||||
|
finished: Finished
|
||||||
comments:
|
comments:
|
||||||
comment:
|
comment:
|
||||||
admin: Administrator
|
admin: Administrator
|
||||||
|
|||||||
@@ -33,6 +33,13 @@ es:
|
|||||||
application:
|
application:
|
||||||
close: Cerrar
|
close: Cerrar
|
||||||
menu: Menú
|
menu: Menú
|
||||||
|
budget:
|
||||||
|
phase:
|
||||||
|
on_hold: En pausa
|
||||||
|
accepting: Aceptando propuestas
|
||||||
|
selecting: Fase de selección
|
||||||
|
balloting: Fase de Votación
|
||||||
|
finished: Terminado
|
||||||
comments:
|
comments:
|
||||||
comment:
|
comment:
|
||||||
admin: Administrador
|
admin: Administrador
|
||||||
|
|||||||
@@ -153,6 +153,13 @@ Rails.application.routes.draw do
|
|||||||
get :summary, on: :collection
|
get :summary, on: :collection
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :budgets do
|
||||||
|
resources :budget_groups do
|
||||||
|
resources :budget_headings do
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
resources :banners, only: [:index, :new, :create, :edit, :update, :destroy] do
|
resources :banners, only: [:index, :new, :create, :edit, :update, :destroy] do
|
||||||
collection { get :search}
|
collection { get :search}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -210,10 +210,10 @@ ActiveRecord::Schema.define(version: 20160617172616) do
|
|||||||
t.string "visit_id"
|
t.string "visit_id"
|
||||||
t.datetime "hidden_at"
|
t.datetime "hidden_at"
|
||||||
t.integer "flags_count", default: 0
|
t.integer "flags_count", default: 0
|
||||||
t.datetime "ignored_flag_at"
|
|
||||||
t.integer "cached_votes_total", default: 0
|
t.integer "cached_votes_total", default: 0
|
||||||
t.integer "cached_votes_up", default: 0
|
t.integer "cached_votes_up", default: 0
|
||||||
t.integer "cached_votes_down", default: 0
|
t.integer "cached_votes_down", default: 0
|
||||||
|
t.datetime "ignored_flag_at"
|
||||||
t.integer "comments_count", default: 0
|
t.integer "comments_count", default: 0
|
||||||
t.datetime "confirmed_hide_at"
|
t.datetime "confirmed_hide_at"
|
||||||
t.integer "cached_anonymous_votes_total", default: 0
|
t.integer "cached_anonymous_votes_total", default: 0
|
||||||
|
|||||||
@@ -193,6 +193,10 @@ FactoryGirl.define do
|
|||||||
currency_symbol "€"
|
currency_symbol "€"
|
||||||
phase 'on_hold'
|
phase 'on_hold'
|
||||||
|
|
||||||
|
trait :accepting do
|
||||||
|
phase 'accepting'
|
||||||
|
end
|
||||||
|
|
||||||
trait :selecting do
|
trait :selecting do
|
||||||
phase 'selecting'
|
phase 'selecting'
|
||||||
end
|
end
|
||||||
|
|||||||
158
spec/features/admin/budgets_spec.rb
Normal file
158
spec/features/admin/budgets_spec.rb
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
feature 'Admin budgets' do
|
||||||
|
|
||||||
|
background do
|
||||||
|
admin = create(:administrator)
|
||||||
|
login_as(admin.user)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'Feature flag' do
|
||||||
|
|
||||||
|
xscenario 'Disabled with a feature flag' do
|
||||||
|
Setting['feature.budgets'] = nil
|
||||||
|
expect{ visit admin_budgets_path }.to raise_exception(FeatureFlags::FeatureDisabled)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'Index' do
|
||||||
|
|
||||||
|
scenario 'Displaying budgets' do
|
||||||
|
budget = create(:budget)
|
||||||
|
visit admin_budgets_path
|
||||||
|
|
||||||
|
expect(page).to have_content(budget.name)
|
||||||
|
expect(page).to have_content(I18n.t("budget.phase.#{budget.phase}"))
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'Filters by phase' do
|
||||||
|
budget1 = create(:budget)
|
||||||
|
budget2 = create(:budget, :accepting)
|
||||||
|
budget3 = create(:budget, :selecting)
|
||||||
|
budget4 = create(:budget, :balloting)
|
||||||
|
budget5 = create(:budget, :finished)
|
||||||
|
|
||||||
|
visit admin_budgets_path
|
||||||
|
expect(page).to have_content(budget1.name)
|
||||||
|
expect(page).to have_content(budget2.name)
|
||||||
|
expect(page).to have_content(budget3.name)
|
||||||
|
expect(page).to have_content(budget4.name)
|
||||||
|
expect(page).to_not have_content(budget5.name)
|
||||||
|
|
||||||
|
click_link 'Finished'
|
||||||
|
expect(page).to_not have_content(budget1.name)
|
||||||
|
expect(page).to_not have_content(budget2.name)
|
||||||
|
expect(page).to_not have_content(budget3.name)
|
||||||
|
expect(page).to_not have_content(budget4.name)
|
||||||
|
expect(page).to have_content(budget5.name)
|
||||||
|
|
||||||
|
click_link 'Open'
|
||||||
|
expect(page).to have_content(budget1.name)
|
||||||
|
expect(page).to have_content(budget2.name)
|
||||||
|
expect(page).to have_content(budget3.name)
|
||||||
|
expect(page).to have_content(budget4.name)
|
||||||
|
expect(page).to_not have_content(budget5.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'Current filter is properly highlighted' do
|
||||||
|
filters_links = {'open' => 'Open', 'finished' => 'Finished'}
|
||||||
|
|
||||||
|
visit admin_budgets_path
|
||||||
|
|
||||||
|
expect(page).to_not have_link(filters_links.values.first)
|
||||||
|
filters_links.keys.drop(1).each { |filter| expect(page).to have_link(filters_links[filter]) }
|
||||||
|
|
||||||
|
filters_links.each_pair do |current_filter, link|
|
||||||
|
visit admin_budgets_path(filter: current_filter)
|
||||||
|
|
||||||
|
expect(page).to_not have_link(link)
|
||||||
|
|
||||||
|
(filters_links.keys - [current_filter]).each do |filter|
|
||||||
|
expect(page).to have_link(filters_links[filter])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'New' do
|
||||||
|
|
||||||
|
scenario 'Create budget' do
|
||||||
|
visit admin_budgets_path
|
||||||
|
click_link 'Create new'
|
||||||
|
|
||||||
|
fill_in 'budget_name', with: 'M30 - Summer campaign'
|
||||||
|
fill_in 'budget_description', with: 'Budgeting for summer 2017 maintenance and improvements of the road M-30'
|
||||||
|
select 'Accepting proposals', from: 'budget[phase]'
|
||||||
|
|
||||||
|
click_button 'Create budget'
|
||||||
|
|
||||||
|
expect(page).to have_content 'New participatory budget created successfully!'
|
||||||
|
expect(page).to have_content 'M30 - Summer campaign'
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'Name is mandatory' do
|
||||||
|
visit new_admin_budget_path
|
||||||
|
click_button 'Create budget'
|
||||||
|
|
||||||
|
expect(page).to_not have_content 'New participatory budget created successfully!'
|
||||||
|
expect(page).to have_css("label.error", text: "Budget's name")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'Manage groups and headings' do
|
||||||
|
|
||||||
|
scenario 'Create group', :js do
|
||||||
|
create(:budget, name: 'Yearly participatory budget')
|
||||||
|
|
||||||
|
visit admin_budgets_path
|
||||||
|
click_link 'Yearly participatory budget'
|
||||||
|
|
||||||
|
expect(page).to have_content 'No groups created yet.'
|
||||||
|
|
||||||
|
click_link 'Add new group'
|
||||||
|
|
||||||
|
fill_in 'budget_group_name', with: 'General improvments'
|
||||||
|
click_button 'Create group'
|
||||||
|
|
||||||
|
expect(page).to have_content 'Yearly participatory budget'
|
||||||
|
expect(page).to_not have_content 'No groups created yet.'
|
||||||
|
|
||||||
|
visit admin_budgets_path
|
||||||
|
click_link 'Yearly participatory budget'
|
||||||
|
|
||||||
|
expect(page).to have_content 'Yearly participatory budget'
|
||||||
|
expect(page).to_not have_content 'No groups created yet.'
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'Create heading', :js do
|
||||||
|
budget = create(:budget, name: 'Yearly participatory budget')
|
||||||
|
group = create(:budget_group, budget: budget, name: 'Districts improvments')
|
||||||
|
|
||||||
|
visit admin_budget_path(budget)
|
||||||
|
|
||||||
|
within("#budget_group_#{group.id}") do
|
||||||
|
expect(page).to have_content 'This group has no assigned heading.'
|
||||||
|
click_link 'Add heading'
|
||||||
|
|
||||||
|
fill_in 'budget_heading_name', with: 'District 9 reconstruction'
|
||||||
|
fill_in 'budget_heading_price', with: '6785'
|
||||||
|
click_button 'Save heading'
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(page).to_not have_content 'This group has no assigned heading.'
|
||||||
|
|
||||||
|
visit admin_budget_path(budget)
|
||||||
|
within("#budget_group_#{group.id}") do
|
||||||
|
expect(page).to_not have_content 'This group has no assigned heading.'
|
||||||
|
|
||||||
|
expect(page).to have_content 'District 9 reconstruction'
|
||||||
|
expect(page).to have_content '6785'
|
||||||
|
expect(page).to have_content 'All city'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -31,4 +31,19 @@ describe GeozonesHelper do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#geozone_name_from_id" do
|
||||||
|
|
||||||
|
it "returns geozone name if present" do
|
||||||
|
g1 = create(:geozone, name: "AAA")
|
||||||
|
g2 = create(:geozone, name: "BBB")
|
||||||
|
|
||||||
|
expect(geozone_name_from_id(g1.id)).to eq "AAA"
|
||||||
|
expect(geozone_name_from_id(g2.id)).to eq "BBB"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns default string for no geozone if geozone is blank" do
|
||||||
|
expect(geozone_name_from_id(nil)).to eq "All city"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user