Reworked top nav. Many improvements to job profile form.

This commit is contained in:
Nick Winter 2014-04-18 09:53:28 -07:00
parent 46487f55a4
commit 844c1391ca
18 changed files with 244 additions and 71 deletions

Binary file not shown.

After

(image error) Size: 376 KiB

Binary file not shown.

After

(image error) Size: 88 KiB

View file

@ -36,6 +36,7 @@
editor: "Editor" editor: "Editor"
blog: "Blog" blog: "Blog"
forum: "Forum" forum: "Forum"
account: "Account"
admin: "Admin" admin: "Admin"
home: "Home" home: "Home"
contribute: "Contribute" contribute: "Contribute"
@ -170,6 +171,8 @@
job_profile: "Job Profile" job_profile: "Job Profile"
job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks." job_profile_approved: "Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks."
job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job." job_profile_explanation: "Hi! Fill this out, and we will get in touch about finding you a software developer job."
sample_profile: "See a sample profile"
view_profile: "View Your Profile"
account_profile: account_profile:
edit_settings: "Edit Settings" edit_settings: "Edit Settings"
@ -306,6 +309,13 @@
lg_title: "Latest Games" lg_title: "Latest Games"
clas: "CLAs" clas: "CLAs"
community:
level_editor: "Level Editor"
main_title: "CodeCombat Community"
facebook: "Facebook"
twitter: "Twitter"
gplus: "Google+"
editor: editor:
main_title: "CodeCombat Editors" main_title: "CodeCombat Editors"
main_description: "Build your own levels, campaigns, units and educational content. We provide all the tools you need!" main_description: "Build your own levels, campaigns, units and educational content. We provide all the tools you need!"
@ -315,8 +325,8 @@
thang_description: "Build units, defining their default logic, graphics and audio. Currently only supports importing Flash exported vector graphics." thang_description: "Build units, defining their default logic, graphics and audio. Currently only supports importing Flash exported vector graphics."
level_title: "Level Editor" level_title: "Level Editor"
level_description: "Includes the tools for scripting, uploading audio, and constructing custom logic to create all sorts of levels. Everything we use ourselves!" level_description: "Includes the tools for scripting, uploading audio, and constructing custom logic to create all sorts of levels. Everything we use ourselves!"
security_notice: "Many major features in these editors are not currently enabled by default. As we improve the security of these systems, they will be made generally available. If you'd like to use these features sooner, " got_questions: "Questions about using the CodeCombat editors?"
contact_us: "contact us!" contact_us: "Contact us!"
hipchat_prefix: "You can also find us in our" hipchat_prefix: "You can also find us in our"
hipchat_url: "HipChat room." hipchat_url: "HipChat room."
back: "Back" back: "Back"

View file

