Merge pull request #2353 from consul/2339-admin_budgets_phases

Allow admins to edit Budget phases
This commit is contained in:
María Checa
2018-01-22 18:07:34 +01:00
committed by GitHub
15 changed files with 299 additions and 34 deletions

View File

@@ -9,6 +9,7 @@
// 07. Legislation // 07. Legislation
// 08. CMS // 08. CMS
// 09. Map // 09. Map
// 10. Budget phases
// //
// 01. Global styles // 01. Global styles
@@ -1032,3 +1033,37 @@ table {
visibility: hidden; visibility: hidden;
height: 0; height: 0;
} }
// 10. Budget phases
// -----------------
.budget-phase-enabled {
font-weight: bold;
padding-left: rem-calc(20);
position: relative;
text-decoration: none;
&.enabled::before,
&.disabled::before {
font-family: 'icons';
left: 0;
position: absolute;
}
&.enabled {
color: $check;
&::before {
color: $check;
content: '\6c';
}
}
&.disabled {
color: #000;
&::before {
color: #000;
content: '\76';
}
}
}

View File

@@ -0,0 +1,26 @@
class Admin::BudgetPhasesController < Admin::BaseController
before_action :load_phase, only: [:edit, :update]
def edit; end
def update
if @phase.update(budget_phase_params)
redirect_to edit_admin_budget_path(@phase.budget), notice: t("flash.actions.save_changes.notice")
else
render :edit
end
end
private
def load_phase
@phase = Budget::Phase.find(params[:id])
end
def budget_phase_params
valid_attributes = [:starts_at, :ends_at, :summary, :description, :enabled]
params.require(:budget_phase).permit(*valid_attributes)
end
end

View File

@@ -3,6 +3,7 @@ class Budget
PHASE_KINDS = %w(drafting accepting reviewing selecting valuating publishing_prices balloting PHASE_KINDS = %w(drafting accepting reviewing selecting valuating publishing_prices balloting
reviewing_ballots finished).freeze reviewing_ballots finished).freeze
PUBLISHED_PRICES_PHASES = %w(publishing_prices balloting reviewing_ballots finished).freeze PUBLISHED_PRICES_PHASES = %w(publishing_prices balloting reviewing_ballots finished).freeze
SUMMARY_MAX_LENGTH = 1000
DESCRIPTION_MAX_LENGTH = 2000 DESCRIPTION_MAX_LENGTH = 2000
belongs_to :budget belongs_to :budget
@@ -11,6 +12,7 @@ class Budget
validates :budget, presence: true validates :budget, presence: true
validates :kind, presence: true, uniqueness: { scope: :budget }, inclusion: { in: PHASE_KINDS } validates :kind, presence: true, uniqueness: { scope: :budget }, inclusion: { in: PHASE_KINDS }
validates :summary, length: { maximum: SUMMARY_MAX_LENGTH }
validates :description, length: { maximum: DESCRIPTION_MAX_LENGTH } validates :description, length: { maximum: DESCRIPTION_MAX_LENGTH }
validate :invalid_dates_range? validate :invalid_dates_range?
validate :prev_phase_dates_valid? validate :prev_phase_dates_valid?

View File

@@ -0,0 +1,67 @@
<%= form_for [:admin, @phase.budget, @phase] do |f| %>
<div class="row">
<div class="row">
<div class="small-12 medium-6 column">
<%= f.label :starts_at, t("admin.budget_phases.edit.start_date") %>
<%= f.text_field :starts_at,
value: format_date_for_calendar_form(@phase.starts_at),
class: "js-calendar-full",
id: "start_date",
label: false %>
</div>
<div class="small-12 medium-6 column">
<%= f.label :ends_at, t("admin.budget_phases.edit.end_date") %>
<%= f.text_field :ends_at,
value: format_date_for_calendar_form(@phase.ends_at),
class: "js-calendar-full",
id: "end_date",
label: false %>
</div>
</div>
<div class="row margin-top">
<div class="small-12 medium-12 column">
<%= f.label :summary, t("admin.budget_phases.edit.summary") %>
<p class="help-text" id="phase-summary-help-text">
<%= t("admin.budget_phases.edit.summary_help_text") %>
</p>
<%= f.cktext_area :summary,
maxlength: Budget::Phase::SUMMARY_MAX_LENGTH,
ckeditor: { language: I18n.locale },
label: false %>
</div>
</div>
<div class="row margin-top">
<div class="small-12 medium-12 column">
<%= f.label :description, t("admin.budget_phases.edit.description") %>
<p class="help-text" id="phase-description-help-text">
<%= t("admin.budget_phases.edit.description_help_text") %>
</p>
<%= f.cktext_area :description,
maxlength: Budget::Phase::DESCRIPTION_MAX_LENGTH,
ckeditor: { language: I18n.locale },
label: false %>
</div>
</div>
<div class="row margin-top">
<div class="small-12 medium-12 column">
<%= f.check_box :enabled, label: t("admin.budget_phases.edit.enabled") %>
<p class="help-text" id="phase-summary-help-text">
<%= t("admin.budget_phases.edit.enabled_help_text") %>
</p>
</div>
</div>
<div class="margin-top">
<%= f.submit t("admin.budget_phases.edit.save_changes"), class: "button success" %>
</div>
</div>
<% end %>

