Add CampaignTemplate

Read S3 bucket name and AWS region from env

Add /postgres-data to .gitignore

Custom campaign layout

Adjust custom layout

wip fix wizard init on campaign page

wip adjust design

adjust campaigner profile section

wider banner, fix button colours

Fix custom layout

Add custom_layout to nonprofit and render, if exists

Fallback profile picture

wip
This commit is contained in:
Kasia Jarmołkowicz 2018-04-11 22:36:23 +02:00 committed by Eric Schultz
parent 9f4a808c20
commit b99f7959ce
16 changed files with 458 additions and 21 deletions

1
.gitignore vendored
View file

@ -7,6 +7,7 @@ client/js/nonprofits/donate/plugins-enabled
.DS_Store .DS_Store
/postgres-data
# Ignore bundler config # Ignore bundler config
/.bundle /.bundle

View file

@ -0,0 +1,78 @@
@import 'mixins';
@import 'common/fundraisers';
button, a.js-contributeButton {
background: #01a490 !important;
}
main a.button {
background: #0089d0 !important;
color: white;
}
main .button.edit, main .button--tiny.edit {
color: white;
}
main {
width: 1200px;
margin: auto;
}
main > header {
display: flex;
justify-content: center;
align-items: center;
margin: auto;
}
body > .ymca-banner {
margin: auto;
width: 100vw;
height: 120px;
background-color: #01a490;
}
main > header div.fundraisingHeader--image-container {
background-position: 0 -80px;
}
img.fundraisingHeader--image-aspectRatio {
width: 1200px;
height: 500px;
}
main > .container {
max-width: none;
margin-top: 15px;
}
.campaigner-profile {
display: flex;
flex-wrap: nowrap;
align-items: baseline;
}
.campaigner-profile > figure {
display: flex;
flex-direction: column;
align-items: center;
margin-right: 20px;
align-self: flex-start;
}
.campaigner-profile > figure > .avatar {
flex-basis: 20%;
}
.avatar > img {
width: 150px;
height: 150px;
clip-path: circle(50% at center);
}
.campaigner-profile > figure > figcaption {
flex-basis: 55%;
padding: 15px;
text-align: left;
}

View file

@ -38,9 +38,13 @@ class CampaignsController < ApplicationController
@campaign_background_image = FetchBackgroundImage.with_model(@campaign) @campaign_background_image = FetchBackgroundImage.with_model(@campaign)
if @nonprofit.custom_layout.blank?
respond_to do |format| respond_to do |format|
format.html format.html
end end
else
render template: "nonprofits/custom_campaign_layouts/" + @nonprofit.custom_layout
end
end end
def activities def activities

View file

@ -13,6 +13,8 @@ class Campaign < ActiveRecord::Base
:remove_main_image, # for carrierwave :remove_main_image, # for carrierwave
:background_image, :background_image,
:remove_background_image, #bool carrierwave :remove_background_image, #bool carrierwave
:custom_banner,
:remove_custom_banner,
:published, :published,
:video_url, #str :video_url, #str
:vimeo_video_id, :vimeo_video_id,
@ -47,6 +49,7 @@ class Campaign < ActiveRecord::Base
mount_uploader :main_image, CampaignMainImageUploader mount_uploader :main_image, CampaignMainImageUploader
mount_uploader :background_image, CampaignBackgroundImageUploader mount_uploader :background_image, CampaignBackgroundImageUploader
mount_uploader :custom_banner, CampaignCustomBannerUploader
has_many :donations has_many :donations
has_many :charges, through: :donations has_many :charges, through: :donations
@ -60,6 +63,7 @@ class Campaign < ActiveRecord::Base
has_many :activities, as: :host, dependent: :destroy has_many :activities, as: :host, dependent: :destroy
belongs_to :profile belongs_to :profile
belongs_to :nonprofit belongs_to :nonprofit
belongs_to :campaign_template
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)}

View file

@ -0,0 +1,24 @@
class CampaignTemplate < ActiveRecord::Base
# these are very arbitrary names some are attrs of campaign, some are not
# might be a good idea to get the default list from settings
CUSTOMIZABLE_ATTR = %i(goal_amount_dollars campaigner_photo reason_for_supporting)
attr_accessible \
:template_name,
:name, # refers to campaign name
:tagline,
:goal_amount,
:main_image,
:remove_main_image, # for carrierwave
:video_url,
:vimeo_video_id,
:youtube_video_id,
:summary,
:body
has_many :campaigns
def customizable_attribute?(attribute_name)
CUSTOMIZABLE_ATTR.include? attribute_name.to_sym
end
end

