428 lines
16 KiB
CoffeeScript
428 lines
16 KiB
CoffeeScript
context = window
|
|
rest = context.JK.Rest()
|
|
logger = context.JK.logger
|
|
|
|
AppStore = context.AppStore
|
|
UserStore = context.UserStore
|
|
|
|
profileUtils = context.JK.ProfileUtils
|
|
|
|
@AccountOnboarderScreen = React.createClass({
|
|
|
|
mixins: [
|
|
Reflux.listenTo(AppStore, "onAppInit"),
|
|
Reflux.listenTo(UserStore, "onUserChanged")
|
|
]
|
|
|
|
shownOnce: false
|
|
screenVisible: false
|
|
updates: []
|
|
|
|
onAppInit: (@app) ->
|
|
@app.bindScreen('account/onboarder', {beforeShow: @beforeShow, afterShow: @afterShow, beforeHide: @beforeHide})
|
|
|
|
onRetailerChanged: (retailerState) ->
|
|
@setState(retailerState)
|
|
|
|
onUserChanged: (userState) ->
|
|
@getOnboardings(userState?.user)
|
|
@setState({user: userState?.user})
|
|
|
|
componentDidMount: () ->
|
|
@root = $(@getDOMNode())
|
|
|
|
componentDidUpdate: () ->
|
|
datePicked = @datePicked.bind(this)
|
|
@sessionDate = @root.find('.date-picker')
|
|
@sessionDate.each(() ->
|
|
$this = $(this)
|
|
id = $this.attr('data-id')
|
|
day = $this.attr('data-date')
|
|
|
|
$this.datepicker({
|
|
dateFormat: "D M d yy",
|
|
onSelect: ((e, inst) ->
|
|
datePicked(id, 'onboarding_test_session_at')
|
|
)
|
|
})
|
|
|
|
# initialize day
|
|
if(day)
|
|
$this.datepicker("setDate", new Date(day))
|
|
)
|
|
|
|
|
|
toggleDate: (dateText, inst) ->
|
|
console.log("THIS", this)
|
|
console.log("IDID ID #{$(this).attr('data-id')}")
|
|
'onboarding_test_session_at'
|
|
|
|
beforeHide: (e) ->
|
|
@screenVisible = false
|
|
return true
|
|
|
|
beforeShow: (e) ->
|
|
LocationActions.load()
|
|
|
|
getOnboardings: (user) ->
|
|
if user?.id? && @screenVisible
|
|
@setState({fetchingOnboardings: true})
|
|
rest.listOnboardings(context.JK.currentUserId).done((onboardings) =>
|
|
@setState({onboardings: onboardings, fetchingOnboardings: false})
|
|
)
|
|
.fail(() =>
|
|
@setState({fetchingOnboardings: false})
|
|
@app.layout.notify({
|
|
title: "onboarding list failure",
|
|
text: "Unable to list out your onboarders. Please contact support@jamkazam.com"
|
|
})
|
|
)
|
|
|
|
|
|
afterShow: (e) ->
|
|
@screenVisible = true
|
|
logger.debug("AccountOnboardingScreen: afterShow")
|
|
logger.debug("after show", @state.user)
|
|
@getOnboardings(@state.user)
|
|
|
|
getInitialState: () ->
|
|
{
|
|
onboardings: [],
|
|
user: null,
|
|
updating: false
|
|
}
|
|
|
|
onCancel: (e) ->
|
|
e.preventDefault()
|
|
context.location.href = '/client#/account'
|
|
|
|
onUpdate: (e) ->
|
|
if e?
|
|
e.preventDefault()
|
|
|
|
if this.state.updating
|
|
return
|
|
|
|
if @updates.length == 0
|
|
return
|
|
update = @updates.pop()
|
|
|
|
@setState({updating: true})
|
|
rest.updateOnboarding(update).done((response) => @onUpdateDone(response)).fail((jqXHR) => @onUpdateFail(jqXHR))
|
|
|
|
|
|
updateRow: (onboarding) ->
|
|
for match in this.state.onboardings
|
|
if match.id == onboarding.id
|
|
$.extend(match, onboarding)
|
|
break
|
|
this.setState({onboardings: this.state.onboardings})
|
|
|
|
onUpdateDone: (response) ->
|
|
this.setState({updating: false})
|
|
@updateRow(response)
|
|
if @updates.length > 0
|
|
@onUpdate()
|
|
|
|
onUpdateFail: (jqXHR) ->
|
|
this.setState({updating: false})
|
|
@app.layout.notify({
|
|
title: "update failure",
|
|
text: "Unable to update user. Please let us know which user and field you can not update."
|
|
})
|
|
if @updates.length > 0
|
|
@onUpdate()
|
|
|
|
|
|
|
|
|
|
updateField: (id, field, e) ->
|
|
if(e)
|
|
e.preventDefault()
|
|
|
|
console.log("Update Field Called for ", field)
|
|
$target = $(e.target)
|
|
console.log("TARGET", $target)
|
|
value = true
|
|
if $target.is('select')
|
|
value = $target.val()
|
|
options = {id: id}
|
|
options[field] = value
|
|
@postUpdate(field, value)
|
|
@queueUpdate(options)
|
|
|
|
postUpdate: (field, value) ->
|
|
if field == 'onboarding_onboarded_at'
|
|
console.log("onboarding onboarded at set")
|
|
context.JK.Banner.showAlert({title: "One Last Thing", html: "Please send this student Email #5 right now.<br/><br/>This is a critical email, and if you don't send it now, you'll forget. Thanks!"});
|
|
|
|
queueUpdate: (update) ->
|
|
@updates.push(update)
|
|
@onUpdate()
|
|
|
|
|
|
createLinkField: (id, onboarding, field, display, timeLabel = null) ->
|
|
if onboarding[field]
|
|
email = `<span style={{whiteSpace:'no-wrap'}}>{timeLabel} {onboarding[field]}</span>`
|
|
else
|
|
email = `<a href='#' className="daystamper" onClick={this.updateField.bind(this, id, field)}>{display}</a>`
|
|
|
|
email
|
|
|
|
showLostBanner: (id, field) ->
|
|
lostHtml = 'Please choose a reason why the user was lost:<br/><br/>' +
|
|
'<select><option value=""></option><option value="Lost Interest">Lost Interest</option><option value="No Win/Mac Computer">No Win/Mac Computer</option><option value="No Broadband Internet">No Broadband Internet</option><option value="No Webcam">No Webcam</option><option value="Bad Internet">Bad Internet</option><option value="Setup Wizard Failure">Setup Wizard Failure</option><option value="No Audio Stream In Session">No Audio Stream In Session</option><option value="No Video Stream In Session">No Video Stream In Session</option><option value="Other">Other</option></select>'
|
|
$item = context.JK.Banner.showAlert(
|
|
{buttons: [{name: 'CANCEL', click: () -> console.log('cancel clicked')}], html: lostHtml});
|
|
$item.find('select').change((e)=> @updateField(id, field, e); context.JK.Banner.hide())
|
|
|
|
showEscalationBanner: (id, field) ->
|
|
lostHtml = 'Please choose a reason why the user needs escalating:<br/><br/>' +
|
|
'<select><option value=""></option><option value="No Audio Stream In Session">No Audio Stream In Session</option><option value="No Video Stream In Session">No Video Stream In Session</option><option value="Setup Wizard Failure">Setup Wizard Failure</option><option value="Other">Other</option></select>'
|
|
$item = context.JK.Banner.showAlert(
|
|
{buttons: [{name: 'CANCEL', click: () -> console.log('cancel clicked')}], html: lostHtml});
|
|
$item.find('select').change((e)=> @updateField(id, field, e); context.JK.Banner.hide())
|
|
|
|
createLostField: (id, onboarding) ->
|
|
field = 'onboarding_lost_reason'
|
|
if onboarding[field]
|
|
field = `<span>{
|
|
onboarding[field]
|
|
}</span>`
|
|
else
|
|
field = `<a href='#' className="daystamper" onClick={this.showLostBanner.bind(this, id, field)}>lost student</a>`
|
|
field
|
|
|
|
createEscalatedField: (id, onboarding) ->
|
|
field = 'onboarding_escalation_reason'
|
|
if onboarding[field]
|
|
field = `<span>{
|
|
onboarding[field]
|
|
}</span>`
|
|
else
|
|
field = `<a href='#' className="daystamper" onClick={this.showEscalationBanner.bind(this, id, field)}>escalate
|
|
student</a>`
|
|
field
|
|
|
|
createOnboardingField: (onboarding) ->
|
|
@createLinkField(onboarding.id, onboarding, 'onboarding_onboarded_at', 'onboarded successfully')
|
|
|
|
watchTextArea: (id, field, e) ->
|
|
$text = $(e.target)
|
|
if @textTimeout
|
|
clearTimeout(@textTimeout)
|
|
@textTimeout = null
|
|
@textTimeout = setTimeout(() =>
|
|
data = {id: id}
|
|
data[field] = $text.val()
|
|
@queueUpdate(data)
|
|
@textTimeout = null
|
|
, 3000)
|
|
|
|
datePicked: (id, field) ->
|
|
$tr = @root.find("tr[data-id='" + id + "']")
|
|
picker = $tr.find(".date-picker")
|
|
|
|
hour = $tr.find('.hour').val()
|
|
minute = $tr.find('.minute').val()
|
|
am_pm = $tr.find('.am_pm').val()
|
|
|
|
|
|
if hour? and hour != ''
|
|
hour = new Number(hour)
|
|
if am_pm == 'PM'
|
|
hour += 12
|
|
else
|
|
hour = null
|
|
|
|
if minute? and minute != ''
|
|
minute = new Number(minute)
|
|
else
|
|
minute = null
|
|
|
|
date = picker.datepicker("getDate")
|
|
if date?
|
|
date.setHours(hour)
|
|
date.setMinutes(minute)
|
|
data = {id: id}
|
|
data[field] = date.toString()
|
|
@queueUpdate(data)
|
|
|
|
mainContent: () ->
|
|
cancelClasses = {"button-orange": true, "update": true}
|
|
onboardings = []
|
|
|
|
if @state.fetchingOnboardings
|
|
onboardings.push(`<tr>
|
|
<td colSpan="6" style={{textAlign:'center'}}>FETCHING STUDENTS</td>
|
|
</tr>`)
|
|
else if @state.onboardings.length > 0
|
|
for onboarding in @state.onboardings
|
|
|
|
hours = []
|
|
for hour in ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
|
|
if hour == '12'
|
|
key = '00'
|
|
else
|
|
key = hour
|
|
hours.push(`<option key={key} value={new Number(key)}>{hour}</option>`)
|
|
|
|
minutes = []
|
|
for minute in ['00', '15', '30', '45']
|
|
minutes.push(`<option key={minute} value={minute}>{minute}</option>`)
|
|
|
|
am_pm = [`<option key="AM" value="AM">AM</option>`, `<option key="PM" value="PM">PM</option>`]
|
|
|
|
|
|
email1 = @createLinkField(onboarding.id, onboarding, 'onboarding_email_1_sent_at', 'sent email 1', 'email 1:')
|
|
email2 = @createLinkField(onboarding.id, onboarding, 'onboarding_email_2_sent_at', 'sent email 2', 'email 2:')
|
|
email3 = @createLinkField(onboarding.id, onboarding, 'onboarding_email_3_sent_at', 'sent email 3', 'email 3:')
|
|
email4 = @createLinkField(onboarding.id, onboarding, 'onboarding_email_4_sent_at', 'sent email 4', 'email 4:')
|
|
email5 = @createLinkField(onboarding.id, onboarding, 'onboarding_email_5_sent_at', 'sent email 5', 'email 5:')
|
|
|
|
scheduledSession = @createLinkField(onboarding.id, onboarding, 'onboarding_test_session_scheduled_at',
|
|
'scheduled session', 'scheduled session:')
|
|
|
|
sessionOutcomes = ['', "Successful", "Setup Wizard Failure", "No Audio Stream in Session",
|
|
"No Video Stream In Session", "Other"]
|
|
sessionOutcomesJSX = []
|
|
for sessionOutcome in sessionOutcomes
|
|
sessionOutcomesJSX.push(`<option key={sessionOutcome} value={sessionOutcome}>{sessionOutcome}</option>`)
|
|
selectSessionOutcome =
|
|
`<select value={onboarding.onboarding_test_session_outcome}
|
|
onChange={this.updateField.bind(this, onboarding.id, 'onboarding_test_session_outcome')}>{sessionOutcomesJSX}</select>`
|
|
|
|
session_at = onboarding.onboarding_test_session_at
|
|
amOrPm = 'AM'
|
|
currentHours = '01'
|
|
currentMinutes = '00'
|
|
if session_at
|
|
sessionTime = new Date(session_at)
|
|
session_at = sessionTime.toLocaleDateString()
|
|
currentHours = sessionTime.getHours()
|
|
if sessionTime.getHours() > 11
|
|
amOrPm = 'PM'
|
|
currentHours -= 12
|
|
|
|
currentMinutes = sessionTime.getMinutes()
|
|
|
|
active = onboarding.onboarding_status == 'Emailed' || onboarding.onboarding_status == 'Assigned'
|
|
|
|
if active
|
|
activeClass = 'active-row'
|
|
else
|
|
activeClass = 'inactive-row'
|
|
|
|
columns = []
|
|
if active
|
|
columns.push(`<td key="1">
|
|
<div className="info-unit">{email1}</div>
|
|
<div className="info-unit">{email2}</div>
|
|
<div className="info-unit">{email3}</div>
|
|
<div className="info-unit">{email4}</div>
|
|
<div className="info-unit">{email5}</div>
|
|
</td>`)
|
|
columns.push(`<td key="2">
|
|
<div className="info-unit" style={{textAlign:'center'}}>{scheduledSession}</div>
|
|
|
|
<div className="info-unit">
|
|
<label style={{marginBottom:'5px'}}>Test Session Date:</label> <input data-id={onboarding.id}
|
|
className="date-picker"
|
|
name="session-date" type="text"
|
|
data-date={session_at}></input>
|
|
<label style={{marginTop:'10px'}}>Test Session Time:</label>
|
|
<select value={currentHours}
|
|
onChange={this.datePicked.bind(this, onboarding.id, 'onboarding_test_session_at')}
|
|
className="hour">{hours}</select> : <select value={currentMinutes}
|
|
onChange={this.datePicked.bind(this, onboarding.id, 'onboarding_test_session_at')}
|
|
className="minute">{minutes}</select>
|
|
<select value={amOrPm} style={{marginLeft:'13px'}}
|
|
onChange={this.datePicked.bind(this, onboarding.id, 'onboarding_test_session_at')}
|
|
className="am_pm">{am_pm}</select>
|
|
</div>
|
|
<div className="info-unit"><label style={{marginTop:'10px'}}>Session Outcome:</label>
|
|
{selectSessionOutcome}</div>
|
|
</td>`)
|
|
columns.push(`<td key="3">
|
|
<div className="info-unit"
|
|
style={{marginBottom:'20px'}}>{this.createLostField(onboarding.id, onboarding)}</div>
|
|
<div className="info-unit">{this.createEscalatedField(onboarding.id, onboarding)}</div>
|
|
</td>`)
|
|
columns.push(`<td key="4">
|
|
<div className="info-unit">{this.createOnboardingField(onboarding)}</div>
|
|
</td>`)
|
|
else
|
|
columns.push(`<td colSpan="4" style={{textAlign:'center', fontSize:'18px'}}>
|
|
<span>{onboarding.onboarding_status}</span><br/><br/><span style={{fontSize:'12px', marginLeft:'10px'}}>This student will drop off your list now next time you visit this page.</span>
|
|
</td>`)
|
|
onboarding = `<tr className={activeClass} key={onboarding.id} data-id={onboarding.id}>
|
|
<td>
|
|
<div className="info-unit">Name: {onboarding.name}</div>
|
|
<div className="info-unit">Email: {onboarding.email}</div>
|
|
<div className="info-unit" style={{marginTop:'20px'}}>Assigned
|
|
on {onboarding.onboarder_assigned_at}</div>
|
|
</td>
|
|
{columns}
|
|
<td>
|
|
<div className="info-unit">
|
|
<textarea className="info-unit"
|
|
onChange={this.watchTextArea.bind(this, onboarding.id, 'onboarding_onboarder_notes')}>{onboarding.onboarding_onboarder_notes}</textarea>
|
|
</div>
|
|
</td>
|
|
</tr>`
|
|
onboardings.push(onboarding)
|
|
else
|
|
onboardings.push(`<tr>
|
|
<td colSpan="6" style={{textAlign:'center'}}>NO STUDENTS ASSIGNED</td>
|
|
</tr>`)
|
|
|
|
`<div className="account-block info-block">
|
|
<div>
|
|
<div className="instructions">
|
|
Manage the users here that you are responsible for onboarding.<br/><br/>
|
|
All links in the table below, when clicked, will immediately mark that item as done. For example, once
|
|
you send your first email to the user, click the <b>sent email 1</b> link.<br/><br/>
|
|
All other fields will automatically save as you use them, such as the <b>Session Outcome</b> dropdown,
|
|
and the <b>Notes</b> text box.
|
|
</div>
|
|
<table className="jamtable">
|
|
<thead>
|
|
<tr>
|
|
<th>User</th>
|
|
<th>Email</th>
|
|
<th>Test Session</th>
|
|
<th>Problems</th>
|
|
<th>Onboarded</th>
|
|
<th>Notes</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{onboardings}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
|
|
<div className="actions">
|
|
<a className={classNames(cancelClasses)} onClick={this.onCancel}>BACK</a>
|
|
</div>
|
|
</div>`
|
|
|
|
render: () ->
|
|
mainContent = @mainContent()
|
|
|
|
`<div className="content-body-scroller">
|
|
<div className="profile-header profile-head">
|
|
<div className="clearall"></div>
|
|
</div>
|
|
|
|
<div className="profile-body">
|
|
<div className="profile-wrapper">
|
|
<div className="main-content">
|
|
{mainContent}
|
|
<br />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>`
|
|
}) |