View File

@@ -0,0 +1,5 @@
<%= back_link_to edit_admin_budget_path(@phase.budget) %>
<h2><%= t("admin.budgets.edit.title") %> - <%= t("budgets.phase.#{@phase.kind}") %></h2>
<%= render '/admin/budget_phases/form' %>

View File

@@ -2,12 +2,6 @@
<%= f.text_field :name, maxlength: Budget.title_max_length %> <%= f.text_field :name, maxlength: Budget.title_max_length %>
<% Budget::Phase::PHASE_KINDS.each do |phase| %>
<div class="margin-top">
<%= f.cktext_area "description_#{phase}", maxlength: Budget::Phase::DESCRIPTION_MAX_LENGTH, ckeditor: { language: I18n.locale } %>
</div>
<% end %>
<div class="row margin-top"> <div class="row margin-top">
<div class="small-12 medium-9 column"> <div class="small-12 medium-9 column">
<%= f.select :phase, budget_phases_select_options %> <%= f.select :phase, budget_phases_select_options %>
@@ -16,15 +10,61 @@
<%= f.select :currency_symbol, budget_currency_symbol_select_options %> <%= f.select :currency_symbol, budget_currency_symbol_select_options %>
</div> </div>
</div> </div>
<% if @budget.phases.present? %>
<table id='budget-phases-table' class="table-for-mobile">
<thead>
<tr>
<th><%= t("admin.budgets.edit.phase") %></th>
<th><%= t("admin.budgets.edit.dates") %></th>
<th class="text-center"><%= t("admin.budgets.edit.enabled") %></th>
<th><%= t("admin.budgets.edit.actions") %></th>
</tr>
</thead>
<% @budget.phases.each do |phase| %>
<tr id="<%= dom_id(phase) %>" class="phase">
<td>
<%= t("budgets.phase.#{phase.kind}") %>
<% if @budget.current_phase == phase %>
<span class="label success"><strong><%= t("admin.budgets.edit.active") %></strong></span>
<% end %>
</td>
<td>
<% if phase.starts_at.present? && phase.ends_at.present? %>
<%= l phase.starts_at.to_date %> - <%= l phase.ends_at.to_date %>
<% else %>
<em><%= t("admin.budgets.edit.blank_dates") %></em>
<% end %>
</td>
<td class="text-center">
<span class="budget-phase-enabled <%= phase.enabled? ? 'enabled' : 'disabled' %>"></span>
</td>
<td>
<%= link_to t("admin.budgets.edit.edit_phase"),
edit_admin_budget_budget_phase_path(@budget, phase),
method: :get, class: "button hollow" %>
</td>
</tr>
<% end %>
</table>
<% end %>
<div class="margin-top"> <div class="margin-top">
<%= f.submit nil, class: "button success" %> <%= f.submit nil, class: "button success" %>
<% if @budget.balloting_process? %>
<div class="float-right"> <div class="float-right">
<% if @budget.balloting_process? %>
<%= link_to t("admin.budgets.winners.calculate"), <%= link_to t("admin.budgets.winners.calculate"),
calculate_winners_admin_budget_path(@budget), calculate_winners_admin_budget_path(@budget),
method: :put, method: :put,
class: "button hollow" %> class: "button hollow" %>
</div> <% end %>
<% end %>
<% if @budget.persisted? %>
<%= link_to t("admin.budgets.edit.delete"), admin_budget_path(@budget), method: :delete, class: "button hollow alert float-right" %>
<% end %>
</div>
</div> </div>
<% end %> <% end %>

View File

@@ -1,7 +1,5 @@
<%= back_link_to admin_budgets_path %> <%= back_link_to admin_budgets_path %>
<%= button_to t("admin.budgets.edit.delete"), admin_budget_path(@budget), method: :delete, class: "button hollow alert float-right small" %> <h2><%= t("admin.budgets.edit.title") %></h2>
<h2 class="inline-block"><%= t("admin.budgets.edit.title") %></h2>
<%= render '/admin/budgets/form' %> <%= render '/admin/budgets/form' %>

