Implements story #136

Adds draft state for proposals
This commit is contained in:
Juan Salvador Pérez García
2018-06-07 12:19:26 +02:00
parent 83b8127b72
commit 77dd60427d
17 changed files with 176 additions and 38 deletions

View File

@@ -7,6 +7,7 @@
@import 'layout';
@import 'participation';
@import 'pages';
@import 'proposal';
@import 'legislation';
@import 'legislation_process';
@import 'community';

View File

@@ -0,0 +1,11 @@
.proposal-show {
p.centered {
text-align: center;
}
div.centered {
display: flex;
justify-content: center;
align-items: center;
}
}

View File

@@ -11,6 +11,21 @@ class Management::ProposalsController < Management::BaseController
has_orders %w{confidence_score hot_score created_at most_commented random}, only: [:index, :print]
has_orders %w{most_voted newest}, only: :show
def create
@resource = resource_model.new(strong_params.merge(author: current_user, published_at: Time.now))
if @resource.save
track_event
redirect_path = url_for(controller: controller_name, action: :show, id: @resource.id)
redirect_to redirect_path, notice: t("flash.actions.create.#{resource_name.underscore}")
else
load_categories
load_geozones
set_resource_instance
render :new
end
end
def show
super
@notifications = @proposal.notifications
@@ -37,7 +52,8 @@ class Management::ProposalsController < Management::BaseController
def proposal_params
params.require(:proposal).permit(:title, :question, :summary, :description, :external_url, :video_url,
:responsible_name, :tag_list, :terms_of_service, :geozone_id)
:responsible_name, :tag_list, :terms_of_service, :geozone_id, :skip_map,
map_location_attributes: [:latitude, :longitude, :zoom])
end
def resource_model

View File

@@ -35,13 +35,16 @@ class ProposalsController < ApplicationController
@proposal = Proposal.new(proposal_params.merge(author: current_user))
if @proposal.save
redirect_to share_proposal_path(@proposal), notice: I18n.t('flash.actions.create.proposal')
redirect_to created_proposal_path(@proposal), notice: I18n.t('flash.actions.create.proposal')
else
render :new
end
end
def created; end
def index_customization
discard_draft
discard_archived
load_retired
load_successful_proposals
@@ -88,6 +91,11 @@ class ProposalsController < ApplicationController
end
end
def publish
@proposal.publish
redirect_to share_proposal_path(@proposal), notice: t('proposals.notice.published')
end
private
def proposal_params
@@ -116,6 +124,10 @@ class ProposalsController < ApplicationController
@featured_proposals_votes = current_user ? current_user.proposal_votes(proposals) : {}
end
def discard_draft
@resources = @resources.published
end
def discard_archived
@resources = @resources.not_archived unless @current_order == "archival_date"
end
@@ -158,5 +170,4 @@ class ProposalsController < ApplicationController
@recommended_proposals = Proposal.recommendations(current_user).sort_by_random.limit(3)
end
end
end

View File

