houdini/client/js/supporters/info-card.es6

110 lines
3.4 KiB
JavaScript

const request = require("../common/super-agent-frp")
const view = require("vvvview")
const flyd = require('flyd')
const flatMap = require('flyd/module/flatmap')
const scanMerge = require('flyd/module/scanmerge')
const h = require('virtual-dom/h')
const Im = require('immutable')
const Map = Im.Map
const fromJS = Im.fromJS
const OrderedMap = Im.OrderedMap
const format = require('../common/format')
var state = fromJS({is_visible: false, data: {}, coords: {top: 0, right: 0, left: 0}})
var $showClicks = flyd.stream()
var $hideClicks = flyd.stream()
const root = state => {
if(state.get('is_visible') && state.get('data')) {
return h('aside.infoCard', {
style: {
display: state.get('is_visible') ? 'block' : 'none',
top: state.getIn(['coords', 'top']) - state.get('rows') * 23 + 'px',
left: state.getIn(['coords', 'left']) + 'px',
right: state.getIn(['coords', 'right']) + 'px'
}
}, [
h('i.fa.fa-times', {onclick: $hideClicks}),
supporterTable(state.get('data')),
h('a.button--micro', {href: state.getIn(['data', 'link'])}, 'View Full Details')
])
} else return h('span')
}
const supporterDetail = pair => {
var [key, val] = pair
if(key === 'link') return ''
return val ? h('tr', [h('td', format.snake_to_words(key)), h('td', val)]) : ''
}
const supporterTable = supporter =>
h('table', supporter.entrySeq().map(supporterDetail).toJS())
const displayCard = (state, node) => {
var clientTop = document.documentElement.clientTop
var clientLeft = document.documentElement.clientLeft
var box = node.getBoundingClientRect()
var top = box.top + window.pageYOffset - clientTop
var left = box.left + window.pageXOffset - clientLeft
// Place card 15px from right when it's too far over.
if(left + 350 >= document.body.offsetWidth) {
state = state.setIn(['coords', 'right'], 15)
state = state.setIn(['coords', 'left'], 'initial')
} else {
state = state.setIn(['coords', 'left'], left)
state = state.setIn(['coords', 'right'], 'initial')
}
return state
.setIn(['coords', 'top'], top)
.set('is_visible', true)
.set('data', false)
}
// Count the number of rows of data the supporter has
const calculateRows = state =>
state.set('rows', state.get('data').entrySeq().filter(pair => {
var [key, val] = pair
return val && String(val).length
}).count() + 1.5)
const ajaxSupporter = node => {
var id = node.getAttribute('data-id')
return request.get(`/nonprofits/${app.nonprofit_id}/supporters/${id}/info_card`).perform()
}
var $responses = flatMap(ajaxSupporter, $showClicks)
const setSupporterData = (state, response) => {
var d = response.body
if(!d) return state
state = state.set('data', OrderedMap({
name: d.name
, email: d.email
, phone: utils.pretty_phone(d.phone)
, address: utils.address_with_commas(d.address, d.city, d.state_code, d.zip_code,d.country)
, organization: d.organization
, total_raised: '$' + utils.cents_to_dollars(d.raised)
, link: `/nonprofits/${app.nonprofit_id}/supporters?sid=${d.id}/`
}))
// Count the rows of present data to calculate the card height
state = calculateRows(state)
return state
}
var $state = flyd.immediate(scanMerge([
[$hideClicks, state => state.set('is_visible', false)],
[$showClicks, displayCard],
[$responses, setSupporterData],
], state))
var infoCard = view(root, document.body, state)
flyd.map(infoCard, $state)
// XXX viewscript lol
appl.def('show_supporter_info_card', function(node){ $showClicks(appl.prev_elem(node)) })