@ -58,7 +58,7 @@ UserSchema = c.object {},
jobProfile: c.object {title: 'Job Profile', required: ['lookingFor', 'jobTitle', 'active', 'name', 'city', 'country', 'skills', 'experience', 'shortDescription', 'longDescription', 'visa', 'work', 'education', 'projects', 'links']}, jobProfile: c.object {title: 'Job Profile', required: ['lookingFor', 'jobTitle', 'active', 'name', 'city', 'country', 'skills', 'experience', 'shortDescription', 'longDescription', 'visa', 'work', 'education', 'projects', 'links']},
lookingFor: {title: 'Looking For', type: 'string', enum: ['Full-time', 'Part-time', 'Remote', 'Contracting', 'Internship'], default: 'Full-time', description: 'What kind of developer position do you want?'} lookingFor: {title: 'Looking For', type: 'string', enum: ['Full-time', 'Part-time', 'Remote', 'Contracting', 'Internship'], default: 'Full-time', description: 'What kind of developer position do you want?'}
jobTitle: {type: 'string', maxLength: 50, title: 'Desired Job Title', description: 'What role are you looking for? Ex.: "Full Stack Engineer", "Front-End Developer", "iOS Developer"', default: 'Software Developer'} jobTitle: {type: 'string', maxLength: 50, title: 'Desired Job Title', description: 'What role are you looking for? Ex.: "Full Stack Engineer", "Front-End Developer", "iOS Developer"', default: 'Software Developer'}
active: {title: 'Active', type: 'boolean', description: 'Want interview offers right now?'} active: {title: 'Open to Offers', type: 'boolean', description: 'Want interview offers right now?'}
updated: c.date {title: 'Last Updated', description: 'How fresh your profile appears to employers. Profiles go inactive after 4 weeks.'} updated: c.date {title: 'Last Updated', description: 'How fresh your profile appears to employers. Profiles go inactive after 4 weeks.'}
name: c.shortString {title: 'Name', description: 'Name you want employers to see, like "Nick Winter".'} name: c.shortString {title: 'Name', description: 'Name you want employers to see, like "Nick Winter".'}
city: c.shortString {title: 'City', description: 'City you want to work in (or live in now), like "San Francisco" or "Lubbock, TX".', default: 'Defaultsville, CA', format: 'city'} city: c.shortString {title: 'City', description: 'City you want to work in (or live in now), like "San Francisco" or "Lubbock, TX".', default: 'Defaultsville, CA', format: 'city'}
@ -74,13 +74,14 @@ UserSchema = c.object {},
employer: c.shortString {title: 'Employer', description: 'Name of your employer.'} employer: c.shortString {title: 'Employer', description: 'Name of your employer.'}
role: c.shortString {title: 'Job Title', description: 'What was your job title or role?'} role: c.shortString {title: 'Job Title', description: 'What was your job title or role?'}
duration: c.shortString {title: 'Duration', description: 'When did you hold this gig? Ex.: "Feb 2013 - present".'} duration: c.shortString {title: 'Duration', description: 'When did you hold this gig? Ex.: "Feb 2013 - present".'}
description: {type: 'string', title: 'Description', description: 'What did you do there? (140 chars)', maxLength: 140} description: {type: 'string', title: 'Description', description: 'What did you do there? (140 chars; optional)', maxLength: 140}
education: c.array {title: 'Education', description: 'List your academic ordeals.'}, education: c.array {title: 'Education', description: 'List your academic ordeals.'},
c.object {title: 'Ordeal', description: 'Some education that befell you.', required: ['school', 'degree', 'duration']}, c.object {title: 'Ordeal', description: 'Some education that befell you.', required: ['school', 'degree', 'duration']},
school: c.shortString {title: 'School', description: 'Name of your school.'} school: c.shortString {title: 'School', description: 'Name of your school.'}
degree: c.shortString {title: 'Degree', description: 'What was your degree and field of study? Ex. Ph.D. Human-Computer Interaction (incomplete)'} degree: c.shortString {title: 'Degree', description: 'What was your degree and field of study? Ex. Ph.D. Human-Computer Interaction (incomplete)'}
duration: c.shortString {title: 'Dates', description: 'When? Ex.: "Aug 2004 - May 2008".'} duration: c.shortString {title: 'Dates', description: 'When? Ex.: "Aug 2004 - May 2008".'}
projects: c.array {title: 'Projects', description: 'Highlight your projects to amaze employers.', maxItems: 3}, description: {type: 'string', title: 'Description', description: 'Highlight anything about this educational experience. (140 chars; optional)', maxLength: 140}
projects: c.array {title: 'Projects (Top 3)', description: 'Highlight your projects to amaze employers.', maxItems: 3},
c.object {title: 'Project', description: 'A project you created.', required: ['name', 'description', 'picture'], default: {name: 'My Project', description: 'A project I worked on.', link: 'http://example.com', picture: ''}}, c.object {title: 'Project', description: 'A project you created.', required: ['name', 'description', 'picture'], default: {name: 'My Project', description: 'A project I worked on.', link: 'http://example.com', picture: ''}},
name: c.shortString {title: 'Project Name', description: 'What was the project called?', default: 'My Project'} name: c.shortString {title: 'Project Name', description: 'What was the project called?', default: 'My Project'}
description: {type: 'string', title: 'Description', description: 'Briefly describe the project.', maxLength: 400, default: 'A project I worked on.', format: 'markdown'} description: {type: 'string', title: 'Description', description: 'Briefly describe the project.', maxLength: 400, default: 'A project I worked on.', format: 'markdown'}
@ -94,6 +95,8 @@ UserSchema = c.object {},
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: ''} jobProfileNotes: {type: 'string', maxLength: 1000, title: 'Our Notes', description: "CodeCombat's notes on the candidate.", format: 'markdown', default: ''}
employerAt: c.shortString {description: "If given employer permissions to view job candidates, for which employer?"}
c.extendBasicProperties UserSchema, 'user' c.extendBasicProperties UserSchema, 'user'
module.exports = UserSchema module.exports = UserSchema

