Merge pull request #4533 from consul/budget-multiple-and-single

Simplify creating single heading budgets
This commit is contained in:
Javi Martín
2021-06-11 10:58:49 +02:00
committed by GitHub
56 changed files with 661 additions and 96 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -665,7 +665,8 @@ code {
display: flex;
flex-wrap: wrap;
a {
a,
button {
@include regular-button;
margin-left: auto;
}

View File

@@ -0,0 +1,77 @@
.admin .heading-mode {
border-radius: rem-calc(12);
color: $admin-text;
display: flex;
flex-wrap: wrap;
max-width: $global-width * 0.6;
width: 100%;
section {
@include grid-column-gutter;
display: flex;
flex-flow: column;
margin-bottom: $line-height;
width: 100%;
@include breakpoint(medium) {
width: 50%;
&:last-of-type {
border-left: $admin-border;
}
}
&.single-heading-option h2::after {
content: image-url("budgets/budget_single_heading_icon.png");
}
&.multiple-heading-option h2::after {
content: image-url("budgets/budget_multiple_heading_icon.png");
}
h2::after {
display: block;
text-align: center;
}
}
h1 {
margin-bottom: $line-height * 2;
margin-top: $line-height;
text-transform: uppercase;
width: 100%;
}
h1,
h2 {
font-size: rem-calc(24);
text-align: center;
}
h2 {
font-weight: 500;
padding-top: $line-height;
}
p {
font-size: $small-font-size;
font-style: italic;
flex: 1;
}
a {
@include hollow-button;
margin: 0 auto;
max-width: max-content;
}
.close-button {
border: 3px solid $admin-text;
border-radius: 50%;
color: $admin-text;
font-size: rem-calc(23);
font-weight: bold;
height: rem-calc(30);
width: rem-calc(30);
}
}

View File

@@ -4,6 +4,8 @@
<%= render "shared/errors", resource: phase %>
<%= render Admin::BudgetsWizard::ModelFieldComponent.new %>
<fieldset>
<legend aria-describedby="phase_duration_description">
<%= t("admin.budget_phases.edit.duration") %>

View File

@@ -31,6 +31,7 @@
<td>
<%= render Admin::TableActionsComponent.new(phase,
actions: [:edit],
edit_path: edit_path(phase),
edit_text: t("admin.budgets.edit.edit_phase")
) %>
</td>

View File

@@ -37,4 +37,10 @@ class Admin::BudgetPhases::PhasesComponent < ApplicationComponent
tag.span t("shared.no"), class: "budget-phase-disabled"
end
end
def edit_path(phase)
if helpers.respond_to?(:single_heading?) && helpers.single_heading?
edit_admin_budgets_wizard_budget_budget_phase_path(budget, phase, helpers.url_params)
end
end
end

View File

@@ -24,6 +24,6 @@ class Admin::Budgets::DurationComponent < ApplicationComponent
end
def formatted_date(time)
time_tag(time, format: :datetime)
time_tag(time, format: :short_datetime)
end
end

View File

@@ -1,4 +1,6 @@
<%= translatable_form_for [namespace, budget], html: { class: "budgets-form" } do |f| %>
<%= render Admin::BudgetsWizard::ModelFieldComponent.new %>
<fieldset>
<legend><%= t("admin.budgets.edit.info.budget_settings") %></legend>
<%= render "shared/globalize_locales", resource: budget %>

View File

@@ -0,0 +1,17 @@
<section class="reveal heading-mode" id="heading_mode" data-reveal>
<h1><%= t("#{i18n_namespace}.title") %></h1>
<% modes.each do |mode| %>
<section class="<%= mode %>-heading-option">
<h2><%= sanitize(t("#{i18n_namespace}.#{mode}.title")) %></h2>
<p><%= t("#{i18n_namespace}.#{mode}.description") %></p>
<%= link_to t("#{i18n_namespace}.#{mode}.link"),
new_admin_budgets_wizard_budget_path(mode: mode), data: { close: "" } %>
</section>
<% end %>
<button class="close-button" aria-label="<%= t("admin.shared.close_modal") %>"
type="button" data-close>
&times;
</button>
</section>

View File

@@ -0,0 +1,11 @@
class Admin::Budgets::HeadingModeComponent < ApplicationComponent
private
def i18n_namespace
"admin.budgets_wizard.heading_mode"
end
def modes
%w[single multiple]
end
end

View File

@@ -5,9 +5,17 @@ class Admin::Budgets::HelpComponent < ApplicationComponent
@i18n_namespace = i18n_namespace
end
def budget_mode
(helpers.budget_mode if helpers.respond_to?(:budget_mode)) || "multiple"
end
private
def text
t("admin.#{i18n_namespace}.index.help")
if t("admin.budgets.help.#{i18n_namespace}").is_a?(Hash)
t("admin.budgets.help.#{i18n_namespace}.#{budget_mode}")
else
t("admin.budgets.help.#{i18n_namespace}")
end
end
end

View File

@@ -1,7 +1,8 @@
<%= header do %>
<%= link_to t("admin.budgets.index.new_link"), new_admin_budgets_wizard_budget_path %>
<button data-open="heading_mode"><%= t("admin.budgets.index.new_link") %></button>
<% end %>
<%= render Admin::Budgets::HeadingModeComponent.new %>
<%= render Admin::Budgets::HelpComponent.new("budgets") %>
<%= render "shared/filter_subnav", i18n_namespace: "admin.budgets.index" %>
@@ -13,6 +14,7 @@
<tr>
<th><%= t("admin.budgets.index.table_name") %></th>
<th><%= t("admin.budgets.index.table_phase") %></th>
<th><%= t("admin.budgets.index.table_budget_type") %></th>
<th><%= t("admin.budgets.index.table_duration") %></th>
<th><%= t("admin.actions.actions") %></th>
</tr>
@@ -28,6 +30,7 @@
<%= budget.current_phase.name %>
<small><%= phase_progress_text(budget) %></small>
</td>
<td><%= type(budget) %></td>
<td>
<%= dates(budget) %>
<%= duration(budget) %>

View File

@@ -26,6 +26,16 @@ class Admin::Budgets::IndexComponent < ApplicationComponent
budget.phases.enabled.order(:id).pluck(:kind).index(budget.phase) || -1
end
def type(budget)
if budget.single_heading?
t("admin.budgets.index.type_single")
elsif budget.headings.blank?
t("admin.budgets.index.type_pending")
else
t("admin.budgets.index.type_multiple")
end
end
def dates(budget)
Admin::Budgets::DurationComponent.new(budget).dates
end

View File

@@ -0,0 +1,7 @@
class Admin::BudgetsWizard::BaseComponent < ApplicationComponent
delegate :single_heading?, :url_params, to: :helpers
def budget_mode
helpers.budget_mode || "multiple"
end
end

View File

@@ -2,5 +2,6 @@
<%= header %>
<%= render Admin::Budgets::HelpComponent.new("budgets") %>
<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("budget") %>
<%= render Admin::Budgets::FormComponent.new(budget, wizard: true) %>

View File

@@ -1,12 +1,17 @@
class Admin::BudgetsWizard::Budgets::NewComponent < ApplicationComponent
include Header
attr_reader :budget
delegate :single_heading?, to: :helpers
def initialize(budget)
@budget = budget
end
def title
t("admin.budgets.new.title")
if single_heading?
t("admin.budgets.new.title_single")
else
t("admin.budgets.new.title_multiple")
end
end
end

View File

@@ -1,19 +1,23 @@
<div class="budget-creation-step">
<button type="button" class="add" aria-expanded="<%= show_form? %>">
<%= t("admin.#{i18n_namespace_with_budget}.index.new_button") %>
</button>
<%= content %>
<button type="button" class="cancel delete"><%= t("links.form.cancel_button") %></button>
<% if next_step_path %>
<%= link_to t("admin.budgets_wizard.#{i18n_namespace}.continue"),
next_step_path,
class: "next-step" %>
<% if single_heading? %>
<%= content %>
<% else %>
<p class="next-step">
<%= t("admin.budgets_wizard.#{i18n_namespace}.continue") %>
</p>
<button type="button" class="add" aria-expanded="<%= show_form? %>">
<%= t("admin.#{i18n_namespace_with_budget}.index.new_button") %>
</button>
<%= content %>
<button type="button" class="cancel delete"><%= t("links.form.cancel_button") %></button>
<% if next_step_path %>
<%= link_to t("admin.budgets_wizard.#{i18n_namespace}.continue"),
next_step_path,
class: "next-step" %>
<% else %>
<p class="next-step">
<%= t("admin.budgets_wizard.#{i18n_namespace}.continue") %>
</p>
<% end %>
<% end %>
</div>

View File

@@ -1,4 +1,4 @@
class Admin::BudgetsWizard::CreationStepComponent < ApplicationComponent
class Admin::BudgetsWizard::CreationStepComponent < Admin::BudgetsWizard::BaseComponent
attr_reader :record, :next_step_path
def initialize(record, next_step_path)

View File

@@ -13,7 +13,11 @@ class Admin::BudgetsWizard::Groups::CreationStepComponent < ApplicationComponent
end
def form_path
admin_budgets_wizard_budget_groups_path(budget)
if group.persisted?
admin_budgets_wizard_budget_group_path(budget, group)
else
admin_budgets_wizard_budget_groups_path(budget)
end
end
def next_step_path

View File

@@ -2,8 +2,10 @@
<%= header %>
<%= render Admin::Budgets::HelpComponent.new("budget_groups") %>
<%= render Admin::Budgets::HelpComponent.new("groups") %>
<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("groups") %>
<%= render Admin::BudgetGroups::GroupsComponent.new(groups) %>
<% unless single_heading? %>
<%= render Admin::BudgetGroups::GroupsComponent.new(groups) %>
<% end %>
<%= render Admin::BudgetsWizard::Groups::CreationStepComponent.new(new_group, groups.first) %>

View File

@@ -1,4 +1,4 @@
class Admin::BudgetsWizard::Groups::IndexComponent < ApplicationComponent
class Admin::BudgetsWizard::Groups::IndexComponent < Admin::BudgetsWizard::BaseComponent
include Header
attr_reader :groups, :new_group

View File

@@ -12,7 +12,11 @@ class Admin::BudgetsWizard::Headings::CreationStepComponent < ApplicationCompone
end
def form_path
admin_budgets_wizard_budget_group_headings_path(heading.group.budget, heading.group)
if heading.persisted?
admin_budgets_wizard_budget_group_heading_path(heading.group.budget, heading.group, heading)
else
admin_budgets_wizard_budget_group_headings_path(heading.group.budget, heading.group)
end
end
def next_step_path

View File

@@ -1,10 +1,12 @@
<%= back_link_to admin_budgets_wizard_budget_groups_path(budget), t("admin.budget_headings.index.back") %>
<%= back_link_to admin_budgets_wizard_budget_groups_path(budget, url_params), back_link_text %>
<%= header %>
<%= render Admin::Budgets::HelpComponent.new("budget_headings") %>
<%= render Admin::Budgets::HelpComponent.new("headings") %>
<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("headings") %>
<%= render Admin::BudgetsWizard::Headings::GroupSwitcherComponent.new(group) %>
<%= render Admin::BudgetHeadings::HeadingsComponent.new(headings) %>
<% unless single_heading? %>
<%= render Admin::BudgetsWizard::Headings::GroupSwitcherComponent.new(group) %>
<%= render Admin::BudgetHeadings::HeadingsComponent.new(headings) %>
<% end %>
<%= render Admin::BudgetsWizard::Headings::CreationStepComponent.new(new_heading) %>

View File

@@ -1,4 +1,4 @@
class Admin::BudgetsWizard::Headings::IndexComponent < ApplicationComponent
class Admin::BudgetsWizard::Headings::IndexComponent < Admin::BudgetsWizard::BaseComponent
include Header
attr_reader :headings, :new_heading
@@ -18,4 +18,12 @@ class Admin::BudgetsWizard::Headings::IndexComponent < ApplicationComponent
def title
t("admin.budget_headings.index.title", budget: budget.name, group: group.name)
end
def back_link_text
if single_heading?
t("admin.budgets_wizard.headings.single.back")
else
t("admin.budget_headings.index.back")
end
end
end

View File

@@ -0,0 +1 @@
<%= hidden_field_tag(:mode, helpers.budget_mode) if helpers.respond_to?(:budget_mode) %>

View File

@@ -0,0 +1,2 @@
class Admin::BudgetsWizard::ModelFieldComponent < ApplicationComponent
end

View File

@@ -1,8 +1,8 @@
<%= back_link_to back_link_path, t("admin.budgets_wizard.phases.back") %>
<%= back_link_to back_link_path, back_link_text %>
<%= header %>
<%= render Admin::Budgets::HelpComponent.new("budget_phases") %>
<%= render Admin::Budgets::HelpComponent.new("phases") %>
<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("phases") %>
<%= form_for budget, url: update_all_admin_budgets_wizard_budget_budget_phases_path(budget) do |f| %>

View File

@@ -1,4 +1,4 @@
class Admin::BudgetsWizard::Phases::IndexComponent < ApplicationComponent
class Admin::BudgetsWizard::Phases::IndexComponent < Admin::BudgetsWizard::BaseComponent
include Header
attr_reader :budget
@@ -13,6 +13,10 @@ class Admin::BudgetsWizard::Phases::IndexComponent < ApplicationComponent
private
def back_link_path
admin_budgets_wizard_budget_group_headings_path(budget, budget.groups.first)
admin_budgets_wizard_budget_group_headings_path(budget, budget.groups.first, url_params)
end
def back_link_text
t("admin.budgets_wizard.phases.#{budget_mode}.back")
end
end

View File

@@ -0,0 +1,17 @@
class Admin::BudgetsWizard::BaseController < Admin::BaseController
helper_method :budget_mode, :single_heading?, :url_params
private
def budget_mode
params[:mode]
end
def single_heading?
budget_mode == "single"
end
def url_params
budget_mode.present? ? { mode: budget_mode } : {}
end
end

View File

@@ -1,4 +1,4 @@
class Admin::BudgetsWizard::BudgetsController < Admin::BaseController
class Admin::BudgetsWizard::BudgetsController < Admin::BudgetsWizard::BaseController
include Translatable
include ImageAttributes
include FeatureFlags
@@ -44,6 +44,6 @@ class Admin::BudgetsWizard::BudgetsController < Admin::BaseController
end
def groups_index
admin_budgets_wizard_budget_groups_path(@budget)
admin_budgets_wizard_budget_groups_path(@budget, url_params)
end
end

View File

@@ -1,16 +1,24 @@
class Admin::BudgetsWizard::GroupsController < Admin::BaseController
class Admin::BudgetsWizard::GroupsController < Admin::BudgetsWizard::BaseController
include Admin::BudgetGroupsActions
before_action :load_groups, only: [:index, :create]
def index
@group = @budget.groups.new
if single_heading?
@group = @budget.groups.first_or_initialize("name_#{I18n.locale}" => @budget.name)
else
@group = @budget.groups.new
end
end
private
def groups_index
admin_budgets_wizard_budget_groups_path(@budget)
if single_heading?
admin_budgets_wizard_budget_group_headings_path(@budget, @group, url_params)
else
admin_budgets_wizard_budget_groups_path(@budget, url_params)
end
end
def new_action

View File

@@ -1,16 +1,24 @@
class Admin::BudgetsWizard::HeadingsController < Admin::BaseController
class Admin::BudgetsWizard::HeadingsController < Admin::BudgetsWizard::BaseController
include Admin::BudgetHeadingsActions
before_action :load_headings, only: [:index, :create]
def index
@heading = @group.headings.new
if single_heading?
@heading = @group.headings.first_or_initialize
else
@heading = @group.headings.new
end
end
private
def headings_index
admin_budgets_wizard_budget_group_headings_path(@budget, @group)
if single_heading?
admin_budgets_wizard_budget_budget_phases_path(@budget, url_params)
else
admin_budgets_wizard_budget_group_headings_path(@budget, @group, url_params)
end
end
def new_action

View File

@@ -1,4 +1,4 @@
class Admin::BudgetsWizard::PhasesController < Admin::BaseController
class Admin::BudgetsWizard::PhasesController < Admin::BudgetsWizard::BaseController
include Admin::BudgetPhasesActions
def index
@@ -13,7 +13,7 @@ class Admin::BudgetsWizard::PhasesController < Admin::BaseController
private
def phases_index
admin_budgets_wizard_budget_budget_phases_path(@phase.budget)
admin_budgets_wizard_budget_budget_phases_path(@phase.budget, url_params)
end
def phases_params

View File

@@ -162,6 +162,10 @@ class Budget < ApplicationRecord
current_phase&.balloting_or_later?
end
def single_heading?
groups.one? && headings.one?
end
def heading_price(heading)
heading_ids.include?(heading.id) ? heading.price : -1
end

View File

@@ -3,6 +3,8 @@
<%= render "shared/errors", resource: group %>
<%= render Admin::BudgetsWizard::ModelFieldComponent.new %>
<%= f.translatable_fields do |translations_form| %>
<div class="small-12 medium-6 column end">
<%= translations_form.text_field :name, maxlength: 50 %>
@@ -18,6 +20,10 @@
<% end %>
<div class="clear">
<%= f.submit t("admin.budget_groups.form.#{action}"), class: "button hollow" %>
<% if respond_to?(:single_heading?) && single_heading? %>
<%= f.submit t("admin.budgets_wizard.groups.continue"), class: "button success" %>
<% else %>
<%= f.submit t("admin.budget_groups.form.#{action}"), class: "button hollow" %>
<% end %>
</div>
<% end %>

View File

@@ -7,5 +7,5 @@
<%= link_to t("admin.budget_groups.form.create"), new_admin_budget_group_path %>
</header>
<%= render Admin::Budgets::HelpComponent.new("budget_groups") %>
<%= render Admin::Budgets::HelpComponent.new("groups") %>
<%= render Admin::BudgetGroups::GroupsComponent.new(@groups) %>

View File

@@ -3,6 +3,8 @@
<%= render "shared/errors", resource: heading %>
<%= render Admin::BudgetsWizard::ModelFieldComponent.new %>
<%= f.translatable_fields do |translations_form| %>
<div class="small-12 medium-6 column end">
<%= translations_form.text_field :name, maxlength: 50 %>
@@ -35,6 +37,10 @@
</div>
<div class="clear">
<%= f.submit t("admin.budget_headings.form.#{action}"), class: "button hollow" %>
<% if respond_to?(:single_heading?) && single_heading? %>
<%= f.submit t("admin.budgets_wizard.headings.continue"), class: "button success" %>
<% else %>
<%= f.submit t("admin.budget_headings.form.#{action}"), class: "button hollow" %>
<% end %>
</div>
<% end %>

View File

@@ -5,5 +5,5 @@
<%= link_to t("admin.budget_headings.form.create"), new_admin_budget_group_heading_path %>
</header>
<%= render Admin::Budgets::HelpComponent.new("budget_headings") %>
<%= render Admin::Budgets::HelpComponent.new("headings") %>
<%= render Admin::BudgetHeadings::HeadingsComponent.new(@headings) %>

View File

@@ -147,10 +147,9 @@ ignore_unused:
- "admin.hidden_proposal_notifications.index.filter*"
- "admin.budgets.index.filter*"
- "admin.budgets.edit.(administrators|valuators).*"
- "admin.budget_groups.index.*.help_block"
- "admin.budget_headings.index.*.help_block"
- "admin.budget_phases.index.help_block"
- "admin.budgets.help.*"
- "admin.budget_investments.index.filter*"
- "admin.budgets_wizard.heading_mode.*"
- "admin.organizations.index.filter*"
- "admin.hidden_users.index.filter*"
- "admin.hidden_budget_investments.index.filter*"

View File

@@ -155,7 +155,7 @@ en:
description_balloting: "Description during Balloting phase"
description_reviewing_ballots: "Description during Reviewing Ballots phase"
description_finished: "Description when the budget is finished"
phase: "Phase"
phase: "Active phase"
currency_symbol: "Currency"
voting_style: "Final voting style"
voting_style_knapsack: "Knapsack"

View File

@@ -78,14 +78,17 @@ en:
all: All
open: Open
finished: Finished
help: "Participatory budgets allow citizens to propose and decide directly how to spend part of the budget, with monitoring and rigorous evaluation of proposals by the institution."
budget_investments: Manage projects
table_budget_type: "Type"
table_completed: Completed
table_draft: "Draft"
table_duration: "Duration"
table_name: Name
table_phase: Phase
table_phase_progress: "(%{current_phase_number}/%{total_phases})"
type_multiple: "Multiple headings"
type_pending: "Pending: No headings yet"
type_single: "Single heading"
edit_groups: Edit headings groups
edit_budget: Edit budget
admin_ballots: Admin ballots
@@ -133,7 +136,17 @@ en:
unable_notice: You cannot delete a budget that has associated investments
unable_notice_polls: You cannot delete a budget that has an associated poll
new:
title: New participatory budget
title_multiple: New multiple headings budget
title_single: New single heading budget
help:
budgets: "Participatory budgets allow citizens to propose and decide directly how to spend part of the budget, with monitoring and rigorous evaluation of proposals by the institution."
groups:
multiple: "Groups are meant to organize headings. After a group is created and it contais headings, it's possible to determine in how many headings a user can vote per group."
single: "Groups are meant to organize headings, since this budget will only contain one heading, the same name can be used because it will not appear anywhere. You can just continue with the next step."
headings:
multiple: "Headings are meant to divide the money of the participatory budget. Here you can add headings for this group and assign the amount of money that will be used for each heading."
single: "Headings are meant to divide the money of the participatory budget. Since this budget will only contain one heading, this is the place where you stablish the money that will be spent in this participaroty budget."
phases: "Participatory budgets have different phases. Here you can enable or disable phases and also customize each individual phase."
winners:
calculate: Calculate Winner Investments
calculated: Winners being calculated, it may take a minute.
@@ -162,7 +175,6 @@ en:
submit: "Save group"
index:
back: "Go back to budgets"
help: "Groups are meant to organize headings. After a group is created and it contais headings, it's possible to determine in how many headings a user can vote per group."
new_button: "Add new group"
budget_headings:
no_headings: "There are no headings."
@@ -190,7 +202,6 @@ en:
the_other_group: "Manage headings from the %{group} group."
index:
back: "Go back to groups"
help: "Headings are meant to divide the money of the participatory budget. Here you can add headings for this group and assign the amount of money that will be used for each heading."
new_button: "Add new heading"
title: "%{budget} / %{group} headings"
budget_phases:
@@ -206,7 +217,6 @@ en:
name_help_text: "This is the title of the phase users will read on the header whenever this phase is active."
save_changes: Save changes
index:
help: "Participatory budgets have different phases. Here you can enable or disable phases and also customize each individual phase."
title: "%{budget} phases"
budget_investments:
index:
@@ -316,9 +326,24 @@ en:
continue: "Continue to headings"
headings:
continue: "Continue to phases"
single:
back: "Go back to edit group"
heading_mode:
multiple:
description: "Create a process with multiple groups, districts or topics. You have something like several bags of money. For example, 25,000€ for \"North District\", 30,000€ for \"Center District\", 28,000€ for \"South District\" and the whole city. Or for several themes like sport, culture or health."
link: "Create multiple headings budget"
title: "<strong>Multiple headings</strong> budget"
single:
description: "Create a process with a group, district or topic. You have something like a bag of money. For example, 25,000€ for the district, or 10,000€ for culture."
link: "Create single heading budget"
title: "<strong>Single heading</strong> budget"
title: "Participatory budget type"
phases:
back: "Go back to headings"
continue: "Finish"
multiple:
back: "Go back to headings"
single:
back: "Go back to edit heading"
update_all:
notice: "Phases configured successfully"
milestones:
@@ -1357,6 +1382,7 @@ en:
example_url: "https://consulproject.org"
show_results_and_stats: "Show results and stats"
results_and_stats_reminder: "Marking these checkboxes the results and/or stats will be publicly available and every user will see them."
close_modal: Close modal
example_url: "https://consulproject.org"
geozones:
index:

View File

@@ -199,5 +199,6 @@ en:
default: "%a, %d %b %Y %H:%M:%S %z"
long: "%B %d, %Y %H:%M"
short: "%d %b %H:%M"
short_datetime: "%Y-%m-%d %H:%M"
api: "%Y-%m-%d %H"
pm: pm

View File

@@ -155,7 +155,7 @@ es:
description_balloting: "Descripción durante la fase de votación"
description_reviewing_ballots: "Descripción durante la fase de revisión de votos"
description_finished: "Descripción cuando el presupuesto ha finalizado / Resultados"
phase: "Fase"
phase: "Fase activa"
currency_symbol: "Divisa"
voting_style: "Estilo de la votación final"
voting_style_knapsack: Bolsa de dinero

View File

@@ -78,14 +78,17 @@ es:
all: Todos
open: Abiertos
finished: Terminados
help: "Los presupuestos participativos permiten que los ciudadanos propongan y decidan de manera directa cómo gastar parte del presupuesto, con un seguimiento y evaluación riguroso de las propuestas por parte de la institución."
budget_investments: Gestionar proyectos de gasto
table_budget_type: "Tipo"
table_completed: Completado
table_draft: "Borrador"
table_duration: "Duración"
table_name: Nombre
table_phase: Fase
table_phase_progress: "(%{current_phase_number}/%{total_phases})"
type_multiple: "Múltiples partidas"
type_pending: "Pendiente: Aún no hay partidas"
type_single: "Partida única"
edit_groups: Editar grupos de partidas
edit_budget: Editar presupuesto
admin_ballots: Gestionar urnas
@@ -133,7 +136,17 @@ es:
unable_notice: No se puede eliminar un presupuesto con proyectos asociados
unable_notice_polls: No se puede eliminar un presupuesto con una votación asociada
new:
title: Nuevo presupuesto ciudadano
title_multiple: Nuevo presupuesto de múltiples partidas
title_single: Nuevo presupuesto de partida única
help:
budgets: "Los presupuestos participativos permiten que los ciudadanos propongan y decidan de manera directa cómo gastar parte del presupuesto, con un seguimiento y evaluación riguroso de las propuestas por parte de la institución."
groups:
multiple: "Los grupos sirven para organizar las partidas del presupuesto. Después de que un grupo sea creado y éste contenga partidas, es posible determinar el número de partidas a las que un usuario puede votar por grupo."
single: "Los grupos sirven para organizar las partidas del presupuesto, como este presupuesto sólo contendrá una partida, se puede usar el mismo nombre y éste no aparecerá en ninguna parte. Puedes continuar con el siguiente paso."
headings:
multiple: "Las partidas sirven para dividir el dinero del presupuesto participativo. Aquí puedes ir añadiendo partidas para cada grupo y establecer la cantidad de dinero que se gastará en cada partida."
single: "Las partidas sirven para dividir el dinero del presupuesto participativo. Como este presupuesto solo tendrá una partida aquí podrás establecer la cantidad de dinero que se gastará en este presupuesto participativo."
phases: "Los presupuestos participativos tienen distintas fases. Aquí puedes habilitar o deshabilitar fases y también personalizar cada una de las fases."
winners:
calculate: Calcular proyectos ganadores
calculated: Calculando ganadores, puede tardar un minuto.
@@ -162,7 +175,6 @@ es:
submit: "Guardar grupo"
index:
back: "Volver a presupuestos"
help: "Los grupos sirven para organizar las partidas del presupuesto. Después de que un grupo sea creado y éste contenga partidas, es posible determinar el número de partidas a las que un usuario puede votar por grupo."
new_button: "Añadir un grupo nuevo"
budget_headings:
no_headings: "No hay partidas."
@@ -190,7 +202,6 @@ es:
the_other_group: "Ir a partidas del grupo %{group}."
index:
back: "Volver a grupos"
help: "Las partidas sirven para dividir el dinero del presupuesto participativo. Aquí puedes ir añadiendo partidas para cada grupo y establecer la cantidad de dinero que se gastará en cada partida."
new_button: "Añadir una partida nueva"
title: "Partidas de %{budget} / %{group}"
budget_phases:
@@ -206,7 +217,6 @@ es:
save_changes: Guardar cambios
title: "Editar fase"
index:
help: "Los presupuestos participativos tienen distintas fases. Aquí puedes habilitar o deshabilitar fases y también personalizar cada una de las fases."
title: "Fases de %{budget}"
budget_investments:
index:
@@ -316,9 +326,24 @@ es:
continue: "Continuar a partidas"
headings:
continue: "Continuar a fases"
single:
back: "Volver a editar grupo"
heading_mode:
multiple:
description: "Crea un proceso con múltiples grupos, distritos o temas. Tienes algo como varias bolsas de dinero. Por ejemplo, 25.000€ para \"Distrito norte\", 30.000€ para \"Distrito centro\", 28.000€ para \"Distrito sur\" y toda la ciudad. O para varios temas como deporte, cultura o salud."
link: "Crear presupuesto de múltiples partidas"
title: "Presupuesto de <strong>múltiples partidas</strong>"
single:
description: "Crea un proceso con un grupo, distrito o tema. Tienes algo como una bolsa de dinero. Por ejemplo, 25.000€ para un distrito, o 10.000€ para cultura."
link: "Crear presupuesto de partida única"
title: "Presupuesto de <strong>partida única</strong>"
title: Tipo de presupuesto participativo
phases:
back: "Volver a partidas"
continue: "Finalizar"
multiple:
back: "Volver a partidas"
single:
back: "Volver a editar partida"
update_all:
notice: "Fases configuradas con éxito"
milestones:
@@ -1356,6 +1381,7 @@ es:
example_url: "https://consulproject.org"
show_results_and_stats: "Mostrar resultados y estadísticas"
results_and_stats_reminder: "Si marcas estas casillas los resultados y/o estadísticas serán públicos y podrán verlos todos los usuarios."
close_modal: Cerrar ventana emergente
example_url: "https://consulproject.org"
geozones:
index:

View File

@@ -9,7 +9,7 @@ es:
- vie
- sáb
abbr_month_names:
-
-
- ene
- feb
- mar
@@ -35,7 +35,7 @@ es:
long: "%d de %B de %Y"
short: "%d de %b"
month_names:
-
-
- enero
- febrero
- marzo
@@ -193,5 +193,6 @@ es:
default: "%A, %d de %B de %Y %H:%M:%S %z"
long: "%d de %B de %Y %H:%M"
short: "%d de %b %H:%M"
short_datetime: "%d/%m/%Y %H:%M"
api: "%d/%m/%Y %H"
pm: pm

View File

@@ -12,7 +12,7 @@ describe Admin::Budgets::DurationComponent, type: :component do
render dates
expect(page.text).to eq "2015-08-01 12:00:00 - 2016-09-30 16:29:59"
expect(page.text).to eq "2015-08-01 12:00 - 2016-09-30 16:29"
expect(dates).to be_html_safe
end
@@ -20,7 +20,7 @@ describe Admin::Budgets::DurationComponent, type: :component do
durable = double(starts_at: Time.zone.local(2015, 8, 1, 12, 0, 0), ends_at: nil)
render Admin::Budgets::DurationComponent.new(durable).dates
expect(page.text).to eq "2015-08-01 12:00:00 - "
expect(page.text).to eq "2015-08-01 12:00 - "
end
it "shows the end date when no start date is defined" do
@@ -28,7 +28,7 @@ describe Admin::Budgets::DurationComponent, type: :component do
render Admin::Budgets::DurationComponent.new(durable).dates
expect(page.text).to eq "- 2016-09-30 16:29:59"
expect(page.text).to eq "- 2016-09-30 16:29"
end
end

View File

@@ -7,21 +7,54 @@ describe Admin::Budgets::IndexComponent, type: :component do
allow_any_instance_of(Admin::BudgetsController).to receive(:current_filter).and_return("all")
end
it "displays current phase zero for budgets with no current phase" do
budget = create(:budget, :accepting, name: "Not enabled phase")
budget.phases.accepting.update!(enabled: false)
describe "#phase_progress_text" do
it "displays current phase zero for budgets with no current phase" do
budget = create(:budget, :accepting, name: "Not enabled phase")
budget.phases.accepting.update!(enabled: false)
render_inline Admin::Budgets::IndexComponent.new(Budget.page(1))
render_inline Admin::Budgets::IndexComponent.new(Budget.page(1))
expect(page.find("tr", text: "Not enabled phase")).to have_content "0/8"
expect(page.find("tr", text: "Not enabled phase")).to have_content "0/8"
end
it "displays phase zero out of zero for budgets with no enabled phases" do
budget = create(:budget, name: "Without phases")
budget.phases.each { |phase| phase.update!(enabled: false) }
render_inline Admin::Budgets::IndexComponent.new(Budget.page(1))
expect(page.find("tr", text: "Without phases")).to have_content "0/0"
end
end
it "displays phase zero out of zero for budgets with no enabled phases" do
budget = create(:budget, name: "Without phases")
budget.phases.each { |phase| phase.update!(enabled: false) }
describe "#type" do
let(:budget) { create(:budget, name: "With type") }
render_inline Admin::Budgets::IndexComponent.new(Budget.page(1))
it "displays 'single heading' for budgets with one heading" do
create(:budget_heading, budget: budget)
expect(page.find("tr", text: "Without phases")).to have_content "0/0"
render_inline Admin::Budgets::IndexComponent.new(Budget.page(1))
expect(page.find("thead")).to have_content "Type"
expect(page.find("tr", text: "With type")).to have_content "Single heading"
end
it "displays 'multiple headings' for budgets with multiple headings" do
2.times { create(:budget_heading, budget: budget) }
render_inline Admin::Budgets::IndexComponent.new(Budget.page(1))
expect(page.find("thead")).to have_content "Type"
expect(page.find("tr", text: "With type")).to have_content "Multiple headings"
end
it "displays 'pending: no headings yet' for budgets without headings" do
create(:budget, name: "Without headings")
render_inline Admin::Budgets::IndexComponent.new(Budget.page(1))
expect(page.find("thead")).to have_content "Type"
expect(page.find("tr", text: "Without headings")).to have_content "Pending: No headings yet"
end
end
end

View File

@@ -447,4 +447,43 @@ describe Budget do
expect(Valuator.count).to be 1
end
end
describe "#single_heading?" do
it "returns false for budgets with no groups nor headings" do
expect(create(:budget).single_heading?).to be false
end
it "returns false for budgets with one group and no headings" do
create(:budget_group, budget: budget)
expect(budget.single_heading?).to be false
end
it "returns false for budgets with multiple groups and one heading" do
2.times { create(:budget_group, budget: budget) }
create(:budget_heading, group: budget.groups.last)
expect(budget.single_heading?).to be false
end
it "returns false for budgets with one group and multiple headings" do
group = create(:budget_group, budget: budget)
2.times { create(:budget_heading, group: group) }
expect(budget.single_heading?).to be false
end
it "returns false for budgets with one group and multiple headings" do
2.times { create(:budget_group, budget: budget) }
2.times { create(:budget_heading, group: budget.groups.sample) }
expect(budget.single_heading?).to be false
end
it "returns true for budgets with one group and one heading" do
create(:budget_heading, group: create(:budget_group, budget: budget))
expect(budget.single_heading?).to be true
end
end
end

View File

@@ -529,7 +529,7 @@ describe "Admin budget investments", :admin do
expect(page).to have_link "Calculate Winner Investments"
select "Accepting projects", from: "Phase"
select "Accepting projects", from: "Active phase"
click_button "Update Budget"
expect(page).to have_content "Participatory budget updated successfully"

View File

@@ -30,8 +30,10 @@ describe "Admin budgets", :admin do
budget = create(:budget, :accepting)
visit admin_budgets_path
expect(page).to have_content budget.name
expect(page).to have_content "Accepting projects"
within "tr", text: budget.name do
expect(page).to have_content "Accepting projects"
expect(page).to have_content "Pending: No headings yet"
end
end
scenario "Filters by phase" do
@@ -188,7 +190,7 @@ describe "Admin budgets", :admin do
visit edit_admin_budget_path(budget)
expect(page).to have_select "Phase", selected: "Selecting projects"
expect(page).to have_select "Active phase", selected: "Selecting projects"
expect(page).to have_table "Phases", with_cols: [
[
@@ -202,14 +204,14 @@ describe "Admin budgets", :admin do
"Reviewing voting"
],
[
"2015-07-15 00:00:00 - 2015-08-14 23:59:59",
"2015-08-15 00:00:00 - 2015-09-14 23:59:59",
"2015-09-15 00:00:00 - 2015-10-14 23:59:59",
"2015-10-15 00:00:00 - 2015-11-14 23:59:59",
"2015-11-15 00:00:00 - 2015-12-14 23:59:59",
"2015-11-15 00:00:00 - 2016-01-14 23:59:59",
"2016-01-15 00:00:00 - 2016-02-14 23:59:59",
"2016-02-15 00:00:00 - 2016-03-14 23:59:59"
"2015-07-15 00:00 - 2015-08-14 23:59",
"2015-08-15 00:00 - 2015-09-14 23:59",
"2015-09-15 00:00 - 2015-10-14 23:59",
"2015-10-15 00:00 - 2015-11-14 23:59",
"2015-11-15 00:00 - 2015-12-14 23:59",
"2015-11-15 00:00 - 2016-01-14 23:59",
"2016-01-15 00:00 - 2016-02-14 23:59",
"2016-02-15 00:00 - 2016-03-14 23:59"
],
[
"Yes",

View File

@@ -4,7 +4,8 @@ describe "Budgets wizard, first step", :admin do
describe "New" do
scenario "Create budget - Knapsack voting (default)" do
visit admin_budgets_path
click_link "Create new budget"
click_button "Create new budget"
click_link "Create multiple headings budget"
fill_in "Name", with: "M30 - Summer campaign"
click_button "Continue to groups"
@@ -21,7 +22,8 @@ describe "Budgets wizard, first step", :admin do
admin = Administrator.first
visit admin_budgets_path
click_link "Create new budget"
click_button "Create new budget"
click_link "Create multiple headings budget"
fill_in "Name", with: "M30 - Summer campaign"
select "Approval", from: "Final voting style"
@@ -73,7 +75,8 @@ describe "Budgets wizard, first step", :admin do
describe "Create" do
scenario "A new budget is always created in draft mode" do
visit admin_budgets_path
click_link "Create new budget"
click_button "Create new budget"
click_link "Create multiple headings budget"
fill_in "Name", with: "M30 - Summer campaign"

View File

@@ -97,6 +97,26 @@ describe "Budgets wizard, groups step", :admin do
expect(page).to have_css ".creation-timeline"
expect(page).to have_css "td", exact_text: "Group without typos"
end
scenario "update group in single heading budget" do
visit admin_budgets_wizard_budget_groups_path(budget, mode: "single")
fill_in "Group name", with: "Group wiht typo"
click_button "Continue to headings"
click_link "Go back to edit group"
expect(page).to have_field "Group name", with: "Group wiht typo"
fill_in "Group name", with: "Group without typos"
click_button "Continue to headings"
expect(page).to have_content "Group updated successfully"
visit admin_budget_groups_path(budget)
expect(page).to have_content "There is 1 group"
within("tbody tr") { expect(page).to have_content "Group without typos" }
end
end
describe "Destroy" do

View File

@@ -118,6 +118,29 @@ describe "Budgets wizard, headings step", :admin do
expect(page).to have_css ".creation-timeline"
expect(page).to have_css "td", exact_text: "Heading without typos"
end
scenario "update heading in single heading budget" do
visit admin_budgets_wizard_budget_group_headings_path(budget, group, mode: "single")
fill_in "Heading name", with: "Heading wiht typo"
fill_in "Money amount", with: "300000"
click_button "Continue to phases"
expect(page).to have_content "Heading created successfully"
click_link "Go back to edit heading"
expect(page).to have_field "Heading name", with: "Heading wiht typo"
fill_in "Heading name", with: "Heading without typos"
click_button "Continue to phases"
expect(page).to have_content "Heading updated successfully"
visit admin_budget_group_headings_path(budget, group)
expect(page).to have_content "There is 1 heading"
within("tbody tr") { expect(page).to have_content "Heading without typos" }
end
end
describe "Destroy" do

View File

@@ -82,5 +82,18 @@ describe "Budgets wizard, phases step", :admin do
expect(page).to have_css ".creation-timeline"
within_table("Phases") { expect(page).to have_content "Welcoming projects" }
end
scenario "update phase in single heading budget" do
visit admin_budgets_wizard_budget_budget_phases_path(budget, mode: "single")
within("tr", text: "Selecting projects") { click_link "Edit phase" }
fill_in "Name", with: "Choosing projects"
click_button "Save changes"
expect(page).to have_content "Changes saved"
expect(page).to have_css ".creation-timeline"
within_table("Phases") { expect(page).to have_content "Choosing projects" }
expect(page).to have_link "Go back to edit heading"
end
end
end

View File

@@ -0,0 +1,148 @@
require "rails_helper"
describe "Budgets creation wizard", :admin do
scenario "Creation of a single-heading budget by steps" do
visit admin_budgets_path
click_button "Create new budget"
click_link "Create single heading budget"
fill_in "Name", with: "Single heading budget"
click_button "Continue to groups"
expect(page).to have_content "New participatory budget created successfully!"
expect(page).to have_field "Group name", with: "Single heading budget"
click_button "Continue to headings"
expect(page).to have_content "Group created successfully"
fill_in "Heading name", with: "One and only heading"
fill_in "Money amount", with: "1000000"
click_button "Continue to phases"
expect(page).to have_css ".budget-phases-table"
click_button "Finish"
expect(page).to have_content "Phases configured successfully"
within "tr", text: "Single heading budget" do
click_link "Edit headings groups"
end
expect(page).to have_content "There is 1 group"
within "tr", text: "Single heading budget" do
click_link "Manage headings"
end
expect(page).to have_content "There is 1 heading"
within "tbody" do
expect(page).to have_content "One and only heading"
end
end
scenario "Creation of a multiple-headings budget by steps" do
visit admin_budgets_path
click_button "Create new budget"
click_link "Create multiple headings budget"
fill_in "Name", with: "Multiple headings budget"
click_button "Continue to groups"
expect(page).to have_content "New participatory budget created successfully!"
expect(page).to have_content "There are no groups."
click_button "Add new group"
fill_in "Group name", with: "All city"
click_button "Create new group"
expect(page).to have_content "Group created successfully!"
within("table") { expect(page).to have_content "All city" }
expect(page).not_to have_content "There are no groups."
click_button "Add new group"
fill_in "Group name", with: "Districts"
click_button "Create new group"
expect(page).to have_content "Group created successfully!"
within("table") { expect(page).to have_content "Districts" }
click_link "Continue to headings"
expect(page).to have_content "Showing headings from the All city group"
expect(page).to have_content "There are no headings."
click_button "Add new heading"
fill_in "Heading name", with: "All city"
fill_in "Money amount", with: "1000000"
click_button "Create new heading"
expect(page).to have_content "Heading created successfully!"
within("table") { expect(page).to have_content "All city" }
expect(page).not_to have_content "There are no headings."
click_link "Manage headings from the Districts group."
expect(page).to have_content "There are no headings."
click_button "Add new heading"
fill_in "Heading name", with: "North"
fill_in "Money amount", with: "500000"
click_button "Create new heading"
expect(page).to have_content "Heading created successfully!"
within("table") { expect(page).to have_content "North" }
expect(page).not_to have_content "There are no headings."
click_button "Add new heading"
fill_in "Heading name", with: "South"
fill_in "Money amount", with: "500000"
click_button "Create new heading"
expect(page).to have_content "Heading created successfully!"
within("table") { expect(page).to have_content "South" }
click_link "Continue to phases"
expect(page).to have_css ".budget-phases-table"
within("tr", text: "Voting projects") { click_link "Edit phase" }
fill_in "Name", with: "Custom phase name"
uncheck "Phase enabled"
click_button "Save changes"
expect(page).to have_content "Changes saved"
within "table" do
expect(page).to have_content "Custom phase name"
expect(page).not_to have_content "Voting projects"
end
click_button "Finish"
expect(page).to have_content "Phases configured successfully"
within "tr", text: "Multiple headings budget" do
click_link "Edit headings groups"
end
expect(page).to have_content "There are 2 groups"
within "tbody" do
expect(page).to have_content "All city"
expect(page).to have_content "Districts"
end
within "tr", text: "Districts" do
click_link "Manage headings"
end
expect(page).to have_content "There are 2 headings"
within "tbody" do
expect(page).to have_content "North"
expect(page).to have_content "South"
end
end
end