diff --git a/spikes/stacks/redux-thunk/.eslintignore b/spikes/stacks/redux-thunk/.eslintignore new file mode 100644 index 00000000..683e721c --- /dev/null +++ b/spikes/stacks/redux-thunk/.eslintignore @@ -0,0 +1,3 @@ +/node_modules +coverage +.nyc_output diff --git a/spikes/stacks/redux-thunk/.gitignore b/spikes/stacks/redux-thunk/.gitignore index 52bbeb64..c21b0ba0 100644 --- a/spikes/stacks/redux-thunk/.gitignore +++ b/spikes/stacks/redux-thunk/.gitignore @@ -1,4 +1,4 @@ /node_modules coverage .nyc_output - +npm-debug.log diff --git a/spikes/stacks/redux-thunk/package.json b/spikes/stacks/redux-thunk/package.json index 27e8365f..0e1452dd 100644 --- a/spikes/stacks/redux-thunk/package.json +++ b/spikes/stacks/redux-thunk/package.json @@ -31,6 +31,7 @@ "graphql-fetch": "^1.0.0", "json-loader": "^0.5.4", "lodash.find": "^4.6.0", + "lodash.get": "^4.4.2", "lodash.values": "^4.3.0", "node-uuid": "^1.4.7", "react": "^15.3.2", @@ -46,6 +47,7 @@ "redux-logger": "^2.7.0", "redux-promise-middleware": "^4.1.0", "redux-thunk": "^2.1.0", + "reselect": "^2.5.4", "webpack": "^1.13.2", "webpack-dev-server": "^1.16.1" }, diff --git a/spikes/stacks/redux-thunk/src/client/actions.js b/spikes/stacks/redux-thunk/src/client/actions.js deleted file mode 100644 index b870d56a..00000000 --- a/spikes/stacks/redux-thunk/src/client/actions.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - ...require('./reducers/app').actions, - ...require('./reducers/printers').actions, - ...require('./reducers/changes').actions -}; diff --git a/spikes/stacks/redux-thunk/src/client/components/changes.js b/spikes/stacks/redux-thunk/src/client/components/changes.js index 216cfe4d..31bd1fa1 100644 --- a/spikes/stacks/redux-thunk/src/client/components/changes.js +++ b/spikes/stacks/redux-thunk/src/client/components/changes.js @@ -7,15 +7,8 @@ const { module.exports = ({ changes = [], - pathname, - onClick + pathname }) => { - const _onClick = (id) => { - return () => { - onClick(id); - }; - }; - const lis = changes.map(({ price, currency, diff --git a/spikes/stacks/redux-thunk/src/client/components/loader.js b/spikes/stacks/redux-thunk/src/client/components/loader.js index 0bc9c9c7..f9ec9b3f 100644 --- a/spikes/stacks/redux-thunk/src/client/components/loader.js +++ b/spikes/stacks/redux-thunk/src/client/components/loader.js @@ -30,7 +30,6 @@ module.exports = React.createClass({ }, render: function() { const { - fetch, loading, loaded, render, @@ -38,7 +37,7 @@ module.exports = React.createClass({ } = this.props; const _loaded = !loading && !loaded; - const component = _loaded ? (children ? children : render()) : null; + const component = _loaded ? (children || render()) : null; return ( { }; }; -module.exports = connect(mapStateToProps, mapDispatchToProps, )(Print); +module.exports = connect(mapStateToProps, mapDispatchToProps)(Print); diff --git a/spikes/stacks/redux-thunk/src/client/index.js b/spikes/stacks/redux-thunk/src/client/index.js index 6ebb1c56..10c5a842 100644 --- a/spikes/stacks/redux-thunk/src/client/index.js +++ b/spikes/stacks/redux-thunk/src/client/index.js @@ -1,4 +1,4 @@ -const worker = require('./worker'); // singleton +require('./worker'); // singleton const React = require('react'); const ReactDOM = require('react-dom'); @@ -16,4 +16,4 @@ render(); if (module.hot) { module.hot.accept('./root', render); -} \ No newline at end of file +} diff --git a/spikes/stacks/redux-thunk/src/client/reducers/app.js b/spikes/stacks/redux-thunk/src/client/reducers/app.js deleted file mode 100644 index afedb378..00000000 --- a/spikes/stacks/redux-thunk/src/client/reducers/app.js +++ /dev/null @@ -1,30 +0,0 @@ -const ReduxActions = require('redux-actions'); -const app = require('../../../package.json').name; - -const { - createAction, - handleActions -} = ReduxActions; - -const UPDATE_ROUTER = `${app}/changes/UPDATE_ROUTER`; - -exports.ui = handleActions({ - [UPDATE_ROUTER]: (state, action) => { - return { - ...state, - router: action.payload - }; - } -}, {}); - -const actions = exports.actions = { - updateRouter: (router) => { - return { - type: UPDATE_ROUTER, - payload: router - }; - }, - transitionTo: (pathname) => (dispatch, getState) => { - return getState().ui.app.router.transitionTo(pathname); - } -}; diff --git a/spikes/stacks/redux-thunk/src/client/reducers/printers.js b/spikes/stacks/redux-thunk/src/client/reducers/printers.js deleted file mode 100644 index 4c2b2e68..00000000 --- a/spikes/stacks/redux-thunk/src/client/reducers/printers.js +++ /dev/null @@ -1,206 +0,0 @@ -const ReduxActions = require('redux-actions'); -const app = require('../../../package.json').name; -const find = require('lodash.find'); -const changes = require('./changes'); - -const { - createAction, - handleActions -} = ReduxActions; - -const { - actions: { - removeChange - } -} = changes; - -const UPDATE_PRINTERS = `${app}/printers/UPDATE_PRINTERS`; -const UPDATE_WORKER_ID = `${app}/printers/UPDATE_WORKER_ID`; -const LOCK_PRINTER = `${app}/printers/LOCK_PRINTER`; -const PRINT = `${app}/printers/PRINT`; - -exports.data = handleActions({ - [UPDATE_PRINTERS]: (state, action) => { - return action.payload; - } -}, []); - -exports.ui = handleActions({ - [UPDATE_WORKER_ID]: (state, action) => { - return { - ...state, - id: action.payload - }; - }, - [UPDATE_PRINTERS]: (state, action) => { - const locked = (find(action.payload, (printer) => { - return ( - printer.lock && - printer.lock === state.id - ); - }) || {}).id || ''; - - return { - ...state, - locked - }; - } -}, { - id: '', - locked: '' -}); - -// confirm should be an async op, -// let's mock it that way -const confirm = (msg) => { - return new Promise((resolve, reject) => { - resolve(window.confirm(msg)); - }); -}; - -// prompt should be an async op, -// let's mock it that way -const prompt = (msg) => { - return new Promise((resolve, reject) => { - resolve(window.prompt(msg)); - }); -}; - -// alert should be an async op, -// let's mock it that way -const alert = (msg) => { - return new Promise((resolve, reject) => { - resolve(window.alert(msg)); - }); -}; - -const actions = exports.actions = { - updatePrinters: createAction(UPDATE_PRINTERS), - updateWorkerId: createAction(UPDATE_WORKER_ID), - lockPrinter: (id) => (dispatch, getState) => { - const { - ui, - data - } = getState(); - - const { - printers - } = data; - - const { - printers: { - locked - } - } = ui; - - if (locked === id) { - return; - } - - const printer = find(printers, ['id', id]); - - if (!printer) { - return window.alert(`Printer ${id} not found`); - } - - const worker = require('../worker'); - - const lock = () => { - return dispatch({ - type: LOCK_PRINTER, - payload: worker.dispatch({ - type: 'LOCK_PRINTER', - payload: id - }) - }); - }; - - const askToLock = () => { - const msg = `Do you want to lock printer ${id}?`; - return confirm(msg).then((yes) => { - return yes ? lock(id) : null; - }); - }; - - const askToOverride = () => { - const msg = `Printer ${id} already locked! Do you want to override?`; - return confirm(msg).then((yes) => { - return yes ? lock(id) : null; - }); - }; - - return printer.lock ? askToOverride() : askToLock(); - }, - print: (changeId) => (dispatch, getState) => { - const { - ui, - data - } = getState(); - - const { - printers - } = data; - - const { - printers: { - locked - } - } = ui; - - const worker = require('../worker'); - - const print = () => { - return dispatch({ - type: PRINT, - payload: worker.dispatch({ - type: 'PRINT', - payload: changeId - }) - }).then(() => { - return dispatch(removeChange(changeId)); - }); - }; - - const lock = (printerId) => { - return dispatch({ - type: LOCK_PRINTER, - payload: worker.dispatch({ - type: 'LOCK_PRINTER', - payload: printerId - }) - }).then(print); - }; - - const askToOverride = (printerId) => { - const msg = `Printer ${printerId} already locked! Do you want to override?`; - return confirm(msg).then((yes) => { - return yes ? lock(printerId) : null; - }); - }; - - const askToLock = () => { - const msg = `Please select a printer to lock: ${ - printers.map(({ - id, - name - }) => { - return `\n(${id}) ${name}`; - }) - }`; - - return prompt(msg).then((printerId) => { - const printer = find(printers, ['id', printerId]); - - if (!printer) { - return alert(`Printer ${printerId} not found. Try again`).then(() => { - return actions.print(printerId)(dispatch, getState); - }); - } - - return printer.lock ? askToOverride(printerId) : lock(printerId); - }); - }; - - return !locked ? askToLock() : print(); - } -}; diff --git a/spikes/stacks/redux-thunk/src/client/root.js b/spikes/stacks/redux-thunk/src/client/root.js index 9f77b3d9..4ecda442 100644 --- a/spikes/stacks/redux-thunk/src/client/root.js +++ b/spikes/stacks/redux-thunk/src/client/root.js @@ -5,15 +5,14 @@ const ReactRedux = require('react-redux'); const ReactIntl = require('react-intl'); const App = require('./containers/app'); - -const store = require('./store'); +const store = require('./state/store'); const { AppContainer } = ReactHotLoader; const { - BrowserRouter, + BrowserRouter } = ReactRouter; const { diff --git a/spikes/stacks/redux-thunk/src/client/state/actions/app.js b/spikes/stacks/redux-thunk/src/client/state/actions/app.js new file mode 100644 index 00000000..65b4371e --- /dev/null +++ b/spikes/stacks/redux-thunk/src/client/state/actions/app.js @@ -0,0 +1,25 @@ +const ReduxActions = require('redux-actions'); +const app = require('../../../../package.json').name; +const selectors = require('../selectors'); + +const { + router +} = selectors; + +const { + createAction +} = ReduxActions; + +const UPDATE_ROUTER = `${app}/changes/UPDATE_ROUTER`; + +const updateRouter = createAction(UPDATE_ROUTER); + +const transitionTo = (pathname) => (dispatch, getState) => { + return router(getState()).transitionTo(pathname); +}; + +module.exports = { + UPDATE_ROUTER, + updateRouter, + transitionTo +}; diff --git a/spikes/stacks/redux-thunk/src/client/state/actions/changes.js b/spikes/stacks/redux-thunk/src/client/state/actions/changes.js new file mode 100644 index 00000000..389e4249 --- /dev/null +++ b/spikes/stacks/redux-thunk/src/client/state/actions/changes.js @@ -0,0 +1,28 @@ +const app = require('../../../../package.json').name; +const api = require('../../api'); + +const FETCH_CHANGES = `${app}/changes/FETCH_CHANGES`; +const REMOVE_CHANGE = `${app}/changes/REMOVE_CHANGE`; + +const fetchChanges = () => { + return { + type: FETCH_CHANGES, + payload: api.fetchChanges() + }; +}; + +const removeChange = (id) => (dispatch) => { + return dispatch({ + type: REMOVE_CHANGE, + payload: api.removeChange(id) + }).then(() => { + return dispatch(fetchChanges()); + }); +}; + +module.exports = { + FETCH_CHANGES, + REMOVE_CHANGE, + fetchChanges, + removeChange +}; diff --git a/spikes/stacks/redux-thunk/src/client/state/actions/index.js b/spikes/stacks/redux-thunk/src/client/state/actions/index.js new file mode 100644 index 00000000..997b6edd --- /dev/null +++ b/spikes/stacks/redux-thunk/src/client/state/actions/index.js @@ -0,0 +1,5 @@ +module.exports = { + ...require('./app'), + ...require('./printers'), + ...require('./changes') +}; diff --git a/spikes/stacks/redux-thunk/src/client/state/actions/printers.js b/spikes/stacks/redux-thunk/src/client/state/actions/printers.js new file mode 100644 index 00000000..b4bbaab9 --- /dev/null +++ b/spikes/stacks/redux-thunk/src/client/state/actions/printers.js @@ -0,0 +1,183 @@ +const ReduxActions = require('redux-actions'); +const app = require('../../../../package.json').name; +const find = require('lodash.find'); +const changes = require('./changes'); + +const { + createAction +} = ReduxActions; + +const { + removeChange +} = changes; + +const UPDATE_PRINTERS = `${app}/printers/UPDATE_PRINTERS`; +const UPDATE_WORKER_ID = `${app}/printers/UPDATE_WORKER_ID`; +const LOCK_PRINTER = `${app}/printers/LOCK_PRINTER`; +const PRINT = `${app}/printers/PRINT`; + +// confirm should be an async op, +// let's mock it that way +const confirm = (msg) => { + return new Promise((resolve, reject) => { + resolve(window.confirm(msg)); + }); +}; + +// prompt should be an async op, +// let's mock it that way +const prompt = (msg) => { + return new Promise((resolve, reject) => { + resolve(window.prompt(msg)); + }); +}; + +// alert should be an async op, +// let's mock it that way +const alert = (msg) => { + return new Promise((resolve, reject) => { + resolve(window.alert(msg)); + }); +}; + +const updatePrinters = createAction(UPDATE_PRINTERS); +const updateWorkerId = createAction(UPDATE_WORKER_ID); + +const lockPrinter = (id) => (dispatch, getState) => { + const { + ui, + data + } = getState(); + + const { + printers + } = data; + + const { + printers: { + locked + } + } = ui; + + if (locked === id) { + return; + } + + const printer = find(printers, ['id', id]); + + if (!printer) { + return window.alert(`Printer ${id} not found`); + } + + const worker = require('../../worker'); + + const lock = () => { + return dispatch({ + type: LOCK_PRINTER, + payload: worker.dispatch({ + type: 'LOCK_PRINTER', + payload: id + }) + }); + }; + + const askToLock = () => { + const msg = `Do you want to lock printer ${id}?`; + return confirm(msg).then((yes) => { + return yes ? lock(id) : null; + }); + }; + + const askToOverride = () => { + const msg = `Printer ${id} already locked! Do you want to override?`; + return confirm(msg).then((yes) => { + return yes ? lock(id) : null; + }); + }; + + return printer.lock ? askToOverride() : askToLock(); +}; + +const print = (changeId) => (dispatch, getState) => { + const { + ui, + data + } = getState(); + + const { + printers + } = data; + + const { + printers: { + locked + } + } = ui; + + const worker = require('../../worker'); + + const _print = () => { + return dispatch({ + type: PRINT, + payload: worker.dispatch({ + type: 'PRINT', + payload: changeId + }) + }).then(() => { + return dispatch(removeChange(changeId)); + }); + }; + + const lock = (printerId) => { + return dispatch({ + type: LOCK_PRINTER, + payload: worker.dispatch({ + type: 'LOCK_PRINTER', + payload: printerId + }) + }).then(_print); + }; + + const askToOverride = (printerId) => { + const msg = `Printer ${printerId} already locked! Do you want to override?`; + return confirm(msg).then((yes) => { + return yes ? lock(printerId) : null; + }); + }; + + const askToLock = () => { + const msg = `Please select a printer to lock: ${ + printers.map(({ + id, + name + }) => { + return `\n(${id}) ${name}`; + }) + }`; + + return prompt(msg).then((printerId) => { + const printer = find(printers, ['id', printerId]); + + if (!printer) { + return alert(`Printer ${printerId} not found. Try again`).then(() => { + return print(printerId)(dispatch, getState); + }); + } + + return printer.lock ? askToOverride(printerId) : lock(printerId); + }); + }; + + return !locked ? askToLock() : _print(); +}; + +module.exports = { + UPDATE_PRINTERS, + UPDATE_WORKER_ID, + LOCK_PRINTER, + PRINT, + updatePrinters, + updateWorkerId, + lockPrinter, + print +}; diff --git a/spikes/stacks/redux-thunk/src/client/state/reducers/app.js b/spikes/stacks/redux-thunk/src/client/state/reducers/app.js new file mode 100644 index 00000000..fc47f8e8 --- /dev/null +++ b/spikes/stacks/redux-thunk/src/client/state/reducers/app.js @@ -0,0 +1,19 @@ +const ReduxActions = require('redux-actions'); +const actions = require('../actions'); + +const { + handleActions +} = ReduxActions; + +const { + UPDATE_ROUTER +} = actions; + +exports.ui = handleActions({ + [UPDATE_ROUTER]: (state, action) => { + return { + ...state, + router: action.payload + }; + } +}, {}); diff --git a/spikes/stacks/redux-thunk/src/client/reducers/changes.js b/spikes/stacks/redux-thunk/src/client/state/reducers/changes.js similarity index 54% rename from spikes/stacks/redux-thunk/src/client/reducers/changes.js rename to spikes/stacks/redux-thunk/src/client/state/reducers/changes.js index 4e300f29..7f5155c6 100644 --- a/spikes/stacks/redux-thunk/src/client/reducers/changes.js +++ b/spikes/stacks/redux-thunk/src/client/state/reducers/changes.js @@ -1,19 +1,13 @@ const ReduxActions = require('redux-actions'); -const app = require('../../../package.json').name; -const api = require('../api'); +const actions = require('../actions'); const { - createAction, handleActions } = ReduxActions; const { - fetchChanges, - removeChange -} = api; - -const FETCH_CHANGES = `${app}/changes/FETCH_CHANGES`; -const REMOVE_CHANGE = `${app}/changes/REMOVE_CHANGE`; + FETCH_CHANGES +} = actions; exports.data = handleActions({ [`${FETCH_CHANGES}_FULFILLED`]: (state, action) => { @@ -47,20 +41,3 @@ exports.ui = handleActions({ loading: false, loaded: false }); - -const actions = exports.actions = { - fetchChanges: () => { - return { - type: FETCH_CHANGES, - payload: fetchChanges() - }; - }, - removeChange: (id) => (dispatch) => { - return dispatch({ - type: REMOVE_CHANGE, - payload: removeChange(id) - }).then(() => { - return dispatch(actions.fetchChanges()); - }); - } -}; diff --git a/spikes/stacks/redux-thunk/src/client/reducers/index.js b/spikes/stacks/redux-thunk/src/client/state/reducers/index.js similarity index 100% rename from spikes/stacks/redux-thunk/src/client/reducers/index.js rename to spikes/stacks/redux-thunk/src/client/state/reducers/index.js diff --git a/spikes/stacks/redux-thunk/src/client/state/reducers/printers.js b/spikes/stacks/redux-thunk/src/client/state/reducers/printers.js new file mode 100644 index 00000000..88399b49 --- /dev/null +++ b/spikes/stacks/redux-thunk/src/client/state/reducers/printers.js @@ -0,0 +1,43 @@ +const ReduxActions = require('redux-actions'); +const find = require('lodash.find'); +const actions = require('../actions'); + +const { + handleActions +} = ReduxActions; + +const { + UPDATE_WORKER_ID, + UPDATE_PRINTERS +} = actions; + +exports.data = handleActions({ + [UPDATE_PRINTERS]: (state, action) => { + return action.payload; + } +}, []); + +exports.ui = handleActions({ + [UPDATE_WORKER_ID]: (state, action) => { + return { + ...state, + id: action.payload + }; + }, + [UPDATE_PRINTERS]: (state, action) => { + const locked = (find(action.payload, (printer) => { + return ( + printer.lock && + printer.lock === state.id + ); + }) || {}).id || ''; + + return { + ...state, + locked + }; + } +}, { + id: '', + locked: '' +}); diff --git a/spikes/stacks/redux-thunk/src/client/state/selectors.js b/spikes/stacks/redux-thunk/src/client/state/selectors.js new file mode 100644 index 00000000..cac1592c --- /dev/null +++ b/spikes/stacks/redux-thunk/src/client/state/selectors.js @@ -0,0 +1,10 @@ +// const Reselect = require('reselect'); +const get = require('lodash.get'); + +const router = (state) => { + return get(state, 'ui.app.router'); +}; + +module.exports = { + router +}; diff --git a/spikes/stacks/redux-thunk/src/client/store.js b/spikes/stacks/redux-thunk/src/client/state/store.js similarity index 92% rename from spikes/stacks/redux-thunk/src/client/store.js rename to spikes/stacks/redux-thunk/src/client/state/store.js index d18102b3..d177ec37 100644 --- a/spikes/stacks/redux-thunk/src/client/store.js +++ b/spikes/stacks/redux-thunk/src/client/state/store.js @@ -2,7 +2,7 @@ const createReducer = require('./reducers'); const enableBatching = require('redux-batched-actions').enableBatching; const thunk = require('redux-thunk').default; const promiseMiddleware = require('redux-promise-middleware').default; -const createLogger = require('redux-logger'); +// const createLogger = require('redux-logger'); const redux = require('redux'); diff --git a/spikes/stacks/redux-thunk/src/client/worker.js b/spikes/stacks/redux-thunk/src/client/worker.js index c3a472a8..e41576e1 100644 --- a/spikes/stacks/redux-thunk/src/client/worker.js +++ b/spikes/stacks/redux-thunk/src/client/worker.js @@ -2,7 +2,7 @@ const uuid = require('node-uuid'); const Emitter = require('component-emitter'); const crosstab = require('crosstab'); const values = require('lodash.values'); -const actions = require('./actions'); +const actions = require('./state/actions'); const emitter = module.exports = new Emitter(); let isMaster = crosstab.util.tabs['MASTER_TAB'].id === crosstab.id; diff --git a/spikes/stacks/redux-thunk/src/server/schema.js b/spikes/stacks/redux-thunk/src/server/schema.js index f9ae591c..521d370b 100644 --- a/spikes/stacks/redux-thunk/src/server/schema.js +++ b/spikes/stacks/redux-thunk/src/server/schema.js @@ -47,7 +47,7 @@ const ChangeType = new GraphQLObjectType({ product: { type: ProductType, resolve: (root, args) => { - return products[root.product] + return products[root.product]; } }, price: { @@ -70,7 +70,7 @@ const query = new GraphQLObjectType({ } }, resolve(root, args, ctx) { - return args.id ? [products[args.id]] : values(products) + return args.id ? [products[args.id]] : values(products); } }, changes: {