View file

@ -44,6 +44,26 @@
.form .form
max-width: 600px max-width: 600px
#job-profile-view
.profile-preview-button
&.bottom-preview
margin: 15px 0 0 0
.sample-profile-thumbnail
margin-top: -60px
.profile-completion-progress
width: 100%
display: inline-block
height: 33px
.progress-bar
line-height: 33px
.progress-next-item
margin-top: -20px
margin-bottom: 15px
#job-profile-treema #job-profile-treema
background-color: white background-color: white
@ -54,3 +74,16 @@
font-size: 14px font-size: 14px
line-height: 22px line-height: 22px
opacity: 1 opacity: 1
.treema-row
padding-top: 6px
.treema-image-file
.btn:after
content: "Upload Picture"
margin-left: 20px
img
display: block
clear: both
max-width: 300px

View file

@ -10,16 +10,19 @@
font-weight: 400 font-weight: 400
letter-spacing: 1px letter-spacing: 1px
.navbuttontext-user-name .navbuttontext-account
max-width: 110px
overflow: hidden
text-overflow: ellipsis
white-space: nowrap
display: inline-block display: inline-block
padding: 0 5px 0 0 padding: 0 5px 0 0
margin: 0 margin: 0 5px 0 0
height: 18px height: 18px
.account-settings-image
width: 18px
height: 18px
.glyphicon-user
font-size: 16px
.nav.navbar-link-text, .nav.navbar-link-text > li > a .nav.navbar-link-text, .nav.navbar-link-text > li > a
font-weight: normal font-weight: normal
font-size: 25px font-size: 25px

View file

@ -1,8 +1,28 @@
h3(data-i18n="account_settings.job_profile") Job Profile h3(data-i18n="account_settings.job_profile") Job Profile
if me.get('jobProfileApproved') .row
p.lead(data-i18n="account_settings.job_profile_approved") Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks. .col-md-9
else if me.get('jobProfileApproved')
p.lead(data-i18n="account_settings.job_profile_explanation") Hi! Fill this out, and we will get in touch about finding you a software developer job. p.lead(data-i18n="account_settings.job_profile_approved") Your job profile has been approved by CodeCombat. Employers will be able to see it until you either mark it inactive or it has not been changed for four weeks.
else
p.lead(data-i18n="account_settings.job_profile_explanation") Hi! Fill this out, and we will get in touch about finding you a software developer job.
#job-profile-treema .row
.col-md-9
.progress.profile-completion-progress
.progress-bar.progress-bar-success
.progress-next-item
.col-md-3
a.btn.btn-large.btn-primary.profile-preview-button.top-preview(href="/account/profile/#{me.id}", target="job_profile", data-i18n="account_settings.view_profile") View Your Profile
.col-md-3
.thumbnail.sample-profile-thumbnail
a(href="http://codecombat.com/images/pages/account/profile/sample_profile.png", target="_blank")
img(src="/images/pages/account/profile/sample_profile_thumb.png" alt="Sample Profile Thumbnail")
.caption
span(data-i18n="account_settings.sample_profile") See a sample profile
#job-profile-treema
a.btn.btn-large.btn-primary.profile-preview-button.bottom-preview(href="/account/profile/#{me.id}", target="job_profile", data-i18n="account_settings.view_profile") View Your Profile

View file

@ -5,7 +5,7 @@ block content
if myProfile || (me.isAdmin() && user.get('jobProfile')) if myProfile || (me.isAdmin() && user.get('jobProfile'))
.profile-control-bar .profile-control-bar
if myProfile if myProfile
a(href="/account/settings") a(href=user.get('jobProfile') ? "/account/settings#job-profile" : "/account/settings")
button.btn.edit-settings-button button.btn.edit-settings-button
i.icon-cog i.icon-cog
span(data-i18n="account_profile.edit_settings") Edit Settings span(data-i18n="account_profile.edit_settings") Edit Settings
@ -28,12 +28,13 @@ block content
if profileLinks.length if profileLinks.length
ul.links ul.links
each link in profileLinks each link in profileLinks
li(title=profile.name + " on " + link.name, class=link.icon ? "has-icon" : "") if link.link && link.name
a(href=link.link) li(title=profile.name + " on " + link.name, class=link.icon ? "has-icon" : "")
if link.icon a(href=link.link)
img(src=link.icon.url, alt=link.icon.name) if link.icon
else img(src=link.icon.url, alt=link.icon.name)
button.btn.btn-large.btn-inverse.flat-button= link.name else
button.btn.btn-large.btn-inverse.flat-button= link.name
div= profile.city + ', ' + profile.country div= profile.city + ', ' + profile.country
div= profile.visa div= profile.visa
@ -50,40 +51,46 @@ block content
.middle-column.full-height-column .middle-column.full-height-column
.sub-column .sub-column
h3= profile.name h3= profile.name || "Anonymous Developer"
p= profile.shortDescription if profile.shortDescription
p= profile.shortDescription
each skill in profile.skills each skill in profile.skills
code= skill code= skill
span span
div.long-description!= marked(profile.longDescription) if profile.longDescription
div.long-description!= marked(profile.longDescription)
if profile.work.length if profile.work.length
h3.experience-header h3.experience-header
img.header-icon(src="/images/pages/account/profile/work.png", alt="") img.header-icon(src="/images/pages/account/profile/work.png", alt="")
span(data-i18n="account_profile.work_experience") Work Experience span(data-i18n="account_profile.work_experience") Work Experience
each job in profile.work each job in profile.work
div.experience-entry if job.role && job.employer
div.duration.pull-right= job.duration div.experience-entry
| #{job.role} at #{job.employer} div.duration.pull-right= job.duration
.clearfix | #{job.role} at #{job.employer}
if job.description .clearfix
div!= marked(job.description) if job.description
div!= marked(job.description)
if profile.education.length if profile.education.length
h3.experience-header h3.experience-header
img.header-icon(src="/images/pages/account/profile/education.png", alt="") img.header-icon(src="/images/pages/account/profile/education.png", alt="")
span(data-i18n="account_profile.education") Education span(data-i18n="account_profile.education") Education
each school in profile.education each school in profile.education
div.experience-entry if school.degree && school.school
div.duration.pull-right= school.duration div.experience-entry
| #{school.degree} at #{school.school} div.duration.pull-right= school.duration
.clearfix | #{school.degree} at #{school.school}
.clearfix
if school.description
div!= marked(school.description)
if user.get('jobProfileNotes') || me.isAdmin() if user.get('jobProfileNotes') || me.isAdmin()
h3.experience-header(data-i18n="account_profile.our_notes") Our Notes h3.experience-header(data-i18n="account_profile.our_notes") Our Notes
- var notes = user.get('jobProfileNotes') || ''; - var notes = user.get('jobProfileNotes') || '';
if me.isAdmin() if !me.isAdmin()
textarea#job-profile-notes!= notes textarea#job-profile-notes!= notes
else else
div!= marked(notes) div!= marked(notes)
@ -94,12 +101,13 @@ block content
h3(data-i18n="account_profile.projects") Projects h3(data-i18n="account_profile.projects") Projects
ul.projects ul.projects
each project in profile.projects each project in profile.projects
li if project.name
a(href=project.link) li
if project.picture a(href=project.link)
.project-image(style="background-image: url(/file/" + project.picture + ")") if project.picture
p= project.name .project-image(style="background-image: url(/file/" + project.picture + ")")
div!= marked(project.description) p= project.name
div!= marked(project.description)
else else
.public-profile-container .public-profile-container