View File

@@ -5,8 +5,8 @@ en:
one: "activity" one: "activity"
other: "activities" other: "activities"
budget: budget:
one: "Participatory budget" one: "Budget"
other: "Participatory budgets" other: "Budgets"
budget/investment: budget/investment:
one: "Investment" one: "Investment"
other: "Investments" other: "Investments"

View File

@@ -85,6 +85,13 @@ en:
edit: edit:
title: Edit Participatory budget title: Edit Participatory budget
delete: Delete budget delete: Delete budget
phase: Phase
dates: Dates
enabled: Enabled
actions: Actions
edit_phase: Edit phase
active: Active
blank_dates: Dates are blank
destroy: destroy:
success_notice: Budget deleted successfully success_notice: Budget deleted successfully
unable_notice: You cannot destroy a Budget that has associated investments unable_notice: You cannot destroy a Budget that has associated investments
@@ -113,6 +120,17 @@ en:
winners: winners:
calculate: Calculate Winner Investments calculate: Calculate Winner Investments
calculated: Winners being calculated, it may take a minute. calculated: Winners being calculated, it may take a minute.
budget_phases:
edit:
start_date: Start date
end_date: End date
summary: Summary
summary_help_text: This text will appear in the header when the phase is active
description: Description
description_help_text: This text will inform the user about the phase. To show it even if the phase is not active, select the checkbox below
enabled: Phase enabled
enabled_help_text: This phase will be public in the budget's phases timeline, as well as active for any other purpose
save_changes: Save changes
budget_investments: budget_investments:
index: index:
heading_filter_all: All headings heading_filter_all: All headings

View File

@@ -43,7 +43,7 @@ en:
section_header: section_header:
icon_alt: Participatory budgets icon icon_alt: Participatory budgets icon
title: Participatory budgets title: Participatory budgets
help: Help about participatory budgets help: Help with participatory budgets
all_phases: See all phases all_phases: See all phases
all_phases: Budget investment's phases all_phases: Budget investment's phases
map: Budget investments' proposals located geographically map: Budget investments' proposals located geographically
@@ -53,7 +53,7 @@ en:
finished_budgets: Finished participatory budgets finished_budgets: Finished participatory budgets
see_results: See results see_results: See results
section_footer: section_footer:
title: Help about participatory budgets title: Help with participatory budgets
description: With the participatory budgets the citizens decide to which projects presented by the neighbors is destined a part of the municipal budget. description: With the participatory budgets the citizens decide to which projects presented by the neighbors is destined a part of the municipal budget.
help_text_1: "Participatory budgets are processes in which citizens decide directly on what is spent part of the municipal budget. Any registered person over 16 years old can propose an investment project that is preselected in a phase of citizen supports." help_text_1: "Participatory budgets are processes in which citizens decide directly on what is spent part of the municipal budget. Any registered person over 16 years old can propose an investment project that is preselected in a phase of citizen supports."
help_text_2: "The most voted projects are evaluated and passed to a final vote in which they decide the actions to be carried out by the City Council once the municipal budgets of the next year are approved." help_text_2: "The most voted projects are evaluated and passed to a final vote in which they decide the actions to be carried out by the City Council once the municipal budgets of the next year are approved."

View File

@@ -85,6 +85,13 @@ es:
edit: edit:
title: Editar campaña de presupuestos participativos title: Editar campaña de presupuestos participativos
delete: Eliminar presupuesto delete: Eliminar presupuesto
phase: Fase
dates: Fechas
enabled: Habilitada
actions: Acciones
edit_phase: Editar fase
active: Activa
blank_dates: Sin fechas
destroy: destroy:
success_notice: Presupuesto eliminado correctamente success_notice: Presupuesto eliminado correctamente
unable_notice: No se puede eliminar un presupuesto con proyectos asociados unable_notice: No se puede eliminar un presupuesto con proyectos asociados
@@ -113,6 +120,17 @@ es:
winners: winners:
calculate: Calcular propuestas ganadoras calculate: Calcular propuestas ganadoras
calculated: Calculando ganadoras, puede tardar un minuto. calculated: Calculando ganadoras, puede tardar un minuto.
budget_phases:
edit:
start_date: Fecha de Inicio
end_date: Fecha de fin
summary: Resumen
summary_help_text: Este texto aparecerá en la cabecera cuando la fase esté activa
description: Descripción
description_help_text: Este texto informará al usuario sobre la fase. Para mostrarlo aunque la fase no esté activa, marca la opción de más abajo.
enabled: Fase habilitada
enabled_help_text: Esta fase será pública en el calendario de fases del presupuesto y estará activa para otros propósitos
save_changes: Guardar cambios
budget_investments: budget_investments:
index: index:
heading_filter_all: Todas las partidas heading_filter_all: Todas las partidas