View file

@ -0,0 +1,39 @@
class CampaignCustomBannerUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
include CarrierWave::MiniMagick
# Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility:
# include Sprockets::Helpers::RailsHelper
# include Sprockets::Helpers::IsolatedHelper
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/campaigns/#{mounted_as}/#{model.id}"
end
# Process files as they are uploaded:
# process :scale => [200, 300]
#
# def scale(width, height)
# # do something
# end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_white_list
%w(jpg jpeg png)
end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
def cache_dir
"#{Rails.root}/tmp/uploads"
end
end

View file

@ -92,6 +92,21 @@
</fieldset> </fieldset>
</section> </section>
<% if @nonprofit.custom_layout %>
<hr>
<section class='layout--one u-overflow--hidden'>
<fieldset>
<label>Banner <small>(1200x120)</small></label>
<p><small>Custom image at the very top of the campaign page</small></p>
<div class='image-upload u-inlineBlock' style='background-image: url("<%= @campaign.custom_banner_url %>");'>
<span><i class='fa fa-pencil'></i> Edit</span>
<input type='file' name='campaign[custom_banner]'>
</div>
</fieldset>
</section>
<% end %>
<hr> <hr>
<section class='layout--two'> <section class='layout--two'>

View file

@ -141,4 +141,3 @@
<%= render 'components/share_modal', name: @campaign.name, type: 'campaign' %> <%= render 'components/share_modal', name: @campaign.name, type: 'campaign' %>
<%= render 'common/email_share_modal', fundraiser: @campaign.name, fundraiser_url: @url %> <%= render 'common/email_share_modal', fundraiser: @campaign.name, fundraiser_url: @url %>

View file

