Finished most of basic features for job profiles.
This commit is contained in:
parent
9af6694054
commit
b96bca8c18
12 changed files with 88 additions and 16 deletions
|
@ -28,3 +28,10 @@
|
||||||
ul.projects
|
ul.projects
|
||||||
li
|
li
|
||||||
margin-top: 20px
|
margin-top: 20px
|
||||||
|
|
||||||
|
.approved, .not-approved
|
||||||
|
display: none
|
||||||
|
|
||||||
|
#job-profile-notes
|
||||||
|
width: 100%
|
||||||
|
height: 100px
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
h3(data-i18n="account_settings.job_profile") Job Profile
|
h3(data-i18n="account_settings.job_profile") Job Profile
|
||||||
|
|
||||||
if me.get('jobProfileApproved')
|
if me.get('jobProfileApproved')
|
||||||
p.lead(data-i18n="account_settings.job_profile_approved") Your job profile has been approved by CodeCombat.
|
p.lead(data-i18n="account_settings.job_profile_approved") Your job profile has been approved by CodeCombat. Hungry employers will see it until you mark it inactive or it is stale for two months.
|
||||||
else
|
else
|
||||||
p.lead(data-i18n="account_settings.job_profile_explanation") Fill this out, and we will try to find you a job, and stuff.
|
p.lead(data-i18n="account_settings.job_profile_explanation") Hi! Fill this out, and if we think we can find you a software developer job, we will get in touch to approve your profile.
|
||||||
|
|
||||||
#job-profile-treema
|
#job-profile-treema
|
|
@ -9,6 +9,12 @@ block content
|
||||||
span(data-i18n="account_profile.edit_settings") Edit Settings
|
span(data-i18n="account_profile.edit_settings") Edit Settings
|
||||||
|
|
||||||
if user.get('jobProfile')
|
if user.get('jobProfile')
|
||||||
|
if me.isAdmin()
|
||||||
|
button.btn#toggle-job-profile-approved
|
||||||
|
i.icon-cog
|
||||||
|
span(data-i18n='account_profile.approved').approved Approved
|
||||||
|
span(data-i18n='account_profile.approved').not-approved Not Approved
|
||||||
|
|
||||||
- var profile = user.get('jobProfile');
|
- var profile = user.get('jobProfile');
|
||||||
.profile-header-container
|
.profile-header-container
|
||||||
img(src=photoURL).img-thumbnail.pull-left
|
img(src=photoURL).img-thumbnail.pull-left
|
||||||
|
@ -52,6 +58,14 @@ block content
|
||||||
strong #{school.degree} at #{school.school}
|
strong #{school.degree} at #{school.school}
|
||||||
.clearfix
|
.clearfix
|
||||||
|
|
||||||
|
if user.get('jobProfileNotes') || me.isAdmin()
|
||||||
|
h3 Our Notes
|
||||||
|
- var notes = user.get('jobProfileNotes') || '';
|
||||||
|
if me.isAdmin()
|
||||||
|
textarea#job-profile-notes!= notes
|
||||||
|
else
|
||||||
|
p!= marked(notes)
|
||||||
|
|
||||||
.col-md-4
|
.col-md-4
|
||||||
if profile.projects.length
|
if profile.projects.length
|
||||||
h2 Projects
|
h2 Projects
|
||||||
|
|
|
@ -21,7 +21,7 @@ block content
|
||||||
a(href="#password-pane", data-toggle="tab", data-i18n="account_settings.password_tab") Password
|
a(href="#password-pane", data-toggle="tab", data-i18n="account_settings.password_tab") Password
|
||||||
li
|
li
|
||||||
a(href="#email-pane", data-toggle="tab", data-i18n="account_settings.emails_tab") Emails
|
a(href="#email-pane", data-toggle="tab", data-i18n="account_settings.emails_tab") Emails
|
||||||
if me.isAdmin()
|
if showsJobProfileTab
|
||||||
li
|
li
|
||||||
a(href="#job-profile-pane", data-toggle="tab", data-i18n="account_settings.job_profile_tab") Job Profile
|
a(href="#job-profile-pane", data-toggle="tab", data-i18n="account_settings.job_profile_tab") Job Profile
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,8 @@ block content
|
||||||
th Yrs Exp
|
th Yrs Exp
|
||||||
th Last Updated
|
th Last Updated
|
||||||
th Current Job
|
th Current Job
|
||||||
|
if me.isAdmin()
|
||||||
|
th ✓?
|
||||||
|
|
||||||
tbody
|
tbody
|
||||||
for candidate, index in candidates
|
for candidate, index in candidates
|
||||||
|
@ -52,10 +54,11 @@ block content
|
||||||
tr(data-candidate-id=candidate.id)
|
tr(data-candidate-id=candidate.id)
|
||||||
td
|
td
|
||||||
if authorized
|
if authorized
|
||||||
img(src=candidate.getPhotoURL(), alt=profile.name, title=profile.name, width=50)
|
// Want image, but it doesn't work without loading every Gravatar profile
|
||||||
|
//img(src=candidate.getPhotoURL(), alt=profile.name, title=profile.name, width=50)
|
||||||
p= profile.name
|
p= profile.name
|
||||||
else
|
else
|
||||||
img(src="/images/pages/contribute/archmage.png", alt="", title="Sign up as an employer to see our candidates", width=50)
|
//img(src="/images/pages/contribute/archmage.png", alt="", title="Sign up as an employer to see our candidates", width=50)
|
||||||
p Developer ##{index + 1}
|
p Developer ##{index + 1}
|
||||||
if profile.country == 'USA'
|
if profile.country == 'USA'
|
||||||
td= profile.city
|
td= profile.city
|
||||||
|
@ -76,3 +79,8 @@ block content
|
||||||
else
|
else
|
||||||
td
|
td
|
||||||
em Employer sign-up required.
|
em Employer sign-up required.
|
||||||
|
if me.isAdmin()
|
||||||
|
if candidate.get('jobProfileApproved')
|
||||||
|
td ✓
|
||||||
|
else
|
||||||
|
td ✗
|
|
@ -1,7 +1,7 @@
|
||||||
extends /templates/modal/modal_base
|
extends /templates/modal/modal_base
|
||||||
|
|
||||||
block modal-header-content
|
block modal-header-content
|
||||||
h3(data-i18n="diplomat_suggestion.title")
|
h3(data-i18n="diplomat_suggestion.title") Help translate CodeCombat!
|
||||||
|
|
||||||
block modal-body-content
|
block modal-body-content
|
||||||
h4(data-i18n="diplomat_suggestion.sub_heading") We need your language skills.
|
h4(data-i18n="diplomat_suggestion.sub_heading") We need your language skills.
|
||||||
|
|
9
app/templates/modal/employer_signup_modal.jade
Normal file
9
app/templates/modal/employer_signup_modal.jade
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
extends /templates/modal/modal_base
|
||||||
|
|
||||||
|
block modal-header-content
|
||||||
|
h3(data-i18n="employer_signup.title") Hire CodeCombat Players
|
||||||
|
|
||||||
|
block modal-body-content
|
||||||
|
h4(data-i18n="employer_signup.sub_heading") Let us find your next brilliant developers.
|
||||||
|
|
||||||
|
p(data-i18n="employer_signup.pitch_body") When you hire one of our players, you will pay CodeCombat 18% of her first-year salary, payable within 30 days of when she starts working. We will fully refund our placement fee if she leaves or is fired within 90 days. Cool? Email george@codecombat.com to get set up with employer permissions to see our candidates.
|
|
@ -7,7 +7,12 @@ module.exports = class ProfileView extends View
|
||||||
template: template
|
template: template
|
||||||
loadingProfile: true
|
loadingProfile: true
|
||||||
|
|
||||||
|
events:
|
||||||
|
'click #toggle-job-profile-approved': 'toggleJobProfileApproved'
|
||||||
|
'keyup #job-profile-notes': 'onJobProfileNotesChanged'
|
||||||
|
|
||||||
constructor: (options, @userID) ->
|
constructor: (options, @userID) ->
|
||||||
|
@onJobProfileNotesChanged = _.debounce @onJobProfileNotesChanged, 1000
|
||||||
super options
|
super options
|
||||||
@user = User.getByID(@userID)
|
@user = User.getByID(@userID)
|
||||||
@loadingProfile = false if 'gravatarProfile' of @user
|
@loadingProfile = false if 'gravatarProfile' of @user
|
||||||
|
@ -36,3 +41,23 @@ module.exports = class ProfileView extends View
|
||||||
context.marked = marked
|
context.marked = marked
|
||||||
context.moment = moment
|
context.moment = moment
|
||||||
context
|
context
|
||||||
|
|
||||||
|
afterRender: ->
|
||||||
|
super()
|
||||||
|
@updateProfileApproval() if me.isAdmin()
|
||||||
|
|
||||||
|
updateProfileApproval: ->
|
||||||
|
approved = @user.get 'jobProfileApproved'
|
||||||
|
@$el.find('.approved').toggle Boolean(approved)
|
||||||
|
@$el.find('.not-approved').toggle not approved
|
||||||
|
|
||||||
|
toggleJobProfileApproved: ->
|
||||||
|
approved = not @user.get 'jobProfileApproved'
|
||||||
|
@user.set 'jobProfileApproved', approved
|
||||||
|
@user.save()
|
||||||
|
@updateProfileApproval()
|
||||||
|
|
||||||
|
onJobProfileNotesChanged: (e) =>
|
||||||
|
notes = @$el.find("#job-profile-notes").val()
|
||||||
|
@user.set 'jobProfileNotes', notes
|
||||||
|
@user.save()
|
||||||
|
|
|
@ -73,6 +73,7 @@ module.exports = class SettingsView extends View
|
||||||
c.chosenPhoto = me.getPhotoURL()
|
c.chosenPhoto = me.getPhotoURL()
|
||||||
c.subs = {}
|
c.subs = {}
|
||||||
c.subs[sub] = 1 for sub in c.me.get('emailSubscriptions') or ['announcement', 'notification', 'tester', 'level_creator', 'developer']
|
c.subs[sub] = 1 for sub in c.me.get('emailSubscriptions') or ['announcement', 'notification', 'tester', 'level_creator', 'developer']
|
||||||
|
c.showsJobProfileTab = me.isAdmin() or me.get('jobProfile') or location.hash.search('job-profile-') isnt -1
|
||||||
c
|
c
|
||||||
|
|
||||||
getSubscriptions: ->
|
getSubscriptions: ->
|
||||||
|
|
|
@ -3,6 +3,8 @@ template = require 'templates/employers'
|
||||||
app = require 'application'
|
app = require 'application'
|
||||||
User = require 'models/User'
|
User = require 'models/User'
|
||||||
CocoCollection = require 'models/CocoCollection'
|
CocoCollection = require 'models/CocoCollection'
|
||||||
|
employerSignupTemplate = require 'templates/modal/employer_signup_modal'
|
||||||
|
ModalView = require 'views/kinds/ModalView'
|
||||||
|
|
||||||
class CandidatesCollection extends CocoCollection
|
class CandidatesCollection extends CocoCollection
|
||||||
url: '/db/user/x/candidates'
|
url: '/db/user/x/candidates'
|
||||||
|
@ -13,7 +15,7 @@ module.exports = class EmployersView extends View
|
||||||
template: template
|
template: template
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click tr': 'onCandidateClicked'
|
'click tbody tr': 'onCandidateClicked'
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super options
|
super options
|
||||||
|
@ -83,5 +85,6 @@ module.exports = class EmployersView extends View
|
||||||
url = "/account/profile/#{id}"
|
url = "/account/profile/#{id}"
|
||||||
app.router.navigate url, {trigger: true}
|
app.router.navigate url, {trigger: true}
|
||||||
else
|
else
|
||||||
console.log 'gotta prompt them to sign up'
|
employerSignupModal = new ModalView()
|
||||||
alert 'sign up for dat my twisted archon'
|
employerSignupModal.template = employerSignupTemplate
|
||||||
|
@openModalView employerSignupModal
|
||||||
|
|
|
@ -16,7 +16,7 @@ privateProperties = [
|
||||||
'gplusID', 'music', 'volume', 'aceConfig'
|
'gplusID', 'music', 'volume', 'aceConfig'
|
||||||
]
|
]
|
||||||
candidateProperties = [
|
candidateProperties = [
|
||||||
'jobProfile', 'jobProfileApproved'
|
'jobProfile', 'jobProfileApproved', 'jobProfileNotes'
|
||||||
]
|
]
|
||||||
|
|
||||||
UserHandler = class UserHandler extends Handler
|
UserHandler = class UserHandler extends Handler
|
||||||
|
@ -35,14 +35,18 @@ UserHandler = class UserHandler extends Handler
|
||||||
super(arguments...)
|
super(arguments...)
|
||||||
@editableProperties.push('permissions') unless config.isProduction
|
@editableProperties.push('permissions') unless config.isProduction
|
||||||
|
|
||||||
|
getEditableProperties: (req, document) ->
|
||||||
|
props = super req, document
|
||||||
|
props.push 'jobProfileApproved', 'jobProfileNotes' if req.user.isAdmin()
|
||||||
|
props
|
||||||
|
|
||||||
formatEntity: (req, document) ->
|
formatEntity: (req, document) ->
|
||||||
return null unless document?
|
return null unless document?
|
||||||
obj = document.toObject()
|
obj = document.toObject()
|
||||||
delete obj[prop] for prop in serverProperties
|
delete obj[prop] for prop in serverProperties
|
||||||
includePrivates = req.user and (req.user.isAdmin() or req.user._id.equals(document._id))
|
includePrivates = req.user and (req.user.isAdmin() or req.user._id.equals(document._id))
|
||||||
delete obj[prop] for prop in privateProperties unless includePrivates
|
delete obj[prop] for prop in privateProperties unless includePrivates
|
||||||
#includeCandidate = includePrivates or (obj.jobProfileApproved and req.user and ('employer' in (req.user.permissions ? [])))
|
includeCandidate = includePrivates or (obj.jobProfileApproved and req.user and ('employer' in (req.user.permissions ? [])))
|
||||||
includeCandidate = includePrivates or (req.user and ('employer' in (req.user.permissions ? []))) # testing
|
|
||||||
delete obj[prop] for prop in candidateProperties unless includeCandidate
|
delete obj[prop] for prop in candidateProperties unless includeCandidate
|
||||||
obj.emailHash = @buildEmailHash document
|
obj.emailHash = @buildEmailHash document
|
||||||
return obj
|
return obj
|
||||||
|
@ -221,16 +225,17 @@ UserHandler = class UserHandler extends Handler
|
||||||
since = (new Date((new Date()) - 2 * 30.4 * 86400 * 1000)).toISOString()
|
since = (new Date((new Date()) - 2 * 30.4 * 86400 * 1000)).toISOString()
|
||||||
#query = {'jobProfileApproved': true, 'jobProfile.active': true, 'jobProfile.updated': {$gt: since}}
|
#query = {'jobProfileApproved': true, 'jobProfile.active': true, 'jobProfile.updated': {$gt: since}}
|
||||||
query = {'jobProfile.active': true, 'jobProfile.updated': {$gt: since}} # testing
|
query = {'jobProfile.active': true, 'jobProfile.updated': {$gt: since}} # testing
|
||||||
|
query.jobProfileApproved = true unless req.user.isAdmin()
|
||||||
selection = 'jobProfile'
|
selection = 'jobProfile'
|
||||||
if authorized
|
selection += ' email' if authorized
|
||||||
selection += ' email'
|
selection += ' jobProfileApproved' if req.user.isAdmin()
|
||||||
User.find(query).select(selection).exec (err, documents) =>
|
User.find(query).select(selection).exec (err, documents) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
candidates = (@formatCandidate(authorized, doc) for doc in documents)
|
candidates = (@formatCandidate(authorized, doc) for doc in documents)
|
||||||
@sendSuccess(res, candidates)
|
@sendSuccess(res, candidates)
|
||||||
|
|
||||||
formatCandidate: (authorized, document) ->
|
formatCandidate: (authorized, document) ->
|
||||||
fields = if authorized then ['jobProfile', '_id'] else ['jobProfile']
|
fields = if authorized then ['jobProfile', 'jobProfileApproved', '_id'] else ['jobProfile']
|
||||||
obj = _.pick document.toObject(), fields
|
obj = _.pick document.toObject(), fields
|
||||||
obj.emailHash = @buildEmailHash document
|
obj.emailHash = @buildEmailHash document
|
||||||
subfields = ['country', 'city', 'lookingFor', 'skills', 'experience', 'updated']
|
subfields = ['country', 'city', 'lookingFor', 'skills', 'experience', 'updated']
|
||||||
|
|
|
@ -90,7 +90,7 @@ UserSchema = c.object {},
|
||||||
name: {type: 'string', maxLength: 30, title: 'Link Name', description: 'What are you linking to? Ex: "Personal Website", "Twitter"'}
|
name: {type: 'string', maxLength: 30, title: 'Link Name', description: 'What are you linking to? Ex: "Personal Website", "Twitter"'}
|
||||||
link: c.url {title: 'Link', description: 'The URL.', default: 'http://codecombat.com'}
|
link: c.url {title: 'Link', description: 'The URL.', default: 'http://codecombat.com'}
|
||||||
jobProfileApproved: {title: 'Job Profile Approved', type: 'boolean', description: 'Whether your profile has been approved by CodeCombat.'}
|
jobProfileApproved: {title: 'Job Profile Approved', type: 'boolean', description: 'Whether your profile has been approved by CodeCombat.'}
|
||||||
|
jobProfileNotes: {type: 'string', maxLength: 1000, title: 'Our Notes', description: "CodeCombat's notes on the candidate.", format: 'markdown', default: ''}
|
||||||
c.extendBasicProperties UserSchema, 'user'
|
c.extendBasicProperties UserSchema, 'user'
|
||||||
|
|
||||||
module.exports = UserSchema
|
module.exports = UserSchema
|
||||||
|
|
Reference in a new issue