361 lines
10 KiB
CoffeeScript
361 lines
10 KiB
CoffeeScript
context = window
|
|
rest = context.JK.Rest()
|
|
logger = context.JK.logger
|
|
|
|
AppStore = context.AppStore
|
|
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) ->
|
|
|
|
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,
|
|
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()
|
|
|
|
@setState(updating: true)
|
|
rest.updateRetailer({
|
|
id: this.state.retailer.id,
|
|
name: name,
|
|
}).done((response) => @onUpdateDone(response)).fail((jqXHR) => @onUpdateFail(jqXHR))
|
|
|
|
onUpdateDone: (response) ->
|
|
@setState({retailer: response, retailerName: 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()
|
|
|
|
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 }
|
|
|
|
`<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>
|
|
|
|
|
|
<h4>Payments</h4>
|
|
|
|
<div className="field stripe-connect">
|
|
<StripeConnect purpose='retailer' user={this.state.user}/>
|
|
</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>`
|
|
}) |