View File

@@ -51,6 +51,8 @@ namespace :admin do
resources :budget_investment_milestones resources :budget_investment_milestones
member { patch :toggle_selection } member { patch :toggle_selection }
end end
resources :budget_phases, only: [:edit, :update]
end end
resources :signature_sheets, only: [:index, :new, :create, :show] resources :signature_sheets, only: [:index, :new, :create, :show]

View File

@@ -0,0 +1,33 @@
require 'rails_helper'
feature 'Admin budget phases' do
let(:budget) { create(:budget) }
context 'Edit' do
before :each do
admin = create(:administrator)
login_as(admin.user)
end
scenario 'Update phase' do
visit edit_admin_budget_budget_phase_path(budget, budget.current_phase)
fill_in 'start_date', with: DateTime.current + 1.days
fill_in 'end_date', with: DateTime.current + 12.days
fill_in 'budget_phase_summary', with: 'This is the summary of the phase.'
fill_in 'budget_phase_description', with: 'This is the description of the phase.'
uncheck 'budget_phase_enabled'
click_button 'Save changes'
expect(page).to have_current_path(edit_admin_budget_path(budget))
expect(page).to have_content 'Changes saved'
expect(budget.current_phase.starts_at.to_date).to eq((DateTime.current + 1.days).to_date)
expect(budget.current_phase.ends_at.to_date).to eq((DateTime.current + 12.days).to_date)
expect(budget.current_phase.summary).to eq('This is the summary of the phase.')
expect(budget.current_phase.description).to eq('This is the description of the phase.')
expect(budget.current_phase.enabled).to be(false)
end
end
end

View File

