From 8148c1a19cedbd1cae09f11d799576f1e099be6f Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 24 Sep 2015 20:28:04 -0700 Subject: [PATCH] save form to localStorage --- common/app/routes/Jobs/components/NewJob.jsx | 121 +++++++++++++++---- common/app/routes/Jobs/flux/Actions.js | 52 ++++++-- common/app/routes/Jobs/flux/Store.js | 4 +- common/app/routes/Jobs/utils.js | 22 ++++ common/models/job.json | 5 +- package.json | 2 + 6 files changed, 169 insertions(+), 37 deletions(-) create mode 100644 common/app/routes/Jobs/utils.js diff --git a/common/app/routes/Jobs/components/NewJob.jsx b/common/app/routes/Jobs/components/NewJob.jsx index 41d20db840..b160671c4b 100644 --- a/common/app/routes/Jobs/components/NewJob.jsx +++ b/common/app/routes/Jobs/components/NewJob.jsx @@ -1,5 +1,13 @@ import React, { PropTypes } from 'react'; import { contain } from 'thundercats-react'; +import debugFactory from 'debug'; +import { getDefaults } from '../utils'; + +import { + inHTMLData, + uriInSingleQuotedAttr +} from 'xss-filters'; + import { Button, Col, @@ -7,6 +15,7 @@ import { Row, Well } from 'react-bootstrap'; + import { isAscii, isEmail, @@ -14,31 +23,34 @@ import { isURL } from 'validator'; -const defaults = { - 'string': { - value: '', - valid: false, - pristine: true - }, - bool: { - value: false - } -}; +const debug = debugFactory('freecc:jobs:newForm'); + +const checkValidity = [ + 'position', + 'locale', + 'description', + 'email', + 'phone', + 'url', + 'logo', + 'name', + 'highlight' +]; export default contain({ actions: 'jobActions', store: 'jobsStore', map({ form = {} }) { const { - position = defaults['string'], - locale = defaults['string'], - description = defaults['string'], - email = defaults['string'], - phone = defaults['string'], - url = defaults['string'], - logo = defaults['string'], - name = defaults['string'], - highlight = defaults['bool'] + position = getDefaults('string'), + locale = getDefaults('string'), + description = getDefaults('string'), + email = getDefaults('string'), + phone = getDefaults('string'), + url = getDefaults('string'), + logo = getDefaults('string'), + name = getDefaults('string'), + highlight = getDefaults('bool') } = form; return { position, @@ -51,6 +63,9 @@ export default contain({ name, highlight }; + }, + subscribeOnWillMount() { + return typeof window !== 'undefined'; } }, React.createClass({ @@ -69,6 +84,63 @@ export default contain({ highlight: PropTypes.object }, + handleSubmit(e) { + e.preventDefault(); + let valid = true; + checkValidity.forEach((prop) => { + // if value exist, check if it is valid + if (this.props[prop].value) { + valid = valid && !!this.props[prop].valid; + } + }); + + if (!valid) { + debug('form not valid'); + return; + } + + const { + position, + locale, + description, + email, + phone, + url, + logo, + name, + highlight, + jobActions + } = this.props; + + // sanitize user output + const jobValues = { + position: inHTMLData(position.value), + location: inHTMLData(locale.value), + description: inHTMLData(description.value), + email: inHTMLData(email.value), + phone: inHTMLData(phone.value), + url: uriInSingleQuotedAttr(url.value), + logo: uriInSingleQuotedAttr(logo.value), + name: inHTMLData(name.value), + highlight: !!highlight.value + }; + + const job = Object.keys(jobValues).reduce((accu, prop) => { + if (jobValues[prop]) { + accu[prop] = jobValues[prop]; + } + return accu; + }, {}); + + debug('job sanitized', job); + jobActions.saveForm(job); + }, + + componentDidMount() { + const { jobActions } = this.props; + jobActions.getSavedForm(); + }, + handleChange(name, validator, { target: { value } }) { const { jobActions: { handleForm } } = this.props; handleForm({ name, value, validator }); @@ -95,7 +167,9 @@ export default contain({

Create Your Job Post

-
+

Job Information

@@ -151,7 +225,7 @@ export default contain({

Company Information

{ @@ -248,8 +322,9 @@ export default contain({ lgOffset={ 3 }> diff --git a/common/app/routes/Jobs/flux/Actions.js b/common/app/routes/Jobs/flux/Actions.js index 5b284e4b8c..771c520013 100644 --- a/common/app/routes/Jobs/flux/Actions.js +++ b/common/app/routes/Jobs/flux/Actions.js @@ -1,4 +1,6 @@ import { Actions } from 'thundercats'; +import store from 'store'; +import { getDefaults } from '../utils'; import debugFactory from 'debug'; const debug = debugFactory('freecc:jobs:actions'); @@ -52,12 +54,14 @@ export default Actions({ newState.form = assign( {}, form, - { [name]: { - value, - valid: false, - pristine: false, - bsStyle: value ? 'error' : null - }} + { + [name]: { + value, + valid: false, + pristine: false, + bsStyle: value ? 'error' : null + } + } ); return newState; } @@ -70,16 +74,31 @@ export default Actions({ newState.form = assign( {}, form, - { [name]: { - value, - valid: true, - pristine: false, - bsStyle: value ? 'success' : null - }} + { + [name]: { + value, + valid: true, + pristine: false, + bsStyle: value ? 'success' : null + } + } ); return newState; } }; + }, + saveForm: null, + getSavedForm: null, + setForm(job) { + const form = Object.keys(job).reduce((accu, prop) => { + console.log('form', accu); + return Object.assign( + accu, + { [prop]: getDefaults(typeof prop, job[prop]) } + ); + }, {}); + + return { form }; } }) .refs({ displayName: 'JobActions' }) @@ -111,5 +130,14 @@ export default Actions({ jobActions.setJobs({}); }); }); + + jobActions.saveForm.subscribe((form) => { + store.set('newJob', form); + }); + + jobActions.getSavedForm.subscribe(() => { + const job = store.get('newJob'); + jobActions.setForm(job); + }); return jobActions; }); diff --git a/common/app/routes/Jobs/flux/Store.js b/common/app/routes/Jobs/flux/Store.js index a73235f1aa..b2f5132013 100644 --- a/common/app/routes/Jobs/flux/Store.js +++ b/common/app/routes/Jobs/flux/Store.js @@ -15,13 +15,15 @@ export default Store({ showModal: false }) setError, openModal, closeModal, - handleForm + handleForm, + setForm } = cat.getActions('JobActions'); const register = createRegistrar(jobsStore); register(setter(setJobs)); register(setter(setError)); register(setter(openModal)); register(setter(closeModal)); + register(setter(setForm)); register(transformer(findJob)); register(handleForm); diff --git a/common/app/routes/Jobs/utils.js b/common/app/routes/Jobs/utils.js new file mode 100644 index 0000000000..3a60c373a8 --- /dev/null +++ b/common/app/routes/Jobs/utils.js @@ -0,0 +1,22 @@ +const defaults = { + 'string': { + value: '', + valid: false, + pristine: true, + type: 'string' + }, + bool: { + value: false, + type: 'boolean' + } +}; + +export function getDefaults(type, value) { + if (!type) { + return defaults['string']; + } + if (value) { + return Object.assign({}, defaults[type], { value }); + } + return defaults[type]; +} diff --git a/common/models/job.json b/common/models/job.json index 197f0619bf..f77fc6defa 100644 --- a/common/models/job.json +++ b/common/models/job.json @@ -30,6 +30,9 @@ "state": { "type": "string" }, + "url": { + "type": "string" + }, "country": { "type": "string" }, @@ -39,7 +42,7 @@ "description": { "type": "string" }, - "isApproverd": { + "isApproved": { "type": "boolean" }, "isHighlighted": { diff --git a/package.json b/package.json index df579eb2bc..f16e4bb9db 100644 --- a/package.json +++ b/package.json @@ -97,12 +97,14 @@ "sanitize-html": "~1.6.1", "sort-keys": "^1.1.1", "source-map-support": "^0.3.2", + "store": "https://github.com/berkeleytrue/store.js.git#feature/noop-server", "thundercats": "^2.1.0", "thundercats-react": "^0.1.0", "twit": "~1.1.20", "uglify-js": "~2.4.15", "validator": "^3.22.1", "webpack": "^1.9.12", + "xss-filters": "^1.2.6", "yui": "~3.18.1" }, "devDependencies": {