@@ -13,7 +13,7 @@ class UsersController < ApplicationController
def set_activity_counts
@activity_counts = HashWithIndifferentAccess.new(
proposals: Proposal.where(author_id: @user.id).count,
proposals: Proposal.created_by(@user).count,
debates: (Setting['feature.debates'] ? Debate.where(author_id: @user.id).count : 0),
budget_investments: (Setting['feature.budgets'] ? Budget::Investment.where(author_id: @user.id).count : 0),
comments: only_active_commentables.count,
@@ -52,7 +52,7 @@ class UsersController < ApplicationController
end
def load_proposals
@proposals = Proposal.where(author_id: @user.id).order(created_at: :desc).page(params[:page])
@proposals = Proposal.created_by(@user).order(created_at: :desc).page(params[:page])
end
def load_debates

View File

@@ -5,7 +5,7 @@ module Abilities
def initialize(user)
merge Abilities::Everyone.new(user)
can [:read, :update], User, id: user.id
can %i[read update], User, id: user.id
can :read, Debate
can :update, Debate do |debate|
@@ -16,6 +16,10 @@ module Abilities
can :update, Proposal do |proposal|
proposal.editable_by?(user)
end
can :publish, Proposal do |proposal|
proposal.draft? && proposal.author.id == user.id
end
can [:retire_form, :retire], Proposal, author_id: user.id
can :read, Legislation::Proposal
@@ -26,7 +30,7 @@ module Abilities
can :create, Comment
can :create, Debate
can :create, Proposal
can %i[create created], Proposal
can :create, Legislation::Proposal
can :suggest, Debate
@@ -60,7 +64,9 @@ module Abilities
end
if user.level_two_or_three_verified?
can :vote, Proposal
can :vote, Proposal do |proposal|
proposal.published?
end
can :vote_featured, Proposal
can :vote, SpendingProposal
can :create, SpendingProposal

View File

@@ -71,11 +71,26 @@ class Proposal < ActiveRecord::Base
scope :unsuccessful, -> { where("cached_votes_up < ?", Proposal.votes_needed_for_success) }
scope :public_for_api, -> { all }
scope :not_supported_by_user, ->(user) { where.not(id: user.find_voted_items(votable_type: "Proposal").compact.map(&:id)) }
scope :published, -> { where.not(published_at: nil) }
scope :draft, -> { where(published_at: nil) }
scope :created_by, ->(author) { unscoped.where(hidden_at: nil, author: author) }
def url
proposal_path(self)
end
def publish
update(published_at: Time.now)
end
def published?
!published_at.nil?
end
def draft?
published_at.nil?
end
def self.recommendations(user)
tagged_with(user.interests, any: true)
.where("author_id != ?", user.id)

View File

@@ -0,0 +1,12 @@
<div class=proposal-created>
<div id="<%= dom_id(@proposal) %>" class="row">
<div class="small-12 medium-12 column">
<h1><%= t '.title' %></h1>
<%= raw t '.motivation' %>
<%= link_to t('.dashboard'), '#', class: 'button' %>
<%= link_to t('.publish'), publish_proposal_path(@proposal), method: :patch, class: 'button' if can? :publish, @proposal %>
</div>
</div>
</div>

View File

@@ -27,6 +27,15 @@
description: @proposal.summary
} %>
<div class="callout light">
<p class="centered">
<strong><%= t '.improve_it' %></strong>
</p>
<div class="centered">
<%= link_to t('.dashboard'), '#', class: 'button' %>
</div>
</div>
<% if @proposal_improvement_path.present? %>
<div class="callout highlight margin-top text-center">
<p class="lead"><strong><%= t("proposals.proposal.improve_info") %></strong></p>

View File