View file

@ -39,7 +39,7 @@ block content
if !isProduction if !isProduction
.form-group.checkbox .form-group.checkbox
label(for="email", data-i18n="account_settings.admin") Admin label(for="email", data-i18n="account_settings.admin") Admin
input#admin(name="admin", type="checkbox", checked=me.get('permissions').indexOf('admin')>-1)) input#admin(name="admin", type="checkbox", checked=me.get('permissions').indexOf('admin') != -1)
#picture-pane.tab-pane #picture-pane.tab-pane

View file

@ -16,12 +16,8 @@ body
ul.nav.navbar-nav ul.nav.navbar-nav
li.play li.play
a.header-font(href='/play', data-i18n="nav.play") Levels a.header-font(href='/play', data-i18n="nav.play") Levels
li.editor li
a.header-font(href='/editor', data-i18n="nav.editor") Editor a.header-font(href='/community', data-i18n="nav.community") Community
li.blog
a.header-font(href='http://blog.codecombat.com/', data-i18n="nav.blog") Blog
li.forum
a.header-font(href='http://discourse.codecombat.com/', data-i18n="nav.forum") Forum
.nav.navbar.navbar-fixed-top#top-nav .nav.navbar.navbar-fixed-top#top-nav
.content.clearfix .content.clearfix
@ -34,9 +30,11 @@ body
if me.get('anonymous') === false if me.get('anonymous') === false
button.btn.btn-primary.navbuttontext.header-font#logout-button(data-i18n="login.log_out") Log Out button.btn.btn-primary.navbuttontext.header-font#logout-button(data-i18n="login.log_out") Log Out
a.btn.btn-primary.navbuttontext.header-font(href=me.get('jobProfile') ? "/account/profile/#{me.id}" : "/account/settings") a.btn.btn-primary.navbuttontext.header-font(href=me.get('jobProfile') ? "/account/profile/#{me.id}" : "/account/settings")
div.navbuttontext-user-name div.navbuttontext-account(data-i18n="nav.account") Account
| #{me.displayName()} if me.get('photoURL')
i.icon-cog.icon-white.big img.account-settings-image(src=me.getPhotoURL(18), alt="")
else
span.glyphicon.glyphicon-user
else else
button.btn.btn-primary.navbuttontext.header-font(data-toggle="coco-modal", data-target="modal/signup", data-i18n="login.sign_up") Create Account button.btn.btn-primary.navbuttontext.header-font(data-toggle="coco-modal", data-target="modal/signup", data-i18n="login.sign_up") Create Account
@ -45,13 +43,8 @@ body
ul(class='navbar-link-text').nav.navbar-nav.pull-right ul(class='navbar-link-text').nav.navbar-nav.pull-right
li.play li.play
a.header-font(href='/play', data-i18n="nav.play") Levels a.header-font(href='/play', data-i18n="nav.play") Levels
li.editor li
a.header-font(href='/editor', data-i18n="nav.editor") Editor a.header-font(href='/community', data-i18n="nav.community") Community
li.blog
a.header-font(href='http://blog.codecombat.com/', data-i18n="nav.blog") Blog
li.forum
a.header-font(href='http://discourse.codecombat.com/', data-i18n="nav.forum") Forum
block outer_content block outer_content
#outer-content-wrapper #outer-content-wrapper
@ -73,6 +66,9 @@ body
a(href='/legal', title='Legal', tabindex=-1, data-i18n="nav.legal") Legal a(href='/legal', title='Legal', tabindex=-1, data-i18n="nav.legal") Legal
a(href='/about', title='About', tabindex=-1, data-i18n="nav.about") About a(href='/about', title='About', tabindex=-1, data-i18n="nav.about") About
a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="nav.contact") Contact a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="nav.contact") Contact
a(href='/editor', data-i18n="nav.editor") Editor
a(href='http://blog.codecombat.com/', data-i18n="nav.blog") Blog
a(href='http://discourse.codecombat.com/', data-i18n="nav.forum") Forum
if me.isAdmin() if me.isAdmin()
a(href='/admin', data-i18n="nav.admin") Admin a(href='/admin', data-i18n="nav.admin") Admin

