Merge branch 'p2p-campaigns' into h-custom-layout
This commit is contained in:
parent
b99f7959ce
commit
399f6ff0c6
24 changed files with 639 additions and 20 deletions
1
app/assets/stylesheets/campaign_templates.css.scss
Normal file
1
app/assets/stylesheets/campaign_templates.css.scss
Normal file
|
@ -0,0 +1 @@
|
||||||
|
@import 'common/fundraisers';
|
|
@ -1,7 +1,7 @@
|
||||||
@import 'mixins';
|
@import 'mixins';
|
||||||
@import 'common/fundraisers';
|
@import 'common/fundraisers';
|
||||||
|
|
||||||
button, a.js-contributeButton {
|
main button, main a.js-contributeButton {
|
||||||
background: #01a490 !important;
|
background: #01a490 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,14 @@ body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: $fog;
|
background: $fog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body > .ymca-banner {
|
||||||
|
margin: auto;
|
||||||
|
width: 100vw;
|
||||||
|
height: 120px;
|
||||||
|
background-color: #01a490;
|
||||||
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 60px 0 50px 0;
|
padding: 60px 0 50px 0;
|
||||||
|
|
|
@ -36,6 +36,13 @@ class CampaignsController < ApplicationController
|
||||||
@nonprofit = current_nonprofit
|
@nonprofit = current_nonprofit
|
||||||
@url = Format::Url.concat(root_url, @campaign.url)
|
@url = Format::Url.concat(root_url, @campaign.url)
|
||||||
|
|
||||||
|
if @campaign.parent_campaign
|
||||||
|
@parent_campaign = @campaign.parent_campaign
|
||||||
|
@peer_to_peer_campaign_param = @parent_campaign.id
|
||||||
|
else
|
||||||
|
@peer_to_peer_campaign_param = @campaign.id
|
||||||
|
end
|
||||||
|
|
||||||
@campaign_background_image = FetchBackgroundImage.with_model(@campaign)
|
@campaign_background_image = FetchBackgroundImage.with_model(@campaign)
|
||||||
|
|
||||||
if @nonprofit.custom_layout.blank?
|
if @nonprofit.custom_layout.blank?
|
||||||
|
@ -55,7 +62,15 @@ class CampaignsController < ApplicationController
|
||||||
Time.use_zone(current_nonprofit.timezone || 'UTC') do
|
Time.use_zone(current_nonprofit.timezone || 'UTC') do
|
||||||
params[:campaign][:end_datetime] = Chronic.parse(params[:campaign][:end_datetime]) if params[:campaign][:end_datetime].present?
|
params[:campaign][:end_datetime] = Chronic.parse(params[:campaign][:end_datetime]) if params[:campaign][:end_datetime].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if !params[:campaign][:parent_campaign_id]
|
||||||
campaign = current_nonprofit.campaigns.create params[:campaign]
|
campaign = current_nonprofit.campaigns.create params[:campaign]
|
||||||
|
else
|
||||||
|
profile_id = params[:campaign][:profile_id]
|
||||||
|
Profile.find(profile_id).update_attributes params[:profile]
|
||||||
|
campaign = create_peer_to_peer_campaign params[:campaign], profile_id
|
||||||
|
end
|
||||||
|
|
||||||
json_saved campaign, 'Campaign created! Well done.'
|
json_saved campaign, 'Campaign created! Well done.'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -103,6 +118,31 @@ class CampaignsController < ApplicationController
|
||||||
def peer_to_peer
|
def peer_to_peer
|
||||||
session[:donor_signup_url] = request.env["REQUEST_URI"]
|
session[:donor_signup_url] = request.env["REQUEST_URI"]
|
||||||
@npo = Nonprofit.find_by_id(params[:npo_id])
|
@npo = Nonprofit.find_by_id(params[:npo_id])
|
||||||
|
@campaign = Campaign.find_by_id(params[:campaign_id])
|
||||||
|
@profile = current_user.profile if current_user
|
||||||
|
end
|
||||||
|
|
||||||
|
def custom_layout
|
||||||
|
@campaign = current_campaign
|
||||||
|
@timezone = Format::Timezone.to_proxy(current_nonprofit.timezone)
|
||||||
|
if @campaign.deleted && !current_campaign_editor?
|
||||||
|
redirect_to nonprofit_path(current_nonprofit)
|
||||||
|
flash[:notice] = "Sorry, we couldn't find that campaign"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
@nonprofit = current_nonprofit
|
||||||
|
@url = Format::Url.concat(root_url, @campaign.url)
|
||||||
|
|
||||||
|
if @campaign.parent_campaign
|
||||||
|
@parent_campaign = @campaign.parent_campaign
|
||||||
|
@peer_to_peer_campaign_param = @parent_campaign.id
|
||||||
|
else
|
||||||
|
@peer_to_peer_campaign_param = @campaign.id
|
||||||
|
end
|
||||||
|
|
||||||
|
@campaign_background_image = FetchBackgroundImage.with_model(@campaign)
|
||||||
|
|
||||||
|
render template: "nonprofits/custom_campaign_layouts/safety_around_water"
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -113,4 +153,25 @@ class CampaignsController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO: refactor
|
||||||
|
def create_peer_to_peer_campaign(params, profile_id)
|
||||||
|
parent_campaign = Campaign.find(params[:parent_campaign_id])
|
||||||
|
profile = Profile.find(profile_id)
|
||||||
|
|
||||||
|
p2p_params = params.except(:nonprofit_id, :summary,:goal_amount)
|
||||||
|
p2p_params.merge!(parent_campaign.child_params)
|
||||||
|
p2p_params[:slug] = Format::Url.convert_to_slug "#{p2p_params[:name]}-#{profile.name}"
|
||||||
|
|
||||||
|
campaign = Campaign.create(p2p_params)
|
||||||
|
|
||||||
|
return campaign unless campaign.errors.empty?
|
||||||
|
|
||||||
|
gift_option_params = []
|
||||||
|
parent_campaign.campaign_gift_options.each do |option|
|
||||||
|
excluded_for_peer_to_peer = %w(id campaign_id created_at updated_at)
|
||||||
|
campaign.campaign_gift_options.create option.attributes.except(*excluded_for_peer_to_peer)
|
||||||
|
end
|
||||||
|
|
||||||
|
campaign
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
18
app/controllers/nonprofits/campaign_templates_controller.rb
Normal file
18
app/controllers/nonprofits/campaign_templates_controller.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
module Nonprofits
|
||||||
|
class CampaignTemplatesController < ApplicationController
|
||||||
|
include NonprofitHelper
|
||||||
|
|
||||||
|
before_filter :authenticate_nonprofit_admin!, only: :create
|
||||||
|
before_filter :authenticate_nonprofit_user!, only: [:index, :show]
|
||||||
|
|
||||||
|
def index
|
||||||
|
@templates = CampaignTemplate.all
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
puts params
|
||||||
|
|
||||||
|
render :status_ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -32,7 +32,9 @@ class Campaign < ActiveRecord::Base
|
||||||
:hide_thermometer, #bool
|
:hide_thermometer, #bool
|
||||||
:hide_title, # bool
|
:hide_title, # bool
|
||||||
:receipt_message, # text
|
:receipt_message, # text
|
||||||
:hide_custom_amounts # boolean
|
:hide_custom_amounts, # boolean
|
||||||
|
:parent_campaign_id,
|
||||||
|
:reason_for_supporting
|
||||||
|
|
||||||
validate :end_datetime_cannot_be_in_past, :on => :create
|
validate :end_datetime_cannot_be_in_past, :on => :create
|
||||||
validates :profile, :presence => true
|
validates :profile, :presence => true
|
||||||
|
@ -65,6 +67,9 @@ class Campaign < ActiveRecord::Base
|
||||||
belongs_to :nonprofit
|
belongs_to :nonprofit
|
||||||
belongs_to :campaign_template
|
belongs_to :campaign_template
|
||||||
|
|
||||||
|
belongs_to :parent_campaign, class_name: 'Campaign'
|
||||||
|
has_many :children_campaigns, class_name: 'Campaign', foreign_key: 'parent_campaign_id'
|
||||||
|
|
||||||
scope :published, -> {where(:published => true)}
|
scope :published, -> {where(:published => true)}
|
||||||
scope :active, -> {where(:published => true).where("end_datetime IS NULL OR end_datetime >= ?", Date.today)}
|
scope :active, -> {where(:published => true).where("end_datetime IS NULL OR end_datetime >= ?", Date.today)}
|
||||||
scope :past, -> {where(:published => true).where("end_datetime < ?", Date.today)}
|
scope :past, -> {where(:published => true).where("end_datetime < ?", Date.today)}
|
||||||
|
@ -159,4 +164,22 @@ class Campaign < ActiveRecord::Base
|
||||||
(self.end_datetime.to_date - Date.today).to_i
|
(self.end_datetime.to_date - Date.today).to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.create_from_template(template_id)
|
||||||
|
# building params handled by another object
|
||||||
|
# not sure this method is needed eventually
|
||||||
|
end
|
||||||
|
|
||||||
|
def customizable_attributes_list
|
||||||
|
campaign_template.customizable_attributes_list if campaign_template
|
||||||
|
end
|
||||||
|
|
||||||
|
def child_params
|
||||||
|
excluded_for_peer_to_peer = %w(
|
||||||
|
id created_at updated_at slug profile_id campaign_template_id url
|
||||||
|
total_raised show_recurring_amount external_identifier parent_campaign_id
|
||||||
|
reason_for_supporting
|
||||||
|
)
|
||||||
|
excluded_for_peer_to_peer.push(customizable_attributes_list)
|
||||||
|
attributes.except(*excluded_for_peer_to_peer)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
84
app/views/campaigns/_new_peer_to_peer_modal.html.erb
Normal file
84
app/views/campaigns/_new_peer_to_peer_modal.html.erb
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<!-- partial campaigns/new-modal -->
|
||||||
|
<!-- include 'campaigns/new/index' -->
|
||||||
|
|
||||||
|
<div class='modal' id='newPeerToPeerCampaign'>
|
||||||
|
|
||||||
|
<%= render 'common/modal_header', title: @campaign.name %>
|
||||||
|
|
||||||
|
<div class='wizard-steps' style='display:none;'>
|
||||||
|
<!--= wizard.init 'new_p2p_campaign_wiz' -->
|
||||||
|
|
||||||
|
<%= render 'components/wizard/step_index', wizard_name: 'new_p2p_campaign_wiz' %>
|
||||||
|
|
||||||
|
<div class='modal-body' style='display: table;'>
|
||||||
|
|
||||||
|
<div class='wizard-step reason-step'>
|
||||||
|
<!--= wizard.set_step 'new_p2p_campaign_wiz' 'About you' -->
|
||||||
|
|
||||||
|
<form parsley-validate>
|
||||||
|
<!--= on 'submit' (def 'new_p2p_campaign' form_object) (wizard.advance 'new_p2p_campaign_wiz') -->
|
||||||
|
<input type='hidden' name='campaign[profile_id]' value='<%= current_user.profile.id %>'>
|
||||||
|
<input type='hidden' name='campaign[parent_campaign_id]' value='<%= @campaign.id %>'>
|
||||||
|
|
||||||
|
<div class='layout--three'>
|
||||||
|
<fieldset>
|
||||||
|
<label>Name</label>
|
||||||
|
<input type='text' name='profile[name]' placeholder='Your Name' value='<%= @profile.name %>'>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<label>City</label>
|
||||||
|
<input type='text' name='profile[city]' placeholder='City' value='<%= @profile.city %>'>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<label>State</label>
|
||||||
|
<%= render 'common/states_dropdown', name: 'profile[state_code]', default: @profile.state_code %>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='group'>
|
||||||
|
<fieldset class='field col-4'>
|
||||||
|
<label>Add a profile image</label>
|
||||||
|
<div class='image-upload u-margin--0' style='background-image: url("<%= @profile.picture %>");'>
|
||||||
|
<span><i class='fa fa-image'></i> Upload</span>
|
||||||
|
<input type='file' name='profile[picture]' >
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class='col-right-8'>
|
||||||
|
<label>What's your reason for supporting <%= @campaign.name %>?</label>
|
||||||
|
<textarea rows='4' name='campaign[reason_for_supporting]' placeholder='' required></textarea>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= render 'components/forms/submit_button', button_text: 'Next', scope: 'new_p2p_campaign_wiz', branded: true %>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='wizard-step amount-step'>
|
||||||
|
<!--= wizard.set_step 'new_p2p_campaign_wiz' 'Customize' -->
|
||||||
|
|
||||||
|
<form parsley-validate>
|
||||||
|
<!--= on 'submit' create_p2p_campaign -->
|
||||||
|
<!--= log new_p2p_campaign -->
|
||||||
|
|
||||||
|
<div class='u-margin--auto'>
|
||||||
|
<fieldset class='group u-marginBottom--0'>
|
||||||
|
<label class='u-paddingTop--5'>Goal Amount</label>
|
||||||
|
<div class='prepend--dollar'>
|
||||||
|
<input class='input--100 u-marginBottom--5' value='1000' type='number' name='campaign[goal_amount_dollars]' required min='1'>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= render 'components/forms/submit_button', button_text: 'Preview Campaign!', scope: 'new_p2p_campaign_wiz', branded: true %>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- end partial campaigns/new-modal -->
|
|
@ -25,6 +25,10 @@ has_mosaic: true %>
|
||||||
<a class='button orange u-marginY--10' open-modal='newCampaign' data-when-confirmed><i class='fa fa-plus'></i> New Campaign</a>
|
<a class='button orange u-marginY--10' open-modal='newCampaign' data-when-confirmed><i class='fa fa-plus'></i> New Campaign</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<a class='button green u-marginY--10' href='<%= nonprofits_campaign_templates_path(@nonprofit.id) %>'>
|
||||||
|
Campaign templates
|
||||||
|
</a>
|
||||||
|
|
||||||
<% if @active_campaigns.empty? %>
|
<% if @active_campaigns.empty? %>
|
||||||
<p class='fundraiser--active u-padding--15 u-marginTop--15'>No active campaigns</p>
|
<p class='fundraiser--active u-padding--15 u-marginTop--15'>No active campaigns</p>
|
||||||
<% else %>
|
<% else %>
|
||||||
|
|
|
@ -11,8 +11,20 @@
|
||||||
<% content_for :javascripts do %>
|
<% content_for :javascripts do %>
|
||||||
<script>
|
<script>
|
||||||
<% if @npo %>
|
<% if @npo %>
|
||||||
appl.def('selected_result', {name: '<%= @npo.name.html_safe %>', id: <%= @npo.id %>})
|
appl.def('selected_result', {
|
||||||
|
name: '<%= @npo.name.html_safe %>',
|
||||||
|
id: <%= @npo.id %>,
|
||||||
|
modal_id: 'newCampaign'
|
||||||
|
})
|
||||||
app.nonprofit_id = <%= @npo.id %>
|
app.nonprofit_id = <%= @npo.id %>
|
||||||
|
<% elsif @campaign %>
|
||||||
|
appl.def('selected_result', {
|
||||||
|
name: '<%= @campaign.nonprofit.name.html_safe %>',
|
||||||
|
parent_campaign_id: <%= @campaign.id %>,
|
||||||
|
campaign_name: '<%= @campaign.name %>',
|
||||||
|
modal_id: 'newPeerToPeerCampaign'
|
||||||
|
})
|
||||||
|
app.nonprofit_id = <%= @campaign.nonprofit.id %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if current_user %>
|
<% if current_user %>
|
||||||
appl.def('is_logged_in', true)
|
appl.def('is_logged_in', true)
|
||||||
|
@ -26,13 +38,12 @@
|
||||||
<%= IncludeAsset.js '/client/js/campaigns/peer_to_peer/page.js' %>
|
<%= IncludeAsset.js '/client/js/campaigns/peer_to_peer/page.js' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= render 'layouts/top_nav' %>
|
<div class='ymca-banner'></div>
|
||||||
|
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<header class='u-paddingX--15 container'>
|
<header class='u-paddingX--15 container'>
|
||||||
<h1>Start a campaign</h1>
|
<h1>Start my campaign</h1>
|
||||||
<h4>Start raising funds for your favorite nonprofit in less than 10 minutes.</h4>
|
|
||||||
</div>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -43,11 +54,22 @@
|
||||||
<i class='icon icon-bicycle u-fontSize--50'></i>
|
<i class='icon icon-bicycle u-fontSize--50'></i>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<h5 class='u-paddingLeft--20'>Below is a roadmap to get you started on your campaign</h5>
|
<h5 class='u-paddingLeft--20'>Below is a roadmap to get you started with your campaign</h5>
|
||||||
</td>
|
</td>
|
||||||
</tr></table>
|
</tr></table>
|
||||||
|
|
||||||
<ul class='u-paddingLeft--10 timeline--checklist'>
|
<ul class='u-paddingLeft--10 timeline--checklist'>
|
||||||
|
<% if @campaign.present? %>
|
||||||
|
<li>
|
||||||
|
<!--= set_attr_if selected_result 'class' 'is-complete' -->
|
||||||
|
<span class='annot'>10 seconds</span>
|
||||||
|
<p class='strong u-marginBottom--15'>My campaign</p>
|
||||||
|
<div class='u-paddingLeft--20 u-maxWidth--500'>
|
||||||
|
<h4><%= @campaign.name %></h4>
|
||||||
|
<p>See our other <%= link_to 'active campaigns', @campaign.nonprofit.url %>.</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<% else %>
|
||||||
<li>
|
<li>
|
||||||
<!--= set_attr_if selected_result 'class' 'is-complete' -->
|
<!--= set_attr_if selected_result 'class' 'is-complete' -->
|
||||||
<span class='annot'>10 seconds</span>
|
<span class='annot'>10 seconds</span>
|
||||||
|
@ -56,6 +78,7 @@
|
||||||
<%= render 'search_for_npo' %>
|
<%= render 'search_for_npo' %>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<!--= set_attr_if is_logged_in 'class' 'is-complete' -->
|
<!--= set_attr_if is_logged_in 'class' 'is-complete' -->
|
||||||
|
@ -107,7 +130,7 @@
|
||||||
<div class='u-centered u-padding--15'>
|
<div class='u-centered u-padding--15'>
|
||||||
<button class='button orange'>
|
<button class='button orange'>
|
||||||
<!--= show_if (all is_logged_in is_confirmed selected_result) -->
|
<!--= show_if (all is_logged_in is_confirmed selected_result) -->
|
||||||
<!--= on 'click' (open_modal 'newCampaign') -->
|
<!--= on 'click' (open_modal selected_result.modal_id) -->
|
||||||
Start Campaign
|
Start Campaign
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -118,5 +141,9 @@
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<% if current_user %>
|
<% if current_user %>
|
||||||
|
<% if @npo.present? %>
|
||||||
<%= render 'campaigns/new_modal' %>
|
<%= render 'campaigns/new_modal' %>
|
||||||
|
<% elsif @campaign.present? %>
|
||||||
|
<%= render 'campaigns/new_peer_to_peer_modal' %>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
115
app/views/nonprofits/campaign_templates/_new_modal.html.erb
Normal file
115
app/views/nonprofits/campaign_templates/_new_modal.html.erb
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
<!-- partial campaigns/new-modal -->
|
||||||
|
<!-- include 'campaigns/new/index' -->
|
||||||
|
|
||||||
|
<div class='modal' id='newCampaignTemplate'>
|
||||||
|
|
||||||
|
<%= render 'common/modal_header', title: 'New Campaign Template' %>
|
||||||
|
|
||||||
|
<!-- <div class='wizard-steps'> -->
|
||||||
|
<div class='wizard-steps' style='display:none;'>
|
||||||
|
<!--= wizard.init 'new_campaign_template_wiz' -->
|
||||||
|
|
||||||
|
<%= render 'components/wizard/step_index', wizard_name: 'new_campaign_template_wiz' %>
|
||||||
|
|
||||||
|
<div class='modal-body' style='display: table;'>
|
||||||
|
|
||||||
|
<div class='wizard-step name-step'>
|
||||||
|
<!--= wizard.set_step 'new_campaign_template_wiz' 'Name' -->
|
||||||
|
|
||||||
|
<form parsley-validate>
|
||||||
|
<!--= on 'submit' (def 'new_campaign_template' form_object) (wizard.advance 'new_campaign_template_wiz') -->
|
||||||
|
<input type='hidden' name='campaign_template[profile_id]' value='<%= current_user.profile.id %>'>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<label>Template Name</label>
|
||||||
|
<input type='text' name='campaign_template[template_name]' required parsley-trigger='change'>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<label>Campaign Name</label>
|
||||||
|
<input type='text' name='campaign_template[name]' parsley-trigger='change'>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<label>Short Tagline</label>
|
||||||
|
<input type='text' name='campaign_template[tagline]' parsley-trigger='change'>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<label>Short Description <small>(used for sharing on social media)</small></label>
|
||||||
|
<textarea name='campaign_template[summary]' parsley-maxlength='300' parsley-trigger='change'></textarea>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<%= render 'components/forms/submit_button', button_text: 'Next', scope: 'new_campaign_template_wiz', branded: true %>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='wizard-step amount-step'>
|
||||||
|
<!--= wizard.set_step 'new_campaign_template_wiz' 'Goals' -->
|
||||||
|
|
||||||
|
<form parsley-validate>
|
||||||
|
<!--= on 'submit' (def 'new_campaign_template' form_object) (wizard.advance 'new_campaign_template_wiz') -->
|
||||||
|
|
||||||
|
|
||||||
|
<div class='u-margin--auto'>
|
||||||
|
<fieldset class='group u-marginBottom--0'>
|
||||||
|
<label class='u-paddingTop--5'>Goal Amount</label>
|
||||||
|
<div class='prepend--dollar'>
|
||||||
|
<input class='input--100 u-marginBottom--5' value='1000' type='number' name='campaign_template[goal_amount_dollars]' min='1'>
|
||||||
|
</div>
|
||||||
|
<input id='goal_customizable-checkbox' type='checkbox' name='campaign_template[goal_customizable]'>
|
||||||
|
<label class='u-marginBottom--10' for='goal_customizable-checkbox'>Allow peer-to-peer campaigners to set their own goal</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<fieldset class='group'>
|
||||||
|
<label class='u-paddingTop--5'>End Date & Time</label>
|
||||||
|
<div pikaday-timepicker='MM/DD/YYYY hh:mm a'>
|
||||||
|
<input class='u-width--200 u-bold u-inlineBlock' type='text' name='campaign_template[end_datetime]' parsley-trigger='change' placeholder='MM/DD/YYYY HH:MM'>
|
||||||
|
<a class='button edit u-inlineBlock'>Set</a>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%= render 'components/forms/submit_button', button_text: 'Next', scope: 'new_campaign_template_wiz', branded: true %>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='wizard-step media-step'>
|
||||||
|
<!--= wizard.set_step 'new_campaign_template_wiz' 'Media' -->
|
||||||
|
|
||||||
|
<form parsley-validate>
|
||||||
|
<!--= on 'submit' create_campaign_template -->
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
|
||||||
|
<p class='u-color--red'>
|
||||||
|
<!--= show_if (length image_upload.error) -->
|
||||||
|
<small><!--= put image_upload.error --></small>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class='col-left-8'>
|
||||||
|
<label>Image <small>(recommended width: 500px)</small></label>
|
||||||
|
<p>This image appears below the campaign's header as well as on the campaign's preview card. You'll have a chance to upload an additional header image once you've created your campaign.</p>
|
||||||
|
</div>
|
||||||
|
<div class='image-upload u-margin--0 u-floatR' if-branded='border-color, light'>
|
||||||
|
<span><i class='fa fa-image'></i> Upload</span>
|
||||||
|
<input type='file' name='campaign[main_image]' parsley-trigger='change'>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<hr>
|
||||||
|
<label>Video</label>
|
||||||
|
<p>If you have a video, it can greatly boost your success. Copy and paste the URL from Vimeo or Youtube.</p>
|
||||||
|
<input type='text' class='u-marginBottom--10' name='campaign_template[video_url]' placeholder='Youtube or Vimeo URL' />
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<%= render 'components/forms/submit_button', button_text: 'Preview Template!', scope: 'new_campaign_template_wiz', branded: true %>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- end partial campaigns/new-modal -->
|
64
app/views/nonprofits/campaign_templates/index.html.erb
Normal file
64
app/views/nonprofits/campaign_templates/index.html.erb
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<%= content_for :stylesheets do %>
|
||||||
|
<%= stylesheet_link_tag 'campaigns/index/page' %>
|
||||||
|
<%= stylesheet_link_tag 'campaign_templates' %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= content_for :javascripts do %>
|
||||||
|
<script>
|
||||||
|
app.current_nonprofit_user = "<%= current_nonprofit_user? %>"
|
||||||
|
</script>
|
||||||
|
<%= IncludeAsset.js '/client/js/campaigns/index/page.js' %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
|
<%= render 'components/header',
|
||||||
|
icon_class: 'icon-thermometer-medium',
|
||||||
|
title: 'Campaign Templates',
|
||||||
|
profile: @nonprofit,
|
||||||
|
has_mosaic: true
|
||||||
|
%>
|
||||||
|
|
||||||
|
<main class='container u-padding--15'>
|
||||||
|
<% if current_user %>
|
||||||
|
<a class='button orange u-marginY--10' open-modal='newCampaignTemplate' data-when-confirmed><i class='fa fa-plus'></i> New Template</a>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if @templates.empty? %>
|
||||||
|
<p class='fundraiser--active u-padding--15 u-marginTop--15'>No templates yet</p>
|
||||||
|
<% else %>
|
||||||
|
<section class='fundraiser--unpublished u-marginTop--15 u-marginBottom--30'>
|
||||||
|
|
||||||
|
<!-- TODO: extract to partial -->
|
||||||
|
<table class='table--striped u-marginBottom--0'>
|
||||||
|
<% @templates.each do |template|%>
|
||||||
|
<tr>
|
||||||
|
<td class='u-padding--0 u-width--200 u-hideIf--400'>
|
||||||
|
<img src='<%= template.main_image_url(:normal) %>'>
|
||||||
|
</td>
|
||||||
|
<td class='u-padding--10'>
|
||||||
|
<h6 class='u-marginTop--0 u-marginBottom--5'>
|
||||||
|
<%= template.template_name %>
|
||||||
|
</h6>
|
||||||
|
<p class='u-marginBottom--15'>
|
||||||
|
<small>Campaign title: <%= template.name %></small>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class='u-marginBottom--15'>
|
||||||
|
<small>Campaign summary:<%= template.summary ? strip_tags(template.summary) : strip_tags(template.tagline) %></small>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class='u-marginBottom--15'>
|
||||||
|
<small>Customizable attributes: <%= template.customizable_attributes_list %></small>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
<% end %>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<% if current_user %>
|
||||||
|
<%= render 'nonprofits/campaign_templates/new_modal' %>
|
||||||
|
<% end %>
|
|
@ -90,6 +90,20 @@
|
||||||
</button>
|
</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<% if !@campaign.parent_campaign %>
|
||||||
|
<aside class='u-marginBottom--15 pastelBox--grey'>
|
||||||
|
<a class='button u-width--full' target='_blank' if-branded='background-color, dark' href='/peer-to-peer?campaign_id=<%= @peer_to_peer_campaign_param %>'>
|
||||||
|
Start Your Own Campaign for <%= @nonprofit.name %>
|
||||||
|
</a>
|
||||||
|
</aside>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> custom-layout
|
||||||
|
>>>>>>> p2p-campaigns
|
||||||
<!-- flimflam gift options javascript gets rendered into this div: -->
|
<!-- flimflam gift options javascript gets rendered into this div: -->
|
||||||
<div class='ff-sidebar'></div>
|
<div class='ff-sidebar'></div>
|
||||||
|
|
||||||
|
@ -102,6 +116,41 @@
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<% if @campaign.parent_campaign %>
|
||||||
|
<section class='box'>
|
||||||
|
|
||||||
|
<section class="campaigner-profile">
|
||||||
|
<figure>
|
||||||
|
<div class="avatar">
|
||||||
|
<img src=<%= @campaign.profile.picture %> />
|
||||||
|
</div>
|
||||||
|
<figcaption>
|
||||||
|
<p><%= @campaign.profile.name %></p>
|
||||||
|
<p><%= @campaign.profile.city %></p>
|
||||||
|
<% if @campaign.profile.state_code && @campaign.profile.city %>
|
||||||
|
<p><%= @campaign.profile.city_state %> <%= @campaign.profile.state_code %></p>
|
||||||
|
<% end %>
|
||||||
|
</figcaption>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<section class='u-marginTop--15 u-marginBottom--15 pastelBox--grey'>
|
||||||
|
<header>I am supporting the Y because…</header>
|
||||||
|
<div class='pastelBox-body'>
|
||||||
|
<%= @campaign.reason_for_supporting %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a class='button u-width--full' target='_blank' if-branded='background-color, dark' href='/peer-to-peer?campaign_id=<%= @peer_to_peer_campaign_param %>'>
|
||||||
|
Start Your Own Campaign for <%= @nonprofit.name %>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
<% end %>
|
||||||
|
=======
|
||||||
|
>>>>>>> p2p-campaigns
|
||||||
<section class='box'>
|
<section class='box'>
|
||||||
|
|
||||||
<section class="campaigner-profile">
|
<section class="campaigner-profile">
|
||||||
|
@ -121,6 +170,7 @@
|
||||||
(customizable reason)
|
(customizable reason)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
<% unless current_campaign_editor? %>
|
<% unless current_campaign_editor? %>
|
||||||
<a class='button u-width--full' target='_blank' if-branded='background-color, dark' href='/peer-to-peer?campaign_id=<%= @campaign.id %>'>
|
<a class='button u-width--full' target='_blank' if-branded='background-color, dark' href='/peer-to-peer?campaign_id=<%= @campaign.id %>'>
|
||||||
Start Your Own Campaign for <%= @nonprofit.name %>
|
Start Your Own Campaign for <%= @nonprofit.name %>
|
||||||
|
@ -129,6 +179,16 @@
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
=======
|
||||||
|
|
||||||
|
<a class='button u-width--full' target='_blank' if-branded='background-color, dark' href='/peer-to-peer?campaign_id=<%= @campaign.id %>'>
|
||||||
|
Start Your Own Campaign for <%= @nonprofit.name %>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
>>>>>>> custom-layout
|
||||||
|
>>>>>>> p2p-campaigns
|
||||||
|
|
||||||
<section class='box'>
|
<section class='box'>
|
||||||
<h1><%= @campaign.name %></h1>
|
<h1><%= @campaign.name %></h1>
|
||||||
|
|
2
client/js/campaign_templates/index/page.js
Normal file
2
client/js/campaign_templates/index/page.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
if(app.user)
|
||||||
|
require('../new/wizard')
|
50
client/js/campaign_templates/new/wizard.js
Normal file
50
client/js/campaign_templates/new/wizard.js
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
require('../../common/pikaday-timepicker')
|
||||||
|
require('../../components/wizard')
|
||||||
|
require('../../common/image_uploader')
|
||||||
|
var checkName = require('../../common/ajax/check_campaign_or_event_name')
|
||||||
|
var format_err = require('../../common/format_response_error')
|
||||||
|
|
||||||
|
appl.def('advance_campaign_template_name_step', function(form_obj) {
|
||||||
|
appl.def('new_campaign_template', form_obj)
|
||||||
|
appl.wizard.advance('new_campaign_template_wiz')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Post a new campaign template
|
||||||
|
appl.def('create_campaign_template', function(el) {
|
||||||
|
var form_data = utils.toFormData(appl.prev_elem(el))
|
||||||
|
form_data = utils.mergeFormData(form_data, appl.new_campaign_template)
|
||||||
|
appl.def('new_campaign_template_wiz.loading', true)
|
||||||
|
|
||||||
|
post_campaign_template(form_data)
|
||||||
|
.then(function(req) {
|
||||||
|
appl.notify("Redirecting to your campaign template...")
|
||||||
|
appl.redirect(JSON.parse(req.response).url)
|
||||||
|
})
|
||||||
|
.catch(function(req) {
|
||||||
|
appl.def('new_campaign_template_wiz.loading', false)
|
||||||
|
appl.def('new_campaign_template_wiz.error', req.responseText)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
var Pikaday = require('pikaday')
|
||||||
|
var moment = require('moment')
|
||||||
|
new Pikaday({
|
||||||
|
field: document.querySelector('.js-date-picker'),
|
||||||
|
format: 'M/D/YYYY',
|
||||||
|
minDate: moment().toDate()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Using the bare-bones XMLHttpRequest API so we can post form data and upload the image
|
||||||
|
function post_campaign_template(form_data) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
var req = new XMLHttpRequest()
|
||||||
|
req.open("POST", '/nonprofits/' + app.nonprofit_id + '/campaign_templates')
|
||||||
|
req.setRequestHeader('X-CSRF-Token', window._csrf)
|
||||||
|
req.send(form_data)
|
||||||
|
req.onload = function(ev) {
|
||||||
|
if(req.status === 200) resolve(req)
|
||||||
|
else reject(req)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
40
client/js/campaigns/new/peer_to_peer_wizard.js
Normal file
40
client/js/campaigns/new/peer_to_peer_wizard.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
require('../../components/wizard')
|
||||||
|
var format_err = require('../../common/format_response_error')
|
||||||
|
|
||||||
|
appl.def('advance_p2p_campaign_name_step', function(form_obj) {
|
||||||
|
var name = form_obj['campaign[name]']
|
||||||
|
appl.def('new_p2p_campaign', form_obj)
|
||||||
|
appl.wizard.advance('new_p2p_campaign_wiz')
|
||||||
|
})
|
||||||
|
|
||||||
|
// Post a new campaign.
|
||||||
|
appl.def('create_p2p_campaign', function(el) {
|
||||||
|
var form_data = utils.toFormData(appl.prev_elem(el))
|
||||||
|
form_data = utils.mergeFormData(form_data, appl.new_p2p_campaign)
|
||||||
|
appl.def('new_p2p_campaign_wiz.loading', true)
|
||||||
|
|
||||||
|
post_campaign(form_data)
|
||||||
|
.then(function(req) {
|
||||||
|
appl.notify("Redirecting to your campaign...")
|
||||||
|
appl.redirect(JSON.parse(req.response).url)
|
||||||
|
})
|
||||||
|
.catch(function(req) {
|
||||||
|
appl.def('new_p2p_campaign_wiz.loading', false)
|
||||||
|
appl.def('new_p2p_campaign_wiz.error', req.responseText)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Using the bare-bones XMLHttpRequest API so we can post form data and upload the image
|
||||||
|
function post_campaign(form_data) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
var req = new XMLHttpRequest()
|
||||||
|
req.open("POST", '/nonprofits/' + app.nonprofit_id + '/campaigns')
|
||||||
|
req.setRequestHeader('X-CSRF-Token', window._csrf)
|
||||||
|
console.log(form_data)
|
||||||
|
req.send(form_data)
|
||||||
|
req.onload = function(ev) {
|
||||||
|
if(req.status === 200) resolve(req)
|
||||||
|
else reject(req)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -20,6 +20,8 @@ appl.def('create_campaign', function(el) {
|
||||||
form_data = utils.mergeFormData(form_data, appl.new_campaign)
|
form_data = utils.mergeFormData(form_data, appl.new_campaign)
|
||||||
appl.def('new_campaign_wiz.loading', true)
|
appl.def('new_campaign_wiz.loading', true)
|
||||||
|
|
||||||
|
// TODO: for p2p capmaigns, merge with preset campaing params
|
||||||
|
|
||||||
post_campaign(form_data)
|
post_campaign(form_data)
|
||||||
.then(function(req) {
|
.then(function(req) {
|
||||||
appl.notify("Redirecting to your campaign...")
|
appl.notify("Redirecting to your campaign...")
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
<<<<<<< HEAD
|
||||||
// License: LGPL-3.0-or-later
|
// License: LGPL-3.0-or-later
|
||||||
|
|
||||||
require('../new/wizard')
|
require('../new/wizard')
|
||||||
|
@ -114,3 +115,7 @@ main.onclick = function(ev) {
|
||||||
}
|
}
|
||||||
appl.def('search_results', [])
|
appl.def('search_results', [])
|
||||||
}
|
}
|
||||||
|
=======
|
||||||
|
require('../new/peer_to_peer_wizard')
|
||||||
|
require('../../common/image_uploader')
|
||||||
|
>>>>>>> 2dc48070e... Merge branch 'p2p-campaigns' into h-custom-layout
|
||||||
|
|
|
@ -110,6 +110,8 @@ Commitchange::Application.routes.draw do
|
||||||
post(:send_code)
|
post(:send_code)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources(:campaign_templates, {only: [:index, :create]})
|
||||||
|
|
||||||
post 'tracking', controller: 'trackings', action: 'create'
|
post 'tracking', controller: 'trackings', action: 'create'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddParentCampaignIdToCampaigns < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :campaigns, :parent_campaign_id, :integer
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddReasonForSupportingToCampaigns < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :campaigns, :reason_for_supporting, :text
|
||||||
|
end
|
||||||
|
end
|
|
@ -438,7 +438,13 @@ CREATE TABLE public.campaigns (
|
||||||
show_recurring_amount boolean DEFAULT false,
|
show_recurring_amount boolean DEFAULT false,
|
||||||
end_datetime timestamp without time zone,
|
end_datetime timestamp without time zone,
|
||||||
external_identifier character varying(255),
|
external_identifier character varying(255),
|
||||||
|
<<<<<<< HEAD
|
||||||
campaign_template_id integer
|
campaign_template_id integer
|
||||||
|
=======
|
||||||
|
campaign_template_id integer,
|
||||||
|
parent_campaign_id integer,
|
||||||
|
reason_for_supporting text
|
||||||
|
>>>>>>> p2p-campaigns
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -4413,3 +4419,8 @@ INSERT INTO schema_migrations (version) VALUES ('201810202124316');
|
||||||
|
|
||||||
INSERT INTO schema_migrations (version) VALUES ('201810202124317');
|
INSERT INTO schema_migrations (version) VALUES ('201810202124317');
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
INSERT INTO schema_migrations (version) VALUES ('201810202124318');
|
||||||
|
|
||||||
|
>>>>>>> p2p-campaigns
|
||||||
|
|
12
spec/controllers/campaign_templates_controller_spec.rb
Normal file
12
spec/controllers/campaign_templates_controller_spec.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe CampaignTemplatesController, :type => :controller do
|
||||||
|
|
||||||
|
describe "GET #index" do
|
||||||
|
it "returns http success" do
|
||||||
|
get :index
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
15
spec/helpers/campaign_templates_helper_spec.rb
Normal file
15
spec/helpers/campaign_templates_helper_spec.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
# Specs in this file have access to a helper object that includes
|
||||||
|
# the CampaignTemplatesHelper. For example:
|
||||||
|
#
|
||||||
|
# describe CampaignTemplatesHelper do
|
||||||
|
# describe "string concat" do
|
||||||
|
# it "concats two strings with spaces" do
|
||||||
|
# expect(helper.concat_strings("this","that")).to eq("this that")
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
RSpec.describe CampaignTemplatesHelper, :type => :helper do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
5
spec/views/campaign_templates/index.html.erb_spec.rb
Normal file
5
spec/views/campaign_templates/index.html.erb_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe "campaign_templates/index.html.erb", :type => :view do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
Loading…
Reference in a new issue