Merge pull request #3106 from consul/2918-crud_budget_groups_headings

Change CRUD for budget groups and headings
This commit is contained in:
Julian Nicolas Herrero
2018-12-18 10:23:24 +01:00
committed by GitHub
50 changed files with 832 additions and 507 deletions

View File

@@ -879,6 +879,13 @@ footer {
}
}
.sidebar-map {
.map {
z-index: -1;
}
}
.sidebar-title {
border-top: 2px solid $brand;
display: inline-block;

View File

@@ -2,20 +2,60 @@ class Admin::BudgetGroupsController < Admin::BaseController
include FeatureFlags
feature_flag :budgets
before_action :load_budget
before_action :load_group, except: [:index, :new, :create]
def index
@groups = @budget.groups.order(:id)
end
def new
@group = @budget.groups.new
end
def edit
end
def create
@budget = Budget.find(params[:budget_id])
@budget.groups.create(budget_group_params)
@groups = @budget.groups.includes(:headings)
@group = @budget.groups.new(budget_group_params)
if @group.save
redirect_to groups_index, notice: t("admin.budget_groups.create.notice")
else
render :new
end
end
def update
@budget = Budget.find(params[:budget_id])
@group = @budget.groups.find(params[:id])
@group.update(budget_group_params)
if @group.update(budget_group_params)
redirect_to groups_index, notice: t("admin.budget_groups.update.notice")
else
render :edit
end
end
def destroy
if @group.headings.any?
redirect_to groups_index, alert: t("admin.budget_groups.destroy.unable_notice")
else
@group.destroy
redirect_to groups_index, notice: t("admin.budget_groups.destroy.success_notice")
end
end
private
def load_budget
@budget = Budget.includes(:groups).find(params[:budget_id])
end
def load_group
@group = @budget.groups.find(params[:id])
end
def groups_index
admin_budget_groups_path(@budget)
end
def budget_group_params
params.require(:budget_group).permit(:name, :max_votable_headings)
end

View File

@@ -2,36 +2,65 @@ class Admin::BudgetHeadingsController < Admin::BaseController
include FeatureFlags
feature_flag :budgets
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
before_action :load_budget
before_action :load_group
before_action :load_heading, except: [:index, :new, :create]
def index
@headings = @group.headings.order(:id)
end
def new
@heading = @group.headings.new
end
def edit
@budget = Budget.find(params[:budget_id])
@budget_group = @budget.groups.find(params[:budget_group_id])
@heading = Budget::Heading.find(params[:id])
end
def create
@heading = @group.headings.new(budget_heading_params)
if @heading.save
redirect_to headings_index, notice: t('admin.budget_headings.create.notice')
else
render :new
end
end
def update
@budget = Budget.find(params[:budget_id])
@budget_group = @budget.groups.find(params[:budget_group_id])
@heading = Budget::Heading.find(params[:id])
@heading.assign_attributes(budget_heading_params)
render :edit unless @heading.save
if @heading.update(budget_heading_params)
redirect_to headings_index, notice: t('admin.budget_headings.update.notice')
else
render :edit
end
end
def destroy
@heading = Budget::Heading.find(params[:id])
@heading.destroy
@budget = Budget.find(params[:budget_id])
redirect_to admin_budget_path(@budget)
if @heading.can_be_deleted?
@heading.destroy
redirect_to headings_index, notice: t('admin.budget_headings.destroy.success_notice')
else
redirect_to headings_index, alert: t('admin.budget_headings.destroy.unable_notice')
end
end
private
def load_budget
@budget = Budget.includes(:groups).find(params[:budget_id])
end
def load_group
@group = @budget.groups.find(params[:group_id])
end
def load_heading
@heading = @group.headings.find(params[:id])
end
def headings_index
admin_budget_group_headings_path(@budget, @group)
end
def budget_heading_params
params.require(:budget_heading).permit(:name, :price, :population, :allow_custom_content, :latitude, :longitude)
end

View File

@@ -11,12 +11,13 @@ class Admin::BudgetsController < Admin::BaseController
end
def show
@budget = Budget.includes(groups: :headings).find(params[:id])
end
def new; end
def new
end
def edit; end
def edit
end
def calculate_winners
return unless @budget.balloting_process?

View File

@@ -17,6 +17,7 @@ module Budgets
def create
load_investment
load_heading
load_map
@ballot.add_investment(@investment)
end
@@ -24,6 +25,7 @@ module Budgets
def destroy
@investment = @line.investment
load_heading
load_map
@line.destroy
load_investments
@@ -74,6 +76,12 @@ module Budgets
@ballot_referer = session[:ballot_referer]
end
def load_map
@investments ||= []
@investments_map_coordinates = MapLocation.where(investment: @investments).map(&:json_data)
@map_location = MapLocation.load_from_heading(@heading)
end
end
end
end

View File

@@ -1,6 +1,5 @@
module Budgets
class InvestmentsController < ApplicationController
OSM_DISTRICT_LEVEL_ZOOM = 12
include FeatureFlags
include CommentableActions
@@ -180,10 +179,7 @@ module Budgets
end
def load_map
@map_location = MapLocation.new
@map_location.zoom = OSM_DISTRICT_LEVEL_ZOOM
@map_location.latitude = @heading.latitude.to_f
@map_location.longitude = @heading.longitude.to_f
@map_location = MapLocation.load_from_heading(@heading)
end
end

View File

@@ -89,10 +89,6 @@ module AdminHelper
user_roles(user).join(", ")
end
def display_budget_goup_form(group)
group.errors.messages.size > 0 ? "" : "display:none"
end
private
def namespace

View File

@@ -1,5 +1,7 @@
class Budget
class Heading < ActiveRecord::Base
OSM_DISTRICT_LEVEL_ZOOM = 12.freeze
include Sluggable
belongs_to :group
@@ -12,9 +14,9 @@ class Budget
validates :price, presence: true
validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/
validates :population, numericality: { greater_than: 0 }, allow_nil: true
validates :latitude, length: { maximum: 22, minimum: 1 }, presence: true, \
validates :latitude, length: { maximum: 22 }, allow_blank: true, \
format: /\A(-|\+)?([1-8]?\d(?:\.\d{1,})?|90(?:\.0{1,6})?)\z/
validates :longitude, length: { maximum: 22, minimum: 1}, presence: true, \
validates :longitude, length: { maximum: 22 }, allow_blank: true, \
format: /\A(-|\+)?((?:1[0-7]|[1-9])?\d(?:\.\d{1,})?|180(?:\.0{1,})?)\z/
delegate :budget, :budget_id, to: :group, allow_nil: true

View File

@@ -18,4 +18,12 @@ class MapLocation < ActiveRecord::Base
}
end
def self.load_from_heading(heading)
map = new
map.zoom = Budget::Heading::OSM_DISTRICT_LEVEL_ZOOM
map.latitude = heading.latitude.to_f if heading.latitude.present?
map.longitude = heading.longitude.to_f if heading.latitude.present?
map
end
end

