jam-cloud/web/app/assets/javascripts/react-components/AccountRetailerScreen.js.js...

476 lines
14 KiB
CoffeeScript

context = window
rest = context.JK.Rest()
logger = context.JK.logger
AppStore = context.AppStore
LocationActions = context.LocationActions
RetailerActions = context.RetailerActions
RetailerStore = context.RetailerStore
UserStore = context.UserStore
profileUtils = context.JK.ProfileUtils
@AccountRetailerScreen = React.createClass({
mixins: [
ICheckMixin,
Reflux.listenTo(AppStore, "onAppInit"),
Reflux.listenTo(RetailerStore, "onRetailerChanged")
Reflux.listenTo(UserStore, "onUserChanged")
]
shownOnce: false
screenVisible: false
TILE_ACCOUNT: 'account'
TILE_TEACHERS: 'teachers'
TILE_SALES: 'sales'
TILE_AGREEMENT: 'agreement'
TILES: ['account', 'teachers', 'sales', 'agreement']
onAppInit: (@app) ->
@app.bindScreen('account/retailer', {beforeShow: @beforeShow, afterShow: @afterShow, beforeHide: @beforeHide})
onRetailerChanged: (retailerState) ->
@setState(retailerState)
onUserChanged: (userState) ->
@noRetailerCheck(userState?.user)
@setState({user: userState?.user})
componentDidMount: () ->
@checkboxes = [{selector: 'input.slot-decision', stateKey: 'slot-decision'}]
@root = $(@getDOMNode())
@iCheckify()
componentDidUpdate: () ->
@iCheckify()
checkboxChanged: (e) ->
checked = $(e.target).is(':checked')
value = $(e.target).val()
#@setState({userSchedulingComm: value})
beforeHide: (e) ->
#ProfileActions.viewTeacherProfileDone()
@screenVisible = false
return true
beforeShow: (e) ->
LocationActions.load()
noRetailerCheck: (user) ->
if user?.id? && @screenVisible
if !user.owned_retailer_id?
window.JK.Banner.showAlert("You are not the owner of a retailer in our systems. If you are, please contact support@jamkazam.com and we'll update your account.")
return false
else
if !@shownOnce
@shownOnce = true
RetailerActions.refresh(user.owned_retailer_id)
return true
else
return false
afterShow: (e) ->
@screenVisible = true
logger.debug("AccountRetailerScreen: afterShow")
logger.debug("after show", @state.user)
@noRetailerCheck(@state.user)
getInitialState: () ->
{
retailer: null,
user: null,
selected: 'account',
updateErrors: null,
retailerName: null,
teacherSplit: null,
teacherInvitations: null,
updating: false
}
nameValue: () ->
if this.state.retailerName?
this.state.retailerName
else
this.state.retailer.name
nameChanged: (e) ->
$target = $(e.target)
val = $target.val()
@setState({retailerName: val})
onCancel: (e) ->
e.preventDefault()
context.location.href = '/client#/account'
onUpdate: (e) ->
e.preventDefault()
if this.state.updating
return
name = @root.find('input[name="name"]').val()
region = @root.find('select[name="regions"]').val()
city = @root.find('select[name="cities"]').val()
password = @root.find('input[type="password"]').val()
teacherSplit = @teacherSplit()
if teacherSplit
retailerSplit = Number((100 - teacherSplit).toFixed(2))
@setState(updating: true)
rest.updateRetailer({
id: this.state.retailer.id,
name: name,
state: region,
city: city,
password:password,
split: {teacher: teacherSplit, retailer: retailerSplit}
}).done((response) => @onUpdateDone(response)).fail((jqXHR) => @onUpdateFail(jqXHR))
onUpdateDone: (response) ->
@setState({retailer: response, retailerName: null, teacherSplit: null, updateErrors: null, updating: false})
@app.layout.notify({title: "update success", text: "Your retailer information has been successfully updated"})
onUpdateFail: (jqXHR) ->
handled = false
@setState({updating: false})
if jqXHR.status == 422
errors = JSON.parse(jqXHR.responseText)
handled = true
@setState({updateErrors: errors})
if !handled
@app.ajaxError(jqXHR, null, null)
inviteTeacher: () ->
@app.layout.showDialog('invite-retailer-user', {d1: true})
resendInvitation: (id, e) ->
e.preventDefault()
rest.resendRetailerInvitation({
id: this.state.retailer.id, invitation_id: id
}).done((response) => @resendInvitationDone(response)).fail((jqXHR) => @resendInvitationFail(jqXHR))
resendInvitationDone: (response) ->
@app.layout.notify({title: 'invitation resent', text: 'Invitation was resent to ' + response.email})
resendInvitationFail: (jqXHR) ->
@app.ajaxError(jqXHR)
deleteInvitation: (id, e) ->
e.preventDefault()
rest.deleteRetailerInvitation({
id: this.state.retailer.id, invitation_id: id
}).done((response) => @deleteInvitationDone(id, response)).fail((jqXHR) => @deleteInvitationFail(jqXHR))
deleteInvitationDone: (id, response) ->
context.RetailerActions.deleteInvitation(id)
deleteInvitationFail: (jqXHR) ->
@app.ajaxError(jqXHR)
removeFromRetailer: (id, isTeacher, e) ->
if isTeacher
rest.deleteRetailerTeacher({id: this.state.retailer.id, teacher_id: id}).done((response) => @removeFromRetailerDone(response)).fail((jqXHR) => @removeFromRetailerFail(jqXHR))
removeFromRetailerDone: (retailer) ->
context.JK.Banner.showNotice("User removed", "User was removed from your retailer.")
context.RetailerActions.updateRetailer(retailer)
removeFromRetailerFail: (jqXHR) ->
@app.ajaxError(jqXHR)
renderUser: (user, isTeacher) ->
photo_url = user.photo_url
if !photo_url?
photo_url = '/assets/shared/avatar_generic.png'
`<div className="retailer-user">
<div className="avatar">
<img src={photo_url} />
</div>
<div className="usersname">
{user.name}
</div>
<div className="user-actions">
<a onClick={this.removeFromRetailer.bind(this, user.id, isTeacher)}>remove from retailer</a>
</div>
</div>`
renderInvitation: (invitation) ->
`<div key={invitation.id} className="retailer-invitation">
<table>
<tbody>
<td className="description">{invitation.first_name} {invitation.last_name}</td>
<td className="message">
<div className="detail-block">has not yet accepted invitation<br/>
<a className="resend" onClick={this.resendInvitation.bind(this, invitation.id)}>resend invitation</a>
<a className="delete" onClick={this.deleteInvitation.bind(this, invitation.id)}>delete</a>
</div>
</td>
</tbody>
</table>
</div>`
renderTeachers: () ->
teachers = []
if this.state.retailer.teachers? && this.state.retailer.teachers.length > 0
for teacher in this.state.retailer.teachers
if teacher.user
teachers.push(@renderUser(teacher.user, true))
else
teachers = `<p>No teachers</p>`
teachers
renderTeacherInvitations: () ->
invitations = []
if this.state.teacherInvitations? && this.state.teacherInvitations.length > 0
for invitation in this.state.teacherInvitations
invitations.push(@renderInvitation(invitation))
else
invitations = `<p>No pending invitations</p>`
invitations
mainContent: () ->
if !@state.user? || !@state.retailer?
`<div className="loading">Loading...</div>`
else if @state.selected == @TILE_ACCOUNT
@account()
else if @state.selected == @TILE_TEACHERS
@teachers()
else if @state.selected == @TILE_SALES
@earnings()
else if @state.selected == @TILE_AGREEMENT
@agreement()
else
@account()
handleLocationChange: (country, region, city) ->
logger.debug("handleLocationChange #{country} #{region} ${city}")
@setState({city: city, region: region})
teacherSplitCurrent: () ->
if this.state.teacherSplit?
console.log("taking state for teacher split")
this.state.teacherSplit
else
this.state.retailer.payment_details.teacher
teacherSplitValue: () ->
@teacherSplitCurrent()
retailerSplitValue: () ->
teacherSplit = @teacherSplitCurrent()
return (100 - teacherSplit).toFixed(2)
onTeacherBlur: () ->
teacherSplit = @root.find('input[name="teacher-split"]').val()
teacherSplit = Number(teacherSplit)
if teacherSplit != teacherSplit #NaN?
@setState({teacherSplit: null})
teacherSplit: () ->
teacherSplit = @root.find('input[name="teacher-split"]').val()
if teacherSplit
teacherSplit = Number(teacherSplit)
if !teacherSplit
console.log("defaulting to 100 because teachersplit is empty")
teacherSplit = 100
teacherSplit
onTeacherSplitChange: (e) ->
$target = $(e.target)
# edge cases first
teacherSplit = @root.find('input[name="teacher-split"]').val()
if teacherSplit == null || teacherSplit == ''
@setState({teacherSplit: ''})
return
teacherSplit = Number(teacherSplit)
if teacherSplit != teacherSplit # NaN?
console.log("teacher split is NaN; ignoring")
# do nothing; this way junk doesn't start showing up in retail square. Onblur will fix
return
teacherSplit = @teacherSplit()
if teacherSplit > 100
console.log("teacher split is > 100. setting to 100")
return
if teacherSplit < 0
console.log("teacher split is < 0. setting to 0")
return
@setState({teacherSplit: teacherSplit})
account: () ->
nameErrors = context.JK.reactSingleFieldErrors('name', @state.updateErrors)
correspondenceEmailErrors = context.JK.reactSingleFieldErrors('correspondence_email', @state.updateErrors)
nameClasses = classNames({name: true, error: nameErrors?, field: true})
cancelClasses = { "button-grey": true, "cancel" : true, disabled: this.state.updating }
updateClasses = { "button-orange": true, "update" : true, disabled: this.state.updating }
processUrl = context.JK.makeAbsolute("/posa/#{this.state.retailer.slug}")
processSaleUrl = `<a href={processUrl}>{processUrl}</a>`
`<div className="account-block info-block">
<div className={nameClasses}>
<label>Retailer Name:</label>
<input type="text" name="name" value={this.nameValue()} onChange={this.nameChanged}/>
{nameErrors}
</div>
<div className="field logo">
<label>Retailer Logo:</label>
<AvatarEditLink target={this.state.retailer} target_type="retailer"/>
</div>
<SelectLocation defaultText={'Not Specified'} showCity={true} hideCountry={true} onItemChanged={this.handleLocationChange} selectedCountry={'US'} selectedCity={this.state.retailer.city} selectedRegion={this.state.retailer.state} />
<div className="field password">
</div>
<div className="field password">
<div className="scooter">
<label>Retailer Username:</label>
<label >Administrator</label>
</div>
<div className="scooter">
<label>Retailer Password:</label>
<input type="password" defaultValue="" placeholder="leave blank for no change"/>
</div>
<div>
<label>Process Sale URL:</label>
{processSaleUrl} <span className="usage-hint">(enter Administrator/password to access this page)</span>
</div>
</div>
<h4>Payments</h4>
<div className="field stripe-connect">
<StripeConnect purpose='retailer' user={this.state.user}/>
</div>
<div className="field split">
<div className="teacher-split">
<label>Teacher % of Each Lesson:</label>
<input name="teacher-split" className="split-input teacher" type="number" defaultValue="" placeholder="please enter a value 0-100" value={this.teacherSplitValue()} onChange={this.onTeacherSplitChange} onBlur={this.onTeacherBlur}/>
<span className="usage-hint">Enter 0-100</span>
</div>
<div className="retailer-split">
<label>Retailer % of Each Lesson:</label>
<input name="retailer-split" className="split-input retailer" type="number" defaultValue="" value={this.retailerSplitValue()} readonly={true} disabled={true}/>
<span className="usage-hint">This is computed automatically based on the Teacher %</span>
</div>
</div>
<div className="actions">
<a className={classNames(cancelClasses)} onClick={this.onCancel}>CANCEL</a>
<a className={classNames(updateClasses)} onClick={this.onUpdate}>UPDATE</a>
</div>
</div>`
teachers: () ->
teachers = @renderTeachers()
teacherInvitations = @renderTeacherInvitations()
`<div className="members-block info-block">
<div className="column column-left">
<div>
<h3>teachers:</h3>
<a onClick={this.inviteTeacher} className="button-orange invite-dialog">INVITE TEACHER</a>
<br className="clearall" />
</div>
<div className="teacher-invites">
{teacherInvitations}
</div>
<div className="teachers">
{teachers}
</div>
</div>
</div>`
earnings: () ->
`<div className="earnings-block info-block">
<p>Coming soon</p>
</div>`
agreement: () ->
`<div className="agreement-block info-block">
<p>The agreement between your retailer and JamKazam is part of JamKazam's terms of service. You can find the
complete terms of service <a href="/corp/terms" target="_blank">here</a>. And you can find the section that is
most specific to the retailer terms <a href="/corp/terms" target="_blank">here</a>.</p>
</div>`
selectionMade: (selection, e) ->
e.preventDefault()
@setState({selected: selection})
createTileLink: (i, tile) ->
active = this.state.selected == tile
classes = classNames({last: i == @TILES.length - 1, activeTile: active})
return `<div key={i} className="profile-tile"><a className={classes}
onClick={this.selectionMade.bind(this, tile)}>{tile}</a></div>`
onCustomBack: (customBack, e) ->
e.preventDefault()
context.location = customBack
render: () ->
mainContent = @mainContent()
profileSelections = []
for tile, i in @TILES
profileSelections.push(@createTileLink(i, tile, profileSelections))
profileNav = `<div className="profile-nav">
{profileSelections}
</div>`
`<div className="content-body-scroller">
<div className="profile-header profile-head">
<div className="store-header">retailer:</div>
{profileNav}
<div className="clearall"></div>
</div>
<div className="profile-body">
<div className="profile-wrapper">
<div className="main-content">
{mainContent}
<br />
</div>
</div>
</div>
</div>`
})