-
Notifications
You must be signed in to change notification settings - Fork 12
Improve/candidate view #119
Changes from 2 commits
33def5a
a08ae25
68850ab
f761f18
3e3bb55
f54c721
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,14 +14,14 @@ var SENDGRID_API_KEY = flag.String("sendgrid", "", "Sendgrid API Key") | |
// TODO - Later just have one IP address with port info. | ||
var Ip = flag.String("ip", "http://localhost:2020", "Public IP address of server") | ||
|
||
func Send(name, email, validity, token string) { | ||
func Send(email, validity, token string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. exported function Send should have comment or be unexported |
||
if *SENDGRID_API_KEY == "" { | ||
fmt.Println(*Ip + "/#/quiz/" + token) | ||
return | ||
} | ||
from := mail.NewEmail("Dgraph", "join@dgraph.io") | ||
subject := "Invitation for screening quiz from Dgraph" | ||
to := mail.NewEmail(name, email) | ||
to := mail.NewEmail("", email) | ||
// TODO - Move this to a template. | ||
url := fmt.Sprintf("%v/#/quiz/%v", *Ip, token) | ||
body := ` | ||
|
@@ -30,7 +30,7 @@ func Send(name, email, validity, token string) { | |
<title></title> | ||
</head> | ||
<body> | ||
Hello ` + name + `, | ||
Hello! | ||
<br/><br/> | ||
You have been invited to take the screening quiz by Dgraph. | ||
<br/> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -254,10 +254,15 @@ | |
} | ||
} | ||
|
||
function candidatesController($rootScope, $stateParams, $state, inviteService) { | ||
function candidatesController($rootScope, $stateParams, $state, inviteService, moment) { | ||
candidatesVm = this; | ||
candidatesVm.sortType = 'score'; | ||
candidatesVm.sortReverse = true; | ||
candidatesVm.expires = expires; | ||
candidatesVm.cancel = cancel; | ||
candidatesVm.resend = resend; | ||
candidatesVm.showModal = showModal; | ||
candidatesVm.candidate_email = ""; | ||
|
||
candidatesVm.quizID = $stateParams.quizID; | ||
|
||
|
@@ -299,6 +304,70 @@ | |
}, function(err) { | ||
console.log(err); | ||
}); | ||
|
||
function showModal(candidateID, email) { | ||
console.log(email) | ||
candidatesVm.candidate_email = email; | ||
dialog.showModal(); | ||
dialog.querySelector('.submit').addEventListener('click', function() { | ||
candidatesVm.cancel(candidateID); | ||
dialog.close(); | ||
}) | ||
} | ||
|
||
angular.element(document).ready(function() { | ||
var dialog = document.querySelector('dialog'); | ||
|
||
dialog.querySelector('.close').addEventListener('click', function() { | ||
dialog.close(); | ||
}); | ||
}); | ||
|
||
function expires(validity) { | ||
var numDays = moment(validity).diff(moment(), 'days') | ||
if (numDays == 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moment.js is awesome library, saves time (not pun intended) and easy to use. We should use it only if we will be dealing with time/date heavily. Else we can work around in javascript. We should try to avoid using external libraries because it will add unnecessary load time by adding 68-70kb of file request for doing one task only. Can decide about this. Moment.js is (time)saviour, has useful methods. var validity_date = new Date(validity)
var today = new Date();
var diff = (validity_date - today)/(1000*60*60*24)
noOfDays = Math.round(diff) |
||
return "Today" | ||
} else if (numDays > 0) { | ||
return numDays | ||
} | ||
return "Expired" | ||
} | ||
|
||
function cancel(candidateID) { | ||
inviteService.cancelInvite(candidateID).then(function(cancelled) { | ||
if (!cancelled) { | ||
SNACKBAR({ | ||
message: "Invite could not be cancelled.", | ||
messageType: "error", | ||
}) | ||
return | ||
} | ||
SNACKBAR({ | ||
message: "Invite cancelled successfully.", | ||
}) | ||
$state.transitionTo("invite.dashboard", { | ||
quizID: candidatesVm.quizID, | ||
}) | ||
}) | ||
} | ||
|
||
function resend(candidateID) { | ||
inviteService.resendInvite(candidateID).then(function(response) { | ||
if (!response.success) { | ||
SNACKBAR({ | ||
message: response.message, | ||
messageType: "error", | ||
}) | ||
return | ||
} | ||
SNACKBAR({ | ||
message: response.message | ||
}) | ||
$state.transitionTo("invite.dashboard", { | ||
quizID: candidatesVm.quizID, | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
function candidateReportController($rootScope, $stateParams, $state, inviteService) { | ||
|
@@ -372,6 +441,7 @@ | |
"$stateParams", | ||
"$state", | ||
"inviteService", | ||
"moment", | ||
candidatesController | ||
]; | ||
angular.module('GruiApp').controller('candidatesController', candidatesDependency); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ | |
var query = "{\ | ||
quiz(_uid_: " + quizId + ") {\ | ||
quiz.candidate {\ | ||
cancel\ | ||
email\ | ||
}\ | ||
}\ | ||
|
@@ -39,6 +40,9 @@ | |
var candidates = data.quiz[0]["quiz.candidate"]; | ||
if (candidates) { | ||
for (var i = 0; i < candidates.length; i++) { | ||
if (candidates[i].cancel === 'true') { | ||
continue | ||
} | ||
if (candidates[i].email === email) { | ||
return deferred.resolve(true); | ||
} | ||
|
@@ -49,6 +53,60 @@ | |
return deferred.promise; | ||
} | ||
|
||
services.resendInvite = function(candidateID) { | ||
var deferred = $q.defer(); | ||
// TODO - User filter on email after incorporating Dgraph schema. | ||
var query = "{\ | ||
quiz.candidate(_uid_: " + candidateID + ") {\ | ||
email\ | ||
token\ | ||
validity\ | ||
}\ | ||
}" | ||
|
||
services.proxy(query).then(function(data) { | ||
var candidate = data["quiz.candidate"][0]; | ||
if (candidate == null) { | ||
return deferred.resolve({ | ||
success: false, | ||
message: "No candidate found." | ||
}); | ||
} | ||
return candidate | ||
}).then(function(candidate) { | ||
var paylaod = { | ||
"email": candidate.email, | ||
"token": candidate.token, | ||
"validity": candidate.validity | ||
} | ||
MainService.post('/candidate/invite/' + candidateID, paylaod).then(function(data) { | ||
return deferred.resolve({ | ||
sucess: true, | ||
message: data.Message | ||
}) | ||
}) | ||
}); | ||
return deferred.promise; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing semicolon. |
||
|
||
services.cancelInvite = function(candidateID) { | ||
var deferred = $q.defer(); | ||
var mutation = "mutation {\ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bad escaping of EOL. Use option multistr if needed. |
||
set {\ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bad escaping of EOL. Use option multistr if needed. |
||
<_uid_:" + candidateID + "> <cancel> \"true\" .\ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bad escaping of EOL. Use option multistr if needed. |
||
}\ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bad escaping of EOL. Use option multistr if needed. |
||
}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing semicolon. |
||
|
||
services.proxy(mutation).then(function(data) { | ||
if (data.code == "ErrorOk") { | ||
return deferred.resolve(true); | ||
} | ||
return deferred.resolve(false); | ||
}); | ||
return deferred.promise; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing semicolon. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing semicolon. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing semicolon. |
||
|
||
|
||
// TODO - Move to a location where other services can access this. | ||
services.proxy = function(data) { | ||
return MainService.post('/proxy', data); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,29 +28,30 @@ <h5>Candidate List for Quiz</h5> | |
<th class="mdl-data-table__cell--non-numeric">Email</th> | ||
<th class="mdl-data-table__cell--non-numeric"> | ||
<a href="" ng-click="candidateVm.sortType = 'score'; candidateVm.sortReverse = !candidateVm.sortReverse"> | ||
Score | ||
<i ng-show="candidateVm.sortType == 'score' && !candidateVm.sortReverse" class="material-icons">arrow_drop_up</i> | ||
<i ng-show="candidateVm.sortType == 'score' && candidateVm.sortReverse" class="material-icons">arrow_drop_down</i> | ||
</a> | ||
Score | ||
<i ng-show="candidateVm.sortType == 'score' && !candidateVm.sortReverse" class="material-icons">arrow_drop_up</i> | ||
<i ng-show="candidateVm.sortType == 'score' && candidateVm.sortReverse" class="material-icons">arrow_drop_down</i> | ||
</a> | ||
</th> | ||
<th class="mdl-data-table__cell--non-numeric"> | ||
<a href="" ng-click="candidateVm.sortType = 'quiz_start'; candidateVm.sortReverse = !candidateVm.sortReverse"> | ||
Quiz Start | ||
<i ng-show="candidateVm.sortType == 'quiz_start' && !candidateVm.sortReverse" class="material-icons">arrow_drop_up</i> | ||
<i ng-show="candidateVm.sortType == 'quiz_start' && candidateVm.sortReverse" class="material-icons">arrow_drop_down</i> | ||
</a> | ||
Quiz Start | ||
<i ng-show="candidateVm.sortType == 'quiz_start' && !candidateVm.sortReverse" class="material-icons">arrow_drop_up</i> | ||
<i ng-show="candidateVm.sortType == 'quiz_start' && candidateVm.sortReverse" class="material-icons">arrow_drop_down</i> | ||
</a> | ||
</th> | ||
<th class="mdl-data-table__cell--non-numeric">Actions</th> | ||
</tr> | ||
</thead> | ||
<tbody ng-repeat="candidate in candidateVm.quizCandidates | orderBy:candidateVm.sortType:candidateVm.sortReverse | filter:searchCand"> | ||
<tr ng-if="candidate.complete == 'true'"> | ||
<tbody ng-repeat="candidate in candidateVm.quizCandidates | orderBy:candidateVm.sortType:candidateVm.sortReverse | filter:searchCand" on-finish-render="ngRepeatFinished"> | ||
<tr ng-show="candidate.complete == 'true'"> | ||
<td class="mdl-data-table__cell--non-numeric">{{candidate.name}}</td> | ||
<td class="mdl-data-table__cell--non-numeric">{{candidate.email}}</td> | ||
<td class="mdl-data-table__cell--non-numeric">{{candidate.score | number: 2}}</td> | ||
<td class="mdl-data-table__cell--non-numeric">{{candidate.quiz_start | date : 'MMM dd, yyyy'}}</td> | ||
<td class="mdl-data-table__cell--non-numeric"> | ||
<a ui-sref="invite.report({candidateID: candidate._uid_})">VIEW REPORT</a> | ||
<a ui-sref="invite.report({candidateID: candidate._uid_})">VIEW REPORT</a> | | ||
<a class="delete-cand" href="">DELETE</a> | ||
</td> | ||
</tr> | ||
</tbody> | ||
|
@@ -63,8 +64,8 @@ <h5>Candidate List for Quiz</h5> | |
<div class="mdl-cell mdl-cell--12-col"> | ||
<form action="#"> | ||
<div class="mdl-textfield mdl-js-textfield"> | ||
<input class="mdl-textfield__input" type="text" id="sample1" ng-model="searchCand"> | ||
<label class="mdl-textfield__label" for="sample1">Search candidate by Name/Email</label> | ||
<input class="mdl-textfield__input" type="text" id="search-cand" ng-model="searchCand"> | ||
<label class="mdl-textfield__label" for="search-cand">Search candidate by Name/Email</label> | ||
</div> | ||
</form> | ||
<table class="mdl-data-table mdl-shadow--2dp"> | ||
|
@@ -78,17 +79,25 @@ <h5>Candidate List for Quiz</h5> | |
<i ng-show="candidateVm.sortType == 'invite_sent' && candidateVm.sortReverse" class="material-icons">arrow_drop_down</i> | ||
</a> | ||
</th> | ||
<th class="mdl-data-table__cell--non-numeric">Validity</th> | ||
<th class="mdl-data-table__cell--non-numeric"> | ||
<a href="" ng-click="candidateVm.sortType = 'validity'; candidateVm.sortReverse = !candidateVm.sortReverse"> | ||
Expires (days) | ||
<i ng-show="candidateVm.sortType == 'validity' && !candidateVm.sortReverse" class="material-icons">arrow_drop_up</i> | ||
<i ng-show="candidateVm.sortType == 'validity' && candidateVm.sortReverse" class="material-icons">arrow_drop_down</i> | ||
</a> | ||
</th> | ||
<th class="mdl-data-table__cell--non-numeric">Actions</th> | ||
</tr> | ||
</thead> | ||
<tbody ng-repeat="candidate in candidateVm.quizCandidates | orderBy:candidateVm.sortType:candidateVm.sortReverse | filter:searchCand"> | ||
<tr ng-if="candidate.complete != 'true'"> | ||
<tr ng-show="candidate.complete != 'true' && candidate.cancel != 'true'"> | ||
<td class="mdl-data-table__cell--non-numeric">{{candidate.email}}</td> | ||
<td class="mdl-data-table__cell--non-numeric">{{candidate.invite_sent | date : 'MMM dd, yyyy'}}</td> | ||
<td class="mdl-data-table__cell--non-numeric">{{candidate.validity}}</td> | ||
<td class="mdl-data-table__cell--non-numeric">{{candidateVm.expires(candidate.validity)}}</td> | ||
<td class="mdl-data-table__cell--non-numeric"> | ||
<a ui-sref="invite.edit({quizID: candidateVm.quizID, candidateID: candidate._uid_})">EDIT</a> | ||
<a ui-sref="invite.edit({quizID: candidateVm.quizID, candidateID: candidate._uid_})">EDIT</a> | | ||
<a ng-click="candidateVm.resend(candidate._uid_)" href="">RESEND</a> | | ||
<a ng-click="candidateVm.showModal(candidate._uid_, candidate.email)" href="">CANCEL</a> | ||
</td> | ||
</tr> | ||
</tbody> | ||
|
@@ -97,4 +106,16 @@ <h5>Candidate List for Quiz</h5> | |
</div> | ||
</div> | ||
</div> | ||
<dialog id="dialog" class="mdl-dialog"> | ||
<h4 class="mdl-dialog__title">Cancel</h4> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This Dialog Box won't work. Need to remove this. This dialog modal is from official MDL doc: https://getmdl.io/components/index.html#dialog-section. The first line of doc says : "Note: Dialogs use the HTML element, which currently has very limited cross-browser support. To ensure support across all modern browsers, please consider using a polyfill or creating your own. There is no polyfill included with MDL." So |
||
<div class="mdl-dialog__content"> | ||
<p> | ||
Are you sure that you wan't to cancel the invite sent to {{candidateVm.candidate_email}}? | ||
</p> | ||
</div> | ||
<div class="mdl-dialog__actions"> | ||
<button type="button" class="mdl-button submit">Yes</button> | ||
<button type="button" class="mdl-button close">Cancel</button> | ||
</div> | ||
</dialog> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exported function ResendInvite should have comment or be unexported