View File

@@ -0,0 +1,18 @@
<div class="small-12 medium-6">
<%= form_for [:admin, @budget, @group], url: path do |f| %>
<%= f.text_field :name,
label: t("admin.budget_groups.form.name"),
maxlength: 50,
placeholder: t("admin.budget_groups.form.name") %>
<% if @group.persisted? %>
<%= f.select :max_votable_headings,
(1..@group.headings.count),
label: t("admin.budget_groups.max_votable_headings"),
placeholder: t("admin.budget_groups.max_votable_headings") %>
<% end %>
<%= f.submit t("admin.budget_groups.form.#{action}"), class: "button success" %>
<% end %>
</div>

View File

@@ -0,0 +1,5 @@
<%= back_link_to admin_budget_groups_path(@budget) %>
<h2><%= @budget.name %></h2>
<h3><%= t("admin.budget_groups.form.#{action}") %></h3>

View File

@@ -1,2 +0,0 @@
$("#<%= dom_id(@budget) %>_groups").html('<%= j render("admin/budgets/groups", groups: @groups) %>');
App.Forms.toggleLink();

View File

@@ -0,0 +1,3 @@
<%= render "header", action: "edit" %>
<%= render "form", path: admin_budget_group_path(@budget, @group), action: "edit" %>

View File

