import { helpers } from 'rx'; import React, { PropTypes } from 'react'; import { History } from 'react-router'; import { contain } from 'thundercats-react'; import debugFactory from 'debug'; import dedent from 'dedent'; import normalizeUrl from 'normalize-url'; import { getDefaults } from '../utils'; import { inHTMLData, uriInSingleQuotedAttr } from 'xss-filters'; import { Button, Col, Input, Row } from 'react-bootstrap'; import { isAscii, isEmail, isURL } from 'validator'; const debug = debugFactory('freecc:jobs:newForm'); const checkValidity = [ 'position', 'locale', 'description', 'email', 'url', 'logo', 'company', 'isHighlighted', 'howToApply' ]; const hightlightCopy = ` Highlight my post to make it stand out. (+$250) `; const isRemoteCopy = ` This job can be performed remotely. `; const howToApplyCopy = dedent` Examples: click here to apply yourcompany.com/jobs/33 Or email jobs@yourcompany.com `; const checkboxClass = dedent` text-left jobs-checkbox-spacer col-sm-offset-2 col-sm-6 col-md-offset-3 `; function formatValue(value, validator, type = 'string') { const formatted = getDefaults(type); if (validator && type === 'string' && typeof value === 'string') { formatted.valid = validator(value); } if (value) { formatted.value = value; formatted.bsStyle = formatted.valid ? 'success' : 'error'; } return formatted; } const normalizeOptions = { stripWWW: false }; function formatUrl(url, shouldKeepTrailingSlash = true) { if ( typeof url === 'string' && url.length > 4 && url.indexOf('.') !== -1 ) { // prevent trailing / from being stripped during typing let lastChar = ''; if (shouldKeepTrailingSlash && url.substring(url.length - 1) === '/') { lastChar = '/'; } return normalizeUrl(url, normalizeOptions) + lastChar; } return url; } function isValidURL(data) { return isURL(data, { 'require_protocol': true }); } function makeRequired(validator) { return (val) => !!val && validator(val); } export default contain({ store: 'appStore', actions: 'jobActions', map({ jobsApp: { form = {} } }) { const { position, locale, description, email, url, logo, company, isFrontEndCert = true, isBackEndCert, isHighlighted, isRemoteOk, howToApply } = form; return { position: formatValue(position, makeRequired(isAscii)), locale: formatValue(locale, makeRequired(isAscii)), description: formatValue(description, makeRequired(helpers.identity)), email: formatValue(email, makeRequired(isEmail)), url: formatValue(formatUrl(url), isValidURL), logo: formatValue(formatUrl(logo), isValidURL), company: formatValue(company, makeRequired(isAscii)), isHighlighted: formatValue(isHighlighted, null, 'bool'), isRemoteOk: formatValue(isRemoteOk, null, 'bool'), howToApply: formatValue(howToApply, makeRequired(isAscii)), isFrontEndCert, isBackEndCert }; }, subscribeOnWillMount() { return typeof window !== 'undefined'; } }, React.createClass({ displayName: 'NewJob', propTypes: { jobActions: PropTypes.object, position: PropTypes.object, locale: PropTypes.object, description: PropTypes.object, email: PropTypes.object, url: PropTypes.object, logo: PropTypes.object, company: PropTypes.object, isHighlighted: PropTypes.object, isRemoteOk: PropTypes.object, isFrontEndCert: PropTypes.bool, isBackEndCert: PropTypes.bool, howToApply: PropTypes.object }, mixins: [History], handleSubmit(e) { e.preventDefault(); const pros = this.props; let valid = true; checkValidity.forEach((prop) => { // if value exist, check if it is valid if (pros[prop].value && pros[prop].type !== 'boolean') { valid = valid && !!pros[prop].valid; } }); if ( !valid || !pros.isFrontEndCert && !pros.isBackEndCert ) { debug('form not valid'); return; } const { jobActions, // form values position, locale, description, email, url, logo, company, isFrontEndCert, isBackEndCert, isHighlighted, isRemoteOk, howToApply } = this.props; // sanitize user output const jobValues = { position: inHTMLData(position.value), locale: inHTMLData(locale.value), description: inHTMLData(description.value), email: inHTMLData(email.value), url: formatUrl(uriInSingleQuotedAttr(url.value), false), logo: formatUrl(uriInSingleQuotedAttr(logo.value), false), company: inHTMLData(company.value), isHighlighted: !!isHighlighted.value, isRemoteOk: !!isRemoteOk.value, howToApply: inHTMLData(howToApply.value), isFrontEndCert, isBackEndCert }; const job = Object.keys(jobValues).reduce((accu, prop) => { if (jobValues[prop]) { accu[prop] = jobValues[prop]; } return accu; }, {}); job.postedOn = new Date(); debug('job sanitized', job); jobActions.saveForm(job); this.history.pushState(null, '/jobs/new/preview'); }, componentDidMount() { const { jobActions } = this.props; jobActions.getSavedForm(); }, handleChange(name, { target: { value } }) { const { jobActions: { handleForm } } = this.props; handleForm({ [name]: value }); }, handleCertClick(name) { const { jobActions: { handleForm } } = this.props; const otherButton = name === 'isFrontEndCert' ? 'isBackEndCert' : 'isFrontEndCert'; handleForm({ [name]: true, [otherButton]: false }); }, render() { const { position, locale, description, email, url, logo, company, isHighlighted, isRemoteOk, howToApply, isFrontEndCert, isBackEndCert, jobActions: { handleForm } } = this.props; const { handleChange } = this; const labelClass = 'col-sm-offset-1 col-sm-2'; const inputClass = 'col-sm-6'; return (