Skip to content

Commit

Permalink
Better winning project publishing form (#426)
Browse files Browse the repository at this point in the history
Setting a project as a winner now happens in a separate form that requires that the user set a specific funding date and encourages her to fill out a funded project description and image in order to make the overall public-facing winner page more appealing.
  • Loading branch information
jcn authored Jan 17, 2020
1 parent 4283236 commit b92f8bb
Show file tree
Hide file tree
Showing 19 changed files with 237 additions and 76 deletions.
3 changes: 2 additions & 1 deletion app/assets/javascripts/projects.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mark_as_winner_before_send = (event, data, xhr) ->

mark_as_winner_success = (event, data, status, xhr) ->
project_container = $('article[data-id="'+data.project_id+'"]')

if data.winner
project_container.addClass('winner')
project_container.find('a.mark-as-winner').attr('data-method', 'delete')
Expand All @@ -46,7 +47,7 @@ $(".short-list")
.bind("ajax:success", shortlist_success)
.bind("ajax:failure", shortlist_failure)

$(".mark-as-winner")
$(".mark-as-winner, .remove-as-winner")
.bind("ajax:beforeSend", mark_as_winner_before_send)
.bind("ajax:success", mark_as_winner_success)
.bind("ajax:failure", mark_as_winner_failure)
Expand Down
10 changes: 8 additions & 2 deletions app/assets/stylesheets/_projects-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,10 @@ body.projects-index {
}
}

&.edit-winner {
&.non-winner-action {
display: block;
}
&.winner-action {
display: none;
}
}
Expand Down Expand Up @@ -590,9 +593,12 @@ body.projects-index {
color: $blue;
font-weight: bolder;
}
li.edit-winner {
li.winner-action {
display: block;
}
li.non-winner-action {
display: none;
}
}

