diff --git a/README b/README index 7570f70..3396e6b 100644 --- a/README +++ b/README @@ -10,4 +10,4 @@ Install browserify globally: $ sudo npm install -g browserify Next, install browserify, babel, react, react-bootstrap, react-widgets, globalize, and big.js in our directory using npm: -$ npm install browserify react react-dom react-addons-update react-bootstrap react-widgets babelify babel-preset-react globalize cldr-data big.js +$ npm install browserify react react-dom react-addons-update react-bootstrap react-widgets redux react-redux redux-thunk babelify babel-preset-react globalize cldr-data big.js keymirror diff --git a/js/actions/AccountActions.js b/js/actions/AccountActions.js new file mode 100644 index 0000000..661c919 --- /dev/null +++ b/js/actions/AccountActions.js @@ -0,0 +1,181 @@ +var AccountConstants = require('../constants/AccountConstants'); + +var ErrorActions = require('ErrorActions'); + +var models = require('../models.js'); +var Account = models.Account; +var Error = models.Error; + +function fetchAccounts() { + return { + type: AccountConstants.FETCH_ACCOUNTS + } +} + +function accountsFetched(accounts) { + return { + type: AccountConstants.ACCOUNTS_FETCHED, + accounts: accounts + } +} + +function createAccount() { + return { + type: AccountConstants.CREATE_ACCOUNT + } +} + +function accountCreated(account) { + return { + type: AccountConstants.ACCOUNT_CREATED, + account: account + } +} + +function updateAccount() { + return { + type: AccountConstants.UPDATE_ACCOUNT + } +} + +function accountUpdated(account) { + return { + type: AccountConstants.ACCOUNT_UPDATED, + account: account + } +} + +function removeAccount() { + return { + type: AccountConstants.REMOVE_ACCOUNT + } +} + +function accountRemoved(accountId) { + return { + type: AccountConstants.ACCOUNT_REMOVED, + accountId: accountId + } +} + +function accountSelected(accountId) { + return { + type: AccountConstants.ACCOUNT_SELECTED, + accountId: accountId + } +} + +function fetchAll() { + return function (dispatch) { + dispatch(fetchAccounts()); + + $.ajax({ + type: "GET", + dataType: "json", + url: "account/", + success: function(data, status, jqXHR) { + var e = new Error(); + var accounts = []; + e.fromJSON(data); + if (e.isError()) { + ErrorActions.serverError(e); + } else { + dispatch(accountsFetched(data.accounts.map(function(json) { + var a = new Account(); + a.fromJSON(json); + return a; + }))); + } + }, + error: function(jqXHR, status, error) { + ErrorActions.ajaxError(e); + } + }); + }; +} + +function create(account) { + return function (dispatch) { + dispatch(createAccount()); + + $.ajax({ + type: "POST", + dataType: "json", + url: "account/", + data: {account: account.toJSON()}, + success: function(data, status, jqXHR) { + var e = new Error(); + e.fromJSON(data); + if (e.isError()) { + ErrorActions.serverError(e); + } else { + var a = new Account(); + a.fromJSON(data); + dispatch(accountCreated(a)); + } + }, + error: function(jqXHR, status, error) { + ErrorActions.ajaxError(e); + } + }); + }; +} + +function update(account) { + return function (dispatch) { + dispatch(updateAccount()); + + $.ajax({ + type: "PUT", + dataType: "json", + url: "account/"+account.AccountId+"/", + data: {account: account.toJSON()}, + success: function(data, status, jqXHR) { + var e = new Error(); + e.fromJSON(data); + if (e.isError()) { + ErrorActions.serverError(e); + } else { + var a = new Account(); + a.fromJSON(data); + dispatch(accountUpdated(a)); + } + }, + error: function(jqXHR, status, error) { + ErrorActions.ajaxError(e); + } + }); + }; +} + +function remove(account) { + return function(dispatch) { + dispatch(removeAccount()); + + $.ajax({ + type: "DELETE", + dataType: "json", + url: "account/"+account.AccountId+"/", + success: function(data, status, jqXHR) { + var e = new Error(); + e.fromJSON(data); + if (e.isError()) { + ErrorActions.serverError(e); + } else { + dispatch(accountRemoved(account.AccountId)); + } + }, + error: function(jqXHR, status, error) { + ErrorActions.ajaxError(e); + } + }); + }; +} + +module.exports = { + fetchAll: fetchAll, + create: create, + update: update, + remove: remove, + select: accountSelected +}; diff --git a/js/actions/ErrorActions.js b/js/actions/ErrorActions.js new file mode 100644 index 0000000..d75ea9b --- /dev/null +++ b/js/actions/ErrorActions.js @@ -0,0 +1,27 @@ +var ErrorConstants = require('../constants/ErrorConstants'); + +var models = require('../models.js'); +var Error = models.Error; + +function serverError(error) { + return { + type: ErrorConstants.ERROR_SERVER, + error: error + }; +} + +function ajaxError(error) { + var e = new Error(); + e.ErrorId = 5; + e.ErrorString = "Request Failed: " + status + error; + + return { + type: ErrorConstants.ERROR_AJAX, + error: e + }; +} + +module.exports = { + serverError: serverError, + ajaxError: ajaxError +}; diff --git a/js/constants/AccountConstants.js b/js/constants/AccountConstants.js new file mode 100644 index 0000000..e8940c2 --- /dev/null +++ b/js/constants/AccountConstants.js @@ -0,0 +1,13 @@ +var keyMirror = require('keymirror'); + +module.exports = keyMirror({ + FETCH_ACCOUNTS: null, + ACCOUNTS_FETCHED: null, + CREATE_ACCOUNT: null, + ACCOUNT_CREATED: null, + UPDATE_ACCOUNT: null, + ACCOUNT_UPDATED: null, + REMOVE_ACCOUNT: null, + ACCOUNT_REMOVED: null, + ACCOUNT_SELECTED: null +}); diff --git a/js/constants/ErrorConstants.js b/js/constants/ErrorConstants.js new file mode 100644 index 0000000..a793905 --- /dev/null +++ b/js/constants/ErrorConstants.js @@ -0,0 +1,8 @@ +var keyMirror = require('keymirror'); + +module.exports = keyMirror({ + ERROR_AJAX: null, + ERROR_SERVER: null, + ERROR_CLIENT: null, + ERROR_USER: null, +}); diff --git a/js/main.js b/js/main.js index f28e7ed..976cefd 100644 --- a/js/main.js +++ b/js/main.js @@ -1,10 +1,15 @@ var React = require('react'); var ReactDOM = require('react-dom'); +var Provider = require('react-redux').Provider; +var Redux = require('redux'); +var ReduxThunk = require('redux-thunk').default; + var Globalize = require('globalize'); var globalizeLocalizer = require('react-widgets/lib/localizers/globalize'); var MoneyGoApp = require('./MoneyGoApp.js'); +var MoneyGoReducer = require('./reducers/MoneyGoReducer'); // Setup globalization for react-widgets //Globalize.load(require("cldr-data").entireSupplemental()); @@ -19,5 +24,17 @@ Globalize.locale('en'); globalizeLocalizer(Globalize); $(document).ready(function() { - ReactDOM.render(, document.getElementById("content")); + var store = Redux.createStore( + MoneyGoReducer, + Redux.applyMiddleware( + ReduxThunk + ) + ); + + ReactDOM.render( + + + , + document.getElementById("content") + ); }); diff --git a/js/reducers/AccountReducer.js b/js/reducers/AccountReducer.js new file mode 100644 index 0000000..16fb033 --- /dev/null +++ b/js/reducers/AccountReducer.js @@ -0,0 +1,27 @@ +var assign = require('object-assign'); + +var AccountConstants = require('../constants/AccountConstants'); + +module.exports = function(state = {}, action) { + switch (action.type) { + case AccountConstants.ACCOUNTS_FETCHED: + var accounts = {}; + for (var i = 0; i < action.accounts.length; i++) { + var account = action.accounts[i]; + accounts[account.AccountId] = account; + } + return accounts; + case AccountConstants.ACCOUNT_CREATED: + case AccountConstants.ACCOUNT_UPDATED: + var account = action.account; + return assign({}, state, { + [account.AccountId]: account + }); + case AccountConstants.ACCOUNT_REMOVED: + var newstate = assign({}, state); + delete newstate[action.accountId]; + return newstate; + default: + return state; + } +}; diff --git a/js/reducers/MoneyGoReducer.js b/js/reducers/MoneyGoReducer.js new file mode 100644 index 0000000..fdd8702 --- /dev/null +++ b/js/reducers/MoneyGoReducer.js @@ -0,0 +1,9 @@ +var Redux = require('redux'); + +var AccountReducer = require('./AccountReducer'); +var SelectedAccountReducer = require('./SelectedAccountReducer'); + +module.exports = Redux.combineReducers({ + accounts: AccountReducer, + selectedAccount: SelectedAccountReducer +}); diff --git a/js/reducers/SelectedAccountReducer.js b/js/reducers/SelectedAccountReducer.js new file mode 100644 index 0000000..fa2f294 --- /dev/null +++ b/js/reducers/SelectedAccountReducer.js @@ -0,0 +1,20 @@ +var AccountConstants = require('../constants/AccountConstants'); + +module.exports = function(state = -1, action) { + switch (action.type) { + case AccountConstants.ACCOUNTS_FETCHED: + for (var i = 0; i < action.accounts.length; i++) { + if (action.accounts[i].AccountId == state) + return state; + } + return -1; + case AccountConstants.ACCOUNT_REMOVED: + if (action.accountId == state) + return -1; + return state; + case AccountConstants.ACCOUNT_SELECTED: + return action.accountId; + default: + return state; + } +};