Merge branch 'master' into add_cards_to_custom_pages

This commit is contained in:
Alberto
2019-01-28 14:49:39 +01:00
committed by GitHub
57 changed files with 1010 additions and 196 deletions

View File

@@ -3,6 +3,30 @@
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html)
## [0.18.1](https://github.com/consul/consul/tree/v0.18.1) (2019-01-17)
### Added
- **Legislation:** Legislation process homepage phase [\#3188](https://github.com/consul/consul/pull/3188)
- **Legislation:** Show documents on processes proposals phase [\#3136](https://github.com/consul/consul/pull/3136)
- **Maintenance-Refactorings:** Remove semicolons from controllers [\#3160](https://github.com/consul/consul/pull/3160)
- **Maintenance-Refactorings:** Remove before action not used [\#3167](https://github.com/consul/consul/pull/3167)
- **Maintenance-Rubocop:** Enable double quotes rubocop rule [\#3175](https://github.com/consul/consul/pull/3175)
- **Maintenance-Rubocop:** Enable line length rubocop rule [\#3165](https://github.com/consul/consul/pull/3165)
- **Maintenance-Rubocop:** Add rubocop rule to indent private methods [\#3134](https://github.com/consul/consul/pull/3134)
### Changed
- **Admin:** Improve CRUD budgets and content blocks [\#3173](https://github.com/consul/consul/pull/3173)
- **Design/UX:** new CRUD budgets, content blocks and heading map [\#3150](https://github.com/consul/consul/pull/3150)
- **Design/UX:** Processes key dates [\#3137](https://github.com/consul/consul/pull/3137)
### Fixed
- **Admin:** checks for deleted proposals [\#3154](https://github.com/consul/consul/pull/3154)
- **Admin:** Add default order for admin budget investments list [\#3151](https://github.com/consul/consul/pull/3151)
- **Budgets:** Bug Management Cannot create Budget Investment without a map location [\#3133](https://github.com/consul/consul/pull/3133)
## [0.18.0](https://github.com/consul/consul/compare/v0.17...v0.18) (2018-12-27)
### Added
@@ -47,6 +71,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- **Admin:** Improve visualization for small resolution [\#3025](https://github.com/consul/consul/pull/3025)
- **Admin:** Budgets admin [\#3012](https://github.com/consul/consul/pull/3012)
- **Budgets:** Budget investments social share [\#3053](https://github.com/consul/consul/pull/3053)
- **Design/UX:** Documents title [\#3131](https://github.com/consul/consul/pull/3131)
- **Design/UX:** Proposal create question [\#3122](https://github.com/consul/consul/pull/3122)
- **Design/UX:** Budget investments price explanation [\#3121](https://github.com/consul/consul/pull/3121)
- **Design/UX:** Change CRUD for budget groups and headings [\#3106](https://github.com/consul/consul/pull/3106)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

@@ -23,8 +23,29 @@ App.Forms =
false
)
synchronizeInputs: ->
$("[name='progress_bar[percentage]']").on
input: ->
$("[name='#{this.name}']").val($(this).val())
$("[name='progress_bar[percentage]'][type='range']").trigger("input")
hideOrShowFieldsAfterSelection: ->
$("[name='progress_bar[kind]']").on
change: ->
title_field = $("[name^='progress_bar'][name$='[title]']").parent()
if this.value == "primary"
title_field.hide()
else
title_field.show()
$("[name='progress_bar[kind]']").change()
initialize: ->
App.Forms.disableEnter()
App.Forms.submitOnChange()
App.Forms.toggleLink()
App.Forms.synchronizeInputs()
App.Forms.hideOrShowFieldsAfterSelection()
false

View File

@@ -251,6 +251,13 @@ $sidebar-active: #f4fcd0;
max-width: none;
}
form {
.input-group-label {
height: $line-height * 2;
}
}
.menu.simple {
margin-bottom: $line-height / 2;

View File

@@ -6,6 +6,7 @@
@import 'admin';
@import 'layout';
@import 'participation';
@import 'milestones';
@import 'pages';
@import 'legislation';
@import 'legislation_process';

View File

@@ -0,0 +1,95 @@
.tab-milestones ul {
margin-top: rem-calc(40);
position: relative;
}
.tab-milestones .timeline li {
margin: 0 auto;
position: relative;
width: 0;
@include breakpoint(small only) {
width: 100%;
}
&::before {
background: $budget;
border-radius: rem-calc(20);
content: '';
height: rem-calc(20);
position: absolute;
top: 5px;
transform: translateX(-50%);
width: rem-calc(20);
z-index: 2;
}
&::after {
background: $light-gray;
bottom: 100%;
content: '';
height: 100%;
position: absolute;
top: 25px;
width: 1px;
z-index: 1;
}
.milestone-content {
padding: $line-height / 6 $line-height / 2;
position: relative;
@include breakpoint(medium) {
width: rem-calc(300);
}
@include breakpoint(large) {
width: rem-calc(450);
}
h3 {
margin-bottom: 0;
}
.milestone-date {
color: $text-medium;
font-size: $small-font-size;
}
}
&:nth-child(odd) {
.milestone-content {
text-align: right;
@include breakpoint(medium) {
margin-left: rem-calc(-315);
}
@include breakpoint(large) {
margin-left: rem-calc(-465);
}
@include breakpoint(small only) {
left: 15px;
text-align: left;
}
}
}
&:nth-child(even) {
.milestone-content {
left: 15px;
}
}
}
.milestone-status {
background: $budget;
border-radius: rem-calc(4);
color: #fff;
display: inline-block;
margin-top: $line-height / 6;
padding: $line-height / 4 $line-height / 2;
}

View File

@@ -523,118 +523,6 @@
}
}
.tab-milestones ul {
margin-top: rem-calc(40);
position: relative;
li {
margin: 0 auto;
position: relative;
width: 0;
}
li::before {
background: $budget;
border-radius: rem-calc(20);
content: '';
height: rem-calc(20);
position: absolute;
top: 5px;
transform: translateX(-50%);
width: rem-calc(20);
z-index: 2;
}
li::after {
background: $light-gray;
bottom: 100%;
content: '';
height: 100%;
position: absolute;
top: 25px;
width: 1px;
z-index: 1;
}
}
.tab-milestones ul .milestone-content {
padding: $line-height / 6 $line-height / 2;
position: relative;
h3 {
margin-bottom: 0;
}
.milestone-date {
color: $text-medium;
font-size: $small-font-size;
}
}
.tab-milestones .timeline ul li:nth-child(odd),
.tab-milestones .timeline ul li:nth-child(even) {
.milestone-content {
@include breakpoint(medium) {
width: rem-calc(300);
}
@include breakpoint(large) {
width: rem-calc(450);
}
}
}
.tab-milestones .timeline ul li:nth-child(odd) {
.milestone-content {
text-align: right;
@include breakpoint(medium) {
margin-left: rem-calc(-315);
}
@include breakpoint(large) {
margin-left: rem-calc(-465);
}
}
}
.tab-milestones .timeline ul li:nth-child(even) {
.milestone-content {
left: 15px;
}
}
.tab-milestones {
@include breakpoint(small only) {
.timeline ul li {
width: 100%;
&:nth-child(odd),
&:nth-child(even) {
.milestone-content {
left: 15px;
text-align: left;
}
}
}
}
}
.milestone-status {
background: $budget;
border-radius: rem-calc(4);
color: #fff;
display: inline-block;
margin-top: $line-height / 6;
padding: $line-height / 4 $line-height / 2;
}
.show-actions-menu {
[class^="icon-"] {

View File

@@ -0,0 +1,8 @@
class Admin::BudgetInvestmentProgressBarsController < Admin::ProgressBarsController
private
def progressable
Budget::Investment.find(params[:budget_investment_id])
end
end

View File

@@ -0,0 +1,14 @@
class Admin::Legislation::ProgressBarsController < Admin::ProgressBarsController
include FeatureFlags
feature_flag :legislation
def index
@process = progressable
end
private
def progressable
::Legislation::Process.find(params[:process_id])
end
end

View File

@@ -0,0 +1,69 @@
class Admin::ProgressBarsController < Admin::BaseController
include Translatable
before_action :load_progressable
before_action :load_progress_bar, only: [:edit, :update, :destroy]
helper_method :progress_bars_index
def index
end
def new
@progress_bar = @progressable.progress_bars.new
end
def create
@progress_bar = @progressable.progress_bars.new(progress_bar_params)
if @progress_bar.save
redirect_to progress_bars_index, notice: t("admin.progress_bars.create.notice")
else
render :new
end
end
def edit
end
def update
if @progress_bar.update(progress_bar_params)
redirect_to progress_bars_index, notice: t('admin.progress_bars.update.notice')
else
render :edit
end
end
def destroy
@progress_bar.destroy
redirect_to progress_bars_index, notice: t('admin.progress_bars.delete.notice')
end
private
def progress_bar_params
params.require(:progress_bar).permit(allowed_params)
end
def allowed_params
[
:kind,
:percentage,
translation_params(ProgressBar)
]
end
def load_progressable
@progressable = progressable
end
def progressable
raise "This method must be implemented in subclass #{self.class.name}"
end
def load_progress_bar
@progress_bar = progressable.progress_bars.find(params[:id])
end
def progress_bars_index
polymorphic_path([:admin, *resource_hierarchy_for(@progressable), ProgressBar.new])
end
end

View File

@@ -0,0 +1,7 @@
class Admin::ProposalProgressBarsController < Admin::ProgressBarsController
private
def progressable
Proposal.find(params[:proposal_id])
end
end

View File

@@ -11,7 +11,7 @@ class DocumentsController < ApplicationController
else
flash[:alert] = t "documents.actions.destroy.alert"
end
redirect_to params[:from]
redirect_to request.referer
end
format.js do
if @document.destroy

View File

@@ -12,7 +12,7 @@ class InstallationController < ApplicationController
def consul_installation_details
{
release: 'v0.18'
release: "v0.18.1"
}.merge(features: settings_feature_flags)
end

View File

@@ -49,4 +49,19 @@ module PollsHelper
question.answers.where(author: current_user).any? { |vote| current_user.current_sign_in_at > vote.updated_at }
end
def show_stats_or_results?
@poll.expired? && (@poll.results_enabled? || @poll.stats_enabled?)
end
def results_menu?
controller_name == "polls" && action_name == "results"
end
def stats_menu?
controller_name == "polls" && action_name == "stats"
end
def info_menu?
controller_name == "polls" && action_name == "show"
end
end

View File

@@ -93,6 +93,7 @@ module Abilities
cannot :comment_as_moderator, [::Legislation::Question, Legislation::Annotation, ::Legislation::Proposal]
can [:create], Document
can [:destroy], Document, documentable_type: "Poll::Question::Answer"
can [:create, :destroy], DirectUpload
can [:deliver], Newsletter, hidden_at: nil

View File

@@ -5,5 +5,7 @@ module Milestoneable
has_many :milestones, as: :milestoneable, dependent: :destroy
scope :with_milestones, -> { joins(:milestones).distinct }
has_many :progress_bars, as: :progressable
end
end

View File

@@ -0,0 +1,28 @@
class ProgressBar < ActiveRecord::Base
self.inheritance_column = nil
RANGE = 0..100
enum kind: %i[primary secondary]
belongs_to :progressable, polymorphic: true
translates :title, touch: true
include Globalizable
validates :progressable, presence: true
validates :kind, presence: true,
uniqueness: {
scope: [:progressable_type, :progressable_id],
conditions: -> { primary }
}
validates :percentage, presence: true, inclusion: RANGE, numericality: { only_integer: true }
before_validation :assign_progress_bar_to_translations
validates_translation :title, presence: true, unless: :primary?
private
def assign_progress_bar_to_translations
translations.each { |translation| translation.globalized_model = self }
end
end

View File

@@ -0,0 +1,3 @@
class ProgressBar::Translation < Globalize::ActiveRecord::Translation
delegate :primary?, to: :globalized_model
end

View File

@@ -4,13 +4,14 @@ class SiteCustomization::Image < ActiveRecord::Base
"social_media_icon" => [470, 246],
"social_media_icon_twitter" => [246, 246],
"apple-touch-icon-200" => [200, 200],
"budget_execution_no_image" => [800, 600]
"budget_execution_no_image" => [800, 600],
"map" => [420, 500]
}
has_attached_file :image
validates :name, presence: true, uniqueness: true, inclusion: { in: VALID_IMAGES.keys }
validates_attachment_content_type :image, content_type: ["image/png"]
validates_attachment_content_type :image, content_type: ["image/png", "image/jpeg"]
validate :check_image
def self.all_images

View File

@@ -6,7 +6,7 @@
<div class="margin-top">
<div class="small-12 medium-6 column">
<%= f.select :phase, budget_phases_select_options, selected: "drafting" %>
<%= f.select :phase, budget_phases_select_options %>
</div>
<div class="small-12 medium-3 column end">
<%= f.select :currency_symbol, budget_currency_symbol_select_options %>

View File

@@ -0,0 +1,12 @@
<% provide :title do %>
<%= "#{t("admin.header.title")} - #{t("admin.menu.legislation")}" %> -
<%= "#{@process.title} - #{t("admin.progress_bars.index.title")}" %>
<% end %>
<%= back_link_to admin_legislation_process_milestones_path(@progressable),
t("admin.legislation.processes.edit.back") %>
<h2><%= @process.title %></h2>
<%= render "admin/legislation/processes/subnav", process: @process, active: "milestones" %>
<%= render "admin/progress_bars/progress_bars", progressable: @process %>

View File

@@ -1,4 +1,8 @@
<h2><%= t("admin.milestones.index.milestone") %></h2>
<h2 class="inline-block"><%= t("admin.milestones.index.milestone") %></h2>
<%= link_to t("admin.progress_bars.manage"),
polymorphic_path([:admin, *resource_hierarchy_for(milestoneable.progress_bars.new)]),
class: "button hollow float-right" %>
<% if milestoneable.milestones.any? %>
<table>

View File

@@ -14,18 +14,12 @@
<%= render 'shared/errors', resource: @answer %>
<div class="row">
<div class="small-12 column">
<div class="documents small-12">
<%= render 'documents/nested_documents', documentable: @answer, f: f %>
</div>
<div class="documents">
<%= render 'documents/nested_documents', documentable: @answer, f: f %>
</div>
<div class="row">
<div class="actions small-12 medium-4 margin-top">
<%= f.submit(class: "button expanded", value: t("shared.save")) %>
</div>
</div>
</div>
<div class="small-12 medium-6 large-2">
<%= f.submit(class: "button expanded", value: t("shared.save")) %>
</div>
<% end %>
@@ -42,11 +36,17 @@
<%= link_to document.title, document.attachment.url %>
</td>
<td class="text-right">
<%= link_to t('documents.buttons.download_document'),
<%= link_to t("documents.buttons.download_document"),
document.attachment.url,
target: "_blank",
rel: "nofollow",
class: 'button hollow' %>
class: "button hollow" %>
<%= link_to t("admin.shared.delete"),
document_path(document),
method: :delete,
class: "button hollow alert",
data: { confirm: t("admin.actions.confirm") } %>
</td>
</tr>
<% end %>

View File

@@ -0,0 +1,31 @@
<%= render "admin/shared/globalize_locales", resource: @progress_bar %>
<%= translatable_form_for [:admin, *resource_hierarchy_for(@progress_bar)] do |f| %>
<div class="small-12 medium-6">
<%= f.enum_select :kind %>
</div>
<%= f.translatable_fields do |translations_form| %>
<div class="small-12 medium-6">
<%= translations_form.text_field :title %>
</div>
<% end %>
<% progress_options = { min: ProgressBar::RANGE.min, max: ProgressBar::RANGE.max, step: 1 } %>
<div class="small-12 medium-6 large-2">
<%= f.text_field :percentage, { type: :range,
id: "percentage_range",
class: "column" }.merge(progress_options) %>
</div>
<div class="small-12 medium-6 large-2">
<div class="input-group">
<%= f.text_field :percentage, { type: :number,
label: false,
class: "input-group-field" }.merge(progress_options) %>
<span class="input-group-label">%</span>
</div>
</div>
<%= f.submit nil, class: "button success" %>
<% end %>

View File

@@ -0,0 +1,57 @@
<h2 class="inline-block"><%= t("admin.progress_bars.index.title") %></h2>
<%= link_to t("admin.progress_bars.index.new_progress_bar"),
polymorphic_path(
[:admin, *resource_hierarchy_for(ProgressBar.new(progressable: progressable))],
action: :new
),
class: "button float-right" %>
<% if progressable.progress_bars.any? %>
<table>
<thead>
<tr>
<th><%= t("admin.progress_bars.index.table_id") %></th>
<th><%= t("admin.progress_bars.index.table_kind") %></th>
<th><%= t("admin.progress_bars.index.table_title") %></th>
<th class="text-center"><%= t("admin.progress_bars.index.table_percentage") %></th>
<th><%= t("admin.actions.actions") %></th>
</tr>
</thead>
<tbody>
<% progressable.progress_bars.each do |progress_bar| %>
<tr id="<%= dom_id(progress_bar) %>" class="progress-bar">
<td>
<%= progress_bar.id %>
</td>
<td><%= ProgressBar.human_attribute_name("kind.#{progress_bar.kind}") %></td>
<td>
<% if progress_bar.title.present? %>
<%= progress_bar.title %>
<% else %>
<strong><%= t("admin.progress_bars.index.primary") %></strong>
<% end %>
</td>
<td class="text-center">
<%= number_to_percentage(progress_bar.percentage, strip_insignificant_zeros: true) %>
</td>
<td>
<%= link_to t("admin.actions.edit"),
polymorphic_path([:admin, *resource_hierarchy_for(progress_bar)],
action: :edit),
class: "button hollow" %>
<%= link_to t("admin.actions.delete"),
polymorphic_path([:admin, *resource_hierarchy_for(progress_bar)]),
method: :delete,
class: "button hollow alert" %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<div class="callout primary">
<%= t("admin.progress_bars.index.no_progress_bars") %>
</div>
<% end %>

View File

@@ -0,0 +1,15 @@
<% if @progress_bar.primary? %>
<% bar_title = t("admin.progress_bars.edit.title.primary") %>
<% else %>
<% bar_title = t("admin.progress_bars.edit.title.secondary", title: @progress_bar.title) %>
<% end %>
<% provide :title do %>
<%= "#{t("admin.header.title")} - #{bar_title}" %>
<% end %>
<%= back_link_to progress_bars_index %>
<h2><%= bar_title %></h2>
<%= render "form" %>

View File

@@ -0,0 +1,9 @@
<% provide :title do %>
<%= "#{t("admin.header.title")} - #{t("admin.progress_bars.index.title")}" %>
<% end %>
<%= back_link_to polymorphic_path([:admin, *resource_hierarchy_for(@progressable)]) %>
<div class="clear"></div>
<%= render "admin/progress_bars/progress_bars", progressable: @progressable %>

View File

@@ -0,0 +1,9 @@
<% provide :title do %>
<%= "#{t("admin.header.title")} - #{t("admin.progress_bars.new.creating")}" %>
<% end %>
<%= back_link_to progress_bars_index %>
<h2><%= t("admin.progress_bars.new.creating") %></h2>
<%= render "form" %>

View File

@@ -9,7 +9,7 @@
</thead>
<tbody>
<% @images.each do |image| %>
<tr class="<%= image.name %>">
<tr id="image_<%= image.name %>">
<td class="small-12 medium-4">
<strong><%= image.name %></strong> (<%= image.required_width %>x<%= image.required_height %>)
</td>

View File

@@ -13,7 +13,8 @@
<% if can?(:destroy, document) %>
<br>
<%= link_to t("documents.buttons.destroy_document"),
document_path(document, from: request.url), method: :delete,
document,
method: :delete,
data: { confirm: t("documents.actions.destroy.confirm") },
class: "delete" %>
<% end %>

View File

@@ -1,24 +1,10 @@
<div class="proposal-form row">
<div class="small-12 medium-9 column">
<div class="small-12 column">
<%= back_link_to %>
<h1><%= t("proposals.new.start_new") %></h1>
<div data-alert class="callout primary">
<%= link_to help_path(anchor: "proposals"), title: t('shared.target_blank_html'), target: "_blank" do %>
<%= t("proposals.new.more_info")%>
<% end %>
</div>
<%= render "legislation/proposals/form", form_url: legislation_process_proposals_url, id: @proposal.id %>
</div>
<div class="small-12 medium-3 column">
<span class="icon-proposals float-right"></span>
<h2><%= t("proposals.new.recommendations_title") %></h2>
<ul class="recommendations">
<li><%= t("proposals.new.recommendation_one") %></li>
<li><%= t("proposals.new.recommendation_two") %></li>
<li><%= t("proposals.new.recommendation_three") %></li>
</ul>
</div>
</div>

View File

@@ -1,10 +1,9 @@
<% if current_user && current_user.administrator? ||
(@poll.expired? && (@poll.results_enabled? || @poll.stats_enabled?)) %>
<% if show_stats_or_results? %>
<div class="row margin-top">
<div class="small-12 column">
<ul class="menu simple clear">
<% if current_user && current_user.administrator? || @poll.results_enabled? %>
<% if controller_name == "polls" && action_name == "results" %>
<% if @poll.results_enabled? %>
<% if results_menu? %>
<li class="is-active">
<h2><%= t("polls.show.results_menu") %></h2>
</li>
@@ -13,8 +12,8 @@
<% end %>
<% end %>
<% if current_user && current_user.administrator? || @poll.stats_enabled? %>
<% if controller_name == "polls" && action_name == "stats" %>
<% if @poll.stats_enabled? %>
<% if stats_menu? %>
<li class="is-active">
<h2><%= t("polls.show.stats_menu") %></h2>
</li>
@@ -23,7 +22,7 @@
<% end %>
<% end %>
<% if controller_name == "polls" && action_name == "show" %>
<% if info_menu? %>
<li class="is-active">
<h2><%= t("polls.show.info_menu") %></h2>
</li>

View File

@@ -2,5 +2,5 @@
<h2 class="sidebar-title"><%= t("shared.tags_cloud.districts") %></h2>
<br>
<%= link_to map_proposals_path, id: 'map', title: t("shared.tags_cloud.districts_list") do %>
<%= image_tag("map.jpg", alt: t("shared.tags_cloud.districts_list")) %>
<%= image_tag(image_path_for("map.jpg"), alt: t("shared.tags_cloud.districts_list")) %>
<% end %>

View File

@@ -5,5 +5,13 @@ module FoundationRailsHelper
super(attribute, opts)
end
end
def enum_select(attribute, options = {}, html_options = {})
choices = object.class.send(attribute.to_s.pluralize).keys.map do |name|
[object.class.human_attribute_name("#{attribute}.#{name}"), name]
end
select attribute, choices, options, html_options
end
end
end

View File

@@ -9,6 +9,8 @@ module ActionDispatch::Routing::UrlFor
[resource.budget, resource]
when "Milestone"
[*resource_hierarchy_for(resource.milestoneable), resource]
when "ProgressBar"
[*resource_hierarchy_for(resource.progressable), resource]
when "Legislation::Annotation"
[resource.draft_version.process, resource.draft_version, resource]
when "Legislation::Proposal", "Legislation::Question", "Legislation::DraftVersion"

View File

@@ -16,6 +16,9 @@ en:
milestone/status:
one: "Milestone Status"
other: "Milestone Statuses"
progress_bar:
one: "Progress bar"
other: "Progress bars"
comment:
one: "Comment"
other: "Comments"
@@ -136,6 +139,13 @@ en:
milestone/status:
name: "Name"
description: "Description (optional)"
progress_bar:
kind: "Type"
title: "Title"
percentage: "Current progress"
progress_bar/kind:
primary: "Primary"
secondary: "Secondary"
budget/heading:
name: "Heading name"
price: "Price"
@@ -355,7 +365,7 @@ en:
proposal_notification:
attributes:
minimum_interval:
invalid: "You have to wait a minium of %{interval} days between notifications"
invalid: "You have to wait a minimum of %{interval} days between notifications"
signature:
attributes:
document_number:

View File

@@ -332,6 +332,29 @@ en:
notice: Milestone status created successfully
delete:
notice: Milestone status deleted successfully
progress_bars:
manage: "Manage progress bars"
index:
title: "Progress bars"
no_progress_bars: "There are no progress bars"
new_progress_bar: "Create new progress bar"
primary: "Primary progress bar"
table_id: "ID"
table_kind: "Type"
table_title: "Title"
table_percentage: "Current progress"
new:
creating: "Create progress bar"
edit:
title:
primary: "Edit primary progress bar"
secondary: "Edit progress bar %{title}"
create:
notice: "Progress bar created successfully!"
update:
notice: "Progress bar updated successfully"
delete:
notice: "Progress bar deleted successfully"
comments:
index:
filter: Filter
@@ -1164,6 +1187,7 @@ en:
author: Author
content: Content
created_at: Created at
delete: Delete
spending_proposals:
index:
geozone_filter_all: All zones

View File

@@ -16,6 +16,9 @@ es:
milestone/status:
one: "Estado de seguimiento"
other: "Estados de seguimiento"
progress_bar:
one: "Barra de progreso"
other: "Barras de progreso"
comment:
one: "Comentario"
other: "Comentarios"
@@ -136,6 +139,13 @@ es:
milestone/status:
name: "Nombre"
description: "Descripción (opcional)"
progress_bar:
kind: "Tipo"
title: "Título"
percentage: "Progreso"
progress_bar/kind:
primary: "Principal"
secondary: "Secundaria"
budget/heading:
name: "Nombre de la partida"
price: "Cantidad"

View File

@@ -332,6 +332,30 @@ es:
notice: Estado de seguimiento creado correctamente
delete:
notice: Estado de seguimiento eliminado correctamente
progress_bars:
manage: "Gestionar barras de progreso"
index:
title: "Barras de progreso"
no_progress_bars: "No hay barras de progreso"
new_progress_bar: "Crear nueva barra de progreso"
primary: "Barra de progreso principal"
table_id: "ID"
table_kind: "Tipo"
table_title: "Título"
table_percentage: "Progreso"
new:
creating: "Crear barra de progreso"
edit:
title:
primary: "Editar barra de progreso principal"
secondary: "Editar barra de progreso %{title}"
create:
notice: "¡Barra de progreso creada con éxito!"
update:
notice: "Barra de progreso actualizada"
delete:
notice: "Barra de progreso eliminada correctamente"
comments:
index:
filter: Filtro
@@ -1163,6 +1187,7 @@ es:
author: Autor
content: Contenido
created_at: Fecha de creación
delete: Eliminar
spending_proposals:
index:
geozone_filter_all: Todos los ámbitos de actuación

View File

@@ -135,8 +135,8 @@ es:
already_supported: Ya has apoyado este proyecto de inversión. ¡Compártelo!
support_title: Apoyar este proyecto
confirm_group:
one: "Sólo puedes apoyar proyectos en %{count} distrito. Si sigues adelante no podrás cambiar la elección de este distrito. ¿Estás seguro?"
other: "Sólo puedes apoyar proyectos en %{count} distritos. Si sigues adelante no podrás cambiar la elección de este distrito. ¿Estás seguro?"
one: "Sólo puedes apoyar proyectos en %{count} distrito. Si sigues adelante no podrás cambiar la elección de este distrito. ¿Estás seguro/a?"
other: "Sólo puedes apoyar proyectos en %{count} distritos. Si sigues adelante no podrás cambiar la elección de este distrito. ¿Estás seguro/a?"
supports:
zero: Sin apoyos
one: 1 apoyo

View File

@@ -31,6 +31,7 @@ namespace :admin do
resources :proposals, only: [:index, :show] do
resources :milestones, controller: "proposal_milestones"
resources :progress_bars, except: :show, controller: "proposal_progress_bars"
end
resources :hidden_proposals, only: :index do
@@ -67,6 +68,7 @@ namespace :admin do
resources :budget_investments, only: [:index, :show, :edit, :update] do
resources :milestones, controller: 'budget_investment_milestones'
resources :progress_bars, except: :show, controller: "budget_investment_progress_bars"
member { patch :toggle_selection }
end
@@ -203,6 +205,7 @@ namespace :admin do
end
resources :draft_versions
resources :milestones
resources :progress_bars, except: :show
resource :homepage, only: [:edit, :update]
end
end

View File

@@ -15,6 +15,10 @@ def log(msg)
@logger.info "#{msg}\n"
end
def random_locales
[I18n.default_locale, *I18n.available_locales.sample(4)].uniq
end
require_relative 'dev_seeds/settings'
require_relative 'dev_seeds/geozones'
require_relative 'dev_seeds/users'

View File

@@ -6,7 +6,7 @@ section "Creating default Milestone Statuses" do
end
section "Creating investment milestones" do
[Budget::Investment, Proposal].each do |model|
[Budget::Investment, Proposal, Legislation::Process].each do |model|
model.find_each do |record|
rand(1..5).times do
milestone = record.milestones.build(
@@ -14,7 +14,7 @@ section "Creating investment milestones" do
status_id: Milestone::Status.all.sample
)
I18n.available_locales.map do |locale|
random_locales.map do |locale|
Globalize.with_locale(locale) do
milestone.description = "Description for locale #{locale}"
milestone.title = I18n.l(Time.current, format: :datetime)
@@ -22,7 +22,24 @@ section "Creating investment milestones" do
end
end
end
if rand < 0.8
record.progress_bars.create!(kind: :primary, percentage: rand(ProgressBar::RANGE))
end
rand(0..3).times do
progress_bar = record.progress_bars.build(
kind: :secondary,
percentage: rand(ProgressBar::RANGE)
)
random_locales.map do |locale|
Globalize.with_locale(locale) do
progress_bar.title = "Description for locale #{locale}"
progress_bar.save!
end
end
end
end
end
end

View File

@@ -0,0 +1,23 @@
class CreateProgressBars < ActiveRecord::Migration
def change
create_table :progress_bars do |t|
t.integer :kind
t.integer :percentage
t.references :progressable, polymorphic: true
t.timestamps null: false
end
reversible do |change|
change.up do
ProgressBar.create_translation_table!({
title: :string
})
end
change.down do
ProgressBar.drop_translation_table!
end
end
end
end

View File

@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20181218164126) do
ActiveRecord::Schema.define(version: 20190103132925) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -1088,6 +1088,26 @@ ActiveRecord::Schema.define(version: 20181218164126) do
add_index "polls", ["starts_at", "ends_at"], name: "index_polls_on_starts_at_and_ends_at", using: :btree
create_table "progress_bar_translations", force: :cascade do |t|
t.integer "progress_bar_id", null: false
t.string "locale", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "title"
end
add_index "progress_bar_translations", ["locale"], name: "index_progress_bar_translations_on_locale", using: :btree
add_index "progress_bar_translations", ["progress_bar_id"], name: "index_progress_bar_translations_on_progress_bar_id", using: :btree
create_table "progress_bars", force: :cascade do |t|
t.integer "kind"
t.integer "percentage"
t.integer "progressable_id"
t.string "progressable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "proposal_notifications", force: :cascade do |t|
t.string "title"
t.text "body"

View File

@@ -195,19 +195,6 @@ FactoryBot.define do
reason "unfeasible"
end
factory :milestone_status, class: 'Milestone::Status' do
sequence(:name) { |n| "Milestone status #{n} name" }
sequence(:description) { |n| "Milestone status #{n} description" }
end
factory :milestone, class: 'Milestone' do
association :milestoneable, factory: :budget_investment
association :status, factory: :milestone_status
sequence(:title) { |n| "Budget investment milestone #{n} title" }
description 'Milestone description'
publication_date { Date.current }
end
factory :valuator_group, class: ValuatorGroup do
sequence(:name) { |n| "Valuator Group #{n}" }
end

View File

@@ -0,0 +1,25 @@
FactoryBot.define do
factory :milestone_status, class: "Milestone::Status" do
sequence(:name) { |n| "Milestone status #{n} name" }
sequence(:description) { |n| "Milestone status #{n} description" }
end
factory :milestone do
association :milestoneable, factory: :budget_investment
association :status, factory: :milestone_status
sequence(:title) { |n| "Milestone #{n} title" }
description "Milestone description"
publication_date { Date.current }
end
factory :progress_bar do
association :progressable, factory: :budget_investment
percentage { rand(0..100) }
kind :primary
trait(:secondary) do
kind :secondary
sequence(:title) { |n| "Progress bar #{n} title" }
end
end
end

View File

@@ -144,9 +144,13 @@ feature 'Admin budgets' do
let!(:budget) { create(:budget) }
scenario 'Show phases table' do
budget.update(phase: "selecting")
visit admin_budgets_path
click_link 'Edit budget'
expect(page).to have_select("budget_phase", selected: "Selecting projects")
within '#budget-phases-table' do
Budget::Phase::PHASE_KINDS.each do |phase_kind|

View File

@@ -0,0 +1,44 @@
require "rails_helper"
feature "Documents" do
background do
admin = create(:administrator)
login_as(admin.user)
end
context "Index" do
scenario "Answer with no documents" do
answer = create(:poll_question_answer, question: create(:poll_question))
document = create(:document)
visit admin_answer_documents_path(answer)
expect(page).not_to have_content(document.title)
end
scenario "Answer with documents" do
answer = create(:poll_question_answer, question: create(:poll_question))
document = create(:document, documentable: answer)
visit admin_answer_documents_path(answer)
expect(page).to have_content(document.title)
end
end
scenario "Remove document from answer", :js do
answer = create(:poll_question_answer, question: create(:poll_question))
document = create(:document, documentable: answer)
visit admin_answer_documents_path(answer)
expect(page).to have_content(document.title)
accept_confirm "Are you sure?" do
click_link "Delete"
end
expect(page).not_to have_content(document.title)
end
end

View File

@@ -7,22 +7,57 @@ feature "Admin custom images" do
login_as(admin.user)
end
scenario "Upload valid image" do
scenario "Upload valid png image" do
visit admin_root_path
within("#side_menu") do
click_link "Custom images"
end
within("tr.logo_header") do
within("tr#image_logo_header") do
attach_file "site_customization_image_image", "spec/fixtures/files/logo_header.png"
click_button "Update"
end
expect(page).to have_css("tr.logo_header img[src*='logo_header.png']")
expect(page).to have_css("tr#image_logo_header img[src*='logo_header.png']")
expect(page).to have_css("img[src*='logo_header.png']", count: 1)
end
scenario "Upload valid jpg image" do
visit admin_root_path
within("#side_menu") do
click_link "Custom images"
end
within("tr#image_map") do
attach_file "site_customization_image_image", "spec/fixtures/files/custom_map.jpg"
click_button "Update"
end
expect(page).to have_css("tr#image_map img[src*='custom_map.jpg']")
expect(page).to have_css("img[src*='custom_map.jpg']", count: 1)
end
scenario "Image is replaced on front view" do
visit admin_root_path
within("#side_menu") do
click_link "Custom images"
end
within("tr#image_map") do
attach_file "site_customization_image_image", "spec/fixtures/files/custom_map.jpg"
click_button "Update"
end
visit proposals_path
within("#map") do
expect(page).to have_css("img[src*='custom_map.jpg']")
end
end
scenario "Upload invalid image" do
visit admin_root_path
@@ -30,7 +65,7 @@ feature "Admin custom images" do
click_link "Custom images"
end
within("tr.social_media_icon") do
within("tr#image_social_media_icon") do
attach_file "site_customization_image_image", "spec/fixtures/files/logo_header.png"
click_button "Update"
end
@@ -46,14 +81,14 @@ feature "Admin custom images" do
click_link "Custom images"
end
within("tr.social_media_icon") do
within("tr#image_social_media_icon") do
attach_file "site_customization_image_image", "spec/fixtures/files/social_media_icon.png"
click_button "Update"
end
expect(page).to have_css("img[src*='social_media_icon.png']")
within("tr.social_media_icon") do
within("tr#image_social_media_icon") do
click_link "Delete"
end

View File

@@ -474,21 +474,28 @@ feature 'Polls' do
expect(page).to have_content("You do not have permission to carry out the action 'stats' on poll.")
end
scenario "Show poll results and stats if user is administrator" do
poll = create(:poll, :current, results_enabled: false, stats_enabled: false)
user = create(:administrator).user
scenario "Do not show poll results or stats if are disabled" do
poll = create(:poll, :expired, results_enabled: false, stats_enabled: false)
question1 = create(:poll_question, poll: poll)
create(:poll_question_answer, question: question1, title: "Han Solo")
create(:poll_question_answer, question: question1, title: "Chewbacca")
question2 = create(:poll_question, poll: poll)
create(:poll_question_answer, question: question2, title: "Leia")
create(:poll_question_answer, question: question2, title: "Luke")
user = create(:user)
admin = create(:administrator).user
login_as user
visit poll_path(poll)
expect(page).to have_content("Poll results")
expect(page).to have_content("Participation statistics")
expect(page).not_to have_content("Poll results")
expect(page).not_to have_content("Participation statistics")
visit results_poll_path(poll)
expect(page).to have_content("Questions")
login_as admin
visit poll_path(poll)
visit stats_poll_path(poll)
expect(page).to have_content("Participation data")
expect(page).not_to have_content("Poll results")
expect(page).not_to have_content("Participation statistics")
end
end
end

View File

@@ -422,8 +422,53 @@ feature 'Proposal Notifications' do
context "Limits" do
pending "Cannot send more than one notification within established interval"
pending "use timecop to make sure notifications can be sent after time interval"
scenario "Cannot send more than one notification within established interval" do
author = create(:user)
proposal = create(:proposal, author: author)
login_as author.reload
visit new_proposal_notification_path(proposal_id: proposal.id)
fill_in "Title", with: "Thank you for supporting my proposal"
fill_in "Message", with: "Please share it with others so we can make it happen!"
click_button "Send message"
expect(page).to have_content "Your message has been sent correctly."
visit new_proposal_notification_path(proposal_id: proposal.id)
fill_in "Title", with: "Thank you again for supporting my proposal"
fill_in "Message", with: "Please share it again with others so we can make it happen!"
click_button "Send message"
expect(page).to have_content "You have to wait a minimum of 3 days between notifications"
expect(page).not_to have_content "Your message has been sent correctly."
end
scenario "Use time traveling to make sure notifications can be sent after time interval" do
author = create(:user)
proposal = create(:proposal, author: author)
login_as author.reload
visit new_proposal_notification_path(proposal_id: proposal.id)
fill_in "Title", with: "Thank you for supporting my proposal"
fill_in "Message", with: "Please share it with others so we can make it happen!"
click_button "Send message"
expect(page).to have_content "Your message has been sent correctly."
travel 3.days + 1.second
visit new_proposal_notification_path(proposal_id: proposal.id)
fill_in "Title", with: "Thank you again for supporting my proposal"
fill_in "Message", with: "Please share it again with others so we can make it happen!"
click_button "Send message"
expect(page).to have_content "Your message has been sent correctly."
expect(page).not_to have_content "You have to wait a minimum of 3 days between notifications"
travel_back
end
end

BIN
spec/fixtures/files/custom_map.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -0,0 +1,97 @@
require "rails_helper"
describe ProgressBar do
let(:progress_bar) { build(:progress_bar) }
it "is valid" do
expect(progress_bar).to be_valid
end
it "is valid without a title" do
progress_bar.title = nil
expect(progress_bar).to be_valid
end
it "is not valid with a custom type" do
expect { progress_bar.kind = "terciary" }.to raise_exception(ArgumentError)
end
it "is not valid without a percentage" do
progress_bar.percentage = nil
expect(progress_bar).not_to be_valid
end
it "is not valid with a non-numeric percentage" do
progress_bar.percentage = "High"
expect(progress_bar).not_to be_valid
end
it "is not valid with a non-integer percentage" do
progress_bar.percentage = 22.83
expect(progress_bar).not_to be_valid
end
it "is not valid with a negative percentage" do
progress_bar.percentage = -1
expect(progress_bar).not_to be_valid
end
it "is not valid with a percentage bigger than 100" do
progress_bar.percentage = 101
expect(progress_bar).not_to be_valid
end
it "is valid with an integer percentage within the limits" do
progress_bar.percentage = 0
expect(progress_bar).to be_valid
progress_bar.percentage = 100
expect(progress_bar).to be_valid
progress_bar.percentage = 83
expect(progress_bar).to be_valid
end
it "is not valid without a progressable" do
progress_bar.progressable = nil
expect(progress_bar).not_to be_valid
end
it "cannot have another primary progress bar for the same progressable" do
progress_bar.save
duplicate = build(:progress_bar, progressable: progress_bar.progressable)
expect(duplicate).not_to be_valid
end
describe "secondary progress bar" do
let(:progress_bar) { build(:progress_bar, :secondary) }
it "is valid" do
expect(progress_bar).to be_valid
end
it "is invalid without a title" do
progress_bar.title = nil
expect(progress_bar).not_to be_valid
end
it "can have another secondary progress bar for the same progressable" do
progress_bar.save
duplicate = build(:progress_bar, progressable: progress_bar.progressable)
expect(duplicate).to be_valid
end
end
end

View File

@@ -50,7 +50,7 @@ describe ProposalNotification do
Setting[:proposal_notification_minimum_interval_in_days] = 3
end
it "is not valid if below minium interval" do
it "is not valid if below minimum interval" do
proposal = create(:proposal)
notification1 = create(:proposal_notification, proposal: proposal)
@@ -60,7 +60,7 @@ describe ProposalNotification do
expect(notification2).not_to be_valid
end
it "is valid if notifications above minium interval" do
it "is valid if notifications above minimum interval" do
proposal = create(:proposal)
notification1 = create(:proposal_notification, proposal: proposal, created_at: 4.days.ago)

View File

@@ -1,4 +1,5 @@
shared_examples "admin_milestoneable" do |factory_name, path_name|
it_behaves_like "progressable", factory_name, path_name
feature "Admin milestones" do
let!(:milestoneable) { create(factory_name) }

View File

@@ -0,0 +1,115 @@
shared_examples "progressable" do |factory_name, path_name|
let!(:progressable) { create(factory_name) }
feature "Manage progress bars" do
let(:progressable_path) { send(path_name, *resource_hierarchy_for(progressable)) }
let(:path) do
polymorphic_path([:admin, *resource_hierarchy_for(progressable.progress_bars.new)])
end
context "Index" do
scenario "Link to index path" do
create(:progress_bar, :secondary, progressable: progressable,
title: "Reading documents",
percentage: 20)
visit progressable_path
click_link "Manage progress bars"
expect(page).to have_content "Reading documents"
end
scenario "No progress bars" do
visit path
expect(page).to have_content("There are no progress bars")
end
end
context "New" do
scenario "Primary progress bar", :js do
visit path
click_link "Create new progress bar"
select "Primary", from: "Type"
expect(page).not_to have_field "Title"
fill_in "Current progress", with: 43
click_button "Create Progress bar"
expect(page).to have_content "Progress bar created successfully"
expect(page).to have_content "43%"
expect(page).to have_content "Primary"
expect(page).to have_content "Primary progress bar"
end
scenario "Secondary progress bar", :js do
visit path
click_link "Create new progress bar"
select "Secondary", from: "Type"
fill_in "Current progress", with: 36
fill_in "Title", with: "Plant trees"
click_button "Create Progress bar"
expect(page).to have_content "Progress bar created successfully"
expect(page).to have_content "36%"
expect(page).to have_content "Secondary"
expect(page).to have_content "Plant trees"
end
end
context "Edit" do
scenario "Primary progress bar", :js do
bar = create(:progress_bar, progressable: progressable)
visit path
within("#progress_bar_#{bar.id}") { click_link "Edit" }
expect(page).to have_field "Current progress"
expect(page).not_to have_field "Title"
fill_in "Current progress", with: 44
click_button "Update Progress bar"
expect(page).to have_content "Progress bar updated successfully"
within("#progress_bar_#{bar.id}") do
expect(page).to have_content "44%"
end
end
scenario "Secondary progress bar", :js do
bar = create(:progress_bar, :secondary, progressable: progressable)
visit path
within("#progress_bar_#{bar.id}") { click_link "Edit" }
fill_in "Current progress", with: 76
fill_in "Title", with: "Updated title"
click_button "Update Progress bar"
expect(page).to have_content "Progress bar updated successfully"
within("#progress_bar_#{bar.id}") do
expect(page).to have_content "76%"
expect(page).to have_content "Updated title"
end
end
end
context "Delete" do
scenario "Remove progress bar" do
bar = create(:progress_bar, progressable: progressable, percentage: 34)
visit path
within("#progress_bar_#{bar.id}") { click_link "Delete" }
expect(page).to have_content "Progress bar deleted successfully"
expect(page).not_to have_content "34%"
end
end
end
end