2018-03-25 18:03:08 +00:00
// License: LGPL-3.0-or-later
2018-03-25 17:30:42 +00:00
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$ => {
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 : R . join ( " - " , [ dedication . supporter . email , dedication . supporter . phone , 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 ? parent . postMessage ( 'commitchange:close' , '*' ) : null }
, class : { 'u-hide' : ! state . params$ ( ) . offsite }
} )
, 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 )
} ) )
] )
}
module . exports = { view , init }