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

537 lines
16 KiB
CoffeeScript
Raw Permalink Normal View History

context = window
rest = context.JK.Rest()
logger = context.JK.logger
AppStore = context.AppStore
SchoolActions = context.SchoolActions
SchoolStore = context.SchoolStore
UserStore = context.UserStore
profileUtils = context.JK.ProfileUtils
@AccountSchoolScreen = React.createClass({
mixins: [
ICheckMixin,
Reflux.listenTo(AppStore, "onAppInit"),
Reflux.listenTo(SchoolStore, "onSchoolChanged")
Reflux.listenTo(UserStore, "onUserChanged")
]
shownOnce: false
screenVisible: false
TILE_ACCOUNT: 'account'
TILE_MEMBERS: 'members'
TILE_EARNINGS: 'earnings'
TILE_AGREEMENT: 'agreement'
TILES: ['account', 'members', 'earnings', 'agreement']
onAppInit: (@app) ->
@app.bindScreen('account/school', {beforeShow: @beforeShow, afterShow: @afterShow, beforeHide: @beforeHide})
onSchoolChanged: (schoolState) ->
@setState(schoolState)
onUserChanged: (userState) ->
@noSchoolCheck(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) ->
noSchoolCheck: (user) ->
if user?.id? && @screenVisible
if !user.owned_school_id?
window.JK.Banner.showAlert("You are not the owner of a school in our systems. If you are, please contact support@jamkazam.com and we'll update your account.")
return false
else
if !@shownOnce
@shownOnce = true
SchoolActions.refresh(user.owned_school_id)
return true
else
return false
afterShow: (e) ->
@screenVisible = true
logger.debug("AccountSchoolScreen: afterShow")
logger.debug("after show", @state.user)
@noSchoolCheck(@state.user)
getInitialState: () ->
{
school: null,
user: null,
selected: 'account',
userSchedulingComm: null,
updateErrors: null,
schoolName: null,
studentInvitations: null,
teacherInvitations: null,
2016-09-08 10:59:58 +00:00
updating: false,
distributions: []
}
isSchoolManaged: () ->
if this.state.userSchedulingComm?
this.state.userSchedulingComm == 'school'
else
this.state.school.scheduling_communication == 'school'
nameValue: () ->
if this.state.schoolName?
this.state.schoolName
else
this.state.school.name
nameChanged: (e) ->
$target = $(e.target)
val = $target.val()
@setState({schoolName: 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()
if @isSchoolManaged()
scheduling_communication = 'school'
else
scheduling_communication = 'teacher'
correspondence_email = @root.find('input[name="correspondence_email"]').val()
@setState(updating: true)
rest.updateSchool({
id: this.state.school.id,
name: name,
scheduling_communication: scheduling_communication,
correspondence_email: correspondence_email
}).done((response) => @onUpdateDone(response)).fail((jqXHR) => @onUpdateFail(jqXHR))
onUpdateDone: (response) ->
@setState({school: response, userSchedulingComm: null, schoolName: null, updateErrors: null, updating: false})
@app.layout.notify({title: "update success", text: "Your school 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-school-user', {d1: true})
inviteStudent: () ->
@app.layout.showDialog('invite-school-user', {d1: false})
resendInvitation: (id, e) ->
e.preventDefault()
rest.resendSchoolInvitation({
id: this.state.school.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.deleteSchoolInvitation({
id: this.state.school.id, invitation_id: id
}).done((response) => @deleteInvitationDone(id, response)).fail((jqXHR) => @deleteInvitationFail(jqXHR))
deleteInvitationDone: (id, response) ->
context.SchoolActions.deleteInvitation(id)
deleteInvitationFail: (jqXHR) ->
@app.ajaxError(jqXHR)
removeFromSchool: (id, isTeacher, e) ->
if isTeacher
2016-09-08 10:59:58 +00:00
rest.deleteSchoolTeacher({
id: this.state.school.id,
teacher_id: id
}).done((response) => @removeFromSchoolDone(response)).fail((jqXHR) => @removeFromSchoolFail(jqXHR))
else
2016-09-08 10:59:58 +00:00
rest.deleteSchoolStudent({
id: this.state.school.id,
student_id: id
}).done((response) => @removeFromSchoolDone(response)).fail((jqXHR) => @removeFromSchoolFail(jqXHR))
removeFromSchoolDone: (school) ->
context.JK.Banner.showNotice("User removed", "User was removed from your school.")
context.SchoolActions.updateSchool(school)
removeFromSchoolFail: (jqXHR) ->
@app.ajaxError(jqXHR)
renderUser: (user, isTeacher) ->
photo_url = user.photo_url
if !photo_url?
photo_url = '/assets/shared/avatar_generic.png'
2016-09-09 14:53:38 +00:00
mailto = "mailto:#{user.email}"
`<div className="school-user">
<div className="avatar">
2016-09-08 10:59:58 +00:00
<img src={photo_url}/>
</div>
<div className="usersname">
2016-09-09 14:53:38 +00:00
<span className="just-name">{user.name}</span>
<span className="just-email"><a href={mailto}>{user.email}</a></span>
</div>
<div className="user-actions">
<a onClick={this.removeFromSchool.bind(this, user.id, isTeacher)}>remove from school</a>
</div>
</div>`
renderInvitation: (invitation) ->
`<div key={invitation.id} className="school-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.school.teachers? && this.state.school.teachers.length > 0
for teacher in this.state.school.teachers
2016-09-08 10:59:58 +00:00
if teacher.user
teachers.push(@renderUser(teacher.user, true))
else
teachers = `<p>No teachers</p>`
teachers
renderStudents: () ->
students = []
if this.state.school.students? && this.state.school.students.length > 0
for student in this.state.school.students
students.push(@renderUser(student, false))
else
students = `<p>No students</p>`
students
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
renderStudentInvitations: () ->
invitations = []
if this.state.studentInvitations? && this.state.studentInvitations.length > 0
for invitation in this.state.studentInvitations
invitations.push(@renderInvitation(invitation))
else
invitations = `<p>No pending invitations</p>`
invitations
mainContent: () ->
if !@state.user? || !@state.school?
`<div className="loading">Loading...</div>`
else if @state.selected == @TILE_ACCOUNT
@account()
else if @state.selected == @TILE_MEMBERS
@members()
else if @state.selected == @TILE_EARNINGS
@earnings()
else if @state.selected == @TILE_AGREEMENT
@agreement()
else
@account()
account: () ->
ownerEmail = this.state.school.owner.email
correspondenceEmail = this.state.school.correspondence_email
correspondenceDisabled = !@isSchoolManaged()
nameErrors = context.JK.reactSingleFieldErrors('name', @state.updateErrors)
correspondenceEmailErrors = context.JK.reactSingleFieldErrors('correspondence_email', @state.updateErrors)
nameClasses = classNames({name: true, error: nameErrors?, field: true})
correspondenceEmailClasses = classNames({
correspondence_email: true,
error: correspondenceEmailErrors?,
field: true
})
2016-09-08 10:59:58 +00:00
cancelClasses = {"button-grey": true, "cancel": true, disabled: this.state.updating}
updateClasses = {"button-orange": true, "update": true, disabled: this.state.updating}
if this.state.school.education
management = null
else
management = `<div>
<h4>Management Preference</h4>
<div className="field scheduling_communication">
<div className="scheduling_communication school">
<input type="radio" name="scheduling_communication" readOnly={true} value="school"
checked={this.isSchoolManaged()}/><label>School owner will manage scheduling of student lessons
sourced
by JamKazam</label>
</div>
<div className="scheduling_communication teacher">
<input type="radio" name="scheduling_communication" readOnly={true} value="teacher"
checked={!this.isSchoolManaged()}/><label>Teacher will manage scheduling of lessons</label>
</div>
</div>
<div className={correspondenceEmailClasses}>
<label>Correspondence Email:</label>
<input type="text" name="correspondence_email" placeholder={ownerEmail} defaultValue={correspondenceEmail}
disabled={correspondenceDisabled}/>
<div className="hint">All emails relating to lesson scheduling will go to this email if school owner manages
scheduling.
</div>
{correspondenceEmailErrors}
</div>
</div>`
`<div className="account-block info-block">
<div className={nameClasses}>
<label>School Name:</label>
<input type="text" name="name" value={this.nameValue()} onChange={this.nameChanged}/>
{nameErrors}
</div>
<div className="field logo">
<label>School Logo:</label>
<AvatarEditLink target={this.state.school} target_type="school"/>
</div>
2016-09-08 10:59:58 +00:00
{management}
<h4>Payments</h4>
<div className="field stripe-connect">
<StripeConnect purpose='school' 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>`
members: () ->
teachers = @renderTeachers()
teacherInvitations = @renderTeacherInvitations()
students = @renderStudents()
studentInvitations = @renderStudentInvitations()
`<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>
2016-09-08 10:59:58 +00:00
<br className="clearall"/>
</div>
<div className="teacher-invites">
{teacherInvitations}
</div>
<div className="teachers">
{teachers}
</div>
</div>
<div className="column column-right">
<div>
<h3>students:</h3>
<a onClick={this.inviteStudent} className="button-orange invite-dialog">INVITE STUDENT</a>
<br className="clearall"/>
</div>
<div className="student-invites">
{studentInvitations}
</div>
<div className="students">
{students}
</div>
</div>
</div>`
earnings: () ->
`<div className="earnings-block info-block">
<p>Coming soon</p>
</div>`
2016-09-08 10:59:58 +00:00
paymentsToYou: () ->
rows = []
for paymentHistory in this.state.distributions
paymentMethod = 'Stripe'
if paymentHistory.distributed
date = paymentHistory.teacher_payment.teacher_payment_charge.last_billing_attempt_at
status = 'Paid'
else
date = paymentHistory.created_at
if paymentHistory.not_collectable
status = 'Uncollectible'
else if !paymentHistory.teacher?.teacher?.stripe_account_id?
status = 'No Stripe Acct'
else
status = 'Collecting'
date = context.JK.formatDate(date, true)
description = paymentHistory.description
if paymentHistory.teacher_payment?
amt = paymentHistory.teacher_payment.real_distribution_in_cents
else
amt = paymentHistory.real_distribution_in_cents
displayAmount = ' $' + (amt / 100).toFixed(2)
amountClasses = {status: status}
row =
`<tr>
<td>{date}</td>
<td className="capitalize">{paymentMethod}</td>
<td>{description}</td>
<td className="capitalize">{status}</td>
<td className={classNames(amountClasses)}>{displayAmount}</td>
</tr>`
rows.push(row)
`<div>
<table className="payment-table">
<thead>
<tr>
<th>DATE</th>
<th>METHOD</th>
<th>DESCRIPTION</th>
<th>STATUS</th>
<th>AMOUNT</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
<a className="btn-next-pager" href="/api/sales?page=1">Next</a>
<div className="end-of-payments-list end-of-list">No more payment history</div>
<div className="input-aligner">
<a className="back button-grey" onClick={this.onBack}>BACK</a>
</div>
<br className="clearall"/>
</div>`
agreement: () ->
`<div className="agreement-block info-block">
<p>The agreement between your music school 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 music school 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">school:</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>`
})