@ -0,0 +1,169 @@
<%= content_for(:title_prefix) { "#{@campaign.name} - #{@campaign.nonprofit.name} | ".html_safe } %>
<% content_for(:fixed_position_cta_hidden) {'hidden'} %>
<%= content_for(:meta_description) {raw @campaign.summary} %>
<% @brand_color = @nonprofit.brand_color ? @nonprofit.brand_color : nil %>
<%= content_for :javascripts do %>
<script>
app.campaign_id = <%= @campaign.id %>
app.campaign = <%= raw(@campaign.to_json) %>
app.header_image_url = '<%= @campaign_background_image %>'
app.campaign_end_datetime = '<%= @campaign.end_datetime %>'
app.timezone = '<%= @timezone %>'
app.end_date_time = app.campaign_end_datetime
app.hide_activities = <%= @campaign.hide_activity_feed %>
app.days_remaining = '<%= @campaign.days_left %>'
app.recurring_fund = <%= @campaign.recurring_fund? %>
app.vimeo_id = "<%= @campaign.vimeo_video_id ? @campaign.vimeo_video_id : '' %>"
app.current_campaign_editor = <%= current_campaign_editor? %>
appl.def('has_video', <%= @campaign.video_url.present? %>)
appl.def('campaign_is_deleted', <%= @campaign.deleted || false %>)
appl.def('has_main_image', <%= @campaign.main_image.file.present? %>)
</script>
<%= render 'schema', campaign: @campaign, url: @url %>
<%= render 'common/froala' if current_campaign_editor? %>
<%= IncludeAsset.js '/client/js/campaigns/show/page.js' %>
<% end %>
<%= content_for :stylesheets do %>
<%= stylesheet_link_tag 'campaigns/show/page' %>
<%= stylesheet_link_tag 'campaigns/edit/page' %>
<%= stylesheet_link_tag 'campaigns/custom_layout/safety_around_water' %>
<style>
.force-
.ios-force-absolute-positioning {
position: absolute !important;
}
</style>
<% end %>
<% content_for :head do %>
<link rel="canonical" href='<%= @url %>' />
<% end %>
<%= content_for :facebook_tags do %>
<meta property="og:title" content="<%= raw @campaign.name %>">
<meta property="og:description" content="<%= @campaign.summary.present? ? raw(@campaign.summary) : raw(@campaign.name) %>">
<meta property="og:image" content="<%= @campaign.main_image_url(:normal) %>">
<% end %>
<%= content_for :twitter_tags do %>
<meta property="twitter:title" content="<%= raw @campaign.name %>">
<meta property="twitter:description" content="<%= raw @campaign.summary %>">
<meta property="twitter:image" content="<%= @campaign.main_image_url(:normal) %>">
<% end %>
<% if current_campaign_editor? %>
<%= render 'admin_top_nav' %>
<% end %>
<%= render '/components/trial_bar' if QueryBillingSubscriptions.currently_in_trial?(@nonprofit.id) %>
<% hide_title = @campaign.hide_title && @campaign_background_image ? true : false %>
<% css_style = current_campaign_editor? ? "style='margin-top: 0'" : '' %>
<div class='ymca-banner'></div>
<main class="custom-campaign-layout" id="safety-around-water">
<%= render 'components/fundraising_pages/header',
image_url: @campaign_background_image,
is_editor: current_campaign_editor?,
hide_title: @campaign.hide_title && @campaign_background_image,
header_content_partial: 'header_content' %>
<%= render 'components/preview_mode_notification' %>
<div class="container <%= @brand_color ? 'is-branded' : '' %>" data-id='<%= @campaign.id %>'>
<section class='box'>
<%= render 'campaign_media' %>
</section>
<section class='box-r'>
<% if current_campaign_editor? %>
<!-- Campaign editor gift option management modal -->
<button class='button edit u-width--full u-marginBottom--15'>
<!--= on 'click' (open_modal 'manageGiftOptionsModal') -->
<i class='fa fa-gift'></i> Manage Gift Options
</button>
<% end %>
<!-- flimflam gift options javascript gets rendered into this div: -->
<div class='ff-sidebar'></div>
<aside class='u-marginTop--15 pastelBox--grey'>
<header>Promote This Campaign</header>
<div class='pastelBox-body'>
<%= render 'common/social_buttons' %>
</div>
</aside>
</section>
<section class='box'>
<section class="campaigner-profile">
<figure>
<div class="avatar">
<img src=<%= @campaign.profile.get_profile_picture %> />
</div>
<figcaption>
<p><%= @campaign.profile.name %></p>
<p><%= @campaign.profile.city %></p>
</figcaption>
</figure>
<section class='u-marginTop--15 u-marginBottom--15 pastelBox--grey'>
<header>I am supporting the Y because…</header>
<div class='pastelBox-body'>
(customizable reason)
</div>
<% 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 %>'>
Start Your Own Campaign for <%= @nonprofit.name %>
</a>
<% end %>
</section>
</section>
</section>
<section class='box'>
<h1><%= @campaign.name %></h1>
<div id='js-campaignBody' class='editable' data-path='/nonprofits/<%= @nonprofit.id %>/campaigns/<%= @campaign.id %>.json' data-key='campaign[body]'>
<%= raw @campaign.body %>
</div>
<% unless @campaign.hide_activity_feed %>
<div class='u-marginTop--15 showWhenMobile'>
<%= render 'components/activity_feed' %>
</div>
<% end %>
</section>
</div>
</main>
<% if current_campaign_editor? %>
<%= render 'settings_modal' %>
<%= render 'video_modal' %>
<%= render 'donations/campaign_new_offline_modal' %>
<%= render 'campaign_gift_options/manage_modal'%>
<%= render 'campaign_gift_options/form_modal'%>
<%= render 'components/upload_background_image',
end_point: "/nonprofits/#{@nonprofit.id}/campaigns/#{@campaign.id}",
image_url: @campaign_background_image,
input_name: 'campaign[background_image]' %>
<%= render 'components/custom_receipt_modal',
title: 'Campaign Receipt Message',
type: 'campaign',
path: nonprofit_campaign_path(@nonprofit, @campaign),
key: 'campaign[receipt_message]',
text: @campaign.receipt_message %>
<%= render 'components/duplicate_fundraiser_modal', type: 'campaign' %>
<% end %>
<%= render 'components/share_modal', name: @campaign.name, type: 'campaign' %>
<%= render 'common/email_share_modal', fundraiser: @campaign.name, fundraiser_url: @url %>

View file