View file

@ -0,0 +1,35 @@
extends /templates/base
block content
h1(data-i18n="community.main_title") CodeCombat Community
h2
a(href="/editor", data-i18n="community.level_editor") Level Editor
h2
a(href="http://blog.codecombat.com", data-i18n="nav.blog") Blog
h2
a(href="http://discourse.codecombat.com", data-i18n="nav.forum") Forum
h2
a(href="/contribute", data-i18n="nav.contribute") Contribute
h2
a(href="https://www.facebook.com/codecombat", data-i18n="community.facebook") Facebook
h2
a(href="https://twitter.com/CodeCombat", data-i18n="community.twitter") Twitter
h2
a(href="https://plus.google.com/115285980638641924488/posts", data-i18n="community.gplus") Google+
h2
a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="community.contact_us") Contact us!
p
span(data-i18n="editor.hipchat_prefix") You can also find us in our
|
strong
a(href="http://www.hipchat.com/g3plnOKqa", data-i18n="editor.hipchat_url") HipChat room.

View file

@ -34,11 +34,9 @@ block content
hr hr
p p
span(data-i18n="editor.security_notice") span(data-i18n="editor.got_questions") Questions about using the CodeCombat editors?
| Many major features in these editors are not currently enabled by default. |
| As we improve the security of these systems, they will be made generally available. a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="editor.contact_us") Contact us!
| If you'd like to use these features sooner,
a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="editor.contact_us") email us!
| |
span(data-i18n="editor.hipchat_prefix") You can also find us in our span(data-i18n="editor.hipchat_prefix") You can also find us in our
| |

View file

@ -21,7 +21,6 @@ module.exports = class JobProfileView extends CocoView
visibleSettings = @editableSettings.concat @readOnlySettings visibleSettings = @editableSettings.concat @readOnlySettings
data = _.pick (me.get('jobProfile') ? {}), (value, key) => key in visibleSettings data = _.pick (me.get('jobProfile') ? {}), (value, key) => key in visibleSettings
data.name ?= (me.get('firstName') + ' ' + me.get('lastName')).trim() if me.get('firstName') data.name ?= (me.get('firstName') + ' ' + me.get('lastName')).trim() if me.get('firstName')
console.log 'schema?', me.schema()
schema = _.cloneDeep me.schema().properties.jobProfile schema = _.cloneDeep me.schema().properties.jobProfile
schema.properties = _.pick schema.properties, (value, key) => key in visibleSettings schema.properties = _.pick schema.properties, (value, key) => key in visibleSettings
schema.required = _.intersection schema.required, visibleSettings schema.required = _.intersection schema.required, visibleSettings
@ -42,10 +41,56 @@ module.exports = class JobProfileView extends CocoView
@jobProfileTreema = @$el.find('#job-profile-treema').treema treemaOptions @jobProfileTreema = @$el.find('#job-profile-treema').treema treemaOptions
@jobProfileTreema.build() @jobProfileTreema.build()
@jobProfileTreema.open() @jobProfileTreema.open()
@updateProgress()
onJobProfileChanged: (e) => onJobProfileChanged: (e) =>
@hasEditedProfile = true @hasEditedProfile = true
@trigger 'change' @trigger 'change'
@updateProgress()
updateProgress: ->
completed = 0
totalWeight = 0
next = null
for metric in metrics = @getProgressMetrics()
done = metric.fn()
completed += metric.weight ? 1 if done
totalWeight += metric.weight
next = metric.name unless next or done
progress = Math.round 100 * completed / totalWeight
bar = @$el.find('.profile-completion-progress .progress-bar')
bar.css 'width', "#{progress}%"
text = ""
if progress > 19
text = "#{progress}% complete"
else if progress > 5
text = "#{progress}%"
bar.text text
bar.parent().toggle Boolean progress
@$el.find('.progress-next-item').text(next).toggle Boolean next
getProgressMetrics: ->
return @progressMetrics if @progressMetrics
schema = me.schema().properties.jobProfile
jobProfile = @jobProfileTreema.data
exists = (field) -> -> jobProfile[field]
modified = (field) -> -> jobProfile[field] and jobProfile[field] isnt schema.properties[field].default
listStarted = (field, subfields) -> -> jobProfile[field]?.length and _.every subfields, (subfield) -> jobProfile[field][0][subfield]
@progressMetrics = [
{name: "Mark yourself open to offers to show up in searches.", weight: 1, fn: modified 'active'}
{name: "Specify your desired job title.", weight: 0, fn: exists 'jobTitle'}
{name: "Provide your name.", weight: 1, fn: modified 'name'}
{name: "Choose your city.", weight: 1, fn: modified 'city'}
{name: "Pick your country.", weight: 0, fn: exists 'country'}
{name: "List at least five skills.", weight: 2, fn: -> jobProfile.skills.length >= 5}
{name: "Write a short description to summarize yourself at a glance.", weight: 2, fn: modified 'shortDescription'}
{name: "Fill in your main description to sell yourself and describe the work you're looking for.", weight: 3, fn: modified 'longDescription'}
{name: "List your work experience.", weight: 3, fn: listStarted 'work', ['role', 'employer']}
{name: "Recount your educational ordeals.", weight: 3, fn: listStarted 'education', ['degree', 'school']}
{name: "Show off up to three projects you've worked on.", weight: 3, fn: listStarted 'projects', ['name']}
{name: "Add any personal or social links.", weight: 2, fn: listStarted 'links', ['link', 'name']}
{name: "Add an optional professional photo.", weight: 2, fn: modified 'photoURL'}
]
getData: -> getData: ->
return {} unless me.get('jobProfile') or @hasEditedProfile return {} unless me.get('jobProfile') or @hasEditedProfile