@@ -90,10 +90,9 @@ feature 'Admin budgets' do
click_link 'Create new budget' click_link 'Create new budget'
fill_in 'budget_name', with: 'M30 - Summer campaign' fill_in 'budget_name', with: 'M30 - Summer campaign'
fill_in 'budget_description_accepting', with: 'Budgeting for summer 2017 maintenance and improvements of the road M-30'
select 'Accepting projects', from: 'budget[phase]' select 'Accepting projects', from: 'budget[phase]'
click_button 'Create Participatory budget' click_button 'Create Budget'
expect(page).to have_content 'New participatory budget created successfully!' expect(page).to have_content 'New participatory budget created successfully!'
expect(page).to have_content 'M30 - Summer campaign' expect(page).to have_content 'M30 - Summer campaign'
@@ -101,7 +100,7 @@ feature 'Admin budgets' do
scenario 'Name is mandatory' do scenario 'Name is mandatory' do
visit new_admin_budget_path visit new_admin_budget_path
click_button 'Create Participatory budget' click_button 'Create Budget'
expect(page).not_to have_content 'New participatory budget created successfully!' expect(page).not_to have_content 'New participatory budget created successfully!'
expect(page).to have_css("label.error", text: "Name") expect(page).to have_css("label.error", text: "Name")
@@ -117,10 +116,10 @@ feature 'Admin budgets' do
scenario 'Destroy a budget without investments' do scenario 'Destroy a budget without investments' do
visit admin_budgets_path visit admin_budgets_path
click_link 'Edit budget' click_link 'Edit budget'
click_button 'Delete budget' click_link 'Delete budget'
expect(page).to have_content('Budget deleted successfully') expect(page).to have_content('Budget deleted successfully')
expect(page).to have_content('participatory budgets cannot be found') expect(page).to have_content('budgets cannot be found')
end end
scenario 'Try to destroy a budget with investments' do scenario 'Try to destroy a budget with investments' do
@@ -128,10 +127,32 @@ feature 'Admin budgets' do
visit admin_budgets_path visit admin_budgets_path
click_link 'Edit budget' click_link 'Edit budget'
click_button 'Delete budget' click_link 'Delete budget'
expect(page).to have_content('You cannot destroy a Budget that has associated investments') expect(page).to have_content('You cannot destroy a Budget that has associated investments')
expect(page).to have_content('There is 1 participatory budget') expect(page).to have_content('There is 1 budget')
end
end
context 'Edit' do
let!(:budget) { create(:budget) }
scenario 'Show phases table' do
visit admin_budgets_path
click_link 'Edit budget'
within '#budget-phases-table' do
budget.phases.each do |phase|
within "#budget_phase_#{phase.id}" do
expect(page).to have_content(I18n.t("budgets.phase.#{phase.kind}"))
expect(page).to have_content("#{phase.starts_at.to_date} - #{phase.ends_at.to_date}")
expect(page).to have_css('.budget-phase-enabled.enabled')
expect(page).to have_link('Edit phase', href: edit_admin_budget_budget_phase_path(budget, phase))
expect(page).to have_content('Active') if budget.current_phase == phase
end
end
end
end end
end end
@@ -146,7 +167,7 @@ feature 'Admin budgets' do
click_link 'Edit budget' click_link 'Edit budget'
fill_in 'budget_name', with: 'More trees on the streets' fill_in 'budget_name', with: 'More trees on the streets'
click_button 'Update Participatory budget' click_button 'Update Budget'
expect(page).to have_content('More trees on the streets') expect(page).to have_content('More trees on the streets')
expect(page).to have_current_path(admin_budgets_path) expect(page).to have_current_path(admin_budgets_path)
@@ -184,7 +205,7 @@ feature 'Admin budgets' do
context 'Manage groups and headings' do context 'Manage groups and headings' do
scenario 'Create group', :js do scenario 'Create group', :js do
budget = create(:budget, name: 'Yearly participatory budget') budget = create(:budget, name: 'Yearly budget')
visit admin_budgets_path visit admin_budgets_path
@@ -202,7 +223,7 @@ feature 'Admin budgets' do
expect(page).to have_content '1 Group of budget headings' expect(page).to have_content '1 Group of budget headings'
expect(page).to have_content 'Health' expect(page).to have_content 'Health'
expect(page).to have_content 'Yearly participatory budget' expect(page).to have_content 'Yearly budget'
expect(page).not_to have_content 'No groups created yet.' expect(page).not_to have_content 'No groups created yet.'
visit admin_budgets_path visit admin_budgets_path
@@ -212,12 +233,12 @@ feature 'Admin budgets' do
expect(page).to have_content '1 Group of budget headings' expect(page).to have_content '1 Group of budget headings'
expect(page).to have_content 'Health' expect(page).to have_content 'Health'
expect(page).to have_content 'Yearly participatory budget' expect(page).to have_content 'Yearly budget'
expect(page).not_to have_content 'No groups created yet.' expect(page).not_to have_content 'No groups created yet.'
end end
scenario 'Create heading', :js do scenario 'Create heading', :js do
budget = create(:budget, name: 'Yearly participatory budget') budget = create(:budget, name: 'Yearly budget')
group = create(:budget_group, budget: budget, name: 'Districts improvments') group = create(:budget_group, budget: budget, name: 'Districts improvments')
visit admin_budget_path(budget) visit admin_budget_path(budget)
@@ -245,7 +266,7 @@ feature 'Admin budgets' do
end end
scenario 'Update heading', :js do scenario 'Update heading', :js do
budget = create(:budget, name: 'Yearly participatory budget') budget = create(:budget, name: 'Yearly budget')
group = create(:budget_group, budget: budget, name: 'Districts improvments') group = create(:budget_group, budget: budget, name: 'Districts improvments')
heading = create(:budget_heading, group: group, name: "District 1") heading = create(:budget_heading, group: group, name: "District 1")
heading = create(:budget_heading, group: group, name: "District 3") heading = create(:budget_heading, group: group, name: "District 3")
@@ -267,7 +288,7 @@ feature 'Admin budgets' do
end end
scenario 'Delete heading', :js do scenario 'Delete heading', :js do
budget = create(:budget, name: 'Yearly participatory budget') budget = create(:budget, name: 'Yearly budget')
group = create(:budget_group, budget: budget, name: 'Districts improvments') group = create(:budget_group, budget: budget, name: 'Districts improvments')
heading = create(:budget_heading, group: group, name: "District 1") heading = create(:budget_heading, group: group, name: "District 1")

View File

@@ -21,7 +21,7 @@ feature 'Budgets' do
expect(page).to have_content(last_budget.description) expect(page).to have_content(last_budget.description)
expect(page).to have_content("Actual phase") expect(page).to have_content("Actual phase")
expect(page).to have_content("Accepting projects") expect(page).to have_content("Accepting projects")
expect(page).to have_link 'Help about participatory budgets' expect(page).to have_link 'Help with participatory budgets'
expect(page).to have_link 'See all phases' expect(page).to have_link 'See all phases'
end end