@ -27,6 +27,7 @@ renderStyles()(brandedWizard(null))
// pass in a stream of configuration parameters // pass in a stream of configuration parameters
const init = params$ => { const init = params$ => {
console.log(params$())
var state = { var state = {
error$: flyd.stream() error$: flyd.stream()
, loading$: flyd.stream() , loading$: flyd.stream()
@ -193,16 +194,18 @@ const headerDesignation = state => {
} }
const wizardWrapper = state => { const wizardWrapper = state => {
return h('div.wizard-steps.donation-steps', [ // return h('div.wizard-steps.donation-steps', [
wizard.view(R.merge(state.wizard, { // wizard.view(R.merge(state.wizard, {
steps: [ // steps: [
{name: I18n.t('nonprofits.donate.amount.label'), body: amountStep.view(state.amountStep)} // {name: I18n.t('nonprofits.donate.amount.label'), body: amountStep.view(state.amountStep)}
, {name: I18n.t('nonprofits.donate.info.label'), body: infoStep.view(state.infoStep)} // , {name: I18n.t('nonprofits.donate.info.label'), body: infoStep.view(state.infoStep)}
, {name: I18n.t('nonprofits.donate.payment.label'), body: paymentStep.view(state.paymentStep)} // , {name: I18n.t('nonprofits.donate.payment.label'), body: paymentStep.view(state.paymentStep)}
] // ]
, followup: followupStep.view(state) // , followup: followupStep.view(state)
})) // }))
]) // ])
return h('div.wizard-steps.donation-steps', 'Donation widget should be here.')
} }
module.exports = {view, init} module.exports = {view, init}

View file

@ -147,6 +147,8 @@ Commitchange::Application.routes.draw do
resources(:campaign_gift_options, {only: [:index, :show, :create, :update, :destroy]}) do resources(:campaign_gift_options, {only: [:index, :show, :create, :update, :destroy]}) do
put(:update_order, {on: :collection}) put(:update_order, {on: :collection})
end end
get 'custom_layout', controller: 'campaigns', action: 'custom_layout'
end end
resource(:billing_subscription, {only: [:create]}) do resource(:billing_subscription, {only: [:create]}) do
@ -224,6 +226,7 @@ Commitchange::Application.routes.draw do
# Campaigns # Campaigns
match ':state_code/:city/:name/campaigns' => 'campaigns#index' match ':state_code/:city/:name/campaigns' => 'campaigns#index'
match ':state_code/:city/:name/campaigns/:campaign_slug' => 'campaigns#show', :as => :campaign_loc match ':state_code/:city/:name/campaigns/:campaign_slug' => 'campaigns#show', :as => :campaign_loc
match ':state_code/:city/:name/campaigns/:campaign_slug/custom_layout' => 'campaigns#custom_layout', :as => :campaign_loc
match ':state_code/:city/:name/campaigns/:campaign_slug/supporters' => 'campaigns/supporters#index', :as => :campaign_loc match ':state_code/:city/:name/campaigns/:campaign_slug/supporters' => 'campaigns/supporters#index', :as => :campaign_loc
match '/peer-to-peer' => 'campaigns#peer_to_peer' match '/peer-to-peer' => 'campaigns#peer_to_peer'

View file

@ -20,7 +20,7 @@ default:
aws: aws:
access_key_id: <%= ENV['AWS_ACCESS_KEY'] %> access_key_id: <%= ENV['AWS_ACCESS_KEY'] %>
secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %> secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
region: test region: <%= ENV['AWS_REGION'] %>
bucket: <%= ENV['S3_BUCKET_NAME'] %> bucket: <%= ENV['S3_BUCKET_NAME'] %>
mailer: mailer:

View file

@ -0,0 +1,22 @@
class AddCampaignTemplates < ActiveRecord::Migration
def change
create_table :campaign_templates do |t|
t.string :template_name, null: false
t.string :name
t.string :tagline
t.integer :goal_amount
t.string :main_image
t.text :video_url
t.string :vimeo_video_id
t.string :youtube_video_id
t.text :summary
t.text :body
t.timestamps
end
change_table :campaigns do |t|
t.references :campaign_template
end
end
end

View file

@ -0,0 +1,5 @@
class AddCustomLayoutToNonprofit < ActiveRecord::Migration
def change
add_column :nonprofits, :custom_layout, :string
end
end

View file

@ -0,0 +1,5 @@
class AddBannerToCampaign < ActiveRecord::Migration
def change
add_column :campaigns, :custom_banner, :string
end
end

View file

@ -2,8 +2,13 @@
-- PostgreSQL database dump -- PostgreSQL database dump
-- --
<<<<<<< HEAD
-- Dumped from database version 9.6.5 -- Dumped from database version 9.6.5
-- Dumped by pg_dump version 9.6.10 -- Dumped by pg_dump version 9.6.10
=======
-- Dumped from database version 9.6.8
-- Dumped by pg_dump version 9.6.8
>>>>>>> Add CampaignTemplate
SET statement_timeout = 0; SET statement_timeout = 0;
SET lock_timeout = 0; SET lock_timeout = 0;
@ -355,6 +360,46 @@ CREATE SEQUENCE public.campaign_gifts_id_seq
ALTER SEQUENCE public.campaign_gifts_id_seq OWNED BY public.campaign_gifts.id; ALTER SEQUENCE public.campaign_gifts_id_seq OWNED BY public.campaign_gifts.id;
--
-- Name: campaign_templates; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.campaign_templates (
id integer NOT NULL,
template_name character varying(255) NOT NULL,
name character varying(255),
tagline character varying(255),
goal_amount integer,
main_image character varying(255),
video_url text,
vimeo_video_id character varying(255),
youtube_video_id character varying(255),
summary text,
body text,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL
);
--
-- Name: campaign_templates_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public.campaign_templates_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: campaign_templates_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public.campaign_templates_id_seq OWNED BY public.campaign_templates.id;
-- --
-- Name: campaigns; Type: TABLE; Schema: public; Owner: - -- Name: campaigns; Type: TABLE; Schema: public; Owner: -
-- --
@ -392,7 +437,8 @@ CREATE TABLE public.campaigns (
hide_custom_amounts boolean, hide_custom_amounts boolean,
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),
campaign_template_id integer
); );
@ -1412,7 +1458,8 @@ CREATE TABLE public.nonprofits (
card_failure_message_bottom text, card_failure_message_bottom text,
fields_needed text, fields_needed text,
autocomplete_supporter_address boolean DEFAULT false, autocomplete_supporter_address boolean DEFAULT false,
currency character varying(255) DEFAULT 'usd'::character varying currency character varying(255) DEFAULT 'usd'::character varying,
custom_layout character varying(255)
); );
@ -2305,6 +2352,13 @@ ALTER TABLE ONLY public.campaign_gift_options ALTER COLUMN id SET DEFAULT nextva
ALTER TABLE ONLY public.campaign_gifts ALTER COLUMN id SET DEFAULT nextval('public.campaign_gifts_id_seq'::regclass); ALTER TABLE ONLY public.campaign_gifts ALTER COLUMN id SET DEFAULT nextval('public.campaign_gifts_id_seq'::regclass);
--
-- Name: campaign_templates id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.campaign_templates ALTER COLUMN id SET DEFAULT nextval('public.campaign_templates_id_seq'::regclass);
-- --
-- Name: campaigns id; Type: DEFAULT; Schema: public; Owner: - -- Name: campaigns id; Type: DEFAULT; Schema: public; Owner: -
-- --
@ -2690,6 +2744,14 @@ ALTER TABLE ONLY public.campaign_gifts
ADD CONSTRAINT campaign_gifts_pkey PRIMARY KEY (id); ADD CONSTRAINT campaign_gifts_pkey PRIMARY KEY (id);
--
-- Name: campaign_templates campaign_templates_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.campaign_templates
ADD CONSTRAINT campaign_templates_pkey PRIMARY KEY (id);
-- --
-- Name: campaigns campaigns_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- Name: campaigns campaigns_pkey; Type: CONSTRAINT; Schema: public; Owner: -
-- --
@ -4347,3 +4409,7 @@ INSERT INTO schema_migrations (version) VALUES ('20181002160627');
INSERT INTO schema_migrations (version) VALUES ('20181003212559'); INSERT INTO schema_migrations (version) VALUES ('20181003212559');
INSERT INTO schema_migrations (version) VALUES ('201810202124316');
INSERT INTO schema_migrations (version) VALUES ('201810202124317');