View file

@ -3,6 +3,7 @@ template = require 'templates/account/settings'
{me} = require('lib/auth') {me} = require('lib/auth')
forms = require('lib/forms') forms = require('lib/forms')
User = require('models/User') User = require('models/User')
LoginModalView = require 'views/modal/login_modal'
WizardSettingsView = require './wizard_settings_view' WizardSettingsView = require './wizard_settings_view'
JobProfileView = require './job_profile_view' JobProfileView = require './job_profile_view'
@ -45,6 +46,11 @@ module.exports = class SettingsView extends View
@insertSubView @jobProfileView @insertSubView @jobProfileView
_.defer => @buildPictureTreema() # Not sure why, but the Treemas don't fully build without this if you reload the page. _.defer => @buildPictureTreema() # Not sure why, but the Treemas don't fully build without this if you reload the page.
afterInsert: ->
super()
if me.get('anonymous')
@openModalView new LoginModalView()
chooseTab: (category) -> chooseTab: (category) ->
id = "##{category}-pane" id = "##{category}-pane"
pane = $(id, @$el) pane = $(id, @$el)

View file

@ -0,0 +1,6 @@
View = require 'views/kinds/RootView'
template = require 'templates/community'
module.exports = class CommunityView extends View
id: "communit-view"
template: template

View file

@ -26,7 +26,7 @@ module.exports = class ModelModal extends View
treemaOptions = treemaOptions =
schema: schema schema: schema
data: data data: data
readOnly: true readOnly: false
modelTreema = @$el.find(".model-treema[data-model-id='#{model.id}']").treema treemaOptions modelTreema = @$el.find(".model-treema[data-model-id='#{model.id}']").treema treemaOptions
modelTreema?.build() modelTreema?.build()
modelTreema?.open() modelTreema?.open()

View file