@@ -0,0 +1,44 @@
<h2 class="inline-block"><%= @budget.name %></h2>
<%= link_to t("admin.budget_groups.form.create"),
new_admin_budget_group_path,
class: "button float-right" %>
<% if @groups.any? %>
<h3><%= t("admin.budget_groups.amount", count: @groups.count) %></h3>
<table>
<thead>
<tr id="<%= dom_id(@budget) %>">
<th><%= t("admin.budget_groups.name") %></th>
<th><%= t("admin.budget_groups.max_votable_headings") %></th>
<th><%= t("admin.budget_groups.headings_name") %></th>
<th><%= t("admin.budget_groups.headings_edit") %></th>
<th><%= t("admin.actions.actions") %></th>
</tr>
</thead>
<tbody>
<% @groups.each do |group| %>
<tr id="<%= dom_id(group) %>">
<td><%= link_to group.name, edit_admin_budget_group_path(@budget, group) %></td>
<td><%= group.max_votable_headings %></td>
<td><%= group.headings.count %></td>
<td><%= link_to t("admin.budget_groups.headings_manage"),
admin_budget_group_headings_path(@budget, group) %></td>
<td>
<%= link_to t("admin.actions.edit"),
edit_admin_budget_group_path(@budget, group),
class: "button hollow" %>
<%= link_to t("admin.actions.delete"),
admin_budget_group_path(@budget, group),
method: :delete,
class: "button hollow alert" %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<div class="callout primary clear">
<%= t("admin.budget_groups.no_groups") %>
</div>
<% end %>

View File

@@ -0,0 +1,3 @@
<%= render "header", action: "create" %>
<%= render "form", path: admin_budget_groups_path(@budget), action: "create" %>

View File

@@ -1,7 +0,0 @@
<% if @group.errors.any? %>
$("#group-form-<%= @group.id %>").html('<%= j render("admin/budgets/group_form", group: @group, budget: @group.budget, button_title: t("admin.budgets.form.submit"), id: "group-form-#{@group.id}", css_class: "group-toggle-#{@group.id}" ) %>');
<% else %>
$("#group-name-<%= @group.id %>").html('<%= @group.name %>')
$("#max-heading-label-<%=@group.id%>").html('<%= j render("admin/budgets/max_headings_label", current: @group.max_votable_headings, max: @group.headings.count, group: @group) %>')
$(".group-toggle-<%= @group.id %>").toggle()
<% end %>

View File

@@ -1,7 +0,0 @@
<div id='error_explanation' class='callout alert'>
<ul>
<% errors.each do |error| %>
<li><%= error %></li>
<% end %>
</ul>
</div>

View File

@@ -0,0 +1,40 @@
<div class="small-12 medium-6">
<%= form_for [:admin, @budget, @group, @heading], url: path do |f| %>
<%= f.text_field :name,
label: t("admin.budget_headings.form.name"),
maxlength: 50,
placeholder: t("admin.budget_headings.form.name") %>
<%= f.text_field :price,
label: t("admin.budget_headings.form.amount"),
maxlength: 8,
placeholder: t("admin.budget_headings.form.amount") %>
<%= f.label :population, t("admin.budget_headings.form.population") %>
<p class="help-text" id="budgets-population-help-text">
<%= t("admin.budget_headings.form.population_info") %>
</p>
<%= f.text_field :population,
label: false,
maxlength: 8,
placeholder: t("admin.budget_headings.form.population"),
data: {toggle_focus: "population-info"},
aria: {describedby: "budgets-population-help-text"} %>
<%= f.text_field :latitude,
label: t("admin.budget_headings.form.latitude"),
maxlength: 22,
placeholder: "latitude" %>
<%= f.text_field :longitude,
label: t("admin.budget_headings.form.longitude"),
maxlength: 22,
placeholder: "longitude" %>
<%= f.check_box :allow_custom_content, label: t("admin.budget_headings.form.allow_content_block") %>
<%= f.submit t("admin.budget_headings.form.#{action}"), class: "button success" %>
<% end %>
</div>

View File

@@ -0,0 +1,5 @@
<%= back_link_to admin_budget_group_headings_path(@budget, @group) %>
<h2><%= "#{@budget.name} / #{@group.name}" %></h2>
<h3><%= t("admin.budget_headings.form.#{action}") %></h3>