div.title {
Expand Down
14 changes: 14 additions & 0 deletions app/assets/stylesheets/_winners.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.center-form__cancel {
text-align: center;
}

.center-form__cancel-link {
color: $lighter-base-font-color !important;
font-size: .85em;

text-decoration: none !important;

&:hover {
text-decoration: underline !important;
}
}
1 change: 1 addition & 0 deletions app/assets/stylesheets/application.css.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
@import 'shared-flash-messages';
@import 'about';
@import 'faq';
@import 'winners';
@import 'pagination';
@import 'magnific-popup';

Expand Down
43 changes: 40 additions & 3 deletions app/controllers/winners_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class WinnersController < ApplicationController
before_action :must_be_able_to_mark_winner
before_action :must_be_able_to_mark_winner, only: [:create, :update, :destroy]

def create
@project = Project.find(params[:project_id])
Expand All @@ -9,6 +9,27 @@ def create
render :json => response_json
end

def edit
@project = FundedProject.find(params[:project_id])
@project.funded_description = @project.about_project if @project.funded_description.blank?
end

def update
@project = FundedProject.find(params[:project_id])
@project.attributes = winner_params

if @project.save
if @project.chapter_id_previously_changed? || params[:return_to].blank?
redirect_to chapter_project_path(@project.chapter, @project)
else
redirect_to params[:return_to]
end
else
flash.now[:notice] = t("flash.projects.error")
render action: "edit"
end
end

def destroy
@project = Project.find(params[:project_id])
@project.revoke_winner!
Expand All @@ -17,9 +38,20 @@ def destroy

private

def winner_params
permitted = [:funded_on, :title, :name, :url, :rss_feed_url, :funded_description, photo_ids_to_delete: [], new_photos: [], new_photo_direct_upload_urls: [] ]
permitted << :chapter_id if current_project.in_any_chapter? || current_user.admin?

params.require(:project).permit(permitted)
end

def winning_chapter
if params[:chapter_id].present?
@chapter ||= Chapter.find(params[:chapter_id])
elsif params.dig(:project, :chapter_id).present?
@chapter ||= Chapter.find(params[:project][:chapter_id])
else
@chapter ||= current_project.chapter
end
end

Expand All @@ -28,9 +60,14 @@ def current_project
end

def must_be_able_to_mark_winner
unless current_user.admin? || (current_user.can_mark_winner?(current_project) && current_user.chapters.include?(winning_chapter))
unless current_user.can_mark_winner?(current_project) && current_user.chapters.include?(winning_chapter)
flash[:notice] = t("flash.permissions.cannot-mark-winner")
render :json => { :location => chapter_projects_path(current_project.chapter) }
redirect_location = chapter_projects_path(current_project.chapter)

respond_to do |format|
format.js { render json: { :location => redirect_location } }
format.html { redirect_to redirect_location and return }
end
end
end
end
8 changes: 8 additions & 0 deletions app/helpers/projects_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ def selectable_chapters_for(user)
end
end

def winnable_chapters_for(project)
if current_user.admin?
Chapter.visitable.for_display
else
project.in_any_chapter? ? current_user.dean_chapters : Array(project.chapter)
end
end

def show_winner_buttons_for(project, options = {})
if @chapter.any_chapter?
winnable_chapters = current_user.dean_chapters
Expand Down
3 changes: 3 additions & 0 deletions app/models/funded_project.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class FundedProject < Project
validates :funded_on, presence: true
end
2 changes: 2 additions & 0 deletions app/models/project.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
require "textacular/searchable"

class Project < ApplicationRecord
MAX_PHOTOS = 5

attr_accessor :photo_order
attr_accessor :photo_ids_to_delete

Expand Down
57 changes: 3 additions & 54 deletions app/views/projects/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,7 @@
</div>
</div>

<div class="image-upload">
<% if form.object.photos.present? %>
<section class="current-images">
<label><%= t "simple_form.labels.project.current_photos-html" %></label>
<ul class="old-photos">
<%= form.input :photo_order, :as => :hidden %>
<% form.object.photos.each do |photo| %>
<li data-id="<%= photo.id %>">
<span><%= photo.image_file_name %></span>
<a href="#" class="remove-image" data-remove="true">remove</a>
</li>
<% end %>
</ul>
</section>
<% end %>

<% if s3_uploader_available? %>
<%= render :partial => "image_form", :locals => { :form => form, :upload_mechanism => :s3 } %>
<% else %>
<%= render :partial => "image_form", :locals => { :form => form, :upload_mechanism => :classic } %>
<% end %>

</div>
<%= render partial: "image_upload", locals: { form: form } %>

<div class="extra-questions" style="display:none">
<%= form.input :extra_question_1, :as => :hidden, :wrapper_html => {:class => "extra-question"} %>
Expand All @@ -62,9 +40,8 @@

<% if current_user.can_edit_project?(project) %>
<a name="winner"></a>
<div class="funded_on">
<%= form.input :funded_on, :as => :string %>
</div>

<%= render partial: "funded_date", locals: { form: form } %>

<div class="funded_description">
<%= form.input :funded_description %>
Expand All @@ -77,11 +54,7 @@

<% content_for :javascript do %>
<%= javascript_tag do %>
window.awesomeEnvironment.remainingUploads = 0;

$(window).load(function(){
$('#project_funded_on').datepicker({dateFormat: 'yy-mm-dd', onClose: function(){ $('#project_funded_on').blur() }});

// Update the remaining character counts on pageload
$('#project_about_me').keydown();
$('#project_about_project').keydown();
Expand All @@ -93,29 +66,5 @@
"<%= chapter.id %>": <%= extra_questions_json(chapter) %>,
<% end %>}

function reorderPhotos(){
var photo_order = $('.old-photos li[data-id]').map(function(x, el){
return $(el).attr('data-id')
});
$('#project_photo_order').val($.makeArray(photo_order).join(" "));
}

$(".old-photos *[data-remove='true']").on("click", function(){
var val = $('#project_photo_order').val();
val = val.replace($(this.parentNode).attr('data-id'), '');
$('#project_photo_order').val(val);

el = document.createElement('input');
el.type = 'hidden';
el.name = 'project[photo_ids_to_delete][]';
el.value = $(this.parentNode).attr('data-id');

$('#project_photo_order').after(el);
});

$(window).load(function(){
$(".old-photos").sortable({
update: reorderPhotos
})});
<% end %>
<% end %>
14 changes: 14 additions & 0 deletions app/views/projects/_funded_date.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<div class="funded_on">
<%= form.input :funded_on, as: :string, input_html: { class: "datepicker", autocomplete: "off" }, required: local_assigns[:required], hint: local_assigns[:hint] %>
</div>

<% content_for :javascript do %>
<%= javascript_tag do %>
$(window).load(function(){
$('.datepicker').datepicker({
dateFormat: 'yy-mm-dd',
onClose: function(){ $('.datepicker').blur() }
});
});
<% end %>
<% end %>
4 changes: 2 additions & 2 deletions app/views/projects/_image_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="input file optional" style="<%= 'display:none' unless display_uploader?(upload_mechanism) %>">
<label class="file optional"><%= t "simple_form.labels.project.new_photos-html" %></label>
<div class="multi-input first noremove" data-num-files="5">
<div class="multi-input first noremove" data-num-files="<%= Project::MAX_PHOTOS %>">
<% if upload_mechanism == :s3 %>
<%= form.s3_file_field :new_photos, :class => "s3_upload_field", :name => "project[new_photo_direct_upload_urls][]", :accept => "image/*" %>
<% else %>
Expand All @@ -12,5 +12,5 @@
<div class="progress-meter"></div>
</div>

<div><%= t("projects.form.image-notes", :dimensions => Photo::DIMENSIONS[:main]) %></div>
<div><%= markdown(local_assigns[:image_notes].presence || t("projects.form.image-notes", :dimensions => Photo::DIMENSIONS[:main])) %></div>
</div>
54 changes: 54 additions & 0 deletions app/views/projects/_image_upload.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<div class="image-upload">
<% if form.object.photos.present? %>
<section class="current-images">
<label><%= t "simple_form.labels.project.current_photos-html" %></label>
<ul class="old-photos">
<%= form.input :photo_order, as: :hidden %>
<% form.object.photos.each do |photo| %>
<li data-id="<%= photo.id %>">
<span><%= photo.image_file_name %></span>
<a href="#" class="remove-image" data-remove="true">remove</a>
</li>
<% end %>
</ul>
</section>
<% end %>

<% if s3_uploader_available? %>
<%= render partial: "projects/image_form", locals: { form: form, upload_mechanism: :s3, image_notes: local_assigns[:image_notes] } %>
<% else %>
<%= render partial: "projects/image_form", locals: { form: form, upload_mechanism: :classic, image_notes: local_assigns[:image_notes] } %>
<% end %>

</div>

<% content_for :javascript do %>
<%= javascript_tag do %>
window.awesomeEnvironment.remainingUploads = 0;

function reorderPhotos(){
var photo_order = $('.old-photos li[data-id]').map(function(x, el){
return $(el).attr('data-id')
});
$('#project_photo_order').val($.makeArray(photo_order).join(" "));
}

$(".old-photos *[data-remove='true']").on("click", function(){
var val = $('#project_photo_order').val();
val = val.replace($(this.parentNode).attr('data-id'), '');
$('#project_photo_order').val(val);

el = document.createElement('input');
el.type = 'hidden';
el.name = 'project[photo_ids_to_delete][]';
el.value = $(this.parentNode).attr('data-id');

$('#project_photo_order').after(el);
});

$(window).load(function(){
$(".old-photos").sortable({
update: reorderPhotos
})});
<% end %>
<% end %>
20 changes: 16 additions & 4 deletions app/views/projects/_project_details.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,23 @@
<section class="project-actions">
<ul class="icons-ul">
<% if current_user.can_mark_winner?(project) %>
<%= show_winner_buttons_for(project, { :pre => "<li>", :post => "</li>", :link_prefix => '<i class="icon-li icon-forward"></i>' }) %>
<li class="non-winner-action">
<%= link_to edit_chapter_project_winner_path(project.chapter, project, return_to: "#{request.path}#project#{project.id}") do %>
<i class="icon-li icon-forward"></i>
<%= t("projects.project.publish-as-winner") %>
<% end %>
</li>

<li class="winner-action">
<%= link_to edit_chapter_project_winner_path(project.chapter, project, return_to: "#{request.path}#project#{project.id}"), class: "mark-as-winner" do %>
<i class="icon-li icon-forward"></i> <%= t("projects.project.edit-winner") %>
<% end %>
</li>

<li class="edit-winner">
<%= link_to edit_project_path(project, :anchor => "winner") do %>
<i class="icon-li icon-ellipsis-horizontal"></i> <%= t("projects.project.edit-winner") %>
<li class="winner-action">
<%= link_to project_winner_path(project), remote: true, method: :delete, class: "remove-as-winner" do %>
<i class="icon-li icon-minus"></i>
<%= t("projects.project.unpublish-as-winner") %>
<% end %>
</li>
<% end %>
Expand Down
1 change: 1 addition & 0 deletions app/views/shared/_navigation.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<% end %>
<p><%= link_to chapter.display_name, chapter %></p>
<% end %>
</div>
</article>
</section>
</div>
Expand Down
17 changes: 17 additions & 0 deletions app/views/winners/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<%= hidden_field_tag :return_to, params[:return_to] %>
<div class="chapter-select">
<%= form.input :chapter_id, collection: winnable_chapters_for(form.object), label: t(".chapter_id"), include_blank: false, disabled: !(form.object.in_any_chapter? || current_user.admin?), hint: t(".hints.chapter_id") %>
</div>
<div class="personal-info">
<%= render partial: "projects/funded_date", locals: { form: form, required: true, hint: t(".hints.funded_on") } %>
<%= form.input :title, maxlength: 50 %>
<%= form.input :name, label: t(".name") %>
<%= form.input :url %>
<%= form.input :rss_feed_url %>
</div>
<div class="project-info">
<%= form.input :funded_description, required: true %>
</div>

<%= render partial: "projects/image_upload", locals: { form: form, image_notes: t(".image-notes") } %>

Loading

0 comments on commit b92f8bb

Please sign in to comment.