@ -46,7 +46,7 @@ module.exports = class LadderTabView extends CocoView
return if @checked or (not window.FB) or (not window.gapi) return if @checked or (not window.FB) or (not window.gapi)
@checked = true @checked = true
@addSomethingToLoad("facebook_status") @addSomethingToLoad("facebook_status", 0) # This might not load ever, so we can't wait for it
FB.getLoginStatus (response) => FB.getLoginStatus (response) =>
@facebookStatus = response.status @facebookStatus = response.status
@loadFacebookFriends() if @facebookStatus is 'connected' @loadFacebookFriends() if @facebookStatus is 'connected'
@ -105,7 +105,7 @@ module.exports = class LadderTabView extends CocoView
gplusSessionStateLoaded: -> gplusSessionStateLoaded: ->
if application.gplusHandler.loggedIn if application.gplusHandler.loggedIn
@addSomethingToLoad("gplus_friends", 0) # this might not load ever, so we can't wait for it @addSomethingToLoad("gplus_friends", 0) # This might not load ever, so we can't wait for it
application.gplusHandler.loadFriends @gplusFriendsLoaded application.gplusHandler.loadFriends @gplusFriendsLoaded
gplusFriendsLoaded: (friends) => gplusFriendsLoaded: (friends) =>

View file

@ -14,7 +14,7 @@ LevelSessionHandler = require '../levels/sessions/level_session_handler'
serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset'] serverProperties = ['passwordHash', 'emailLower', 'nameLower', 'passwordReset']
privateProperties = [ privateProperties = [
'permissions', 'email', 'firstName', 'lastName', 'gender', 'facebookID', 'permissions', 'email', 'firstName', 'lastName', 'gender', 'facebookID',
'gplusID', 'music', 'volume', 'aceConfig' 'gplusID', 'music', 'volume', 'aceConfig', 'employerAt'
] ]
candidateProperties = [ candidateProperties = [
'jobProfile', 'jobProfileApproved', 'jobProfileNotes' 'jobProfile', 'jobProfileApproved', 'jobProfileNotes'
@ -47,7 +47,7 @@ UserHandler = class UserHandler extends Handler
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 ? [])) and @employerCanViewCandidate req.user, obj)
delete obj[prop] for prop in candidateProperties unless includeCandidate delete obj[prop] for prop in candidateProperties unless includeCandidate
return obj return obj
@ -259,6 +259,7 @@ UserHandler = class UserHandler extends Handler
authorized = req.user.isAdmin() or ('employer' in req.user.get('permissions')) authorized = req.user.isAdmin() or ('employer' in req.user.get('permissions'))
since = (new Date((new Date()) - 2 * 30.4 * 86400 * 1000)).toISOString() since = (new Date((new Date()) - 2 * 30.4 * 86400 * 1000)).toISOString()
query = {'jobProfile.active': true, 'jobProfile.updated': {$gt: since}} query = {'jobProfile.active': true, 'jobProfile.updated': {$gt: since}}
#query = {'jobProfile.updated': {$gt: since}}
query.jobProfileApproved = true unless req.user.isAdmin() query.jobProfileApproved = true unless req.user.isAdmin()
selection = 'jobProfile' selection = 'jobProfile'
selection += ' email' if authorized selection += ' email' if authorized
@ -266,6 +267,7 @@ UserHandler = class UserHandler extends Handler
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)
candidates = (candidate for candidate in candidates when @employerCanViewCandidate req.user, candidate)
@sendSuccess(res, candidates) @sendSuccess(res, candidates)
formatCandidate: (authorized, document) -> formatCandidate: (authorized, document) ->
@ -278,6 +280,14 @@ UserHandler = class UserHandler extends Handler
obj.jobProfile = _.pick obj.jobProfile, subfields obj.jobProfile = _.pick obj.jobProfile, subfields
obj obj
employerCanViewCandidate: (employer, candidate) ->
return true if employer.isAdmin()
for job in candidate.jobProfile?.work ? []
# TODO: be smarter about different ways to write same company names to ensure privacy.
# We'll have to manually pay attention to how we set employer names for now.
return false if job.employer?.toLowerCase() is employer.get('employerAt')?.toLowerCase()
true
buildGravatarURL: (user, size, fallback) -> buildGravatarURL: (user, size, fallback) ->
emailHash = @buildEmailHash user emailHash = @buildEmailHash user
fallback ?= "http://codecombat.com/file/db/thang.type/52a00d55cf1818f2be00000b/portrait.png" fallback ?= "http://codecombat.com/file/db/thang.type/52a00d55cf1818f2be00000b/portrait.png"