Merge pull request #3232 from FreeCodeCamp/feature/jobs

add individual pages to jobs
This commit is contained in:
Aniruddh Agarwal
2015-09-11 11:01:35 +08:00
15 changed files with 375 additions and 176 deletions

2
client/es6-shims.js Normal file
View File

@ -0,0 +1,2 @@
require('object.assign').shim();
require('es6-map/implement');

View File

@ -1,3 +1,4 @@
import unused from './es6-shims'; // eslint-disable-line
import Rx from 'rx'; import Rx from 'rx';
import React from 'react'; import React from 'react';
import Fetchr from 'fetchr'; import Fetchr from 'fetchr';

View File

@ -1,5 +1,4 @@
import Rx from 'rx'; import Rx from 'rx';
import assign from 'object.assign';
import { Router } from 'react-router'; import { Router } from 'react-router';
import App from './App.jsx'; import App from './App.jsx';
import AppCat from './Cat'; import AppCat from './Cat';
@ -8,7 +7,7 @@ import childRoutes from './routes';
const router$ = Rx.Observable.fromNodeCallback(Router.run, Router); const router$ = Rx.Observable.fromNodeCallback(Router.run, Router);
const routes = assign({ components: App }, childRoutes); const routes = Object.assign({ components: App }, childRoutes);
export default function app$(location) { export default function app$(location) {
return router$(routes, location) return router$(routes, location)

View File

@ -1,5 +1,4 @@
import { Actions } from 'thundercats'; import { Actions } from 'thundercats';
import assign from 'object.assign';
import debugFactory from 'debug'; import debugFactory from 'debug';
const debug = debugFactory('freecc:hikes:actions'); const debug = debugFactory('freecc:hikes:actions');
@ -45,7 +44,7 @@ export default Actions({
dashedName, dashedName,
oldState.currentHike oldState.currentHike
); );
return assign({}, oldState, { currentHike }); return Object.assign({}, oldState, { currentHike });
} }
}); });
} }

View File

