render individual job on request and window transition

This commit is contained in:
Berkeley Martinez
2015-09-10 16:26:41 -07:00
parent db80c098e5
commit dfed1538c7
6 changed files with 109 additions and 38 deletions

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,5 +1,4 @@
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import { PanelGroup, Thumbnail, Panel, Well } from 'react-bootstrap'; import { PanelGroup, Thumbnail, Panel, Well } from 'react-bootstrap';
import moment from 'moment'; import moment from 'moment';
@ -7,15 +6,17 @@ export default React.createClass({
displayName: 'ListJobs', displayName: 'ListJobs',
propTypes: { propTypes: {
handleClick: PropTypes.func,
jobs: PropTypes.array jobs: PropTypes.array
}, },
renderJobs(jobs =[]) { renderJobs(handleClick, jobs =[]) {
const thumbnailStyle = { const thumbnailStyle = {
backgroundColor: 'white', backgroundColor: 'white',
maxHeight: '100px', maxHeight: '100px',
maxWidth: '100px' maxWidth: '100px'
}; };
return jobs.map(( return jobs.map((
{ {
id, id,
@ -47,7 +48,6 @@ export default React.createClass({
eventKey={ index } eventKey={ index }
header={ header } header={ header }
key={ id }> key={ id }>
<Link to={ `/jobs/${id}` }>
<Well> <Well>
<Thumbnail <Thumbnail
alt={ company + 'company logo' } alt={ company + 'company logo' }
@ -61,19 +61,22 @@ export default React.createClass({
<br /> <br />
Posted On: { moment(postedOn).format('MMMM Do, YYYY') } Posted On: { moment(postedOn).format('MMMM Do, YYYY') }
</Panel> </Panel>
<p>{ description }</p> <p onClick={ () => handleClick(id) }>{ description }</p>
</Well> </Well>
</Link>
</Panel> </Panel>
); );
}); });
}, },
render() { render() {
const { jobs } = this.props; const {
handleClick,
jobs
} = this.props;
return ( return (
<PanelGroup> <PanelGroup>
{ this.renderJobs(jobs) } { this.renderJobs(handleClick, jobs) }
</PanelGroup> </PanelGroup>
); );
} }

View File

@ -14,15 +14,18 @@ export default contain(
store: 'jobsStore', store: 'jobsStore',
fetchAction: 'jobActions.getJob', fetchAction: 'jobActions.getJob',
map({ currentJob }) { map({ currentJob }) {
return { job: currentJob };
},
getPayload({ params: { id }, job = {} }) {
return { return {
job: currentJob id,
isPrimed: job.id === id
}; };
}, },
getPayload({ params }) { // using es6 destructuring
return { id: params.id }; shouldContainerFetch({ job = {} }, { params: { id } }
}, ) {
shouldContainerFetch({ currentJob = {} }, { currentJob: nextJob = {}}) { return job.id !== id;
return currentJob.id !== nextJob.id;
} }
}, },
React.createClass({ React.createClass({
@ -46,7 +49,7 @@ export default contain(
}, },
render() { render() {
const { job } = this.props; const { job = {} } = this.props;
const { const {
logo, logo,
position, position,

View File

@ -5,6 +5,29 @@ const debug = debugFactory('freecc:jobs:actions');
export default Actions({ export default Actions({
setJobs: null, 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, getJob: null,
getJobs(params) { getJobs(params) {
return { params }; return { params };
@ -13,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

@ -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);
}); });