www/project/icla/views/pages/vote.js.rb (239 lines of code) (raw):
class Vote < Vue
def initialize
@disabled = true
@alert = nil
# initialize form fields
@token = Server.data.token
@member = Server.data.member
@debug = Server.data.debug
console.log('vote')
console.log('token: ' + @token)
console.log('member: ' + @member)
@progress = Server.data.progress
console.log('progress: ' + @progress.inspect)
if @progress
@phase = @progress[:phase]
else
@phase = 'unknown' # flag
end
console.log('phase: ' + @phase)
if not @token
@alert = 'Token is required for this page'
elsif @phase == 'unknown'
@alert = 'Cannot determine phase: could not read token file'
elsif @phase == 'error'
@alert = @progress[:errorMessage]
elsif @phase != 'vote'
@alert = 'Wrong phase: ' + @phase + '; should be vote'
else
@pmc = @progress[:project]
console.log('pmc: ' + @pmc)
@proposer = @progress[:proposer]
@contributor = @progress[:contributor]
@iclaname = @contributor[:name]
@iclaemail = @contributor[:email]
@comments = @progress[:comments]
@votes = @progress[:votes]
@vote = ''
@timestamp = ''
@commentBody = ''
@subject = @progress[:subject]
@showComment = false;
end
end
def render
_p %{
This form allows PMC and PPMC members to
vote to invite a contributor to become a committer or a PMC/PPMC member.
}
if @phase == 'vote'
_b 'Project: ' + @pmc
_p
_b 'Contributor: ' + @iclaname + ' (' + @iclaemail + ')'
_p
_b 'Proposed by: ' + @proposer
_p
_p 'Subject: ' + @subject
_p
_div.form_group.vote do
_label do
_input type: 'radio', name: 'vote', value: '+1',
onClick: lambda {@vote = '+1'; @showComment = false; checkValidity()}
_span ' +1 approve '
end
_p
_label do
_input type: 'radio', name: 'vote', value: '+0',
onClick: lambda {@vote = '+0'; @showComment = false; checkValidity()}
_span " +0 don't care "
end
_p
_label do
_input type: 'radio', name: 'vote', value: '-0',
onClick: lambda {@vote = '-0'; @showComment = false; checkValidity()}
_span " -0 don't care "
end
_p
_label do
_input type: 'radio', name: 'vote', value: '-1',
onClick: lambda {@vote = '-1'; @showComment = true; checkValidity()}
_span ' -1 disapprove, because... '
end
_p
end
#
# Form fields
#
if @showComment
_div.form_group do
_textarea.form_control rows: 4,
placeholder: 'reason to disapprove',
id: 'commentBody', value: @commentBody,
onChange: self.setCommentBody
end
end
_h5 'Voting history'
tally = {} # most recent vote details for each member
# previous votes
@votes.each {|v|
tally[v.member] = [v.vote, v.timestamp]
_p v.vote + ' From: ' + v.member + ' Date: ' + v.timestamp
}
_h5 'Summary of voting so far'
vote_count = {}
tally.each_key { |k|
vote_count[tally[k][0]] ||= 0
vote_count[tally[k][0]] += 1
_ k + ' ' + tally[k][0] + ' ' + tally[k][1]
_br
}
_br
vote_count.each_key {|k|
_ k + ': ' + vote_count[k]
_br
}
started = new Date(@votes[0]['timestamp'])
now = new Date()
elapsed = (now - started) / (1000 * 60 * 60)
_ 'Voting started: ' + started.toISOString() + ' Hours elapsed: ' + elapsed.to_i
_p
_h5 'Previous discussion'
# previous comments
@comments.each {|c|
_b 'From: ' + c.member + ' Date: ' + c.timestamp
_p c.comment
}
#
# Submission buttons
#
_p do
_button.btn.btn_primary 'Submit my vote', disabled: @disabled,
onClick: self.submitVote
_b ' or '
_button.btn.btn_primary 'Cancel the vote', disabled: false,
onClick: self.cancelVote
_b ' or '
_button.btn.btn_primary 'Tally the vote', disabled: false,
onClick: self.tallyVote
end
end
if @debug
_p 'token: ' + @token
_p 'comment: ' + @commentBody
_p 'vote: ' + @vote
end
# error messages
if @alert
_div.alert.alert_danger do
_b 'Error: '
_span @alert
end
end
#
# Hidden form: preview invite email
#
_div.modal.fade.invitation_preview! do
_div.modal_dialog do
_div.modal_content do
_div.modal_header do
_button.close "\u00d7", type: 'button', data_dismiss: 'modal'
_h4 'Preview Invitation Email'
end
_div.modal_body do
# headers
_div do
_b 'From: '
_span @memberEmail
end
_div do
_b 'To: '
_span "#{@iclaname} <#{@iclaemail}>"
end
_div do
_b 'cc: '
_span @pmcEmail
end
# draft invitation email
_div.form_group do
_label :for => 'invitation'
_textarea.form_control.invitation! value: @invitation, rows: 12,
onChange: self.setInvitation # does not appear to work
end
end
_div.modal_footer do
_button.btn.btn_default 'Cancel', data_dismiss: 'modal'
_button.btn.btn_primary 'Mock Send', onClick: self.mockSend
end
end
end
end
end
# TODO: finish the code!
def setInvitation(event)
console.log('setInvitation:' + event)
alert('setInvitation: Not yet implemented')
end
def submitVote(event)
console.log('submitVote:' + event)
updateVoteFile('submitVote')
end
def cancelVote(event)
console.log('cancelVote:' + event)
updateVoteFile('cancelVote', 'cancelled')
end
def tallyVote(event)
console.log('tallyVote:' + event)
updateVoteFile('tallyVote', 'tallied') # is this correct? TODO
end
def updateVoteFile(action, newPhase)
data = {
action: action,
vote: @vote,
member: @member,
token: @token,
expectedPhase: 'vote',
newPhase: newPhase,
}
data['comment']=@commentBody if @vote == '-1'
console.log('>update: '+ data.inspect) # debug
post 'update', data do |response|
console.log('<update: '+ response.inspect) # debug
@alert = response.error
unless @alert
@votes = response['contents']['votes']
@comments = response['contents']['comments']
end
end
end
# when the form is redisplayed, e.g. after displaying/hiding the commentBody
def updated()
focusComment()
end
# when the form is initially loaded
def mounted()
end
#
# field setters
#
def setCommentBody(event)
@commentBody = event.target.value
checkValidity()
end
def focusComment()
f = document.getElementById('commentBody')
f.focus() if f
end
#
# validation and processing
#
# client side field validations
def checkValidity()
# disabled if no vote or vote -1 without comment
@disabled = (@vote == '' or (@vote == '-1' and @commentBody.empty?))
end
# pretend to send an invitation
def mockSend()
# dismiss modal dialog
jQuery('#invitation-preview').modal(:hide)
# save information for later use (for demo purposes, this is client only)
FormData.token = @token
FormData.fullname = @iclaname
FormData.email = @iclaemail
FormData.pmc = @pmc
FormData.votelink = @votelink
FormData.noticelink = @noticelink
# for demo purposes advance to the interview. Note: the below line
# updates the URL in a way that breaks the back button.
history.replaceState({}, nil, "form?token=#@token")
# change the view
Main.navigate(Interview)
end
end