@ -1,23 +1,39 @@
import React, { cloneElement, PropTypes } from 'react'; import React, { cloneElement, PropTypes } from 'react';
import { contain } from 'thundercats-react'; import { contain } from 'thundercats-react';
import { Navigation } from 'react-router';
import { Button, Jumbotron, Row } from 'react-bootstrap'; import { Button, Jumbotron, Row } from 'react-bootstrap';
import ListJobs from './List.jsx'; import ListJobs from './List.jsx';
export default contain( export default contain(
{ {
store: 'jobsStore', store: 'jobsStore',
fetchAction: 'jobActions.getJobs' fetchAction: 'jobActions.getJobs',
actions: 'jobActions'
}, },
React.createClass({ React.createClass({
displayName: 'Jobs', displayName: 'Jobs',
propTypes: { propTypes: {
children: PropTypes.element, children: PropTypes.element,
jobActions: PropTypes.object,
jobs: PropTypes.array jobs: PropTypes.array
}, },
mixins: [Navigation],
renderList(jobs) { handleJobClick(id) {
const { jobActions } = this.props;
if (!id) {
return null;
}
jobActions.findJob(id);
this.transitionTo(`/jobs/${id}`);
},
renderList(handleJobClick, jobs) {
return ( return (
<ListJobs jobs={ jobs }/> <ListJobs
handleClick={ handleJobClick }
jobs={ jobs }/>
); );
}, },
@ -53,7 +69,7 @@ export default contain(
</Row> </Row>
<Row> <Row>
{ this.renderChild(children, jobs) || { this.renderChild(children, jobs) ||
this.renderList(jobs) } this.renderList(this.handleJobClick, jobs) }
</Row> </Row>
</div> </div>
); );

View File

@ -1,81 +1,83 @@
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import { contain } from 'thundercats-react';
import { PanelGroup, Thumbnail, Panel, Well } from 'react-bootstrap'; import { PanelGroup, Thumbnail, Panel, Well } from 'react-bootstrap';
import moment from 'moment'; import moment from 'moment';
export default contain( export default React.createClass({
{ displayName: 'ListJobs',
propTypes: {
handleClick: PropTypes.func,
jobs: PropTypes.array
}, },
React.createClass({
displayName: 'ListJobs',
propTypes: { renderJobs(handleClick, jobs =[]) {
jobs: PropTypes.array const thumbnailStyle = {
}, backgroundColor: 'white',
maxHeight: '100px',
maxWidth: '100px'
};
renderJobs(jobs =[]) { return jobs.map((
const thumbnailStyle = { {
backgroundColor: 'white', id,
maxHeight: '100px', company,
maxWidth: '100px' position,
}; description,
return jobs.map(( logo,
{ city,
id, state,
company, email,
position, phone,
description, postedOn
logo, },
city, index
state, ) => {
email, const header = (
phone, <div>
postedOn <h4 style={{ display: 'inline-block' }}>{ company }</h4>
}, <h5
index className='pull-right hidden-xs hidden-md'
) => { style={{ display: 'inline-block' }}>
const header = ( { position }
<div> </h5>
<h4 style={{ display: 'inline-block' }}>{ company }</h4> </div>
<h5
className='pull-right hidden-xs hidden-md'
style={{ display: 'inline-block' }}>
{ position }
</h5>
</div>
);
return (
<Panel
collapsible={ true }
eventKey={ index }
header={ header }
key={ id }>
<Well>
<Thumbnail
alt='200x200' src={ logo }
style={ thumbnailStyle } />
<Panel>
Position: { position }
Location: { city }, { state }
<br />
Contact: { email || phone || 'N/A' }
<br />
Posted On: { moment(postedOn).format('MMMM Do, YYYY') }
</Panel>
<p>{ description }</p>
</Well>
</Panel>
);
});
},
render() {
const { jobs } = this.props;
return (
<PanelGroup>
{ this.renderJobs(jobs) }
</PanelGroup>
); );
} return (
}) <Panel
); collapsible={ true }
eventKey={ index }
header={ header }
key={ id }>
<Well>
<Thumbnail
alt={ company + 'company logo' }
src={ logo }
style={ thumbnailStyle } />
<Panel>
Position: { position }
Location: { city }, { state }
<br />
Contact: { email || phone || 'N/A' }
<br />
Posted On: { moment(postedOn).format('MMMM Do, YYYY') }
</Panel>
<p onClick={ () => handleClick(id) }>{ description }</p>
</Well>
</Panel>
);
});
},
render() {
const {
handleClick,
jobs
} = this.props;
return (
<PanelGroup>
{ this.renderJobs(handleClick, jobs) }
</PanelGroup>
);
}
});

View File

@ -1,5 +1,6 @@
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import { Thumbnail, Panel, Well } from 'react-bootstrap'; import { contain } from 'thundercats-react';
import { Row, Thumbnail, Panel, Well } from 'react-bootstrap';
import moment from 'moment'; import moment from 'moment';
const thumbnailStyle = { const thumbnailStyle = {
@ -7,53 +8,81 @@ const thumbnailStyle = {
maxHeight: '100px', maxHeight: '100px',
maxWidth: '100px' maxWidth: '100px'
}; };
export default React.createClass({
displayName: 'ShowJob', export default contain(
propTypes: { {
job: PropTypes.object store: 'jobsStore',
fetchAction: 'jobActions.getJob',
map({ currentJob }) {
return { job: currentJob };
},
getPayload({ params: { id }, job = {} }) {
return {
id,
isPrimed: job.id === id
};
},
// using es6 destructuring
shouldContainerFetch({ job = {} }, { params: { id } }
) {
return job.id !== id;
}
}, },
React.createClass({
displayName: 'ShowJob',
propTypes: {
job: PropTypes.object,
params: PropTypes.object
},
renderHeader({ company, position }) { renderHeader({ company, position }) {
return ( return (
<div> <div>
<h4 style={{ display: 'inline-block' }}>{ company }</h4> <h4 style={{ display: 'inline-block' }}>{ company }</h4>
<h5 <h5
className='pull-right hidden-xs hidden-md' className='pull-right hidden-xs hidden-md'
style={{ display: 'inline-block' }}> style={{ display: 'inline-block' }}>
{ position } { position }
</h5> </h5>
</div> </div>
); );
}, },
render() { render() {
const { job } = this.props; const { job = {} } = this.props;
const { const {
logo, logo,
position, position,
city, city,
state, company,
email, state,
phone, email,
postedOn, phone,
description postedOn,
} = job; description
} = job;
return ( return (
<Well> <div>
<Thumbnail <Row>
alt='200x200' src={ logo } <Well>
style={ thumbnailStyle } /> <Thumbnail
<Panel> alt={ company + 'company logo' }
Position: { position } src={ logo }
Location: { city }, { state } style={ thumbnailStyle } />
<br /> <Panel>
Contact: { email || phone || 'N/A' } Position: { position }
<br /> Location: { city }, { state }
Posted On: { moment(postedOn).format('MMMM Do, YYYY') } <br />
</Panel> Contact: { email || phone || 'N/A' }
<p>{ description }</p> <br />
</Well> Posted On: { moment(postedOn).format('MMMM Do, YYYY') }
); </Panel>
} <p>{ description }</p>
}); </Well>
</Row>
</div>
);
}
})
);

View File

@ -5,9 +5,30 @@ const debug = debugFactory('freecc:jobs:actions');
export default Actions({ export default Actions({
setJobs: null, setJobs: null,
getJob(id) { // findJob assumes that the job is already in the list of jobs
return { id }; findJob(id) {
return oldState => {
const { currentJob = {}, jobs = [] } = oldState;
// currentJob already set
// do nothing
if (currentJob.id === id) {
return null;
}
const foundJob = jobs.reduce((newJob, job) => {
if (job.id === id) {
return job;
}
return newJob;
}, null);
// if no job found this will be null which is a op noop
return foundJob ?
Object.assign({}, oldState, { currentJob: foundJob }) :
null;
};
}, },
setError: null,
getJob: null,
getJobs(params) { getJobs(params) {
return { params }; return { params };
} }
@ -15,13 +36,28 @@ export default Actions({
.refs({ displayName: 'JobActions' }) .refs({ displayName: 'JobActions' })
.init(({ instance: jobActions, args: [services] }) => { .init(({ instance: jobActions, args: [services] }) => {
jobActions.getJobs.subscribe(() => { jobActions.getJobs.subscribe(() => {
services.read('job', null, null, (err, jobs) => { services.read('jobs', null, null, (err, jobs) => {
if (err) { if (err) {
debug('job services experienced an issue', err); debug('job services experienced an issue', err);
jobActions.setJobs({ jobs: [] }); return jobActions.setError({ err });
} }
jobActions.setJobs({ jobs }); jobActions.setJobs({ jobs });
}); });
}); });
jobActions.getJob.subscribe(({ id, isPrimed }) => {
// job is already set, do nothing.
if (isPrimed) {
debug('job is primed');
return;
}
services.read('jobs', { id }, null, (err, job) => {
if (err) {
debug('job services experienced an issue', err);
return jobActions.setError({ err });
}
jobActions.setJobs({ currentJob: job });
});
});
return jobActions; return jobActions;
}); });

View File

@ -1,10 +1,17 @@
import { Store } from 'thundercats'; import { Store } from 'thundercats';
const { setter } = Store; const {
createRegistrar,
setter,
transformer
} = Store;
export default Store() export default Store()
.refs({ displayName: 'JobsStore' }) .refs({ displayName: 'JobsStore' })
.init(({ instance: jobsStore, args: [cat] }) => { .init(({ instance: jobsStore, args: [cat] }) => {
let jobActions = cat.getActions('JobActions'); const { setJobs, findJob, setError } = cat.getActions('JobActions');
jobsStore.register(setter(jobActions.setJobs)); const register = createRegistrar(jobsStore);
register(setter(setJobs));
register(transformer(findJob));
register(setter(setError));
}); });

View File

@ -1,14 +1,18 @@
import Jobs from './components/Jobs.jsx'; import Jobs from './components/Jobs.jsx';
import Show from './components/Show.jsx';
/* /*
* show: /jobs * index: /jobs list jobs
* showOne: /jobs/:id * show: /jobs/:id show one job
* edit /jobs/:id * create /jobs/new create a new job
* delete /jobs/:id
* createOne /jobs/new
*/ */
export default { export default {
path: 'jobs', childRoutes: [{
component: Jobs path: '/jobs',
component: Jobs
}, {
path: 'jobs/:id',
component: Show
}]
}; };

View File

@ -1,3 +1,6 @@
// enable debug for gulp
process.env.DEBUG = process.env.DEBUG || 'freecc:*';
require('babel-core/register'); require('babel-core/register');
var Rx = require('rx'), var Rx = require('rx'),
gulp = require('gulp'), gulp = require('gulp'),
@ -6,6 +9,7 @@ var Rx = require('rx'),
// utils // utils
plumber = require('gulp-plumber'), plumber = require('gulp-plumber'),
notify = require('gulp-notify'), notify = require('gulp-notify'),
gutil = require('gulp-util'),
reduce = require('gulp-reduce-file'), reduce = require('gulp-reduce-file'),
sortKeys = require('sort-keys'), sortKeys = require('sort-keys'),
debug = require('debug')('freecc:gulp'), debug = require('debug')('freecc:gulp'),
@ -25,6 +29,7 @@ var Rx = require('rx'),
// rev // rev
rev = require('gulp-rev'), rev = require('gulp-rev'),
revReplace = require('gulp-rev-replace'), revReplace = require('gulp-rev-replace'),
revDel = require('rev-del'),
// lint // lint
jsonlint = require('gulp-jsonlint'), jsonlint = require('gulp-jsonlint'),
@ -33,6 +38,7 @@ var Rx = require('rx'),
Rx.config.longStackSupport = true; Rx.config.longStackSupport = true;
var __DEV__ = process.env.NODE_ENV !== 'production';
var reloadDelay = 1000; var reloadDelay = 1000;
var reload = sync.reload; var reload = sync.reload;
var paths = { var paths = {
@ -43,6 +49,7 @@ var paths = {
'!public/js/bundle*', '!public/js/bundle*',
'node_modules/', 'node_modules/',
'client/', 'client/',
'server/manifests/*.json',
'server/rev-manifest.json' 'server/rev-manifest.json'
], ],
@ -67,8 +74,7 @@ var paths = {
], ],
dependents: [ dependents: [
'client/commonFramework.js', 'client/commonFramework.js'
'client/sandbox.js'
], ],
less: './client/less/main.less', less: './client/less/main.less',
@ -107,11 +113,22 @@ function errorHandler() {
this.emit('end'); this.emit('end');
} }
function delRev(dest, manifestName) {
// in production do not delete old revisions
if (!__DEV__) {
return gutil.noop();
}
return revDel({
oldManifest: path.join(paths.manifest, manifestName),
dest: dest
});
}
gulp.task('serve', function(cb) { gulp.task('serve', function(cb) {
var called = false; var called = false;
nodemon({ nodemon({
script: paths.server, script: paths.server,
ext: '.js .json', ext: '.jsx .js .json',
ignore: paths.serverIgnore, ignore: paths.serverIgnore,
exec: path.join(__dirname, 'node_modules/.bin/babel-node'), exec: path.join(__dirname, 'node_modules/.bin/babel-node'),
env: { env: {
@ -143,7 +160,7 @@ var syncDepenedents = [
'js', 'js',
'less', 'less',
'dependents', 'dependents',
'pack-client', 'pack-watch',
'build-manifest' 'build-manifest'
]; ];
@ -173,6 +190,9 @@ gulp.task('lint-json', function() {
gulp.task('test-challenges', ['lint-json']); gulp.task('test-challenges', ['lint-json']);
gulp.task('pack-client', function() { gulp.task('pack-client', function() {
var manifestName = 'react-manifest.json';
var dest = webpackConfig.output.path;
return gulp.src(webpackConfig.entry) return gulp.src(webpackConfig.entry)
.pipe(plumber({ errorHandler: errorHandler })) .pipe(plumber({ errorHandler: errorHandler }))
.pipe(webpack(Object.assign( .pipe(webpack(Object.assign(
@ -180,30 +200,73 @@ gulp.task('pack-client', function() {
webpackConfig, webpackConfig,
webpackOptions webpackOptions
))) )))
.pipe(gulp.dest(webpackConfig.output.path)) .pipe(gulp.dest(dest))
.pipe(rev()) .pipe(rev())
// copy files to public // copy files to public
.pipe(gulp.dest(webpackConfig.output.path)) .pipe(gulp.dest(dest))
// create and merge manifest // create manifest
.pipe(rev.manifest('react-manifest.json')) .pipe(rev.manifest(manifestName))
// delete old rev
.pipe(delRev(
dest,
manifestName
))
.pipe(gulp.dest(paths.manifest)); .pipe(gulp.dest(paths.manifest));
}); });
gulp.task('pack-watch', function() { var defaultStatsOptions = {
return gulp.src(webpackConfig.entry) colors: gutil.colors.supportsColor,
hash: false,
timings: false,
chunks: false,
chunkModules: false,
modules: false,
children: true,
version: true,
cached: false,
cachedAssets: false,
reasons: false,
source: false,
errorDetails: false
};
gulp.task('pack-watch', function(cb) {
var called = false;
gulp.src(webpackConfig.entry)
.pipe(plumber({ errorHandler: errorHandler })) .pipe(plumber({ errorHandler: errorHandler }))
.pipe(webpack(Object.assign( .pipe(webpack(Object.assign(
{}, {},
webpackConfig, webpackConfig,
webpackOptions, webpackOptions,
{ watch: true } { watch: true }
))) ), null, function(notUsed, stats) {
.pipe(gulp.dest(webpackConfig.output.path)) if (stats) {
gutil.log(stats.toString(defaultStatsOptions));
}
if (!called) {
debug('webpack watch completed');
called = true;
cb();
}
}))
.pipe(gulp.dest(webpackConfig.output.path));
});
gulp.task('pack-watch-manifest', function() {
var manifestName = 'react-manifest.json';
var dest = webpackConfig.output.path;
return gulp.src(dest + '/bundle.js')
.pipe(rev()) .pipe(rev())
// copy files to public // copy files to public
.pipe(gulp.dest(webpackConfig.output.path)) .pipe(gulp.dest(dest))
// create manifest // create manifest
.pipe(rev.manifest('react-manifest.json')) .pipe(rev.manifest(manifestName))
.pipe(delRev(
dest,
manifestName
))
.pipe(gulp.dest(paths.manifest)); .pipe(gulp.dest(paths.manifest));
}); });
@ -217,32 +280,45 @@ gulp.task('pack-node', function() {
gulp.task('pack', ['pack-client', 'pack-node']); gulp.task('pack', ['pack-client', 'pack-node']);
gulp.task('less', function() { gulp.task('less', function() {
var manifestName = 'css-manifest.json';
var dest = paths.css;
return gulp.src(paths.less) return gulp.src(paths.less)
.pipe(plumber({ errorHandler: errorHandler })) .pipe(plumber({ errorHandler: errorHandler }))
// copile // copile
.pipe(less({ .pipe(less({
paths: [ path.join(__dirname, 'less', 'includes') ] paths: [ path.join(__dirname, 'less', 'includes') ]
})) }))
.pipe(gulp.dest(paths.css)) .pipe(gulp.dest(dest))
// add revision // add revision
.pipe(rev()) .pipe(rev())
// copy files to public // copy files to public
.pipe(gulp.dest(paths.css)) .pipe(gulp.dest(dest))
// create and merge manifest // create and merge manifest
.pipe(rev.manifest('css-manifest.json')) .pipe(rev.manifest(manifestName))
.pipe(delRev(
dest,
manifestName
))
.pipe(gulp.dest(paths.manifest)); .pipe(gulp.dest(paths.manifest));
}); });
gulp.task('js', function() { gulp.task('js', function() {
var manifestName = 'js-manifest.json';
var dest = paths.publicJs;
return gulp.src(paths.js) return gulp.src(paths.js)
.pipe(plumber({ errorHandler: errorHandler })) .pipe(plumber({ errorHandler: errorHandler }))
.pipe(gulp.dest(paths.publicJs)) .pipe(gulp.dest(dest))
// create registry file // create registry file
.pipe(rev()) .pipe(rev())
// copy revisioned assets to dest // copy revisioned assets to dest
.pipe(gulp.dest(paths.publicJs)) .pipe(gulp.dest(dest))
// create manifest file // create manifest file
.pipe(rev.manifest('js-manifest.json')) .pipe(rev.manifest(manifestName))
.pipe(delRev(
dest,
manifestName
))
// copy manifest file to dest // copy manifest file to dest
.pipe(gulp.dest(paths.manifest)); .pipe(gulp.dest(paths.manifest));
}); });
@ -250,6 +326,9 @@ gulp.task('js', function() {
// commonFramework depend on iFrameScripts // commonFramework depend on iFrameScripts
// sandbox depends on plugin // sandbox depends on plugin
gulp.task('dependents', ['js'], function() { gulp.task('dependents', ['js'], function() {
var manifestName = 'dependents-manifest.json';
var dest = paths.publicJs;
var manifest = gulp.src( var manifest = gulp.src(
path.join(__dirname, paths.manifest, 'js-manifest.json') path.join(__dirname, paths.manifest, 'js-manifest.json')
); );
@ -257,9 +336,14 @@ gulp.task('dependents', ['js'], function() {
return gulp.src(paths.dependents) return gulp.src(paths.dependents)
.pipe(plumber({ errorHandler: errorHandler })) .pipe(plumber({ errorHandler: errorHandler }))
.pipe(revReplace({ manifest: manifest })) .pipe(revReplace({ manifest: manifest }))
.pipe(gulp.dest(dest))
.pipe(rev()) .pipe(rev())
.pipe(gulp.dest(paths.publicJs)) .pipe(gulp.dest(dest))
.pipe(rev.manifest('dependents-manifest.json')) .pipe(rev.manifest(manifestName))
.pipe(delRev(
dest,
manifestName
))
.pipe(gulp.dest(paths.manifest)); .pipe(gulp.dest(paths.manifest));
}); });
@ -301,7 +385,9 @@ var watchDependents = [
'dependents', 'dependents',
'serve', 'serve',
'sync', 'sync',
'build-manifest' 'build-manifest',
'pack-watch',
'pack-watch-manifest'
]; ];
gulp.task('watch', watchDependents, function() { gulp.task('watch', watchDependents, function() {
@ -311,7 +397,15 @@ gulp.task('watch', watchDependents, function() {
gulp.watch(paths.js, ['js', 'dependents']); gulp.watch(paths.js, ['js', 'dependents']);
gulp.watch(paths.dependents, ['dependents']); gulp.watch(paths.dependents, ['dependents']);
gulp.watch(paths.manifest + '/*.json', ['build-manifest-watch']); gulp.watch(paths.manifest + '/*.json', ['build-manifest-watch']);
gulp.watch(webpackConfig.output.path + '/bundle.js', ['pack-watch-manifest']);
}); });
gulp.task('default', ['less', 'serve', 'sync', 'watch', 'pack-watch']); gulp.task('default', [
'less',
'serve',
'pack-watch',
'pack-watch-manifest',
'watch',
'sync'
]);

View File

@ -17,9 +17,9 @@
"dependencies": { "dependencies": {
"accepts": "~1.2.5", "accepts": "~1.2.5",
"async": "~0.9.0", "async": "~0.9.0",
"babel": "5.6.14", "babel": "5.8.23",
"babel-core": "5.6.15", "babel-core": "5.8.23",
"babel-eslint": "^4.0.5", "babel-eslint": "4.1.1",
"babel-loader": "5.2.2", "babel-loader": "5.2.2",
"bcrypt-nodejs": "~0.0.3", "bcrypt-nodejs": "~0.0.3",
"body-parser": "^1.13.2", "body-parser": "^1.13.2",
@ -36,6 +36,7 @@
"dedent": "^0.4.0", "dedent": "^0.4.0",
"dotenv": "~0.4.0", "dotenv": "~0.4.0",
"errorhandler": "~1.3.0", "errorhandler": "~1.3.0",
"es6-map": "^0.1.1",
"eslint": "^1.1.0", "eslint": "^1.1.0",
"eslint-plugin-react": "^3.2.1", "eslint-plugin-react": "^3.2.1",
"express": "~4.10.4", "express": "~4.10.4",
@ -53,6 +54,7 @@
"gulp-reduce-file": "0.0.1", "gulp-reduce-file": "0.0.1",
"gulp-rev": "^6.0.1", "gulp-rev": "^6.0.1",
"gulp-rev-replace": "^0.4.2", "gulp-rev-replace": "^0.4.2",
"gulp-util": "^3.0.6",
"gulp-webpack": "^1.5.0", "gulp-webpack": "^1.5.0",
"helmet": "~0.9.0", "helmet": "~0.9.0",
"helmet-csp": "^0.2.3", "helmet-csp": "^0.2.3",
@ -85,11 +87,12 @@
"pmx": "^0.3.16", "pmx": "^0.3.16",
"ramda": "~0.10.0", "ramda": "~0.10.0",
"react": "^0.13.3", "react": "^0.13.3",
"react-bootstrap": "^0.23.7", "react-bootstrap": "~0.23.7",
"react-motion": "~0.1.0", "react-motion": "~0.1.0",
"react-router": "https://github.com/BerkeleyTrue/react-router#freecodecamp", "react-router": "https://github.com/BerkeleyTrue/react-router#freecodecamp",
"react-vimeo": "^0.0.3", "react-vimeo": "^0.0.3",
"request": "~2.53.0", "request": "~2.53.0",
"rev-del": "^1.0.5",
"rx": "^2.5.3", "rx": "^2.5.3",
"sanitize-html": "~1.6.1", "sanitize-html": "~1.6.1",
"sort-keys": "^1.1.1", "sort-keys": "^1.1.1",
@ -103,15 +106,12 @@
"yui": "~3.18.1" "yui": "~3.18.1"
}, },
"devDependencies": { "devDependencies": {
"babel-eslint": "^3.1.7",
"blessed": "~0.0.37", "blessed": "~0.0.37",
"bower-main-files": "~0.0.4", "bower-main-files": "~0.0.4",
"browser-sync": "~1.8.1", "browser-sync": "~1.8.1",
"browserify": "^10.2.4", "browserify": "^10.2.4",
"chai": "~1.10.0", "chai": "~1.10.0",
"envify": "^3.4.0", "envify": "^3.4.0",
"eslint": "^0.21.2",
"eslint-plugin-react": "^2.3.0",
"gulp": "~3.8.8", "gulp": "~3.8.8",
"gulp-eslint": "~0.9.0", "gulp-eslint": "~0.9.0",
"gulp-inject": "~1.0.2", "gulp-inject": "~1.0.2",

View File

@ -13,7 +13,8 @@ const debug = debugFactory('freecc:react-server');
const routes = [ const routes = [
'/hikes', '/hikes',
'/hikes/*', '/hikes/*',
'/jobs' '/jobs',
'/jobs/*'
]; ];
export default function reactSubRouter(app) { export default function reactSubRouter(app) {

View File

@ -1,6 +1,8 @@
import manifest from '../rev-manifest.json'; import manifest from '../rev-manifest.json';
const __DEV__ = process.env.NODE_ENV === 'development'; const __DEV__ = process.env.NODE_ENV === 'development';
const manifestPath = '../rev-manifest.json';
export default function({ globalPrepend = '' } = {}) { export default function({ globalPrepend = '' } = {}) {
function rev(manifest, scopedPrepend, asset) { function rev(manifest, scopedPrepend, asset) {
@ -13,7 +15,10 @@ export default function({ globalPrepend = '' } = {}) {
// this means we do not need to restart server on every change to // this means we do not need to restart server on every change to
// client code // client code
if (__DEV__) { if (__DEV__) {
const manifest = require('../rev-manifest.json'); // we first need to remove the manifest from require cache
delete require.cache[require.resolve(manifestPath)];
// and re-require
const manifest = require(manifestPath);
res.locals.rev = rev.bind(null, manifest); res.locals.rev = rev.bind(null, manifest);
return next(); return next();
} }

View File

@ -2,8 +2,12 @@ export default function getJobServices(app) {
const { Job } = app.models; const { Job } = app.models;
return { return {
name: 'job', name: 'jobs',
read: (req, resource, params, config, cb) => { read: (req, resource, params, config, cb) => {
const id = params ? params.id : null;
if (id) {
return Job.findById(id, cb);
}
Job.find({}, (err, jobs) => { Job.find({}, (err, jobs) => {
cb(err, jobs); cb(err, jobs);
}); });