From ac193dc4c47793c9bb24f0429ee62423924ca6da Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 29 Aug 2015 01:00:52 -0700 Subject: [PATCH 01/17] split jobs routes, add show comp --- common/app/routes/Jobs/components/List.jsx | 3 +- common/app/routes/Jobs/components/Show.jsx | 112 ++++++++++++--------- common/app/routes/Jobs/flux/Actions.js | 4 +- common/app/routes/Jobs/index.js | 17 ++-- 4 files changed, 79 insertions(+), 57 deletions(-) diff --git a/common/app/routes/Jobs/components/List.jsx b/common/app/routes/Jobs/components/List.jsx index b90050aa74..1688cc42e1 100644 --- a/common/app/routes/Jobs/components/List.jsx +++ b/common/app/routes/Jobs/components/List.jsx @@ -52,7 +52,8 @@ export default contain( key={ id }> Position: { position } diff --git a/common/app/routes/Jobs/components/Show.jsx b/common/app/routes/Jobs/components/Show.jsx index 209917cff7..43bb22c667 100644 --- a/common/app/routes/Jobs/components/Show.jsx +++ b/common/app/routes/Jobs/components/Show.jsx @@ -1,5 +1,6 @@ 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'; const thumbnailStyle = { @@ -7,53 +8,70 @@ const thumbnailStyle = { maxHeight: '100px', maxWidth: '100px' }; -export default React.createClass({ - displayName: 'ShowJob', - propTypes: { - job: PropTypes.object + +export default contain( + { + store: 'jobsStore', + fetchAction: 'jobActions.getJob', + getPayload({ params }) { + return { id: params.id }; + } }, + React.createClass({ + displayName: 'ShowJob', + propTypes: { + job: PropTypes.object, + params: PropTypes.object + }, - renderHeader({ company, position }) { - return ( -
-

{ company }

-
- { position } -
-
- ); - }, + renderHeader({ company, position }) { + return ( +
+

{ company }

+
+ { position } +
+
+ ); + }, - render() { - const { job } = this.props; - const { - logo, - position, - city, - state, - email, - phone, - postedOn, - description - } = job; + render() { + const { job } = this.props; + const { + logo, + position, + city, + company, + state, + email, + phone, + postedOn, + description + } = job; - return ( - - - - Position: { position } - Location: { city }, { state } -
- Contact: { email || phone || 'N/A' } -
- Posted On: { moment(postedOn).format('MMMM Do, YYYY') } -
-

{ description }

-
- ); - } -}); + return ( +
+ + + + + Position: { position } + Location: { city }, { state } +
+ Contact: { email || phone || 'N/A' } +
+ Posted On: { moment(postedOn).format('MMMM Do, YYYY') } +
+

{ description }

+
+
+
+ ); + } + }) +); diff --git a/common/app/routes/Jobs/flux/Actions.js b/common/app/routes/Jobs/flux/Actions.js index 68d5eeae57..f74c8092a4 100644 --- a/common/app/routes/Jobs/flux/Actions.js +++ b/common/app/routes/Jobs/flux/Actions.js @@ -5,9 +5,7 @@ const debug = debugFactory('freecc:jobs:actions'); export default Actions({ setJobs: null, - getJob(id) { - return { id }; - }, + getJob: null, getJobs(params) { return { params }; } diff --git a/common/app/routes/Jobs/index.js b/common/app/routes/Jobs/index.js index 9d52bf2b53..1148cf265a 100644 --- a/common/app/routes/Jobs/index.js +++ b/common/app/routes/Jobs/index.js @@ -1,14 +1,19 @@ import Jobs from './components/Jobs.jsx'; +import Show from './components/Show.jsx'; /* - * show: /jobs - * showOne: /jobs/:id - * edit /jobs/:id - * delete /jobs/:id - * createOne /jobs/new + * index: /jobs list jobs + * show: /jobs/:id show one job + * create /jobs/new create a new job */ export default { path: 'jobs', - component: Jobs + childRoutes: [{ + path: 'jobs', + component: Jobs + }, { + path: 'jobs/:id', + component: Show + }] }; From 70cf48cf7b75583ebbdc5d925cc1ea46fb9a6322 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 31 Aug 2015 09:20:55 -0700 Subject: [PATCH 02/17] fix manifest generation during webpack watch fix gulp debug statements during gulp should always be on --- gulpfile.js | 61 ++++++++++++++++++++++++++++++++++++++++++++++------ package.json | 1 + 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index b5eee5417a..010e83bf65 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,3 +1,6 @@ +// enable debug for gulp +process.env.DEBUG = process.env.DEBUG || 'freecc:*'; + require('babel-core/register'); var Rx = require('rx'), gulp = require('gulp'), @@ -6,6 +9,7 @@ var Rx = require('rx'), // utils plumber = require('gulp-plumber'), notify = require('gulp-notify'), + gutil = require('gulp-util'), reduce = require('gulp-reduce-file'), sortKeys = require('sort-keys'), debug = require('debug')('freecc:gulp'), @@ -43,6 +47,7 @@ var paths = { '!public/js/bundle*', 'node_modules/', 'client/', + 'server/manifests/*.json', 'server/rev-manifest.json' ], @@ -143,7 +148,7 @@ var syncDepenedents = [ 'js', 'less', 'dependents', - 'pack-client', + 'pack-watch', 'build-manifest' ]; @@ -189,16 +194,48 @@ gulp.task('pack-client', function() { .pipe(gulp.dest(paths.manifest)); }); -gulp.task('pack-watch', function() { - return gulp.src(webpackConfig.entry) +var defaultStatsOptions = { + 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(webpack(Object.assign( {}, webpackConfig, webpackOptions, { watch: true } - ))) - .pipe(gulp.dest(webpackConfig.output.path)) + ), null, function(notUsed, stats) { + 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', ['pack-watch'], function() { + return gulp.src(webpackConfig.output.path + '/bundle.js') .pipe(rev()) // copy files to public .pipe(gulp.dest(webpackConfig.output.path)) @@ -301,7 +338,9 @@ var watchDependents = [ 'dependents', 'serve', 'sync', - 'build-manifest' + 'build-manifest', + 'pack-watch', + 'pack-watch-manifest' ]; gulp.task('watch', watchDependents, function() { @@ -311,7 +350,15 @@ gulp.task('watch', watchDependents, function() { gulp.watch(paths.js, ['js', 'dependents']); gulp.watch(paths.dependents, ['dependents']); 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' +]); diff --git a/package.json b/package.json index 44833cc65d..30b00f8ef2 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "gulp-reduce-file": "0.0.1", "gulp-rev": "^6.0.1", "gulp-rev-replace": "^0.4.2", + "gulp-util": "^3.0.6", "gulp-webpack": "^1.5.0", "helmet": "~0.9.0", "helmet-csp": "^0.2.3", From cd4ee1083832fc2ba0d3b877ddfb1ca5ffe292f4 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 31 Aug 2015 09:53:25 -0700 Subject: [PATCH 03/17] add job subroutes --- server/boot/a-react.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/boot/a-react.js b/server/boot/a-react.js index 286a356b5b..6c0cd04819 100644 --- a/server/boot/a-react.js +++ b/server/boot/a-react.js @@ -13,7 +13,8 @@ const debug = debugFactory('freecc:react-server'); const routes = [ '/hikes', '/hikes/*', - '/jobs' + '/jobs', + '/jobs/*' ]; export default function reactSubRouter(app) { From 0f11168f40b6b07ff36492db91e0baa5083c0026 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 31 Aug 2015 09:53:40 -0700 Subject: [PATCH 04/17] fix react manifest feedback loop --- gulpfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 010e83bf65..0c8923dcd8 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -234,7 +234,7 @@ gulp.task('pack-watch', function(cb) { .pipe(gulp.dest(webpackConfig.output.path)); }); -gulp.task('pack-watch-manifest', ['pack-watch'], function() { +gulp.task('pack-watch-manifest', function() { return gulp.src(webpackConfig.output.path + '/bundle.js') .pipe(rev()) // copy files to public From 9e947fa094b8d6223a9761c55382fd6f50060a3e Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 31 Aug 2015 09:54:28 -0700 Subject: [PATCH 05/17] fix parent route for jobs shouldn't have a path --- common/app/routes/Jobs/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/app/routes/Jobs/index.js b/common/app/routes/Jobs/index.js index 1148cf265a..ac6b07f866 100644 --- a/common/app/routes/Jobs/index.js +++ b/common/app/routes/Jobs/index.js @@ -8,9 +8,8 @@ import Show from './components/Show.jsx'; */ export default { - path: 'jobs', childRoutes: [{ - path: 'jobs', + path: '/jobs', component: Jobs }, { path: 'jobs/:id', From 451c3acb2ef0e3c98c82a7315c567f55a4088cb8 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 31 Aug 2015 14:32:31 -0700 Subject: [PATCH 06/17] fetch single jobs --- common/app/routes/Jobs/components/Show.jsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/common/app/routes/Jobs/components/Show.jsx b/common/app/routes/Jobs/components/Show.jsx index 43bb22c667..99f473d14f 100644 --- a/common/app/routes/Jobs/components/Show.jsx +++ b/common/app/routes/Jobs/components/Show.jsx @@ -13,8 +13,16 @@ export default contain( { store: 'jobsStore', fetchAction: 'jobActions.getJob', + map({ currentJob }) { + return { + job: currentJob + }; + }, getPayload({ params }) { return { id: params.id }; + }, + shouldContainerFetch({ currentJob = {} }, { currentJob: nextJob = {}}) { + return currentJob.id !== nextJob.id; } }, React.createClass({ From f26bb16855432fdca22ddcf3a3e7c96ab9714c60 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Mon, 7 Sep 2015 23:52:21 -0700 Subject: [PATCH 07/17] remove container from list component --- common/app/routes/Jobs/components/List.jsx | 145 ++++++++++----------- 1 file changed, 70 insertions(+), 75 deletions(-) diff --git a/common/app/routes/Jobs/components/List.jsx b/common/app/routes/Jobs/components/List.jsx index 1688cc42e1..d3070d177f 100644 --- a/common/app/routes/Jobs/components/List.jsx +++ b/common/app/routes/Jobs/components/List.jsx @@ -1,82 +1,77 @@ import React, { PropTypes } from 'react'; -import { contain } from 'thundercats-react'; import { PanelGroup, Thumbnail, Panel, Well } from 'react-bootstrap'; import moment from 'moment'; -export default contain( - { +export default React.createClass({ + displayName: 'ListJobs', + + propTypes: { + jobs: PropTypes.array }, - React.createClass({ - displayName: 'ListJobs', - propTypes: { - jobs: PropTypes.array - }, - - renderJobs(jobs =[]) { - const thumbnailStyle = { - backgroundColor: 'white', - maxHeight: '100px', - maxWidth: '100px' - }; - return jobs.map(( - { - id, - company, - position, - description, - logo, - city, - state, - email, - phone, - postedOn - }, - index - ) => { - const header = ( -
-

{ company }

-
- { position } -
-
- ); - return ( - - - - - Position: { position } - Location: { city }, { state } -
- Contact: { email || phone || 'N/A' } -
- Posted On: { moment(postedOn).format('MMMM Do, YYYY') } -
-

{ description }

-
-
- ); - }); - }, - - render() { - const { jobs } = this.props; - return ( - - { this.renderJobs(jobs) } - + renderJobs(jobs =[]) { + const thumbnailStyle = { + backgroundColor: 'white', + maxHeight: '100px', + maxWidth: '100px' + }; + return jobs.map(( + { + id, + company, + position, + description, + logo, + city, + state, + email, + phone, + postedOn + }, + index + ) => { + const header = ( +
+

{ company }

+
+ { position } +
+
); - } - }) -); + return ( + + + + + Position: { position } + Location: { city }, { state } +
+ Contact: { email || phone || 'N/A' } +
+ Posted On: { moment(postedOn).format('MMMM Do, YYYY') } +
+

{ description }

+
+
+ ); + }); + }, + + render() { + const { jobs } = this.props; + return ( + + { this.renderJobs(jobs) } + + ); + } +}); From 4cd5c34cbc0f4b0b32e50dc10b24289170321fdb Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Tue, 8 Sep 2015 14:06:17 -0700 Subject: [PATCH 08/17] lock react-bootstrap to minor version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 30b00f8ef2..b1b4352f04 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "pmx": "^0.3.16", "ramda": "~0.10.0", "react": "^0.13.3", - "react-bootstrap": "^0.23.7", + "react-bootstrap": "~0.23.7", "react-motion": "~0.1.0", "react-router": "https://github.com/BerkeleyTrue/react-router#freecodecamp", "react-vimeo": "^0.0.3", From e3ac50a0d59c5540176478f05c6eb386032aa7bc Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Tue, 8 Sep 2015 15:03:27 -0700 Subject: [PATCH 09/17] remove old manifest file on request in dev --- server/middlewares/revision-helpers.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/middlewares/revision-helpers.js b/server/middlewares/revision-helpers.js index aa8a7bfed8..3f84fe9c90 100644 --- a/server/middlewares/revision-helpers.js +++ b/server/middlewares/revision-helpers.js @@ -1,6 +1,8 @@ import manifest from '../rev-manifest.json'; const __DEV__ = process.env.NODE_ENV === 'development'; +const manifestPath = '../rev-manifest.json'; + export default function({ globalPrepend = '' } = {}) { 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 // client code 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); return next(); } From 8112702e81627a73ef1ab9136f86fec6c47c792d Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Tue, 8 Sep 2015 15:04:15 -0700 Subject: [PATCH 10/17] link job in list to show job view --- common/app/routes/Jobs/components/List.jsx | 33 ++++++++++++---------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/common/app/routes/Jobs/components/List.jsx b/common/app/routes/Jobs/components/List.jsx index d3070d177f..a01c70b1d4 100644 --- a/common/app/routes/Jobs/components/List.jsx +++ b/common/app/routes/Jobs/components/List.jsx @@ -1,4 +1,5 @@ import React, { PropTypes } from 'react'; +import { Link } from 'react-router'; import { PanelGroup, Thumbnail, Panel, Well } from 'react-bootstrap'; import moment from 'moment'; @@ -46,21 +47,23 @@ export default React.createClass({ eventKey={ index } header={ header } key={ id }> - - - - Position: { position } - Location: { city }, { state } -
- Contact: { email || phone || 'N/A' } -
- Posted On: { moment(postedOn).format('MMMM Do, YYYY') } -
-

{ description }

-
+ + + + + Position: { position } + Location: { city }, { state } +
+ Contact: { email || phone || 'N/A' } +
+ Posted On: { moment(postedOn).format('MMMM Do, YYYY') } +
+

{ description }

+
+
); }); From 5f3b57dc11d2df3e340a97b59fbdc7d5c1bf6023 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Tue, 8 Sep 2015 16:53:57 -0700 Subject: [PATCH 11/17] add .jsx ext to nodemon nodemon will restart when jsx files in common change --- gulpfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gulpfile.js b/gulpfile.js index 0c8923dcd8..7c52a5a7ad 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -116,7 +116,7 @@ gulp.task('serve', function(cb) { var called = false; nodemon({ script: paths.server, - ext: '.js .json', + ext: '.jsx .js .json', ignore: paths.serverIgnore, exec: path.join(__dirname, 'node_modules/.bin/babel-node'), env: { From 152e8398c207abb38419a65ed101d65223c9a517 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Tue, 8 Sep 2015 17:00:04 -0700 Subject: [PATCH 12/17] bump babel --- package.json | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b1b4352f04..35bed40eb5 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,9 @@ "dependencies": { "accepts": "~1.2.5", "async": "~0.9.0", - "babel": "5.6.14", - "babel-core": "5.6.15", - "babel-eslint": "^4.0.5", + "babel": "5.8.23", + "babel-core": "5.8.23", + "babel-eslint": "4.1.1", "babel-loader": "5.2.2", "bcrypt-nodejs": "~0.0.3", "body-parser": "^1.13.2", @@ -104,15 +104,12 @@ "yui": "~3.18.1" }, "devDependencies": { - "babel-eslint": "^3.1.7", "blessed": "~0.0.37", "bower-main-files": "~0.0.4", "browser-sync": "~1.8.1", "browserify": "^10.2.4", "chai": "~1.10.0", "envify": "^3.4.0", - "eslint": "^0.21.2", - "eslint-plugin-react": "^2.3.0", "gulp": "~3.8.8", "gulp-eslint": "~0.9.0", "gulp-inject": "~1.0.2", From db80c098e5521c8a61c8f24efc92dfa2f4cb1730 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Tue, 8 Sep 2015 19:32:45 -0700 Subject: [PATCH 13/17] add es6 shim file use Object.assign --- client/es6-shims.js | 1 + client/index.js | 1 + common/app/app-stream.jsx | 3 +-- common/app/routes/Hikes/flux/Actions.js | 3 +-- 4 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 client/es6-shims.js diff --git a/client/es6-shims.js b/client/es6-shims.js new file mode 100644 index 0000000000..f20e1bcac2 --- /dev/null +++ b/client/es6-shims.js @@ -0,0 +1 @@ +require('object.assign').shim(); diff --git a/client/index.js b/client/index.js index 95be7ca9ae..79c5c541c1 100644 --- a/client/index.js +++ b/client/index.js @@ -1,3 +1,4 @@ +import unused from './es6-shims'; // eslint-disable-line import Rx from 'rx'; import React from 'react'; import Fetchr from 'fetchr'; diff --git a/common/app/app-stream.jsx b/common/app/app-stream.jsx index dcba9b7ca0..25ae2a6300 100644 --- a/common/app/app-stream.jsx +++ b/common/app/app-stream.jsx @@ -1,5 +1,4 @@ import Rx from 'rx'; -import assign from 'object.assign'; import { Router } from 'react-router'; import App from './App.jsx'; import AppCat from './Cat'; @@ -8,7 +7,7 @@ import childRoutes from './routes'; 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) { return router$(routes, location) diff --git a/common/app/routes/Hikes/flux/Actions.js b/common/app/routes/Hikes/flux/Actions.js index 94f3403590..2d9d44a4fd 100644 --- a/common/app/routes/Hikes/flux/Actions.js +++ b/common/app/routes/Hikes/flux/Actions.js @@ -1,5 +1,4 @@ import { Actions } from 'thundercats'; -import assign from 'object.assign'; import debugFactory from 'debug'; const debug = debugFactory('freecc:hikes:actions'); @@ -45,7 +44,7 @@ export default Actions({ dashedName, oldState.currentHike ); - return assign({}, oldState, { currentHike }); + return Object.assign({}, oldState, { currentHike }); } }); } From dfed1538c7cf331c5e5622e1e15b6c8a75e9f53c Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 10 Sep 2015 16:26:41 -0700 Subject: [PATCH 14/17] render individual job on request and window transition --- common/app/routes/Jobs/components/Jobs.jsx | 24 ++++++++++-- common/app/routes/Jobs/components/List.jsx | 45 ++++++++++++---------- common/app/routes/Jobs/components/Show.jsx | 17 ++++---- common/app/routes/Jobs/flux/Actions.js | 42 +++++++++++++++++++- common/app/routes/Jobs/flux/Store.js | 13 +++++-- server/services/job.js | 6 ++- 6 files changed, 109 insertions(+), 38 deletions(-) diff --git a/common/app/routes/Jobs/components/Jobs.jsx b/common/app/routes/Jobs/components/Jobs.jsx index 4b37571eb2..a6bc6a9a9f 100644 --- a/common/app/routes/Jobs/components/Jobs.jsx +++ b/common/app/routes/Jobs/components/Jobs.jsx @@ -1,23 +1,39 @@ import React, { cloneElement, PropTypes } from 'react'; import { contain } from 'thundercats-react'; +import { Navigation } from 'react-router'; import { Button, Jumbotron, Row } from 'react-bootstrap'; import ListJobs from './List.jsx'; export default contain( { store: 'jobsStore', - fetchAction: 'jobActions.getJobs' + fetchAction: 'jobActions.getJobs', + actions: 'jobActions' }, React.createClass({ displayName: 'Jobs', + propTypes: { children: PropTypes.element, + jobActions: PropTypes.object, 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 ( - + ); }, @@ -53,7 +69,7 @@ export default contain( { this.renderChild(children, jobs) || - this.renderList(jobs) } + this.renderList(this.handleJobClick, jobs) } ); diff --git a/common/app/routes/Jobs/components/List.jsx b/common/app/routes/Jobs/components/List.jsx index a01c70b1d4..ec8325a98b 100644 --- a/common/app/routes/Jobs/components/List.jsx +++ b/common/app/routes/Jobs/components/List.jsx @@ -1,5 +1,4 @@ import React, { PropTypes } from 'react'; -import { Link } from 'react-router'; import { PanelGroup, Thumbnail, Panel, Well } from 'react-bootstrap'; import moment from 'moment'; @@ -7,15 +6,17 @@ export default React.createClass({ displayName: 'ListJobs', propTypes: { + handleClick: PropTypes.func, jobs: PropTypes.array }, - renderJobs(jobs =[]) { + renderJobs(handleClick, jobs =[]) { const thumbnailStyle = { backgroundColor: 'white', maxHeight: '100px', maxWidth: '100px' }; + return jobs.map(( { id, @@ -47,33 +48,35 @@ export default React.createClass({ eventKey={ index } header={ header } key={ id }> - - - - - Position: { position } - Location: { city }, { state } -
- Contact: { email || phone || 'N/A' } -
- Posted On: { moment(postedOn).format('MMMM Do, YYYY') } -
-

{ description }

-
- + + + + Position: { position } + Location: { city }, { state } +
+ Contact: { email || phone || 'N/A' } +
+ Posted On: { moment(postedOn).format('MMMM Do, YYYY') } +
+

handleClick(id) }>{ description }

+
); }); }, render() { - const { jobs } = this.props; + const { + handleClick, + jobs + } = this.props; + return ( - { this.renderJobs(jobs) } + { this.renderJobs(handleClick, jobs) } ); } diff --git a/common/app/routes/Jobs/components/Show.jsx b/common/app/routes/Jobs/components/Show.jsx index 99f473d14f..0baedb82b3 100644 --- a/common/app/routes/Jobs/components/Show.jsx +++ b/common/app/routes/Jobs/components/Show.jsx @@ -14,15 +14,18 @@ export default contain( store: 'jobsStore', fetchAction: 'jobActions.getJob', map({ currentJob }) { + return { job: currentJob }; + }, + getPayload({ params: { id }, job = {} }) { return { - job: currentJob + id, + isPrimed: job.id === id }; }, - getPayload({ params }) { - return { id: params.id }; - }, - shouldContainerFetch({ currentJob = {} }, { currentJob: nextJob = {}}) { - return currentJob.id !== nextJob.id; + // using es6 destructuring + shouldContainerFetch({ job = {} }, { params: { id } } + ) { + return job.id !== id; } }, React.createClass({ @@ -46,7 +49,7 @@ export default contain( }, render() { - const { job } = this.props; + const { job = {} } = this.props; const { logo, position, diff --git a/common/app/routes/Jobs/flux/Actions.js b/common/app/routes/Jobs/flux/Actions.js index f74c8092a4..4df2ae42c6 100644 --- a/common/app/routes/Jobs/flux/Actions.js +++ b/common/app/routes/Jobs/flux/Actions.js @@ -5,6 +5,29 @@ const debug = debugFactory('freecc:jobs:actions'); export default Actions({ setJobs: null, + // findJob assumes that the job is already in the list of jobs + 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) { return { params }; @@ -13,13 +36,28 @@ export default Actions({ .refs({ displayName: 'JobActions' }) .init(({ instance: jobActions, args: [services] }) => { jobActions.getJobs.subscribe(() => { - services.read('job', null, null, (err, jobs) => { + services.read('jobs', null, null, (err, jobs) => { if (err) { debug('job services experienced an issue', err); - jobActions.setJobs({ jobs: [] }); + return jobActions.setError({ err }); } 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; }); diff --git a/common/app/routes/Jobs/flux/Store.js b/common/app/routes/Jobs/flux/Store.js index 803bc7b0bd..2fdfa50207 100644 --- a/common/app/routes/Jobs/flux/Store.js +++ b/common/app/routes/Jobs/flux/Store.js @@ -1,10 +1,17 @@ import { Store } from 'thundercats'; -const { setter } = Store; +const { + createRegistrar, + setter, + transformer +} = Store; export default Store() .refs({ displayName: 'JobsStore' }) .init(({ instance: jobsStore, args: [cat] }) => { - let jobActions = cat.getActions('JobActions'); - jobsStore.register(setter(jobActions.setJobs)); + const { setJobs, findJob, setError } = cat.getActions('JobActions'); + const register = createRegistrar(jobsStore); + register(setter(setJobs)); + register(transformer(findJob)); + register(setter(setError)); }); diff --git a/server/services/job.js b/server/services/job.js index 8c38a3661a..fe54d0064c 100644 --- a/server/services/job.js +++ b/server/services/job.js @@ -2,8 +2,12 @@ export default function getJobServices(app) { const { Job } = app.models; return { - name: 'job', + name: 'jobs', read: (req, resource, params, config, cb) => { + const id = params ? params.id : null; + if (id) { + return Job.findById(id, cb); + } Job.find({}, (err, jobs) => { cb(err, jobs); }); From 55065a18b99cee0fd4ddfdd71b02819959170d62 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 10 Sep 2015 16:59:27 -0700 Subject: [PATCH 15/17] add es6 Map shim to shims --- client/es6-shims.js | 1 + package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/client/es6-shims.js b/client/es6-shims.js index f20e1bcac2..43abd2ed1a 100644 --- a/client/es6-shims.js +++ b/client/es6-shims.js @@ -1 +1,2 @@ require('object.assign').shim(); +require('es6-map/implement'); diff --git a/package.json b/package.json index 35bed40eb5..9be7048cbe 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "dedent": "^0.4.0", "dotenv": "~0.4.0", "errorhandler": "~1.3.0", + "es6-map": "^0.1.1", "eslint": "^1.1.0", "eslint-plugin-react": "^3.2.1", "express": "~4.10.4", From 88d7ca1f486e5ad301bb55734072f0f9af171604 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 10 Sep 2015 19:01:12 -0700 Subject: [PATCH 16/17] add rev deletion --- gulpfile.js | 25 +++++++++++++++++++++++-- package.json | 1 + 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 7c52a5a7ad..4aeea066c6 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -29,6 +29,7 @@ var Rx = require('rx'), // rev rev = require('gulp-rev'), revReplace = require('gulp-rev-replace'), + revDel = require('rev-del'), // lint jsonlint = require('gulp-jsonlint'), @@ -72,8 +73,7 @@ var paths = { ], dependents: [ - 'client/commonFramework.js', - 'client/sandbox.js' + 'client/commonFramework.js' ], less: './client/less/main.less', @@ -191,6 +191,10 @@ gulp.task('pack-client', function() { .pipe(gulp.dest(webpackConfig.output.path)) // create and merge manifest .pipe(rev.manifest('react-manifest.json')) + .pipe(revDel({ + oldManifest: path.join(paths.manifest, 'react-manifest.json'), + dest: webpackConfig.output.path + })) .pipe(gulp.dest(paths.manifest)); }); @@ -241,6 +245,10 @@ gulp.task('pack-watch-manifest', function() { .pipe(gulp.dest(webpackConfig.output.path)) // create manifest .pipe(rev.manifest('react-manifest.json')) + .pipe(revDel({ + oldManifest: path.join(paths.manifest, 'react-manifest.json'), + dest: webpackConfig.output.path + })) .pipe(gulp.dest(paths.manifest)); }); @@ -267,6 +275,10 @@ gulp.task('less', function() { .pipe(gulp.dest(paths.css)) // create and merge manifest .pipe(rev.manifest('css-manifest.json')) + .pipe(revDel({ + oldManifest: path.join(paths.manifest, 'css-manifest.json'), + dest: paths.css + })) .pipe(gulp.dest(paths.manifest)); }); @@ -280,6 +292,10 @@ gulp.task('js', function() { .pipe(gulp.dest(paths.publicJs)) // create manifest file .pipe(rev.manifest('js-manifest.json')) + .pipe(revDel({ + oldManifest: path.join(paths.manifest, 'js-manifest.json'), + dest: paths.publicJs + })) // copy manifest file to dest .pipe(gulp.dest(paths.manifest)); }); @@ -294,9 +310,14 @@ gulp.task('dependents', ['js'], function() { return gulp.src(paths.dependents) .pipe(plumber({ errorHandler: errorHandler })) .pipe(revReplace({ manifest: manifest })) + .pipe(gulp.dest(paths.publicJs)) .pipe(rev()) .pipe(gulp.dest(paths.publicJs)) .pipe(rev.manifest('dependents-manifest.json')) + .pipe(revDel({ + oldManifest: path.join(paths.manifest, 'dependents-manifest.json'), + dest: paths.publicJs + })) .pipe(gulp.dest(paths.manifest)); }); diff --git a/package.json b/package.json index 9be7048cbe..901b54ea3a 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "react-router": "https://github.com/BerkeleyTrue/react-router#freecodecamp", "react-vimeo": "^0.0.3", "request": "~2.53.0", + "rev-del": "^1.0.5", "rx": "^2.5.3", "sanitize-html": "~1.6.1", "sort-keys": "^1.1.1", From 7741b3065cdd8711e4586f7e141c995cb3bc5b87 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Thu, 10 Sep 2015 19:34:40 -0700 Subject: [PATCH 17/17] only delete rev files are development reved files should not be deleted in production to prevent 404 from those using cached versions --- gulpfile.js | 98 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 4aeea066c6..afe9b421f5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -38,6 +38,7 @@ var Rx = require('rx'), Rx.config.longStackSupport = true; +var __DEV__ = process.env.NODE_ENV !== 'production'; var reloadDelay = 1000; var reload = sync.reload; var paths = { @@ -112,6 +113,17 @@ function errorHandler() { 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) { var called = false; nodemon({ @@ -178,6 +190,9 @@ gulp.task('lint-json', function() { gulp.task('test-challenges', ['lint-json']); gulp.task('pack-client', function() { + var manifestName = 'react-manifest.json'; + var dest = webpackConfig.output.path; + return gulp.src(webpackConfig.entry) .pipe(plumber({ errorHandler: errorHandler })) .pipe(webpack(Object.assign( @@ -185,16 +200,17 @@ gulp.task('pack-client', function() { webpackConfig, webpackOptions ))) - .pipe(gulp.dest(webpackConfig.output.path)) + .pipe(gulp.dest(dest)) .pipe(rev()) // copy files to public - .pipe(gulp.dest(webpackConfig.output.path)) - // create and merge manifest - .pipe(rev.manifest('react-manifest.json')) - .pipe(revDel({ - oldManifest: path.join(paths.manifest, 'react-manifest.json'), - dest: webpackConfig.output.path - })) + .pipe(gulp.dest(dest)) + // create manifest + .pipe(rev.manifest(manifestName)) + // delete old rev + .pipe(delRev( + dest, + manifestName + )) .pipe(gulp.dest(paths.manifest)); }); @@ -239,16 +255,18 @@ gulp.task('pack-watch', function(cb) { }); gulp.task('pack-watch-manifest', function() { - return gulp.src(webpackConfig.output.path + '/bundle.js') + var manifestName = 'react-manifest.json'; + var dest = webpackConfig.output.path; + return gulp.src(dest + '/bundle.js') .pipe(rev()) // copy files to public - .pipe(gulp.dest(webpackConfig.output.path)) + .pipe(gulp.dest(dest)) // create manifest - .pipe(rev.manifest('react-manifest.json')) - .pipe(revDel({ - oldManifest: path.join(paths.manifest, 'react-manifest.json'), - dest: webpackConfig.output.path - })) + .pipe(rev.manifest(manifestName)) + .pipe(delRev( + dest, + manifestName + )) .pipe(gulp.dest(paths.manifest)); }); @@ -262,40 +280,45 @@ gulp.task('pack-node', function() { gulp.task('pack', ['pack-client', 'pack-node']); gulp.task('less', function() { + var manifestName = 'css-manifest.json'; + var dest = paths.css; return gulp.src(paths.less) .pipe(plumber({ errorHandler: errorHandler })) // copile .pipe(less({ paths: [ path.join(__dirname, 'less', 'includes') ] })) - .pipe(gulp.dest(paths.css)) + .pipe(gulp.dest(dest)) // add revision .pipe(rev()) // copy files to public - .pipe(gulp.dest(paths.css)) + .pipe(gulp.dest(dest)) // create and merge manifest - .pipe(rev.manifest('css-manifest.json')) - .pipe(revDel({ - oldManifest: path.join(paths.manifest, 'css-manifest.json'), - dest: paths.css - })) + .pipe(rev.manifest(manifestName)) + .pipe(delRev( + dest, + manifestName + )) .pipe(gulp.dest(paths.manifest)); }); gulp.task('js', function() { + var manifestName = 'js-manifest.json'; + var dest = paths.publicJs; + return gulp.src(paths.js) .pipe(plumber({ errorHandler: errorHandler })) - .pipe(gulp.dest(paths.publicJs)) + .pipe(gulp.dest(dest)) // create registry file .pipe(rev()) // copy revisioned assets to dest - .pipe(gulp.dest(paths.publicJs)) + .pipe(gulp.dest(dest)) // create manifest file - .pipe(rev.manifest('js-manifest.json')) - .pipe(revDel({ - oldManifest: path.join(paths.manifest, 'js-manifest.json'), - dest: paths.publicJs - })) + .pipe(rev.manifest(manifestName)) + .pipe(delRev( + dest, + manifestName + )) // copy manifest file to dest .pipe(gulp.dest(paths.manifest)); }); @@ -303,6 +326,9 @@ gulp.task('js', function() { // commonFramework depend on iFrameScripts // sandbox depends on plugin gulp.task('dependents', ['js'], function() { + var manifestName = 'dependents-manifest.json'; + var dest = paths.publicJs; + var manifest = gulp.src( path.join(__dirname, paths.manifest, 'js-manifest.json') ); @@ -310,14 +336,14 @@ gulp.task('dependents', ['js'], function() { return gulp.src(paths.dependents) .pipe(plumber({ errorHandler: errorHandler })) .pipe(revReplace({ manifest: manifest })) - .pipe(gulp.dest(paths.publicJs)) + .pipe(gulp.dest(dest)) .pipe(rev()) - .pipe(gulp.dest(paths.publicJs)) - .pipe(rev.manifest('dependents-manifest.json')) - .pipe(revDel({ - oldManifest: path.join(paths.manifest, 'dependents-manifest.json'), - dest: paths.publicJs - })) + .pipe(gulp.dest(dest)) + .pipe(rev.manifest(manifestName)) + .pipe(delRev( + dest, + manifestName + )) .pipe(gulp.dest(paths.manifest)); });