mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-05-01 08:23:57 -04:00
Reworked top nav. Many improvements to job profile form.
This commit is contained in:
parent
46487f55a4
commit
844c1391ca
18 changed files with 244 additions and 71 deletions
app
assets/images/pages/account/profile
locale
schemas/models
styles
templates
views
server/users
BIN
app/assets/images/pages/account/profile/sample_profile.png
Normal file
BIN
app/assets/images/pages/account/profile/sample_profile.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 376 KiB |
BIN
app/assets/images/pages/account/profile/sample_profile_thumb.png
Normal file
BIN
app/assets/images/pages/account/profile/sample_profile_thumb.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 88 KiB |
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
35
app/templates/community.jade
Normal file
35
app/templates/community.jade
Normal 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.
|
|
@ -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
|
||||||
|
|
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
6
app/views/community_view.coffee
Normal file
6
app/views/community_view.coffee
Normal 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
|
|
@ -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()
|
||||||
|
|
|
@ -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) =>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue