diff --git a/client/sagas/index.js b/client/sagas/index.js index fc72193446..cb4e09a185 100644 --- a/client/sagas/index.js +++ b/client/sagas/index.js @@ -1,4 +1,5 @@ import errSaga from './err-saga'; import titleSaga from './title-saga'; +import localStorageSaga from './local-storage-saga'; -export default [errSaga, titleSaga]; +export default [errSaga, titleSaga, localStorageSaga]; diff --git a/client/sagas/local-storage-saga.js b/client/sagas/local-storage-saga.js index 7913dcf42f..ecf69bcf31 100644 --- a/client/sagas/local-storage-saga.js +++ b/client/sagas/local-storage-saga.js @@ -2,11 +2,12 @@ import { saveForm, clearForm, loadSavedForm -} from '../common/app/routes/Jobs/redux/types'; +} from '../../common/app/routes/Jobs/redux/types'; import { + saveCompleted, loadSavedFormCompleted -} from '../common/app/routes/Jobs/redux/actions'; +} from '../../common/app/routes/Jobs/redux/actions'; const formKey = 'newJob'; let enabled = false; @@ -17,7 +18,7 @@ let store = typeof window !== 'undefined' ? try { const testKey = '__testKey__'; store.setItem(testKey, testKey); - enabled = store.getItem(testKey) !== testKey; + enabled = store.getItem(testKey) === testKey; store.removeItem(testKey); } catch (e) { enabled = !e; @@ -35,11 +36,12 @@ export default () => ({ dispatch }) => next => { const form = action.payload; try { store.setItem(formKey, JSON.stringify(form)); - return null; - } catch (e) { + next(action); + return dispatch(saveCompleted(form)); + } catch (error) { return dispatch({ type: 'app.handleError', - error: new Error('could not parse form data') + error }); } } @@ -54,10 +56,10 @@ export default () => ({ dispatch }) => next => { try { const form = JSON.parse(formString); return dispatch(loadSavedFormCompleted(form)); - } catch (err) { + } catch (error) { return dispatch({ type: 'app.handleError', - error: new Error('could not parse form data') + error }); } } diff --git a/common/app/routes/Jobs/components/JobTotal.jsx b/common/app/routes/Jobs/components/JobTotal.jsx index 266d3d9812..9524d44288 100644 --- a/common/app/routes/Jobs/components/JobTotal.jsx +++ b/common/app/routes/Jobs/components/JobTotal.jsx @@ -1,9 +1,17 @@ +import { CompositeDisposable } from 'rx'; import React, { PropTypes } from 'react'; import { Button, Input, Col, Row, Well } from 'react-bootstrap'; import { connect } from 'react-redux'; +import { push } from 'react-router-redux'; import PureComponent from 'react-pure-render/component'; import { createSelector } from 'reselect'; +import { + applyPromo, + clearPromo, + updatePromo +} from '../redux/actions'; + // real paypal buttons // will take your money const paypalIds = { @@ -12,10 +20,14 @@ const paypalIds = { }; const bindableActions = { + applyPromo, + clearPromo, + push, + updatePromo }; const mapStateToProps = createSelector( - state => state.jobsApp.currentJob, + state => state.jobsApp.newJob, state => state.jobsApp, ( { id, isHighlighted } = {}, @@ -46,6 +58,11 @@ const mapStateToProps = createSelector( ); export class JobTotal extends PureComponent { + constructor(...args) { + super(...args); + this._subscriptions = new CompositeDisposable(); + } + static displayName = 'JobTotal'; static propTypes = { @@ -60,13 +77,15 @@ export class JobTotal extends PureComponent { }; componentDidMount() { - const { jobActions } = this.props; - jobActions.clearPromo(); + if (!this.props.id) { + this.props.push('/jobs'); + } + + this.props.clearPromo(); } - goToJobBoard() { - const { appActions } = this.props; - setTimeout(() => appActions.goTo('/jobs'), 0); + componentWillUnmount() { + this._subscriptions.dispose(); } renderDiscount(discountAmount) { @@ -114,7 +133,8 @@ export class JobTotal extends PureComponent { promoCode, promoName, isHighlighted, - jobActions + applyPromo, + updatePromo } = this.props; if (promoApplied) { @@ -147,7 +167,7 @@ export class JobTotal extends PureComponent { md={ 3 } mdOffset={ 3 }> @@ -156,11 +176,12 @@ export class JobTotal extends PureComponent { @@ -176,7 +197,8 @@ export class JobTotal extends PureComponent { isHighlighted, buttonId, price, - discountAmount + discountAmount, + push } = this.props; return ( @@ -239,7 +261,7 @@ export class JobTotal extends PureComponent {
setTimeout(push, 0, '/jobs') } target='_blank'> ({ initialValues: state.jobsApp.initialValues }), { - saveJob + loadSavedForm, + push, + saveForm } )(NewJob); diff --git a/common/app/routes/Jobs/components/Preview.jsx b/common/app/routes/Jobs/components/Preview.jsx index e7eca1b92a..478b3cfea8 100644 --- a/common/app/routes/Jobs/components/Preview.jsx +++ b/common/app/routes/Jobs/components/Preview.jsx @@ -1,3 +1,4 @@ +import { CompositeDisposable } from 'rx'; import React, { PropTypes } from 'react'; import { Button, Row, Col } from 'react-bootstrap'; import { connect } from 'react-redux'; @@ -7,22 +8,30 @@ import { goBack, push } from 'react-router-redux'; import ShowJob from './ShowJob.jsx'; import JobNotFound from './JobNotFound.jsx'; -import { clearSavedForm, saveJobToDb } from '../redux/actions'; +import { clearForm, saveJob } from '../redux/actions'; const mapStateToProps = state => ({ job: state.jobsApp.newJob }); const bindableActions = { goBack, push, - clearSavedForm, - saveJobToDb + clearForm, + saveJob }; export class JobPreview extends PureComponent { + constructor(...args) { + super(...args); + this._subscriptions = new CompositeDisposable(); + } + static displayName = 'Preview'; static propTypes = { - job: PropTypes.object + job: PropTypes.object, + saveJob: PropTypes.func, + clearForm: PropTypes.func, + push: PropTypes.func }; componentDidMount() { @@ -33,8 +42,19 @@ export class JobPreview extends PureComponent { } } + componentWillUnmount() { + this._subscriptions.dispose(); + } + + handleJobSubmit() { + const { clearForm, saveJob, job } = this.props; + clearForm(); + const subscription = saveJob(job).subscribe(); + this._subscriptions.add(subscription); + } + render() { - const { job, goBack, clearSavedForm, saveJobToDb } = this.props; + const { job, goBack } = this.props; if (!job || !job.position || !job.description) { return ; @@ -54,25 +74,19 @@ export class JobPreview extends PureComponent { - - - - - + + + + + + ); } } diff --git a/common/app/routes/Jobs/redux/actions.js b/common/app/routes/Jobs/redux/actions.js index 0212a2a375..e3a6f7a6de 100644 --- a/common/app/routes/Jobs/redux/actions.js +++ b/common/app/routes/Jobs/redux/actions.js @@ -10,11 +10,25 @@ export const fetchJobsCompleted = createAction( export const findJob = createAction(types.findJob); +// saves to database export const saveJob = createAction(types.saveJob); -export const saveJobCompleted = createAction(types.saveJobCompleted); - +// saves to localStorage export const saveForm = createAction(types.saveForm); -export const clearForm = createAction(types.clearSavedForm); + +export const saveCompleted = createAction(types.saveCompleted); + +export const clearForm = createAction(types.clearForm); + +export const loadSavedForm = createAction(types.loadSavedForm); export const loadSavedFormCompleted = createAction( types.loadSavedFormCompleted ); + +export const clearPromo = createAction(types.clearPromo); +export const updatePromo = createAction( + types.updatePromo, + ({ target: { value = '' } = {} } = {}) => value +); + +export const applyPromo = createAction(types.applyPromo); +export const applyPromoCompleted = createAction(types.applyPromoCompleted); diff --git a/common/app/routes/Jobs/redux/apply-promo-saga.js b/common/app/routes/Jobs/redux/apply-promo-saga.js index e5b182baf5..4268383e35 100644 --- a/common/app/routes/Jobs/redux/apply-promo-saga.js +++ b/common/app/routes/Jobs/redux/apply-promo-saga.js @@ -1,12 +1,12 @@ import { Observable } from 'rx'; -import { testPromo } from './types'; -import { applyPromo } from './actions'; +import { applyPromo } from './types'; +import { applyPromoCompleted } from './actions'; import { postJSON$ } from '../../../../utils/ajax-stream'; export default () => ({ dispatch }) => next => { return function applyPromoSaga(action) { - if (action.type !== testPromo) { + if (action.type !== applyPromo) { return next(action); } @@ -28,7 +28,7 @@ export default () => ({ dispatch }) => next => { throw new Error('No promo returned by server'); } - return applyPromo(promo); + return applyPromoCompleted(promo); }) .catch(error => Observable.just({ type: 'app.handleError', diff --git a/common/app/routes/Jobs/redux/index.js b/common/app/routes/Jobs/redux/index.js index e11361c7b2..fe3f432b3e 100644 --- a/common/app/routes/Jobs/redux/index.js +++ b/common/app/routes/Jobs/redux/index.js @@ -4,6 +4,8 @@ export types from './types'; import fetchJobsSaga from './fetch-jobs-saga'; import saveJobSaga from './save-job-saga'; +import applyPromoSaga from './apply-promo-saga'; + export formNormalizer from './jobs-form-normalizer'; -export const sagas = [ fetchJobsSaga, saveJobSaga ]; +export const sagas = [ fetchJobsSaga, saveJobSaga, applyPromoSaga ]; diff --git a/common/app/routes/Jobs/redux/reducer.js b/common/app/routes/Jobs/redux/reducer.js index c5e366d467..e227f9c254 100644 --- a/common/app/routes/Jobs/redux/reducer.js +++ b/common/app/routes/Jobs/redux/reducer.js @@ -9,6 +9,8 @@ function replace(str) { } const initialState = { + // used by NewJob form + initialValues: {}, currentJob: '', newJob: {}, jobs: { @@ -28,22 +30,26 @@ export default handleActions( state.currentJob }; }, - [types.saveJobCompleted]: (state, { payload: newJob }) => { - return { - ...state, - newJob - }; - }, [types.fetchJobsCompleted]: (state, { payload: { jobs, currentJob } }) => ({ ...state, currentJob, jobs }), - [types.updatePromoCode]: (state, { payload }) => ({ + [types.updatePromo]: (state, { payload }) => ({ ...state, promoCode: replace(payload) }), - [types.applyPromo]: (state, { payload: promo }) => { + [types.saveCompleted]: (state, { payload: newJob }) => { + return { + ...state, + newJob + }; + }, + [types.loadSavedFormCompleted]: (state, { payload: initialValues }) => ({ + ...state, + initialValues + }), + [types.applyPromoCompleted]: (state, { payload: promo }) => { const { fullPrice: price, diff --git a/common/app/routes/Jobs/redux/save-job-saga.js b/common/app/routes/Jobs/redux/save-job-saga.js index 34d59d5753..0bb3c2c562 100644 --- a/common/app/routes/Jobs/redux/save-job-saga.js +++ b/common/app/routes/Jobs/redux/save-job-saga.js @@ -1,15 +1,16 @@ -import { Observable } from 'rx'; import { push } from 'react-router-redux'; +import { Observable } from 'rx'; -import { saveJobCompleted } from './actions'; +import { saveCompleted } from './actions'; import { saveJob } from './types'; import { handleError } from '../../../redux/types'; export default ({ services }) => ({ dispatch }) => next => { return function saveJobSaga(action) { + const result = next(action); if (action.type !== saveJob) { - return next(action); + return result; } const { payload: job } = action; @@ -19,8 +20,8 @@ export default ({ services }) => ({ dispatch }) => next => { }) .retry(3) .flatMap(job => Observable.of( - saveJobCompleted(job), - push('/jobs/new/preview') + saveCompleted(job), + push('/jobs/new/check-out') )) .catch(error => Observable.just({ type: handleError, diff --git a/common/app/routes/Jobs/redux/types.js b/common/app/routes/Jobs/redux/types.js index befa6299c0..aa6fe40a06 100644 --- a/common/app/routes/Jobs/redux/types.js +++ b/common/app/routes/Jobs/redux/types.js @@ -4,12 +4,19 @@ const types = [ 'findJob', 'saveJob', - 'saveJobCompleted', - 'saveForm', + + 'saveCompleted', + 'clearForm', + 'loadSavedForm', - 'loadSavedFormCompleted' + 'loadSavedFormCompleted', + + 'clearPromo', + 'updatePromo', + 'applyPromo', + 'applyPromoCompleted' ]; export default types.reduce((types, type) => {