houdini/client/js/nonprofits/donate/wizard.js
Kasia Jarmołkowicz b99f7959ce 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
2018-11-21 11:00:44 -06:00

211 lines
7.9 KiB
JavaScript

// License: LGPL-3.0-or-later
const flyd = require('flyd')
const R = require('ramda')
const h = require('snabbdom/h')
const url = require('url')
const render = require('ff-core/render')
const wizard = require('ff-core/wizard')
const scanMerge = require('flyd/module/scanmerge')
flyd.mergeAll = require('flyd/module/mergeall')
flyd.flatMap = require('flyd/module/flatmap')
flyd.zip = require('flyd-zip')
const getParams = require('./get-params')
const paymentStep = require('./payment-step')
const amountStep = require('./amount-step')
const infoStep = require('./info-step')
const followupStep = require('./followup-step')
const request = require('../../common/request')
const format = require('../../common/format')
const brandedWizard = require('../../components/styles/branded-wizard')
const renderStyles = require('../../components/styles/render-styles')
renderStyles()(brandedWizard(null))
// pass in a stream of configuration parameters
const init = params$ => {
console.log(params$())
var state = {
error$: flyd.stream()
, loading$: flyd.stream()
, clickLogout$: flyd.stream()
, clickFinish$: flyd.stream()
, params$: flyd.map(getParams, params$)
}
app.iframeParams = app.iframeParams || ""
app.utmParams = app.utmParams || {}
// maps utmParams from URL params string into object:
// { $utm_param: … } if params from iframe are present
app.iframeParams = app.iframeParams.split("?")[2] ? Object.assign(...app.iframeParams.split("?")[2].split("&").map((param) => param.split("=")).map(
array => ({[array[0]]: array[1]}))
) : {}
app.utmParams = {
utm_campaign: app.utmParams.utm_campaign || app.iframeParams.utm_campaign,
utm_content: app.utmParams.utm_content || app.iframeParams.utm_content,
utm_medium: app.utmParams.utm_medium || app.iframeParams.utm_medium,
utm_source: app.utmParams.utm_source || app.iframeParams.utm_source
}
app.campaign = app.campaign || {} // so we don't have to hot switch all the calls to app.campaign.name, etc
var donationDefaults = setDonationFromParams({
nonprofit_id: app.nonprofit_id
, campaign_id: app.campaign.id
, event_id: app.event_id
}, state.params$())
state.selectedPayment$ = flyd.stream('sepa')
state.amountStep = amountStep.init(donationDefaults, state.params$)
state.infoStep = infoStep.init(state.amountStep.donation$, state)
state.donation$ = scanMerge([
[state.amountStep.donation$, R.merge]
, [state.infoStep.savedSupp$, (d, supp) => R.assoc('supporter_id', supp.id, d)]
, [state.params$, setDonationFromParams]
, [state.infoStep.savedDedicatee$, setDonationDedication]
], donationDefaults )
state.paymentStep = paymentStep.init({
supporter$: state.infoStep.savedSupp$
, donation$: state.donation$
, dedicationData$: state.infoStep.dedicationData$
, activePaymentTab$: state.selectedPayment$
, params$: state.params$
})
const currentStep$ = flyd.mergeAll([
state.amountStep.currentStep$
, state.infoStep.currentStep$
, flyd.map(R.always(0), state.params$) // if the params ever change, jump back to step one
, flyd.stream(0)
])
state.wizard = wizard.init({currentStep$, isCompleted$: state.paymentStep.paid$})
// Save dedication as a supporter note once the donation is saved
// Requires the donor supporter, the dedicatee supporter, the dedication form data, and the paid donation
const dedicationParams$ = flyd.zip([state.infoStep.savedDedicatee$, state.infoStep.savedSupp$, state.paymentStep.paid$])
const savedDedication$ = flyd.flatMap(R.apply(postDedication), dedicationParams$)
// Log people out
flyd.map(ev => {request({method: 'get', path: '/users/sign_out'}); window.location.reload()}, state.clickLogout$)
// Handle the Finish button from the followup step -- will close modal, redirect, or refresh
flyd.lift(
(ev, params) => {
if(!parent) return
if(params.redirect) parent.postMessage(`commitchange:redirect:${params.redirect}`, '*')
else if(params.mode !== 'embedded'){
parent.postMessage('commitchange:close', '*');
} else {
if (window.parent) {window.parent.postMessage('commitchange:close', '*');};
}
}
, state.clickFinish$, state.params$ )
return state
}
const setDonationFromParams = (don, params) => {
if(!params.single_amount || isNaN(format.dollarsToCents(params.single_amount))) delete params.single_amount
return R.merge({
amount: params.single_amount ? format.dollarsToCents(params.single_amount) : 0
, recurring: params.type === 'recurring'
, gift_option_id: params.gift_option_id
, designation: params.designation
}, don)
}
// Set the text field to save to the server as serialized JSON
const setDonationDedication = (don, dedication) => {
return R.assoc(
'dedication'
, JSON.stringify({
supporter_id: dedication.supporter.id
, name: dedication.supporter.name
, contact: {email: dedication.supporter.email,
phone: dedication.supporter.phone,
address: dedication.supporter.address}
, note: dedication.note
, type: dedication.type
})
, don)
}
// Save a dedication to the server by saving a note to the supporter
const postDedication = (dedication, donor, donation) => {
const pathPrefix = `/nonprofits/${ENV.nonprofitID}`
// TODO: translate content
var content = `[${donor.name}](${pathPrefix}/supporters?sid=${donor.id}) made a [donation of $${format.centsToDollars(donation.donation.amount)}](${pathPrefix}/payments?pid=${donation.payment.id}) in ${dedication.type || 'honor'} of this person.`
if(dedication.note) content += ` ${I18n.t('nonprofits.donate.dedication.donor_note')} "${dedication.note}".`
return flyd.map(r => r.body, request({
method: 'post'
, path: `/nonprofits/${app.nonprofit_id}/supporters/${dedication.supporter.id}/supporter_notes`
, send: {supporter_note: {supporter_id: dedication.supporter.id, user_id: ENV.support_user_id, content}}
}).load)
}
const view = state => {
return h('div.js-donateForm', {
class: {'is-modal': state.params$().offsite}
}, [
h('img.closeButton', {
props: {src: '/assets/ui_components/close.svg'}
, on: {click: ev => state.params$().offsite && !state.params$().embedded ? parent.postMessage('commitchange:close', '*') : null}
, class: {'u-hide': !state.params$().offsite || !state.params$().embedded}
})
, h('div.titleRow', [
h('img', {props: {src: app.nonprofit.logo.normal.url}})
, h('div.titleRow-info', [
h('h2', app.campaign.name || app.nonprofit.name )
, h('p', [
state.params$().designation && !state.params$().single_amount
? headerDesignation(state)
: app.campaign.tagline || app.nonprofit.tagline || ''
])
])
])
, wizardWrapper(state)
, h('footer.donateForm-footer', {
class: {'u-hide': !app.user}
}, [
h('span', `${I18n.t('nonprofits.donate.signed_in')} `)
, h('strong', String(app.user && app.user.email))
, h('a.logout-button', {on: {click: state.clickLogout$}}, ` ${I18n.t('nonprofits.donate.log_out')}`)
])
])
}
const headerDesignation = state => {
return h('span', [
h('i.fa.fa-star', {style: {color: app.nonprofit.brand_color || ''}})
, h('strong', ` ${I18n.t('nonprofits.donate.amount.designation.label')} `)
, String(state.params$().designation)
, state.params$().designation_desc
? h('span', [h('br'), h('small', state.params$().designation_desc)])
: ''
])
}
const wizardWrapper = state => {
// return h('div.wizard-steps.donation-steps', [
// wizard.view(R.merge(state.wizard, {
// steps: [
// {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.payment.label'), body: paymentStep.view(state.paymentStep)}
// ]
// , followup: followupStep.view(state)
// }))
// ])
return h('div.wizard-steps.donation-steps', 'Donation widget should be here.')
}
module.exports = {view, init}