@@ -165,34 +165,35 @@
</div>
<% end %>
<div class="sidebar-divider"></div>
<h2><%= t("votes.supports") %></h2>
<div id="<%= dom_id(@proposal) %>_votes">
<% if @proposal.successful? %>
<p>
<%= t("proposals.proposal.successful",
voting: link_to(t("proposals.proposal.voting"), polls_path)).html_safe %>
</p>
<% if can? :create, Poll::Question %>
<p class="text-center">
<%= link_to t('poll_questions.create_question'),
new_admin_question_path(proposal_id: @proposal.id),
class: "button hollow expanded" %>
</p>
<% end %>
<% elsif @proposal.archived? %>
<div class="padding text-center">
<% if @proposal.published? %>
<div class="sidebar-divider"></div>
<h2><%= t("votes.supports") %></h2>
<div id="<%= dom_id(@proposal) %>_votes">
<% if @proposal.successful? %>
<p>
<strong><%= t("proposals.proposal.supports", count: @proposal.total_votes) %></strong>
<%= t("proposals.proposal.successful",
voting: link_to(t("proposals.proposal.voting"), polls_path)).html_safe %>
</p>
<p><%= t("proposals.proposal.archived") %></p>
</div>
<% else %>
<%= render 'votes',
{ proposal: @proposal, vote_url: vote_proposal_path(@proposal, value: 'yes') } %>
<% end %>
</div>
<% if can? :create, Poll::Question %>
<p class="text-center">
<%= link_to t('poll_questions.create_question'),
new_admin_question_path(proposal_id: @proposal.id),
class: "button hollow expanded" %>
</p>
<% end %>
<% elsif @proposal.archived? %>
<div class="padding text-center">
<p>
<strong><%= t("proposals.proposal.supports", count: @proposal.total_votes) %></strong>
</p>
<p><%= t("proposals.proposal.archived") %></p>
</div>
<% else %>
<%= render 'votes',
{ proposal: @proposal, vote_url: vote_proposal_path(@proposal, value: 'yes') } %>
<% end %>
</div>
<% end %>
<%= render partial: 'shared/social_share', locals: {
share_title: t("proposals.show.share"),
title: @proposal.title,

View File

@@ -312,6 +312,22 @@ en:
create:
form:
submit_button: Create proposal
created:
title: Congratulations! You have taken the first step.
motivation: |
<p>
It is important to have at least 100 supports on the first day of the campaign to be able to
boost your proposal.
</p>
<p>
Do you think you need help to achieve this goal? If so, leave your proposal
as a draft and we will guide you.
</p>
publish: No, I want to publish the proposal
dashboard: Yes, I want help and I'll publish later
share:
improve_it: Improve your campaign and get more support.
dashboard: See more information
edit:
editing: Edit proposal
form:
@@ -415,6 +431,7 @@ en:
start_new: Create new proposal
notice:
retired: Proposal retired
published: The proposal has been published
proposal:
created: "You've created a proposal!"
share:

View File

@@ -312,6 +312,22 @@ es:
create:
form:
submit_button: Crear propuesta
created:
title: ¡Enhorabuena! Has dado el primer paso.
motivation: |
<p>
Es importante contar al menos con 100 apoyos el primer día de campaña para poder
impulsar tu propuesta.
</p>
<p>
¿Crees que necesitas ayuda para alcanzar esta meta? Si es así, deja tu propuesta
como borrador y te guiaremos.
</p>
publish: No, quiero publicar la propuesta ya
dashboard: Si, quiero ayuda y publicaré mas tarde
share:
improve_it: Mejora tu campaña y consigue mas apoyos.
dashboard: Ver mas información
edit:
editing: Editar propuesta
form:
@@ -415,6 +431,7 @@ es:
start_new: Crear una propuesta
notice:
retired: Propuesta retirada
published: La propuesta ha sido publicada
proposal:
created: "¡Has creado una propuesta!"
share:

View File

@@ -6,7 +6,9 @@ resources :proposals do
put :unflag
get :retire_form
get :share
get :created
patch :retire
patch :publish
end
collection do

View File

@@ -37,7 +37,8 @@ section "Creating Proposals" do
tag_list: tags.sample(3).join(','),
geozone: Geozone.all.sample,
skip_map: "1",
terms_of_service: "1")
terms_of_service: "1",
published_at: Time.now)
add_image_to proposal
end
end
@@ -58,7 +59,8 @@ section "Creating Archived Proposals" do
geozone: Geozone.all.sample,
skip_map: "1",
terms_of_service: "1",
created_at: Setting["months_to_archive_proposals"].to_i.months.ago)
created_at: Setting["months_to_archive_proposals"].to_i.months.ago,
published_at: Setting["months_to_archive_proposals"].to_i.months.ago)
add_image_to proposal
end
end
@@ -80,7 +82,8 @@ section "Creating Successful Proposals" do
geozone: Geozone.all.sample,
skip_map: "1",
terms_of_service: "1",
cached_votes_up: Setting["votes_for_proposal_success"])
cached_votes_up: Setting["votes_for_proposal_success"],
published_at: Time.now)
add_image_to proposal
end
@@ -99,7 +102,8 @@ section "Creating Successful Proposals" do
tag_list: tags.sample(3).join(','),
geozone: Geozone.all.sample,
skip_map: "1",
terms_of_service: "1")
terms_of_service: "1",
published_at: Time.now)
add_image_to proposal
end
end
@@ -110,5 +114,6 @@ section "Creating proposal notifications" do
body: "Proposal notification body #{i}",
author: User.all.sample,
proposal: Proposal.all.sample)
end
end

View File

@@ -0,0 +1,9 @@
class AddPublishedAtToProposal < ActiveRecord::Migration
def change
add_column :proposals, :published_at, :datetime, null: true
Proposal.draft.find_each do |proposal|
proposal.update(published_at: proposal.created_at)
end
end
end

View File

@@ -916,6 +916,7 @@ ActiveRecord::Schema.define(version: 20180711224810) do
t.string "retired_reason"
t.text "retired_explanation"
t.integer "community_id"
t.datetime "published_at"
end
add_index "proposals", ["author_id", "hidden_at"], name: "index_proposals_on_author_id_and_hidden_at", using: :btree

View File

@@ -170,6 +170,7 @@ FactoryBot.define do
responsible_name 'John Snow'
terms_of_service '1'
skip_map '1'
published_at { Time.now }
association :author, factory: :user
trait :hidden do
@@ -212,6 +213,10 @@ FactoryBot.define do
trait :successful do
cached_votes_up { Proposal.votes_needed_for_success + 100 }
end
trait :draft do
published_at nil
end
end
factory :spending_proposal do