View File

@@ -1,2 +0,0 @@
$("#<%= dom_id(@budget_group) %>").html('<%= j render("admin/budgets/group", group: @budget_group, headings: @headings) %>');
App.Forms.toggleLink();

View File

@@ -0,0 +1,3 @@
<%= render "header", action: "edit" %>
<%= render "form", path: admin_budget_group_heading_path(@budget, @group, @heading), action: "edit" %>

View File

@@ -1 +0,0 @@
$("#heading-<%=@heading.id%>").html("<td colspan='4'><%= j render("admin/budgets/heading_form", group: @budget_group, budget: @budget, heading: @heading) %></td>");

View File

@@ -0,0 +1,47 @@
<%= back_link_to admin_budget_groups_path(@budget) %>
<div class="clear"></div>
<h2 class="inline-block"><%= "#{@budget.name} / #{@group.name}" %></h2>
<%= link_to t("admin.budget_headings.form.create"),
new_admin_budget_group_heading_path,
class: "button float-right" %>
<% if @headings.any? %>
<h3><%= t("admin.budget_headings.amount", count: @headings.count) %></h3>
<table>
<thead>
<tr id="<%= dom_id(@group) %>">
<th><%= t("admin.budget_headings.name") %></th>
<th><%= t("admin.budget_headings.form.amount") %></th>
<th><%= t("admin.budget_headings.form.population") %></th>
<th><%= t("admin.budget_headings.form.allow_content_block") %></th>
<th><%= t("admin.actions.actions") %></th>
</tr>
</thead>
<tbody>
<% @headings.each do |heading| %>
<tr id="<%= dom_id(heading) %>">
<td><%= link_to heading.name, edit_admin_budget_group_heading_path(@budget, @group, heading) %></td>
<td><%= @budget.formatted_heading_price(heading) %></td>
<td><%= heading.population %></td>
<td>
<%= heading.allow_custom_content ? t("admin.shared.true_value") : t("admin.shared.false_value") %>
</td>
<td>
<%= link_to t("admin.actions.edit"),
edit_admin_budget_group_heading_path(@budget, @group, heading),
class: "button hollow" %>
<%= link_to t("admin.actions.delete"),
admin_budget_group_heading_path(@budget, @group, heading),
method: :delete,
class: "button hollow alert" %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<div class="callout primary clear">
<%= t("admin.budget_headings.no_headings") %>
</div>
<% end %>

View File

@@ -0,0 +1,3 @@
<%= render "header", action: "create" %>
<%= render "form", path: admin_budget_group_headings_path(@budget, @group), action: "create" %>

View File

@@ -1 +0,0 @@
$("#<%= dom_id(@budget_group) %>").html('<%= j render("admin/budgets/group", group: @budget_group, headings: @budget_group.headings) %>');

View File

@@ -1,48 +0,0 @@
<table>
<thead>
<tr>
<th colspan="5" class="with-button">
<%= content_tag(:span, group.name, class:"group-toggle-#{group.id}", id:"group-name-#{group.id}") %>
<%= content_tag(:span, (render 'admin/budgets/max_headings_label', current: group.max_votable_headings, max: group.headings.count, group: group if group.max_votable_headings), class:"small group-toggle-#{group.id}", id:"max-heading-label-#{group.id}") %>
<%= render 'admin/budgets/group_form', budget: @budget, group: group, id: "group-form-#{group.id}", button_title: t("admin.budgets.form.submit"), css_class: "group-toggle-#{group.id}" %>
<%= link_to t("admin.budgets.form.edit_group"), "#", class: "button float-right js-toggle-link hollow", data: { "toggle-selector" => ".group-toggle-#{group.id}" } %>
<%= 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.present? %>
<tr>
<th><%= t("admin.budgets.form.table_heading") %></th>
<th class="text-right"><%= t("admin.budgets.form.table_amount") %></th>
<th class="text-right"><%= t("admin.budgets.form.table_population") %></th>
<th class="text-right"><%= t("admin.budgets.form.table_allow_custom_contents") %></th>
<th><%= t("admin.actions.actions") %></th>
</tr>
<% end %>
</thead>
<tbody>
<% if headings.blank? %>
<tr>
<td colspan="5">
<div class="callout primary">
<%= t("admin.budgets.form.no_heading") %>
</div>
</td>
</tr>
<% end %>
<tr id="group-<%= group.id %>-new-heading-form" style="display:none">
<td colspan="5">
<%= render "admin/budgets/heading_form", group: group, budget: @budget, heading: Budget::Heading.new %>
</td>
</tr>
<% headings.each do |heading| %>
<%= render "admin/budgets/heading", group: group, budget: @budget, heading: heading %>
<% end %>
</tbody>
</table>

