Update /jobs
This commit is contained in:
@ -5,7 +5,7 @@ import { Disposable, Observable } from 'rx';
|
||||
import { post$, postJSON$ } from '../utils/ajax-stream.js';
|
||||
import { AppActions, AppStore } from './flux';
|
||||
import { HikesActions } from './routes/Hikes/flux';
|
||||
import { JobActions, JobsStore} from './routes/Jobs/flux';
|
||||
import { JobActions } from './routes/Jobs/flux';
|
||||
|
||||
const ajaxStamp = stamp({
|
||||
methods: {
|
||||
@ -22,8 +22,7 @@ export default Cat().init(({ instance: cat, args: [services] }) => {
|
||||
return Observable.create(function(observer) {
|
||||
services.read(resource, params, config, (err, res) => {
|
||||
if (err) {
|
||||
observer.onError(err);
|
||||
return observer.onCompleted();
|
||||
return observer.onError(err);
|
||||
}
|
||||
|
||||
observer.onNext(res);
|
||||
@ -31,8 +30,24 @@ export default Cat().init(({ instance: cat, args: [services] }) => {
|
||||
});
|
||||
|
||||
return Disposable.create(function() {
|
||||
observer.dispose();
|
||||
});
|
||||
});
|
||||
},
|
||||
createService$(resource, params, body, config) {
|
||||
return Observable.create(function(observer) {
|
||||
services.create(resource, params, body, config, (err, res) => {
|
||||
if (err) {
|
||||
return observer.onError(err);
|
||||
}
|
||||
|
||||
observer.onNext(res);
|
||||
observer.onCompleted();
|
||||
});
|
||||
|
||||
return Disposable.create(function() {
|
||||
observer.dispose();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -40,9 +55,11 @@ export default Cat().init(({ instance: cat, args: [services] }) => {
|
||||
|
||||
cat.register(HikesActions.compose(serviceStamp, ajaxStamp), null, services);
|
||||
cat.register(AppActions.compose(serviceStamp), null, services);
|
||||
cat.register(
|
||||
JobActions.compose(serviceStamp, ajaxStamp),
|
||||
null,
|
||||
cat,
|
||||
services
|
||||
);
|
||||
cat.register(AppStore, null, cat);
|
||||
|
||||
|
||||
cat.register(JobActions, null, cat, services);
|
||||
cat.register(JobsStore.compose(serviceStamp), null, cat);
|
||||
});
|
||||
|
@ -11,6 +11,9 @@ const initValue = {
|
||||
// lecture state
|
||||
currentHike: {},
|
||||
showQuestion: false
|
||||
},
|
||||
jobsApp: {
|
||||
showModal: false
|
||||
}
|
||||
};
|
||||
|
||||
@ -19,25 +22,15 @@ export default Store({
|
||||
displayName: 'AppStore',
|
||||
value: initValue
|
||||
},
|
||||
init({ instance: appStore, args: [cat] }) {
|
||||
init({ instance: store, args: [cat] }) {
|
||||
const register = createRegistrar(store);
|
||||
// app
|
||||
const {
|
||||
updateLocation,
|
||||
getUser,
|
||||
setTitle
|
||||
} = cat.getActions('appActions');
|
||||
|
||||
const register = createRegistrar(appStore);
|
||||
const {
|
||||
toggleQuestions,
|
||||
fetchHikes,
|
||||
hideInfo,
|
||||
grabQuestion,
|
||||
releaseQuestion,
|
||||
moveQuestion,
|
||||
answer
|
||||
} = cat.getActions('hikesActions');
|
||||
|
||||
// app
|
||||
register(
|
||||
fromMany(
|
||||
setter(
|
||||
@ -51,6 +44,16 @@ export default Store({
|
||||
);
|
||||
|
||||
// hikes
|
||||
const {
|
||||
toggleQuestions,
|
||||
fetchHikes,
|
||||
hideInfo,
|
||||
grabQuestion,
|
||||
releaseQuestion,
|
||||
moveQuestion,
|
||||
answer
|
||||
} = cat.getActions('hikesActions');
|
||||
|
||||
register(
|
||||
fromMany(
|
||||
toggleQuestions,
|
||||
@ -63,6 +66,36 @@ export default Store({
|
||||
)
|
||||
);
|
||||
|
||||
return appStore;
|
||||
|
||||
// jobs
|
||||
const {
|
||||
findJob,
|
||||
saveJobToDb,
|
||||
getJob,
|
||||
getJobs,
|
||||
openModal,
|
||||
closeModal,
|
||||
handleForm,
|
||||
getSavedForm,
|
||||
setPromoCode,
|
||||
applyCode,
|
||||
clearPromo
|
||||
} = cat.getActions('JobActions');
|
||||
|
||||
register(
|
||||
fromMany(
|
||||
findJob,
|
||||
saveJobToDb,
|
||||
getJob,
|
||||
getJobs,
|
||||
openModal,
|
||||
closeModal,
|
||||
handleForm,
|
||||
getSavedForm,
|
||||
setPromoCode,
|
||||
applyCode,
|
||||
clearPromo
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -11,12 +11,12 @@ const paypalIds = {
|
||||
|
||||
export default contain(
|
||||
{
|
||||
store: 'JobsStore',
|
||||
store: 'appStore',
|
||||
actions: [
|
||||
'jobActions',
|
||||
'appActions'
|
||||
],
|
||||
map({
|
||||
map({ jobApp: {
|
||||
job: { id, isHighlighted } = {},
|
||||
buttonId = isHighlighted ?
|
||||
paypalIds.highlighted :
|
||||
@ -26,7 +26,7 @@ export default contain(
|
||||
promoCode = '',
|
||||
promoApplied = false,
|
||||
promoName
|
||||
}) {
|
||||
}}) {
|
||||
return {
|
||||
id,
|
||||
isHighlighted,
|
||||
@ -57,7 +57,7 @@ export default contain(
|
||||
|
||||
goToJobBoard() {
|
||||
const { appActions } = this.props;
|
||||
appActions.updateRoute('/jobs');
|
||||
appActions.goTo('/jobs');
|
||||
},
|
||||
|
||||
renderDiscount(discountAmount) {
|
||||
|
@ -6,7 +6,10 @@ import ListJobs from './List.jsx';
|
||||
|
||||
export default contain(
|
||||
{
|
||||
store: 'jobsStore',
|
||||
store: 'appStore',
|
||||
map({ jobsApp: { jobs, showModal }}) {
|
||||
return { jobs, showModal };
|
||||
},
|
||||
fetchAction: 'jobActions.getJobs',
|
||||
actions: [
|
||||
'appActions',
|
||||
@ -18,25 +21,19 @@ export default contain(
|
||||
|
||||
propTypes: {
|
||||
children: PropTypes.element,
|
||||
numOfFollowers: PropTypes.number,
|
||||
appActions: PropTypes.object,
|
||||
jobActions: PropTypes.object,
|
||||
jobs: PropTypes.array,
|
||||
showModal: PropTypes.bool
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
const { jobActions } = this.props;
|
||||
jobActions.getFollowers();
|
||||
},
|
||||
|
||||
handleJobClick(id) {
|
||||
const { appActions, jobActions } = this.props;
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
jobActions.findJob(id);
|
||||
appActions.updateRoute(`/jobs/${id}`);
|
||||
appActions.goTo(`/jobs/${id}`);
|
||||
},
|
||||
|
||||
renderList(handleJobClick, jobs) {
|
||||
@ -84,7 +81,7 @@ export default contain(
|
||||
<Button
|
||||
className='signup-btn btn-block btn-cta'
|
||||
onClick={ ()=> {
|
||||
appActions.updateRoute('/jobs/new');
|
||||
appActions.goTo('/jobs/new');
|
||||
}}>
|
||||
Post a job: $1,000
|
||||
</Button>
|
||||
|
@ -103,9 +103,9 @@ function makeRequired(validator) {
|
||||
}
|
||||
|
||||
export default contain({
|
||||
store: 'appStore',
|
||||
actions: 'jobActions',
|
||||
store: 'jobsStore',
|
||||
map({ form = {} }) {
|
||||
map({ jobsApp: { form = {} } }) {
|
||||
const {
|
||||
position,
|
||||
locale,
|
||||
|
@ -8,12 +8,12 @@ import JobNotFound from './JobNotFound.jsx';
|
||||
|
||||
export default contain(
|
||||
{
|
||||
store: 'JobsStore',
|
||||
store: 'appStore',
|
||||
actions: [
|
||||
'appActions',
|
||||
'jobActions'
|
||||
],
|
||||
map({ form: job = {} }) {
|
||||
map({ jobApp: { form: job = {} } }) {
|
||||
return { job };
|
||||
}
|
||||
},
|
||||
@ -32,7 +32,7 @@ export default contain(
|
||||
const { appActions, job } = this.props;
|
||||
// redirect user in client
|
||||
if (!job || !job.position || !job.description) {
|
||||
appActions.updateRoute('/jobs/new');
|
||||
appActions.goTo('/jobs/new');
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -53,13 +53,14 @@ function generateMessage(
|
||||
|
||||
export default contain(
|
||||
{
|
||||
stores: ['appStore', 'jobsStore'],
|
||||
fetchWaitFor: 'jobsStore',
|
||||
store: 'appStore',
|
||||
fetchAction: 'jobActions.getJob',
|
||||
combineLatest(
|
||||
{ username, isFrontEndCert, isFullStackCert },
|
||||
{ currentJob }
|
||||
) {
|
||||
map({
|
||||
username,
|
||||
isFrontEndCert,
|
||||
isFullStackCert,
|
||||
jobsApp: { currentJob }
|
||||
}) {
|
||||
return {
|
||||
username,
|
||||
job: currentJob,
|
||||
@ -67,11 +68,11 @@ export default contain(
|
||||
isFullStackCert
|
||||
};
|
||||
},
|
||||
getPayload({ params: { id }, job = {} }) {
|
||||
return {
|
||||
id,
|
||||
isPrimed: job.id === id
|
||||
};
|
||||
getPayload({ params: { id } }) {
|
||||
return id;
|
||||
},
|
||||
isPrimed({ params: { id } = {}, job = {} }) {
|
||||
return job.id === id;
|
||||
},
|
||||
// using es6 destructuring
|
||||
shouldContainerFetch({ job = {} }, { params: { id } }
|
||||
|
@ -1,51 +1,100 @@
|
||||
import { Actions } from 'thundercats';
|
||||
import store from 'store';
|
||||
import debugFactory from 'debug';
|
||||
import { jsonp$ } from '../../../../utils/jsonp$';
|
||||
import { postJSON$ } from '../../../../utils/ajax-stream';
|
||||
import { nameSpacedTransformer } from '../../../../utils';
|
||||
|
||||
const debug = debugFactory('freecc:jobs:actions');
|
||||
const assign = Object.assign;
|
||||
const jobsTranformer = nameSpacedTransformer('jobsApp');
|
||||
const noOper = { transform: () => {} };
|
||||
|
||||
export default Actions({
|
||||
setJobs: null,
|
||||
refs: { displayName: 'JobActions' },
|
||||
shouldBindMethods: true,
|
||||
// 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 {
|
||||
transform: jobsTranformer(oldState => {
|
||||
const { currentJob = {}, jobs = [] } = oldState;
|
||||
// currentJob already set
|
||||
// do nothing
|
||||
if (currentJob.id === id) {
|
||||
return null;
|
||||
}
|
||||
return newJob;
|
||||
}, 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 ?
|
||||
assign({}, oldState, { currentJob: foundJob }) :
|
||||
null;
|
||||
// if no job found this will be null which is a op noop
|
||||
return foundJob ?
|
||||
assign({}, oldState, { currentJob: foundJob }) :
|
||||
null;
|
||||
})
|
||||
};
|
||||
},
|
||||
setError: null,
|
||||
getJob: null,
|
||||
saveJobToDb: null,
|
||||
getJobs(params) {
|
||||
return { params };
|
||||
saveJobToDb({ goTo, job }) {
|
||||
return this.createService$('jobs', { job })
|
||||
.map(job => ({
|
||||
transform(state) {
|
||||
state.location = {
|
||||
action: 'PUSH',
|
||||
pathname: goTo
|
||||
};
|
||||
return {
|
||||
...state,
|
||||
jobs: {
|
||||
...state.jobs,
|
||||
currentJob: job
|
||||
}
|
||||
};
|
||||
}
|
||||
}))
|
||||
.catch(err => ({
|
||||
transform(state) {
|
||||
return { ...state, err };
|
||||
}
|
||||
}));
|
||||
},
|
||||
getJob(id) {
|
||||
return this.readService$('jobs', { id })
|
||||
.map(job => ({
|
||||
transform: jobsTranformer(state => {
|
||||
return { ...state, currentJob: job };
|
||||
})
|
||||
}))
|
||||
.catch(err => ({
|
||||
transform(state) {
|
||||
return { ...state, err };
|
||||
}
|
||||
}));
|
||||
},
|
||||
getJobs() {
|
||||
return this.readService$('jobs')
|
||||
.map(jobs => ({
|
||||
transform: jobsTranformer(state => {
|
||||
return { ...state, jobs };
|
||||
})
|
||||
}))
|
||||
.catch(err => ({
|
||||
transform(state) {
|
||||
return { state, err };
|
||||
}
|
||||
}));
|
||||
},
|
||||
openModal() {
|
||||
return { showModal: true };
|
||||
return {
|
||||
transform: jobsTranformer(state => ({ ...state, showModal: true }))
|
||||
};
|
||||
},
|
||||
closeModal() {
|
||||
return { showModal: false };
|
||||
return {
|
||||
transform: jobsTranformer(state => ({ ...state, showModal: false }))
|
||||
};
|
||||
},
|
||||
handleForm(value) {
|
||||
return {
|
||||
transform(oldState) {
|
||||
transform: jobsTranformer(oldState => {
|
||||
const { form } = oldState;
|
||||
const newState = assign({}, oldState);
|
||||
newState.form = assign(
|
||||
@ -54,142 +103,92 @@ export default Actions({
|
||||
value
|
||||
);
|
||||
return newState;
|
||||
}
|
||||
})
|
||||
};
|
||||
},
|
||||
saveForm: null,
|
||||
getSavedForm: null,
|
||||
clearSavedForm: null,
|
||||
setForm(form) {
|
||||
return { form };
|
||||
},
|
||||
getFollowers: null,
|
||||
setFollowersCount(numOfFollowers) {
|
||||
return { numOfFollowers };
|
||||
getSavedForm() {
|
||||
const form = store.get('newJob');
|
||||
if (form && !Array.isArray(form) && typeof form === 'object') {
|
||||
return {
|
||||
transform: jobsTranformer(state => {
|
||||
return { ...state, form };
|
||||
})
|
||||
};
|
||||
}
|
||||
return noOper;
|
||||
},
|
||||
setPromoCode({ target: { value = '' }} = {}) {
|
||||
return { promoCode: value.replace(/[^\d\w\s]/, '') };
|
||||
return {
|
||||
transform: jobsTranformer(state => ({
|
||||
...state,
|
||||
promoCode: value.replace(/[^\d\w\s]/, '')
|
||||
}))
|
||||
};
|
||||
},
|
||||
applyCode({ id, code = '', type = null}) {
|
||||
const body = {
|
||||
id,
|
||||
code: code.replace(/[^\d\w\s]/, '')
|
||||
};
|
||||
if (type) {
|
||||
body.type = type;
|
||||
}
|
||||
return this.postJSON$('/api/promos/getButton', body)
|
||||
.pluck('response')
|
||||
.map(({ promo }) => {
|
||||
if (!promo || !promo.buttonId) {
|
||||
return noOper;
|
||||
}
|
||||
const {
|
||||
fullPrice: price,
|
||||
buttonId,
|
||||
discountAmount,
|
||||
code: promoCode,
|
||||
name: promoName
|
||||
} = promo;
|
||||
|
||||
return {
|
||||
transform: jobsTranformer(state => ({
|
||||
...state,
|
||||
price,
|
||||
buttonId,
|
||||
discountAmount,
|
||||
promoCode,
|
||||
promoApplied: true,
|
||||
promoName
|
||||
}))
|
||||
};
|
||||
})
|
||||
.catch(err => ({
|
||||
transform(state) {
|
||||
return { ...state, err };
|
||||
}
|
||||
}));
|
||||
},
|
||||
applyCode: null,
|
||||
clearPromo(foo, undef) {
|
||||
return {
|
||||
price: undef,
|
||||
buttonId: undef,
|
||||
discountAmount: undef,
|
||||
promoCode: undef,
|
||||
promoApplied: false,
|
||||
promoName: undef
|
||||
transform: jobsTranformer(state => ({
|
||||
...state,
|
||||
price: undef,
|
||||
buttonId: undef,
|
||||
discountAmount: undef,
|
||||
promoCode: undef,
|
||||
promoApplied: false,
|
||||
promoName: undef
|
||||
}))
|
||||
};
|
||||
},
|
||||
applyPromo({
|
||||
fullPrice: price,
|
||||
buttonId,
|
||||
discountAmount,
|
||||
code: promoCode,
|
||||
name: promoName
|
||||
} = {}) {
|
||||
return {
|
||||
price,
|
||||
buttonId,
|
||||
discountAmount,
|
||||
promoCode,
|
||||
promoApplied: true,
|
||||
promoName
|
||||
};
|
||||
}
|
||||
})
|
||||
.refs({ displayName: 'JobActions' })
|
||||
.init(({ instance: jobActions, args: [cat, services] }) => {
|
||||
jobActions.getJobs.subscribe(() => {
|
||||
services.read('jobs', null, null, (err, jobs) => {
|
||||
if (err) {
|
||||
debug('job services experienced an issue', err);
|
||||
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 });
|
||||
}
|
||||
if (job) {
|
||||
jobActions.setJobs({ currentJob: job });
|
||||
}
|
||||
jobActions.setJobs({});
|
||||
});
|
||||
});
|
||||
|
||||
init({ instance: jobActions }) {
|
||||
jobActions.saveForm.subscribe((form) => {
|
||||
store.set('newJob', form);
|
||||
});
|
||||
|
||||
jobActions.getSavedForm.subscribe(() => {
|
||||
const job = store.get('newJob');
|
||||
if (job && !Array.isArray(job) && typeof job === 'object') {
|
||||
jobActions.setForm(job);
|
||||
}
|
||||
});
|
||||
|
||||
jobActions.clearSavedForm.subscribe(() => {
|
||||
store.remove('newJob');
|
||||
});
|
||||
|
||||
jobActions.saveJobToDb.subscribe(({ goTo, job }) => {
|
||||
const appActions = cat.getActions('appActions');
|
||||
services.create('jobs', { job }, null, (err, job) => {
|
||||
if (err) {
|
||||
debug('job services experienced an issue', err);
|
||||
return jobActions.setError(err);
|
||||
}
|
||||
jobActions.setJobs({ job });
|
||||
appActions.updateRoute(goTo);
|
||||
});
|
||||
});
|
||||
|
||||
jobActions.getFollowers.subscribe(() => {
|
||||
const url = 'https://cdn.syndication.twimg.com/widgets/followbutton/' +
|
||||
'info.json?lang=en&screen_names=CamperJobs' +
|
||||
'&callback=JSONPCallback';
|
||||
|
||||
jsonp$(url)
|
||||
.map(({ response }) => {
|
||||
return response[0]['followers_count'];
|
||||
})
|
||||
.subscribe(
|
||||
count => jobActions.setFollowersCount(count),
|
||||
err => jobActions.setError(err)
|
||||
);
|
||||
});
|
||||
|
||||
jobActions.applyCode.subscribe(({ id, code = '', type = null}) => {
|
||||
const body = {
|
||||
id,
|
||||
code: code.replace(/[^\d\w\s]/, '')
|
||||
};
|
||||
if (type) {
|
||||
body.type = type;
|
||||
}
|
||||
postJSON$('/api/promos/getButton', body)
|
||||
.pluck('response')
|
||||
.subscribe(
|
||||
({ promo }) => {
|
||||
if (promo && promo.buttonId) {
|
||||
jobActions.applyPromo(promo);
|
||||
}
|
||||
jobActions.setError(new Error('no promo found'));
|
||||
},
|
||||
jobActions.setError
|
||||
);
|
||||
});
|
||||
|
||||
return jobActions;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -1,42 +0,0 @@
|
||||
import { Store } from 'thundercats';
|
||||
|
||||
const {
|
||||
createRegistrar,
|
||||
setter,
|
||||
transformer
|
||||
} = Store;
|
||||
|
||||
export default Store({
|
||||
refs: {
|
||||
displayName: 'JobsStore',
|
||||
value: { showModal: false }
|
||||
},
|
||||
init({ instance: jobsStore, args: [cat] }) {
|
||||
const {
|
||||
setJobs,
|
||||
findJob,
|
||||
setError,
|
||||
openModal,
|
||||
closeModal,
|
||||
handleForm,
|
||||
setForm,
|
||||
setFollowersCount,
|
||||
setPromoCode,
|
||||
applyPromo,
|
||||
clearPromo
|
||||
} = cat.getActions('JobActions');
|
||||
const register = createRegistrar(jobsStore);
|
||||
register(setter(setJobs));
|
||||
register(setter(setError));
|
||||
register(setter(openModal));
|
||||
register(setter(closeModal));
|
||||
register(setter(setForm));
|
||||
register(setter(setPromoCode));
|
||||
register(setter(applyPromo));
|
||||
register(setter(clearPromo));
|
||||
register(setter(setFollowersCount));
|
||||
|
||||
register(transformer(findJob));
|
||||
register(handleForm);
|
||||
}
|
||||
});
|
@ -1,2 +1 @@
|
||||
export { default as JobActions } from './Actions';
|
||||
export { default as JobsStore } from './Store';
|
||||
|
13
common/utils/index.js
Normal file
13
common/utils/index.js
Normal file
@ -0,0 +1,13 @@
|
||||
export function nameSpacedTransformer(ns, transformer) {
|
||||
if (!transformer) {
|
||||
return nameSpacedTransformer.bind(null, ns);
|
||||
}
|
||||
return (state) => {
|
||||
const newState = transformer(state[ns]);
|
||||
// nothing has changed
|
||||
if (newState === state[ns]) {
|
||||
return state;
|
||||
}
|
||||
return { ...state, [ns]: newState };
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user