diff --git a/common/app/create-reducer.js b/common/app/create-reducer.js
index ee7478011e..82d51d9546 100644
--- a/common/app/create-reducer.js
+++ b/common/app/create-reducer.js
@@ -1,4 +1,5 @@
import { combineReducers } from 'redux';
+import { reducer as formReducer } from 'redux-form';
import { reducer as app } from './redux';
import { reducer as hikesApp } from './routes/Hikes/redux';
@@ -7,6 +8,7 @@ export default function createReducer(sideReducers = {}) {
return combineReducers({
...sideReducers,
app,
- hikesApp
+ hikesApp,
+ form: formReducer
});
}
diff --git a/common/app/routes/Jobs/components/GoToPayPal.jsx b/common/app/routes/Jobs/components/GoToPayPal.jsx
deleted file mode 100644
index 38f3a15c5b..0000000000
--- a/common/app/routes/Jobs/components/GoToPayPal.jsx
+++ /dev/null
@@ -1,277 +0,0 @@
-import React, { PropTypes } from 'react';
-import { Button, Input, Col, Row, Well } from 'react-bootstrap';
-import { contain } from 'thundercats-react';
-
-// real paypal buttons
-// will take your money
-const paypalIds = {
- regular: 'Q8Z82ZLAX3Q8N',
- highlighted: 'VC8QPSKCYMZLN'
-};
-
-export default contain(
- {
- store: 'appStore',
- actions: [
- 'jobActions',
- 'appActions'
- ],
- map({ jobsApp: {
- currentJob: { id, isHighlighted } = {},
- buttonId = isHighlighted ?
- paypalIds.highlighted :
- paypalIds.regular,
- price = 1000,
- discountAmount = 0,
- promoCode = '',
- promoApplied = false,
- promoName = ''
- }}) {
- return {
- id,
- isHighlighted,
- buttonId,
- price,
- discountAmount,
- promoName,
- promoCode,
- promoApplied
- };
- }
- },
- React.createClass({
- displayName: 'GoToPayPal',
-
- propTypes: {
- appActions: PropTypes.object,
- id: PropTypes.string,
- isHighlighted: PropTypes.bool,
- buttonId: PropTypes.string,
- price: PropTypes.number,
- discountAmount: PropTypes.number,
- promoName: PropTypes.string,
- promoCode: PropTypes.string,
- promoApplied: PropTypes.bool,
- jobActions: PropTypes.object
- },
-
- componentDidMount() {
- const { jobActions } = this.props;
- jobActions.clearPromo();
- },
-
- goToJobBoard() {
- const { appActions } = this.props;
- setTimeout(() => appActions.goTo('/jobs'), 0);
- },
-
- renderDiscount(discountAmount) {
- if (!discountAmount) {
- return null;
- }
- return (
-
-
- Promo Discount
-
-
- -{ discountAmount }
-
-
- );
- },
-
- renderHighlightPrice(isHighlighted) {
- if (!isHighlighted) {
- return null;
- }
- return (
-
-
- Highlighting
-
-
- + 250
-
-
- );
- },
-
- renderPromo() {
- const {
- id,
- promoApplied,
- promoCode,
- promoName,
- isHighlighted,
- jobActions
- } = this.props;
- if (promoApplied) {
- return (
-
-
-
-
- { promoName } applied
-
-
-
- );
- }
- return (
-
-
-
-
- Have a promo code?
-
-
-
-
-
-
-
- {
- jobActions.applyCode({
- id,
- code: promoCode,
- type: isHighlighted ? 'isHighlighted' : null
- });
- }}>
- Apply Promo Code
-
-
-
-
- );
- },
-
- render() {
- const {
- id,
- isHighlighted,
- buttonId,
- price,
- discountAmount
- } = this.props;
-
- return (
-
-
-
-
-
-
-
- One more step
-
-
- You're Awesome! just one more step to go.
- Clicking on the link below will redirect to paypal.
-
-
-
-
-
-
- Job Posting
-
-
- + { price }
-
-
- { this.renderHighlightPrice(isHighlighted) }
- { this.renderDiscount(discountAmount) }
-
-
- Total
-
-
- ${
- price - discountAmount + (isHighlighted ? 250 : 0)
- }
-
-
-
- { this.renderPromo() }
-
-
-
-
-
-
-
-
-
-
-
- );
- }
- })
-);
diff --git a/common/app/routes/Jobs/components/JobNotFound.jsx b/common/app/routes/Jobs/components/JobNotFound.jsx
index 343e068f56..e05a886694 100644
--- a/common/app/routes/Jobs/components/JobNotFound.jsx
+++ b/common/app/routes/Jobs/components/JobNotFound.jsx
@@ -2,8 +2,12 @@ import React from 'react';
import { LinkContainer } from 'react-router-bootstrap';
import { Button, Row, Col } from 'react-bootstrap';
-export default React.createClass({
- displayName: 'NoJobFound',
+export default class extends React.Component {
+ static displayName = 'NoJobFound';
+
+ shouldComponentUpdate() {
+ return false;
+ }
render() {
return (
@@ -28,4 +32,4 @@ export default React.createClass({
);
}
-});
+}
diff --git a/common/app/routes/Jobs/components/JobTotal.jsx b/common/app/routes/Jobs/components/JobTotal.jsx
new file mode 100644
index 0000000000..266d3d9812
--- /dev/null
+++ b/common/app/routes/Jobs/components/JobTotal.jsx
@@ -0,0 +1,284 @@
+import React, { PropTypes } from 'react';
+import { Button, Input, Col, Row, Well } from 'react-bootstrap';
+import { connect } from 'react-redux';
+import PureComponent from 'react-pure-render/component';
+import { createSelector } from 'reselect';
+
+// real paypal buttons
+// will take your money
+const paypalIds = {
+ regular: 'Q8Z82ZLAX3Q8N',
+ highlighted: 'VC8QPSKCYMZLN'
+};
+
+const bindableActions = {
+};
+
+const mapStateToProps = createSelector(
+ state => state.jobsApp.currentJob,
+ state => state.jobsApp,
+ (
+ { id, isHighlighted } = {},
+ {
+ buttonId,
+ price = 1000,
+ discountAmount = 0,
+ promoCode = '',
+ promoApplied = false,
+ promoName = ''
+ }
+ ) => {
+ if (!buttonId) {
+ buttonId = isHighlighted ?
+ paypalIds.highlighted :
+ paypalIds.regular;
+ }
+ return {
+ id,
+ isHighlighted,
+ price,
+ discountAmount,
+ promoName,
+ promoCode,
+ promoApplied
+ };
+ }
+);
+
+export class JobTotal extends PureComponent {
+ static displayName = 'JobTotal';
+
+ static propTypes = {
+ id: PropTypes.string,
+ isHighlighted: PropTypes.bool,
+ buttonId: PropTypes.string,
+ price: PropTypes.number,
+ discountAmount: PropTypes.number,
+ promoName: PropTypes.string,
+ promoCode: PropTypes.string,
+ promoApplied: PropTypes.bool
+ };
+
+ componentDidMount() {
+ const { jobActions } = this.props;
+ jobActions.clearPromo();
+ }
+
+ goToJobBoard() {
+ const { appActions } = this.props;
+ setTimeout(() => appActions.goTo('/jobs'), 0);
+ }
+
+ renderDiscount(discountAmount) {
+ if (!discountAmount) {
+ return null;
+ }
+ return (
+
+
+ Promo Discount
+
+
+ -{ discountAmount }
+
+
+ );
+ }
+
+ renderHighlightPrice(isHighlighted) {
+ if (!isHighlighted) {
+ return null;
+ }
+ return (
+
+
+ Highlighting
+
+
+ + 250
+
+
+ );
+ }
+
+ renderPromo() {
+ const {
+ id,
+ promoApplied,
+ promoCode,
+ promoName,
+ isHighlighted,
+ jobActions
+ } = this.props;
+
+ if (promoApplied) {
+ return (
+
+
+
+
+ { promoName } applied
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+ Have a promo code?
+
+
+
+
+
+
+
+ {
+ jobActions.applyCode({
+ id,
+ code: promoCode,
+ type: isHighlighted ? 'isHighlighted' : null
+ });
+ }}>
+ Apply Promo Code
+
+
+
+
+ );
+ }
+
+ render() {
+ const {
+ id,
+ isHighlighted,
+ buttonId,
+ price,
+ discountAmount
+ } = this.props;
+
+ return (
+
+
+
+
+
+
+
+ One more step
+
+
+ You're Awesome! just one more step to go.
+ Clicking on the link below will redirect to paypal.
+
+
+
+
+
+
+ Job Posting
+
+
+ + { price }
+
+
+ { this.renderHighlightPrice(isHighlighted) }
+ { this.renderDiscount(discountAmount) }
+
+
+ Total
+
+
+ ${
+ price - discountAmount + (isHighlighted ? 250 : 0)
+ }
+
+
+
+ { this.renderPromo() }
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default connect(mapStateToProps, bindableActions)(JobTotal);
diff --git a/common/app/routes/Jobs/components/Jobs.jsx b/common/app/routes/Jobs/components/Jobs.jsx
index 9704d26931..a315227d70 100644
--- a/common/app/routes/Jobs/components/Jobs.jsx
+++ b/common/app/routes/Jobs/components/Jobs.jsx
@@ -1,132 +1,156 @@
import React, { cloneElement, PropTypes } from 'react';
-import { contain } from 'thundercats-react';
+import { connect, compose } from 'redux';
+import { createSelector } from 'reselect';
+import { push } from 'react-router-redux';
+
+import PureComponent from 'react-pure-render/component';
import { Button, Row, Col } from 'react-bootstrap';
+import contain from '../../../utils/professor-x';
import ListJobs from './List.jsx';
-export default contain(
- {
- store: 'appStore',
- map({ jobsApp: { jobs, showModal }}) {
- return { jobs, showModal };
- },
- fetchAction: 'jobActions.getJobs',
- isPrimed({ jobs = [] }) {
- return !!jobs.length;
- },
- actions: [
- 'appActions',
- 'jobActions'
- ]
- },
- React.createClass({
- displayName: 'Jobs',
+import {
+ findJob,
+ fetchJobs
+} from '../redux/actions';
- propTypes: {
- children: PropTypes.element,
- appActions: PropTypes.object,
- jobActions: PropTypes.object,
- jobs: PropTypes.array,
- showModal: PropTypes.bool
- },
+const mapSateToProps = createSelector(
+ state => state.jobsApp.jobs.entities,
+ state => state.jobsApp.jobs.results,
+ state => state.jobsApp,
+ (jobsMap, jobsById) => {
+ return jobsById.map(id => jobsMap[id]);
+ }
+);
- handleJobClick(id) {
- const { appActions, jobActions } = this.props;
- if (!id) {
- return null;
- }
- jobActions.findJob(id);
- appActions.goTo(`/jobs/${id}`);
- },
+const bindableActions = {
+ findJob,
+ fetchJobs,
+ push
+};
- renderList(handleJobClick, jobs) {
- return (
-
- );
- },
+const fetchOptions = {
+ fetchAction: 'fetchJobs',
+ isPrimed({ jobs }) {
+ return !!jobs.results.length;
+ }
+};
- renderChild(child, jobs) {
- if (!child) {
- return null;
- }
- return cloneElement(
- child,
- { jobs }
- );
- },
+export class Jobs extends PureComponent {
+ static displayName = 'Jobs';
- render() {
- const {
- children,
- jobs,
- appActions
- } = this.props;
+ static propTypes = {
+ push: PropTypes.func,
+ findJob: PropTypes.func,
+ fetchJobs: PropTypes.func,
+ children: PropTypes.element,
+ jobs: PropTypes.array,
+ showModal: PropTypes.bool
+ };
- return (
+ createJobClickHandler(id) {
+ const { findJob, push } = this.props;
+ if (!id) {
+ return null;
+ }
+
+ return id => {
+ findJob(id);
+ push(`/jobs/${id}`);
+ };
+ }
+
+ renderList(handleJobClick, jobs) {
+ return (
+
+ );
+ }
+
+ renderChild(child, jobs) {
+ if (!child) {
+ return null;
+ }
+ return cloneElement(
+ child,
+ { jobs }
+ );
+ }
+
+ render() {
+ const {
+ children,
+ jobs
+ } = this.props;
+
+ return (
+
+
+
+ Hire a JavaScript engineer who's experienced in HTML5,
+ Node.js, MongoDB, and Agile Development.
+
+
+
+
+ {
+ push('/jobs/new');
+ }}>
+ Post a job: $1,000
+
+
+
+
+
-
- Hire a JavaScript engineer who's experienced in HTML5,
- Node.js, MongoDB, and Agile Development.
-
-
-
-
- {
- appActions.goTo('/jobs/new');
- }}>
- Post a job: $1,000
-
-
-
-
-
-
-
-
-
-
-
-
- We hired our last developer out of Free Code Camp
- and couldn't be happier. Free Code Camp is now
- our go-to way to bring on pre-screened candidates
- who are enthusiastic about learning quickly and
- becoming immediately productive in their new career.
-
-
- Michael Gai, CEO at CoNarrative
-
-
-
-
-
+ md={ 2 }
+ xs={ 4 }>
+
+
+
+
+
+ We hired our last developer out of Free Code Camp
+ and couldn't be happier. Free Code Camp is now
+ our go-to way to bring on pre-screened candidates
+ who are enthusiastic about learning quickly and
+ becoming immediately productive in their new career.
+
+
+ Michael Gai, CEO at CoNarrative
+
+
+
+
+
{ this.renderChild(children, jobs) ||
- this.renderList(this.handleJobClick, jobs) }
+ this.renderList(this.createJobClickHandler(), jobs) }
- );
- }
- })
-);
+ );
+ }
+}
+
+export default compose(
+ connect(mapSateToProps, bindableActions),
+ contain(fetchOptions)
+)(Jobs);
diff --git a/common/app/routes/Jobs/components/List.jsx b/common/app/routes/Jobs/components/List.jsx
index 4edc4cd5ad..940937436f 100644
--- a/common/app/routes/Jobs/components/List.jsx
+++ b/common/app/routes/Jobs/components/List.jsx
@@ -1,14 +1,15 @@
import React, { PropTypes } from 'react';
import classnames from 'classnames';
import { ListGroup, ListGroupItem } from 'react-bootstrap';
+import PureComponent from 'react-pure-render/component';
-export default React.createClass({
- displayName: 'ListJobs',
+export default class ListJobs extends PureComponent {
+ static displayName = 'ListJobs';
- propTypes: {
+ static propTypes = {
handleClick: PropTypes.func,
jobs: PropTypes.array
- },
+ };
addLocation(locale) {
if (!locale) {
@@ -19,50 +20,50 @@ export default React.createClass({
{ locale }
);
- },
+ }
renderJobs(handleClick, jobs = []) {
return jobs
- .filter(({ isPaid, isApproved, isFilled }) => {
- return isPaid && isApproved && !isFilled;
- })
- .map(({
- id,
- company,
- position,
- isHighlighted,
- locale
- }) => {
+ .filter(({ isPaid, isApproved, isFilled }) => {
+ return isPaid && isApproved && !isFilled;
+ })
+ .map(({
+ id,
+ company,
+ position,
+ isHighlighted,
+ locale
+ }) => {
- const className = classnames({
- 'jobs-list': true,
- 'col-xs-12': true,
- 'jobs-list-highlight': isHighlighted
- });
-
- return (
- handleClick(id) }>
-
-
- { company }
- {' '}
-
- - { position }
-
-
-
- { this.addLocation(locale) }
-
-
-
- );
+ const className = classnames({
+ 'jobs-list': true,
+ 'col-xs-12': true,
+ 'jobs-list-highlight': isHighlighted
});
- },
+
+ return (
+ handleClick(id) }>
+
+
+ { company }
+ {' '}
+
+ - { position }
+
+
+
+ { this.addLocation(locale) }
+
+
+
+ );
+ });
+ }
render() {
const {
@@ -76,4 +77,4 @@ export default React.createClass({
);
}
-});
+}
diff --git a/common/app/routes/Jobs/components/NewJobCompleted.jsx b/common/app/routes/Jobs/components/NewJobCompleted.jsx
index 4f11a2371d..8767960e38 100644
--- a/common/app/routes/Jobs/components/NewJobCompleted.jsx
+++ b/common/app/routes/Jobs/components/NewJobCompleted.jsx
@@ -2,8 +2,8 @@ import React from 'react';
import { LinkContainer } from 'react-router-bootstrap';
import { Button, Col, Row } from 'react-bootstrap';
-export default React.createClass({
- displayName: 'NewJobCompleted',
+export default class extends React.createClass {
+ static displayName = 'NewJobCompleted';
render() {
return (
@@ -36,4 +36,4 @@ export default React.createClass({
);
}
-});
+}
diff --git a/common/app/routes/Jobs/components/Preview.jsx b/common/app/routes/Jobs/components/Preview.jsx
index 804b34b6d8..a50eca2794 100644
--- a/common/app/routes/Jobs/components/Preview.jsx
+++ b/common/app/routes/Jobs/components/Preview.jsx
@@ -1,79 +1,84 @@
import React, { PropTypes } from 'react';
import { Button, Row, Col } from 'react-bootstrap';
-import { contain } from 'thundercats-react';
+import { connect } from 'redux';
+import { createSelector } from 'reselect';
+import PureComponent from 'react-pure-render/component';
+import { goBack, push } from 'react-router-redux';
import ShowJob from './ShowJob.jsx';
import JobNotFound from './JobNotFound.jsx';
-export default contain(
- {
- store: 'appStore',
- actions: [
- 'appActions',
- 'jobActions'
- ],
- map({ jobsApp: { form: job = {} } }) {
- return { job };
+import { clearSavedForm, saveJobToDb } from '../redux/actions';
+
+const mapStateToProps = createSelector(
+ state => state.jobsApp.form,
+ (job = {}) => ({ job })
+);
+
+const bindableActions = {
+ goBack,
+ push,
+ clearSavedForm,
+ saveJobToDb
+};
+
+export class JobPreview extends PureComponent {
+ static displayName = 'Preview';
+
+ static propTypes = {
+ job: PropTypes.object
+ };
+
+ componentDidMount() {
+ const { push, job } = this.props;
+ // redirect user in client
+ if (!job || !job.position || !job.description) {
+ push('/jobs/new');
}
- },
- React.createClass({
- displayName: 'Preview',
+ }
- propTypes: {
- appActions: PropTypes.object,
- job: PropTypes.object,
- jobActions: PropTypes.object
- },
+ render() {
+ const { job, goBack, clearSavedForm, saveJobToDb } = this.props;
- componentDidMount() {
- const { appActions, job } = this.props;
- // redirect user in client
- if (!job || !job.position || !job.description) {
- appActions.goTo('/jobs/new');
- }
- },
+ if (!job || !job.position || !job.description) {
+ return ;
+ }
- render() {
- const { appActions, job, jobActions } = this.props;
+ return (
+
+
+
+
+
+
+
+
{
+ clearSavedForm();
+ saveJobToDb({
+ goTo: '/jobs/new/check-out',
+ job
+ });
+ }}>
- if (!job || !job.position || !job.description) {
- return ;
- }
-
- return (
-
-
-
-
-
-
-
- {
- jobActions.clearSavedForm();
- jobActions.saveJobToDb({
- goTo: '/jobs/new/check-out',
- job
- });
- }}>
-
- Looks great! Let's Check Out
+ Looks great! Let's Check Out
appActions.goBack() } >
+ onClick={ goBack } >
Head back and make edits
- );
- }
- })
-);
+ );
+ }
+}
+
+export default connect(mapStateToProps, bindableActions)(JobPreview);
diff --git a/common/app/routes/Jobs/components/Show.jsx b/common/app/routes/Jobs/components/Show.jsx
index 5ccee6e131..84ca630b92 100644
--- a/common/app/routes/Jobs/components/Show.jsx
+++ b/common/app/routes/Jobs/components/Show.jsx
@@ -1,6 +1,11 @@
import React, { PropTypes } from 'react';
-import { History } from 'react-router';
-import { contain } from 'thundercats-react';
+import { connect, compose } from 'redux';
+import { push } from 'react-router-redux';
+import PureComponent from 'react-pure-render/component';
+import { createSelector } from 'reselect';
+
+import contain from '../../../utils/professor-x';
+import { fetchJob } from '../redux/actions';
import ShowJob from './ShowJob.jsx';
import JobNotFound from './JobNotFound.jsx';
@@ -51,86 +56,89 @@ function generateMessage(
"You've earned it, so feel free to apply.";
}
-export default contain(
- {
- store: 'appStore',
- fetchAction: 'jobActions.getJob',
- map({
- username,
- isFrontEndCert,
- isBackEndCert,
- jobsApp: { currentJob }
- }) {
- return {
- username,
- job: currentJob,
- isFrontEndCert,
- isBackEndCert
- };
- },
- getPayload({ params: { id } }) {
- return id;
- },
- isPrimed({ params: { id } = {}, job = {} }) {
- return job.id === id;
- },
- // using es6 destructuring
- shouldContainerFetch({ job = {} }, { params: { id } }
- ) {
- return job.id !== id;
- }
- },
- React.createClass({
- displayName: 'Show',
-
- propTypes: {
- job: PropTypes.object,
- isBackEndCert: PropTypes.bool,
- isFrontEndCert: PropTypes.bool,
- username: PropTypes.string
- },
-
- mixins: [History],
-
- componentDidMount() {
- const { job } = this.props;
- // redirect user in client
- if (!isJobValid(job)) {
- this.history.pushState(null, '/jobs');
- }
- },
-
- render() {
- const {
- isBackEndCert,
- isFrontEndCert,
- job,
- username
- } = this.props;
-
- if (!isJobValid(job)) {
- return ;
- }
-
- const isSignedIn = !!username;
-
- const showApply = shouldShowApply(
- job,
- { isFrontEndCert, isBackEndCert }
- );
-
- const message = generateMessage(
- job,
- { isFrontEndCert, isBackEndCert, isSignedIn }
- );
-
- return (
-
- );
- }
+const mapStateToProps = createSelector(
+ state => state.app,
+ state => state.jobsApp.currentJob,
+ ({ username, isFrontEndCert, isBackEndCert }, job = {}) => ({
+ username,
+ isFrontEndCert,
+ isBackEndCert,
+ job
})
);
+
+const bindableActions = {
+ push,
+ fetchJob
+};
+
+const fetchOptions = {
+ fetchAction: 'fetchJob',
+ getActionArgs({ params: { id } }) {
+ return [ id ];
+ },
+ isPrimed({ params: { id } = {}, job = {} }) {
+ return job.id === id;
+ },
+ // using es6 destructuring
+ shouldRefetch({ job }, { params: { id } }) {
+ return job.id !== id;
+ }
+};
+
+export class Show extends PureComponent {
+ static displayName = 'Show';
+
+ static propTypes = {
+ job: PropTypes.object,
+ isBackEndCert: PropTypes.bool,
+ isFrontEndCert: PropTypes.bool,
+ username: PropTypes.string
+ };
+
+ componentDidMount() {
+ const { job, push } = this.props;
+ // redirect user in client
+ if (!isJobValid(job)) {
+ push('/jobs');
+ }
+ }
+
+ render() {
+ const {
+ isBackEndCert,
+ isFrontEndCert,
+ job,
+ username
+ } = this.props;
+
+ if (!isJobValid(job)) {
+ return ;
+ }
+
+ const isSignedIn = !!username;
+
+ const showApply = shouldShowApply(
+ job,
+ { isFrontEndCert, isBackEndCert }
+ );
+
+ const message = generateMessage(
+ job,
+ { isFrontEndCert, isBackEndCert, isSignedIn }
+ );
+
+ return (
+
+ );
+ }
+}
+
+export default compose(
+ connect(mapStateToProps, bindableActions),
+ contain(fetchOptions)
+)(Show);
diff --git a/common/app/routes/Jobs/redux/actions.js b/common/app/routes/Jobs/redux/actions.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/common/app/routes/Jobs/redux/fetch-jobs-saga.js b/common/app/routes/Jobs/redux/fetch-jobs-saga.js
new file mode 100644
index 0000000000..2712d2e47d
--- /dev/null
+++ b/common/app/routes/Jobs/redux/fetch-jobs-saga.js
@@ -0,0 +1,7 @@
+import { fetchJobs, fetchJobsCompleted } from './types';
+
+export default ({ services }) => ({ dispatch, getState }) => next => {
+ return function fetchJobsSaga(action) {
+ return next(action);
+ };
+};
diff --git a/common/app/routes/Jobs/flux/index.js b/common/app/routes/Jobs/redux/index.js
similarity index 100%
rename from common/app/routes/Jobs/flux/index.js
rename to common/app/routes/Jobs/redux/index.js
diff --git a/common/app/routes/Jobs/flux/Actions.js b/common/app/routes/Jobs/redux/oldActions.js
similarity index 100%
rename from common/app/routes/Jobs/flux/Actions.js
rename to common/app/routes/Jobs/redux/oldActions.js
diff --git a/common/app/routes/Jobs/redux/reducer.js b/common/app/routes/Jobs/redux/reducer.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/common/app/routes/Jobs/redux/types.js b/common/app/routes/Jobs/redux/types.js
new file mode 100644
index 0000000000..3e3ccc2b6c
--- /dev/null
+++ b/common/app/routes/Jobs/redux/types.js
@@ -0,0 +1,18 @@
+const types = [
+ 'fetchJobs',
+ 'fetchJobsCompleted',
+
+ 'findJob',
+
+ 'saveJob',
+ 'getJob',
+ 'getJobs',
+ 'openModal',
+ 'closeModal',
+ 'handleFormUpdate',
+ 'saveForm',
+ 'clear'
+];
+
+export default types
+ .reduce((types, type) => ({ ...types, [type]: `jobs.${type}` }), {});