View File

@@ -1,28 +0,0 @@
<%= form_for [:admin, budget, group], html: {id: id, style: display_budget_goup_form(group), class: css_class}, remote: true do |f| %>
<%= f.label :name, t("admin.budgets.form.group") %>
<div class="input-group">
<%= f.text_field :name,
label: false,
maxlength: 50,
placeholder: t("admin.budgets.form.group"),
class: "input-group-field" %>
<% if group.persisted? %>
<div class="small-12 medium-6 large-4">
<%= f.label :name, t("admin.budgets.form.max_votable_headings") %>
<%= f.select :max_votable_headings,
(1..group.headings.count),
label: false,
placeholder: t("admin.budgets.form.max_votable_headings"),
class: "input-group-field" %>
</div>
<% end %>
<div class="input-group-button">
<%= f.submit button_title, class: "button success" %>
</div>
</div>
<% end %>

View File

@@ -1,21 +0,0 @@
<h3 class="inline-block"><%= t('admin.budgets.show.groups', count: groups.count) %></h3>
<%= link_to t("admin.budgets.form.add_group"), "#", class: "button float-right js-toggle-link", data: { "toggle-selector" => "#new-group-form" } %>
<% if groups.blank? %>
<div class="callout primary clear">
<%= t("admin.budgets.form.no_groups") %>
</div>
<% end %>
<%= render 'admin/budgets/group_form',
budget: @budget,
group: Budget::Group.new,
id: "new-group-form",
button_title: t("admin.budgets.form.create_group"),
css_class: '' %>
<% groups.each do |group| %>
<div id="<%= dom_id(group) %>">
<%= render "admin/budgets/group", group: group, headings: group.headings.order(:id) %>
</div>
<% end %>

View File

@@ -1,27 +0,0 @@
<tr id="heading-<%=heading.id%>">
<td>
<%= heading.name %>
</td>
<td class="text-right">
<%= heading.budget.formatted_heading_price(heading) %>
</td>
<td class="text-right">
<%= heading.population %>
</td>
<td class="text-right">
<%= heading.allow_custom_content ? t("true_value") : t("false_value") %>
</td>
<td>
<%= link_to t("admin.actions.edit"),
edit_admin_budget_budget_group_budget_heading_path(budget_id: group.budget.id, budget_group_id: group.id, id: heading.id),
class: "button hollow",
remote: true %>
<% if heading.can_be_deleted? %>
<%= link_to t('admin.administrators.administrator.delete'),
#admin_budget_budget_group_budget_headings_path(group.budget.id, group.id),
[:admin, group.budget, group, heading],
method: :delete,
class: "button hollow alert" %>
<% end %>
</td>
</tr>

View File

@@ -1,58 +0,0 @@
<%= form_for [:admin, budget, group, heading], remote: true do |f| %>
<%= render 'shared/errors', resource: heading %>
<%= f.text_field :name,
label: t("admin.budgets.form.heading"),
maxlength: 50,
placeholder: t("admin.budgets.form.heading") %>
<div class="row">
<div class="small-12 medium-6 column">
<%= f.text_field :price,
label: t("admin.budgets.form.amount"),
maxlength: 8,
placeholder: t("admin.budgets.form.amount") %>
</div>
</div>
<div class="row">
<div class="small-12 medium-6 column">
<p class="help-text" id="budgets-population-help-text">
<%= t("admin.budgets.form.population_help_text") %>
</p>
<%= f.text_field :population,
label: t("admin.budgets.form.population"),
maxlength: 8,
placeholder: t("admin.budgets.form.population"),
data: {toggle_focus: "population-info"},
aria: {describedby: "budgets-population-help-text"} %>
</div>
<div class="small-12 medium-6 column " >
<div id="population-info" class="is-hidden" data-toggler="is-hidden">
<%= t("admin.budgets.form.population_info") %>
</div>
</div>
</div>
<div class="row">
<div class="small-12 medium-6 column">
<%= f.text_field :latitude,
label: t("admin.budgets.form.latitude"),
maxlength: 22,
placeholder: "latitude" %>
</div>
</div>
<div class="row">
<div class="small-12 medium-6 column">
<%= f.text_field :longitude,
label: t("admin.budgets.form.longitude"),
maxlength: 22,
placeholder: "longitude" %>
</div>
</div>
<div class="row">
<div class="small-12 medium-6 column">
<%= f.check_box :allow_custom_content, label: t('admin.budgets.form.allow_content_block') %>
</div>
</div>
<%= f.submit t("admin.budgets.form.save_heading"), class: "button success" %>
<% end %>

