houdini/client/js/common/onboard.js

281 lines
9.7 KiB
JavaScript
Raw Normal View History

// License: LGPL-3.0-or-later
const flyd = require('flimflam/flyd')
const h = require('flimflam/h')
const R = require('ramda')
const modal = require('flimflam/ui/modal')
const render = require('flimflam/render')
const wizard = require('flimflam/ui/wizard')
const validatedForm = require('flimflam/ui/validated-form')
const request = require('./request')
const notification = require('flimflam/ui/notification')
const fieldWithError = require('../components/field-with-error')
const init = () => {
const orgForm = validatedForm.init({constraints: constraints.org})
const contactForm = validatedForm.init({constraints: constraints.contact})
const infoForm = validatedForm.init({constraints: constraints.info})
const currentStep$ = flyd.mergeAll([
flyd.stream(0)
, flyd.map(R.always(1), orgForm.validSubmit$)
, flyd.map(R.always(2), infoForm.validSubmit$)
])
const wiz = wizard.init({currentStep$})
const openModal$ = flyd.stream()
document.querySelectorAll('[data-ff-open-onboard]')
.forEach(x => {x.addEventListener('click', openModal$)})
//flyd.map(trackGA, openModal$)
const response$ = flyd.flatMap(postData(orgForm, infoForm, contactForm), contactForm.validData$)
const respOk$ = flyd.filter(resp => resp.status === 200, response$)
const respErr$ = flyd.filter(resp => resp.status !== 200, response$)
const loading$ = flyd.mergeAll([
flyd.map(R.always(true), contactForm.validSubmit$)
, flyd.map(R.always(false), response$)
])
const message$ = flyd.mergeAll([
flyd.map(R.always("Saving your data..."), contactForm.validSubmit$)
, flyd.map(R.always("Thank you! Now redirecting..."), respOk$)
, flyd.map(resp => `There was an error: ${resp.body.error}`, respErr$)
])
const notif = notification.init({message$, hideDelay: 20000})
flyd.map(resp => {setTourCookies(resp.body.nonprofit); window.location = '/'}, respOk$)
return {
openModal$
, currentStep$
, wiz
, orgForm
, contactForm
, infoForm
, loading$
, notif
}
}
// const trackGA = () => {
// if(!ga) return
// ga('send', {
// hitType: 'event',
// eventCategory: 'ClickSignUp',
// eventAction: 'click',
// eventLabel: location.pathname
// })
// }
const setTourCookies = nonprofit => {
document.cookie = `tour_dashboard=${nonprofit.id};path=/`
document.cookie = `tour_campaign=${nonprofit.id};path=/`
document.cookie = `tour_event=${nonprofit.id};path=/`
document.cookie = `tour_profile=${nonprofit.id};path=/`
document.cookie = `tour_transactions=${nonprofit.id};path=/`
document.cookie = `tour_supporters=${nonprofit.id};path=/`
document.cookie = `tour_subscribers=${nonprofit.id};path=/`
}
const postData = (orgForm, infoForm) => contactFormData => {
const send = {
nonprofit: orgForm.validData$()
, extraInfo: infoForm.validData$()
, user: contactFormData
}
return request({
method: 'post'
, path: '/nonprofits/onboard'
, send
}).load
}
const constraints = {
org: {
name: {required: true}
, city: {required: true}
, state_code: {required: true}
, zip_code: {required: true}
}
, contact: {
email: {required: true, email: true}
, name: {required: true}
, phone: {required: true}
, password: {required: true, minLength: 7}
, password_confirmation: {required: true, matchesField: 'password'}
}
, info: {}
}
const view = state => {
return h('div', [
modal({
show$: state.openModal$
, body: onboardWizard(state)
, title: 'Get started'
})
, notification.view(state.notif)
])
}
const onboardWizard = state => {
const labels = [ 'Org', 'Info', 'Contact' ]
const steps = [ orgForm(state) , infoForm(state), contactForm(state) ]
return h('div', [
wizard.labels(state.wiz, labels)
, wizard.content(state.wiz, steps)
])
}
const pricingDetails = h('div.u-marginTop--15.u-padding--10.u-background--fog', [
h('p', [
"CommitChange uses "
, h('a.strong', {props: {href: 'https://www.stripe.com/', target :'_blank'}}, 'Stripe')
, ' to process transactions. Stripe takes a '
, h('strong', `${ENV.feeRate}% + ${ENV.perTransaction}¢`)
, ' processing fee on every transaction.'])
, h('p', [
'In order to support operations, feature development, and community building, '
, 'CommitChange takes an additional fee of '
, h('strong', `${ENV.platformFeeRate}%.`)
])
, h('p.u-marginBottom--0', [
"Our fee scales down as your transaction volume scales up. "
, h('a.strong', {props: {href: 'mailto:support@commitchange.com'}}, 'Contact us')
, " to chat about volume discounts."
])
])
const orgForm = state => {
const form = validatedForm.form(state.orgForm)
const field = fieldWithError(state.orgForm)
return h('div', [
form(h('form', [
h('fieldset', [
h('label', 'Organization Name')
, field(h('input', {props: {type: 'text', name: 'name', placeholder: ''}}))
])
, h('fieldset', [
h('label', 'Website URL')
, field(h('input', {props: {type: 'text', name: 'website', placeholder: 'https://your-website.org'}}))
])
, h('div.clearfix', [
h('fieldset.col-left-6.u-paddingRight--10', [
h('label', 'Org Email (public)')
, field(h('input', {props: {type: 'email', name: 'email', placeholder: 'example@name.org'}}))
])
, h('fieldset.col-left-6', [
h('label', 'Org Phone (public)')
, field(h('input', {props: {type: 'text', name: 'phone', placeholder: '(XXX) XXX-XXXX'}}))
])
])
, h('div.clearfix', [
h('fieldset.col-left-6.u-paddingRight--10', [
h('label', 'City')
, field(h('input', {props: {type: 'text', name: 'city', placeholder: ''}}))
])
, h('fieldset.col-left-3.u-paddingRight--10', [
h('label', 'State')
, field(h('input', {props: {type: 'text', name: 'state_code', placeholder: 'NY'}}))
])
, h('fieldset.col-left-3', [
h('label', 'Zip Code')
, field(h('input', {props: {type: 'text', name: 'zip_code', placeholder: ''}}))
])
])
, h('div', [
h('button.button', 'Next')
])
]))
])
}
const infoForm = state => {
const form = validatedForm.form(state.infoForm)
const field = fieldWithError(state.infoForm)
return h('div', [
form(h('form', [
h('div.u-marginBottom--20', [
h('fieldset', [
h('label', {props: {htmlFor: 'registered-npo-checkbox'}}, 'What kind of entity are you fundraising for?')
])
, h('fieldset', [
h('input', {props: {type: 'radio', name: 'entity_type', value: 'nonprofit', id: 'onboard-entity-nonprofit'}})
, h('label', {props: {htmlFor: 'onboard-entity-nonprofit'}}, 'A registered nonprofit')
])
, h('fieldset', [
h('input', {props: {type: 'radio', name: 'entity_type', value: 'forprofit', id: 'onboard-entity-forprofit'}})
, h('label', {props: {htmlFor: 'onboard-entity-forprofit'}}, 'A for-profit company')
])
, h('fieldset', [
h('input', {props: {type: 'radio', name: 'entity_type', value: 'unregistered', id: 'onboard-entity-unregistered'}})
, h('label', {props: {htmlFor: 'onboard-entity-unregistered'}}, 'An unregistered project, group, club, or other cause')
])
])
, h('div.u-marginBottom--20', [
h('fieldset', [
h('label', 'How do you want to use CommitChange?')
])
, h('fieldset', [
h('input', {props: {type: 'checkbox', name: 'use_donations', id: 'onboard-use-donations'}})
, h('label', {props: {htmlFor: 'onboard-use-donations'}}, 'Donation processing')
])
, h('fieldset', [
h('input', {props: {type: 'checkbox', name: 'use_crm', id: 'onboard-use-crm'}})
, h('label', {props: {htmlFor: 'onboard-use-crm'}}, 'Supporter relationship management')
])
, h('fieldset', [
h('input', {props: {type: 'checkbox', name: 'use_campaigns', id: 'onboard-use-campaigns'}})
, h('label', {props: {htmlFor: 'onboard-use-campaigns'}}, 'Campaign fundriasing')
])
, h('fieldset', [
h('input', {props: {type: 'checkbox', name: 'use_events', id: 'onboard-use-events'}})
, h('label', {props: {htmlFor: 'onboard-use-events'}}, 'Event pages and ticketing')
])
])
, h('fieldset', [
h('label', 'How did you hear about CommitChange?')
, field(h('input', {props: {type: 'text', name: 'how_they_heard', placeholder: 'Google, radio, referral, etc'}}))
])
, h('button.button', 'Next')
]))
])
}
const contactForm = state => {
const form = validatedForm.form(state.contactForm)
const field = fieldWithError(state.contactForm)
return h('div', [
form(h('form', [
h('div.clearfix', [
h('fieldset.col-left-6.u-paddingRight--10', [
h('label', 'Your Name')
, field(h('input', {props: {type: 'text', name: 'name', placeholder: 'Full Name'}}))
])
, h('fieldset.col-left-6', [
h('label', 'Your Email (used for login)')
, field(h('input', {props: {type: 'email', name: 'email', placeholder: 'youremail@example.com'}}))
])
])
, h('fieldset', [
h('label', 'New Password')
, field(h('input', {props: {type: 'password', name: 'password', placeholder: ''}}))
])
, h('fieldset', [
h('label', 'Retype Password')
, field(h('input', {props: {type: 'password', name: 'password_confirmation', placeholder: ''}}))
])
, h('fieldset', [
h('label', ['Your Phone', h('small', ' (for account recovery)')])
, field(h('input', {props: {type: 'text', name: 'phone', placeholder: '(XXX) XXX-XXXX'}}))
])
, h('button.button', {props: {disabled: state.loading$()}}, 'Save & Finish')
]))
])
}
const container = document.querySelector("#ff-render-onboard")
render(view, init(), container)