View File

@@ -1,2 +0,0 @@
<%= t("admin.budgets.form.max_votable_headings")%>
<%= t("admin.budgets.form.current_of_max_headings", current: current, max: max) %>

View File

@@ -23,7 +23,7 @@
<% @budgets.each do |budget| %>
<tr id="<%= dom_id(budget) %>" class="budget">
<td>
<%= budget.name %>
<%= link_to budget.name, admin_budget_path(budget) %>
</td>
<td class="small">
<%= t("budgets.phase.#{budget.phase}") %>
@@ -34,7 +34,7 @@
class: "button hollow medium" %>
</td>
<td class="small">
<%= link_to t("admin.budgets.index.edit_groups"), admin_budget_path(budget) %>
<%= link_to t("admin.budgets.index.edit_groups"), admin_budget_groups_path(budget) %>
</td>
<td class="small">
<%= link_to t("admin.budgets.index.edit_budget"), edit_admin_budget_path(budget) %>

View File

@@ -2,6 +2,4 @@
<h2><%= @budget.name %></h2>
<div id="<%= dom_id @budget %>_groups">
<%= render "groups", groups: @budget.groups.order(:id) %>
</div>
<%= render "form" %>

View File

@@ -54,5 +54,5 @@
<% else %>
<div class="callout primary">
<%= t("admin.site_customization.content_blocks.no_blocks") %>
</div-->
</div>
<% end %>

View File

@@ -9,3 +9,5 @@ $("#<%= dom_id(@investment) %>_ballot").html('<%= j render("/budgets/investments
investment: @investment,
investment_ids: @investment_ids,
ballot: @ballot %>
App.Map.initialize();

View File

@@ -10,3 +10,5 @@ $("#<%= dom_id(@investment) %>_ballot").html('<%= j render("/budgets/investments
investment: @investment,
investment_ids: @investment_ids,
ballot: @ballot %>
App.Map.initialize();

View File

@@ -1,9 +1,9 @@
<% if @heading.allow_custom_content %>
<br>
<br>
<ul id="content_block" class="no-bullet categories">
<% @heading_content_blocks.each do |content_block| %>
<%= raw content_block.body %>
<% end %>
</ul>
<ul id="content_block" class="no-bullet categories">
<% @heading_content_blocks.each do |content_block| %>
<%= raw content_block.body %>
<% end %>
</ul>
<% end %>

View File

@@ -2,7 +2,7 @@
<br>
<ul id="sidebar-map" class="no-bullet sidebar-map">
<div class="map">
<%= render_map(@map_location, "budgets", false, nil, @investments_map_coordinates) %>
</div>
<div class="map">
<%= render_map(@map_location, "budgets", false, nil, @investments_map_coordinates) %>
</div>
</ul>

View File

@@ -24,11 +24,12 @@
<% if @heading && !@heading.content_blocks.where(locale: I18n.locale).empty? %>
<%= render 'budgets/investments/content_blocks' %>
<% end %>
<%= render 'budgets/investments/map' %>
<% if @map_location&.available? %>
<%= render 'budgets/investments/map' %>
<% end %>
<%= render "shared/tag_cloud", taggable: 'budget/investment' %>
<%= render 'budgets/investments/categories' %>
<% if @heading && can?(:show, @ballot) %>
<div class="sidebar-divider"></div>