diff --git a/js/AccountCombobox.js b/js/AccountCombobox.js
index 94ce5de..098d789 100644
--- a/js/AccountCombobox.js
+++ b/js/AccountCombobox.js
@@ -16,13 +16,13 @@ module.exports = React.createClass({
handleAccountChange: function(account) {
if (this.props.onChange != null &&
account.hasOwnProperty('AccountId') &&
- (this.props.account_map.hasOwnProperty([account.AccountId]) ||
+ (this.props.accounts.hasOwnProperty([account.AccountId]) ||
account.AccountId == -1)) {
this.props.onChange(account)
}
},
render: function() {
- var accounts = getAccountDisplayList(this.props.accounts, this.props.includeRoot, this.props.rootName);
+ var accounts = getAccountDisplayList(this.props.accounts, this.props.accountChildren, this.props.includeRoot, this.props.rootName);
var className = "";
if (this.props.className)
className = this.props.className;
diff --git a/js/AccountRegister.js b/js/AccountRegister.js
index 936c86c..4887cf5 100644
--- a/js/AccountRegister.js
+++ b/js/AccountRegister.js
@@ -62,7 +62,7 @@ const TransactionRow = React.createClass({
var number = ""
var accountName = "";
var status = "";
- var security = this.props.security_map[this.props.account.SecurityId];
+ var security = this.props.securities[this.props.account.SecurityId];
if (this.props.transaction.isTransaction()) {
var thisAccountSplit;
@@ -78,9 +78,9 @@ const TransactionRow = React.createClass({
var otherSplit = this.props.transaction.Splits[1];
if (otherSplit.AccountId == -1)
- var accountName = "Unbalanced " + this.props.security_map[otherSplit.SecurityId].Symbol + " transaction";
+ var accountName = "Unbalanced " + this.props.securities[otherSplit.SecurityId].Symbol + " transaction";
else
- var accountName = getAccountDisplayName(this.props.account_map[otherSplit.AccountId], this.props.account_map);
+ var accountName = getAccountDisplayName(this.props.accounts[otherSplit.AccountId], this.props.accounts);
} else {
accountName = "--Split Transaction--";
}
@@ -277,12 +277,12 @@ const AddEditTransactionModal = React.createClass({
},
handleSubmit: function() {
var errorString = ""
- var imbalancedSecurityList = this.state.transaction.imbalancedSplitSecurities(this.props.account_map);
+ var imbalancedSecurityList = this.state.transaction.imbalancedSplitSecurities(this.props.accounts);
if (imbalancedSecurityList.length > 0)
errorString = "Transaction must balance"
for (var i = 0; i < this.state.transaction.Splits.length; i++) {
var s = this.state.transaction.Splits[i];
- if (!(s.AccountId in this.props.account_map)) {
+ if (!(s.AccountId in this.props.accounts)) {
errorString = "All accounts must be valid"
}
}
@@ -312,7 +312,7 @@ const AddEditTransactionModal = React.createClass({
);
}
- var imbalancedSecurityList = this.state.transaction.imbalancedSplitSecurities(this.props.account_map);
+ var imbalancedSecurityList = this.state.transaction.imbalancedSplitSecurities(this.props.accounts);
var imbalancedSecurityMap = {};
for (i = 0; i < imbalancedSecurityList.length; i++)
imbalancedSecurityMap[imbalancedSecurityList[i]] = i;
@@ -324,11 +324,11 @@ const AddEditTransactionModal = React.createClass({
var security = null;
var amountValidation = undefined;
var accountValidation = "";
- if (s.AccountId in this.props.account_map) {
- security = this.props.security_map[this.props.account_map[s.AccountId].SecurityId];
+ if (s.AccountId in this.props.accounts) {
+ security = this.props.securities[this.props.accounts[s.AccountId].SecurityId];
} else {
- if (s.SecurityId in this.props.security_map) {
- security = this.props.security_map[s.SecurityId];
+ if (s.SecurityId in this.props.securities) {
+ security = this.props.securities[s.SecurityId];
}
accountValidation = "has-error";
}
@@ -380,7 +380,7 @@ const AddEditTransactionModal = React.createClass({
ref={"memo-"+i} />
= this.state.numPages)
newpage = this.state.numPages-1;
if (newpage != this.state.currentPage) {
- if (this.props.selectedAccount != null) {
- this.getTransactionPage(this.props.selectedAccount, newpage);
+ if (this.props.selectedAccount != -1) {
+ this.getTransactionPage(this.props.accounts[this.props.selectedAccount], newpage);
}
this.setState({currentPage: newpage});
}
},
onNewTransaction: function() {
- this.getTransactionPage(this.props.selectedAccount, this.state.currentPage);
+ this.getTransactionPage(this.props.accounts[this.props.selectedAccount], this.state.currentPage);
},
onUpdatedTransaction: function() {
- this.getTransactionPage(this.props.selectedAccount, this.state.currentPage);
+ this.getTransactionPage(this.props.accounts[this.props.selectedAccount], this.state.currentPage);
},
onDeletedTransaction: function() {
- this.getTransactionPage(this.props.selectedAccount, this.state.currentPage);
+ this.getTransactionPage(this.props.accounts[this.props.selectedAccount], this.state.currentPage);
},
createNewTransaction: function(transaction) {
$.ajax({
@@ -828,7 +828,7 @@ module.exports = React.createClass({
},
handleImportComplete: function() {
this.setState({importingTransactions: false});
- this.getTransactionPage(this.props.selectedAccount, this.state.currentPage);
+ this.getTransactionPage(this.props.accounts[this.props.selectedAccount], this.state.currentPage);
},
handleDeleteTransaction: function(transaction) {
this.setState({
@@ -853,16 +853,16 @@ module.exports = React.createClass({
transactions: [],
currentPage: 0
});
- if (nextProps.selectedAccount != null)
- this.getTransactionPage(nextProps.selectedAccount, 0);
+ if (nextProps.selectedAccount != -1)
+ this.getTransactionPage(nextProps.accounts[nextProps.selectedAccount], 0);
}
},
render: function() {
var name = "Please select an account";
register = [];
- if (this.props.selectedAccount != null) {
- name = this.props.selectedAccount.Name;
+ if (this.props.selectedAccount != -1) {
+ name = this.props.accounts[this.props.selectedAccount].Name;
var transactionRows = [];
for (var i = 0; i < this.state.transactions.length; i++) {
@@ -871,11 +871,9 @@ module.exports = React.createClass({
));
}
@@ -901,7 +899,7 @@ module.exports = React.createClass({
);
}
- var disabled = (this.props.selectedAccount == null) ? true : false;
+ var disabled = (this.props.selectedAccount == -1) ? true : false;
return (
@@ -909,17 +907,15 @@ module.exports = React.createClass({
show={this.state.editingTransaction}
transaction={this.state.selectedTransaction}
accounts={this.props.accounts}
- account_map={this.props.account_map}
+ accountChildren={this.props.accountChildren}
onCancel={this.handleEditingCancel}
onSubmit={this.handleUpdateTransaction}
onDelete={this.handleDeleteTransaction}
- securities={this.props.securities}
- security_map={this.props.security_map}/>
+ securities={this.props.securities} />
diff --git a/js/AccountSettingsModal.js b/js/AccountSettingsModal.js
index 02443c5..70963fa 100644
--- a/js/AccountSettingsModal.js
+++ b/js/AccountSettingsModal.js
@@ -14,7 +14,6 @@ var Col = ReactBootstrap.Col;
var models = require('./models.js');
var User = models.User;
-var Error = models.Error;
module.exports = React.createClass({
displayName: "AccountSettingsModal",
@@ -71,7 +70,6 @@ module.exports = React.createClass({
},
handleSubmit: function(e) {
var u = new User();
- var error = "";
e.preventDefault();
u.UserId = this.props.user.UserId;
@@ -88,31 +86,8 @@ module.exports = React.createClass({
u.Password = models.BogusPassword;
}
- this.handleSaveSettings(u);
- },
- handleSaveSettings: function(user) {
- $.ajax({
- type: "PUT",
- dataType: "json",
- url: "user/"+user.UserId+"/",
- data: {user: user.toJSON()},
- success: function(data, status, jqXHR) {
- var e = new Error();
- e.fromJSON(data);
- if (e.isError()) {
- this.setState({error: e});
- } else {
- user.Password = "";
- this.props.onSubmit(user);
- }
- }.bind(this),
- error: function(jqXHR, status, error) {
- var e = new Error();
- e.ErrorId = 5;
- e.ErrorString = "Request Failed: " + status + error;
- this.setState({error: e});
- }.bind(this),
- });
+ this.props.onUpdateUser(u);
+ this.props.onSubmit();
},
render: function() {
return (
diff --git a/js/AccountsTab.js b/js/AccountsTab.js
index 131d795..cc22ff4 100644
--- a/js/AccountsTab.js
+++ b/js/AccountsTab.js
@@ -119,7 +119,7 @@ const AddEditAccountModal = React.createClass({
Security
item.Name + " - " + item.Description}
defaultValue={this.state.security}
@@ -188,10 +188,10 @@ const DeleteAccountModal = React.createClass({
this.setState({checked: !this.state.checked});
},
handleSubmit: function() {
- if (this.props.account_map.hasOwnProperty(this.state.accountid)) {
+ if (this.props.accounts.hasOwnProperty(this.state.accountid)) {
if (this.state.checked) {
if (this.props.onSubmit != null)
- this.props.onSubmit(this.props.account_map[this.state.accountid]);
+ this.props.onSubmit(this.props.accounts[this.state.accountid]);
} else {
this.setState({error: "You must confirm you wish to delete this account."});
}
@@ -206,11 +206,11 @@ const DeleteAccountModal = React.createClass({
},
render: function() {
var checkbox = [];
- if (this.props.account_map.hasOwnProperty(this.state.accountid)) {
- var parentAccountId = this.props.account_map[this.state.accountid].ParentAccountId;
+ if (this.props.accounts.hasOwnProperty(this.state.accountid)) {
+ var parentAccountId = this.props.accounts[this.state.accountid].ParentAccountId;
var parentAccount = "will be deleted and any child accounts will become top-level accounts.";
if (parentAccountId != -1)
- parentAccount = "and any child accounts will be re-parented to: " + this.props.account_map[parentAccountId].Name;
+ parentAccount = "and any child accounts will be re-parented to: " + this.props.accounts[parentAccountId].Name;
var warningString = "I understand that deleting this account cannot be undone and that all transactions " + parentAccount;
checkbox = (
@@ -248,7 +248,7 @@ const DeleteAccountModal = React.createClass({
@@ -285,17 +285,20 @@ const AccountTreeNode = React.createClass({
},
render: function() {
var glyph = this.state.expanded ? 'minus' : 'plus';
- var active = (this.props.selectedAccount != null &&
- this.props.account.AccountId == this.props.selectedAccount.AccountId);
+ var active = (this.props.selectedAccount != -1 &&
+ this.props.account.AccountId == this.props.selectedAccount);
var buttonStyle = active ? "info" : "link";
var self = this;
- var children = this.props.account.Children.map(function(account) {
+ var children = this.props.accountChildren[this.props.account.AccountId].map(function(childId) {
+ var account = self.props.accounts[childId];
return (
);
});
@@ -334,11 +337,9 @@ const AccountTreeNode = React.createClass({
const AccountTree = React.createClass({
getInitialState: function() {
- return {selectedAccount: null,
- height: 0};
+ return {height: 0};
},
handleSelect: function(account) {
- this.setState({selectedAccount: account});
if (this.props.onSelect != null) {
this.props.onSelect(account);
}
@@ -356,13 +357,17 @@ const AccountTree = React.createClass({
var accounts = this.props.accounts;
var children = [];
- for (var i = 0; i < accounts.length; i++) {
- if (accounts[i].isRootAccount())
+ for (var accountId in accounts) {
+ if (accounts.hasOwnProperty(accountId) &&
+ accounts[accountId].isRootAccount()) {
children.push(());
+ }
}
var style = {height: this.state.height + "px"};
@@ -379,7 +384,6 @@ module.exports = React.createClass({
displayName: "AccountsTab",
getInitialState: function() {
return {
- selectedAccount: null,
creatingNewAccount: false,
editingAccount: false,
deletingAccount: false
@@ -416,46 +420,48 @@ module.exports = React.createClass({
handleRemoveAccount: function(account) {
if (this.props.onDeleteAccount != null)
this.props.onDeleteAccount(account);
- this.setState({deletingAccount: false,
- selectedAccount: null});
+ this.setState({deletingAccount: false});
},
handleAccountSelected: function(account) {
- this.setState({selectedAccount: account});
+ this.props.onSelectAccount(account.AccountId);
},
render: function() {
- var accounts = this.props.accounts;
- var account_map = this.props.account_map;
+ var disabled = (this.props.selectedAccount == -1) ? true : false;
- var disabled = (this.state.selectedAccount == null) ? true : false;
+ var selectedAccount = null;
+ if (this.props.accounts.hasOwnProperty(this.props.selectedAccount))
+ selectedAccount = this.props.accounts[this.props.selectedAccount];
return (
+ security_list={this.props.security_list}/>
+ security_list={this.props.security_list}/>
+ accountChildren={this.props.accountChildren}
+ securities={this.props.securities} />
);
diff --git a/js/MoneyGoApp.js b/js/MoneyGoApp.js
index 0194b53..9e3b8e9 100644
--- a/js/MoneyGoApp.js
+++ b/js/MoneyGoApp.js
@@ -6,35 +6,21 @@ var Tabs = ReactBootstrap.Tabs;
var Tab = ReactBootstrap.Tab;
var Modal = ReactBootstrap.Modal;
-var models = require('./models.js');
-var User = models.User;
-var Session = models.Session;
-var Security = models.Security;
-var Account = models.Account;
-var Error = models.Error;
-
-var TopBar = require('./TopBar.js');
+var TopBarContainer = require('./containers/TopBarContainer');
var NewUserForm = require('./NewUserForm.js');
-var AccountSettingsModal = require('./AccountSettingsModal.js');
-var AccountsTab = require('./AccountsTab.js');
+var AccountSettingsModalContainer = require('./containers/AccountSettingsModalContainer');
+var AccountsTabContainer = require('./containers/AccountsTabContainer');
module.exports = React.createClass({
displayName: "MoneyGoApp",
getInitialState: function() {
return {
hash: "home",
- session: new Session(),
- user: new User(),
- accounts: [],
- account_map: {},
- securities: [],
- security_map: {},
- error: new Error(),
showAccountSettingsModal: false
};
},
componentDidMount: function() {
- this.getSession();
+ this.props.tryResumingSession();
this.handleHashChange();
if ("onhashchange" in window) {
window.onhashchange = this.handleHashChange;
@@ -52,248 +38,34 @@ module.exports = React.createClass({
if (this.state.hash != hash)
this.setState({hash: hash});
},
- ajaxError: function(jqXHR, status, error) {
- var e = new Error();
- e.ErrorId = 5;
- e.ErrorString = "Request Failed: " + status + error;
- this.setState({error: e});
- },
- getSession: function() {
- $.ajax({
- type: "GET",
- dataType: "json",
- url: "session/",
- success: function(data, status, jqXHR) {
- var e = new Error();
- var s = new Session();
- e.fromJSON(data);
- if (e.isError()) {
- if (e.ErrorId != 1 /* Not Signed In*/)
- this.setState({error: e});
- } else {
- s.fromJSON(data);
- }
- this.setState({session: s});
- this.getUser();
- this.getAccounts();
- this.getSecurities();
- }.bind(this),
- error: this.ajaxError
- });
- },
- getUser: function() {
- if (!this.state.session.isSession())
- return;
- $.ajax({
- type: "GET",
- dataType: "json",
- url: "user/"+this.state.session.UserId+"/",
- success: function(data, status, jqXHR) {
- var e = new Error();
- var u = new User();
- e.fromJSON(data);
- if (e.isError()) {
- this.setState({error: e});
- } else {
- u.fromJSON(data);
- }
- this.setState({user: u});
- }.bind(this),
- error: this.ajaxError
- });
- },
- getSecurities: function() {
- if (!this.state.session.isSession()) {
- this.setState({securities: [], security_map: {}});
- return;
- }
- $.ajax({
- type: "GET",
- dataType: "json",
- url: "security/",
- success: function(data, status, jqXHR) {
- var e = new Error();
- var securities = [];
- var security_map = {};
- e.fromJSON(data);
- if (e.isError()) {
- this.setState({error: e});
- } else {
- for (var i = 0; i < data.securities.length; i++) {
- var s = new Security();
- s.fromJSON(data.securities[i]);
- securities.push(s);
- security_map[s.SecurityId] = s;
- }
- }
- this.setState({securities: securities, security_map: security_map});
- }.bind(this),
- error: this.ajaxError
- });
- },
- getAccounts: function() {
- if (!this.state.session.isSession()) {
- this.setState({accounts: [], account_map: {}});
- return;
- }
- $.ajax({
- type: "GET",
- dataType: "json",
- url: "account/",
- success: function(data, status, jqXHR) {
- var e = new Error();
- var accounts = [];
- var account_map = {};
- e.fromJSON(data);
- if (e.isError()) {
- this.setState({error: e});
- } else {
- for (var i = 0; i < data.accounts.length; i++) {
- var a = new Account();
- a.fromJSON(data.accounts[i]);
- accounts.push(a);
- account_map[a.AccountId] = a;
- }
- //Populate Children arrays in account objects
- for (var i = 0; i < accounts.length; i++) {
- var a = accounts[i];
- if (!a.isRootAccount())
- account_map[a.ParentAccountId].Children.push(a);
- }
- }
- this.setState({accounts: accounts, account_map: account_map});
- }.bind(this),
- error: this.ajaxError
- });
- },
- handleErrorClear: function() {
- this.setState({error: new Error()});
- },
- handleLoginSubmit: function(user) {
- $.ajax({
- type: "POST",
- dataType: "json",
- url: "session/",
- data: {user: user.toJSON()},
- success: function(data, status, jqXHR) {
- var e = new Error();
- e.fromJSON(data);
- if (e.isError()) {
- this.setState({error: e});
- } else {
- this.getSession();
- this.setHash("home");
- }
- }.bind(this),
- error: this.ajaxError
- });
- },
- handleLogoutSubmit: function() {
- this.setState({accounts: [], account_map: {}});
- $.ajax({
- type: "DELETE",
- dataType: "json",
- url: "session/",
- success: function(data, status, jqXHR) {
- var e = new Error();
- e.fromJSON(data);
- if (e.isError()) {
- this.setState({error: e});
- }
- this.setState({session: new Session(), user: new User()});
- }.bind(this),
- error: this.ajaxError
- });
- },
handleAccountSettings: function() {
this.setState({showAccountSettingsModal: true});
},
handleSettingsSubmitted: function(user) {
this.setState({
- user: user,
showAccountSettingsModal: false
});
},
- handleSettingsCanceled: function(user) {
+ handleSettingsCanceled: function() {
this.setState({showAccountSettingsModal: false});
},
handleCreateNewUser: function() {
this.setHash("new_user");
},
- handleGoHome: function(user) {
+ handleGoHome: function() {
this.setHash("home");
},
- handleCreateAccount: function(account) {
- $.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()) {
- this.setState({error: e});
- } else {
- this.getAccounts();
- }
- }.bind(this),
- error: this.ajaxError
- });
- },
- handleUpdateAccount: function(account) {
- $.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()) {
- this.setState({error: e});
- } else {
- this.getAccounts();
- }
- }.bind(this),
- error: this.ajaxError
- });
- },
- handleDeleteAccount: function(account) {
- $.ajax({
- type: "DELETE",
- dataType: "json",
- url: "account/"+account.AccountId+"/",
- success: function(data, status, jqXHR) {
- var e = new Error();
- e.fromJSON(data);
- if (e.isError()) {
- this.setState({error: e});
- } else {
- this.getAccounts();
- }
- }.bind(this),
- error: this.ajaxError
- });
- },
render: function() {
var mainContent;
if (this.state.hash == "new_user") {
mainContent =
} else {
- if (this.state.user.isUser())
+ if (this.props.user.isUser())
mainContent = (
-
+
Scheduled transactions go here...
Budgets go here...
@@ -311,18 +83,12 @@ module.exports = React.createClass({
return (
-
+ onAccountSettings={this.handleAccountSettings} />
{mainContent}
-
diff --git a/js/TopBar.js b/js/TopBar.js
index cf21125..fa9c181 100644
--- a/js/TopBar.js
+++ b/js/TopBar.js
@@ -29,7 +29,7 @@ const LoginBar = React.createClass({
e.preventDefault();
user.Username = ReactDOM.findDOMNode(this.refs.username).value;
user.Password = ReactDOM.findDOMNode(this.refs.password).value;
- this.props.onLoginSubmit(user);
+ this.props.onLogin(user);
},
handleNewUserSubmit: function(e) {
e.preventDefault();
@@ -72,7 +72,7 @@ const LogoutBar = React.createClass({
if (this.props.onAccountSettings != null)
this.props.onAccountSettings();
} else if (key == 2) {
- this.props.onLogoutSubmit();
+ this.props.onLogout();
}
},
render: function() {
@@ -102,15 +102,15 @@ module.exports = React.createClass({
var barContents;
var errorAlert;
if (!this.props.user.isUser())
- barContents = ;
+ barContents = ;
else
- barContents = ;
+ barContents = ;
if (this.props.error.isError())
errorAlert =
-
+
Error!
Error {this.props.error.ErrorId}: {this.props.error.ErrorString}
-
+
;
return (
diff --git a/js/actions/AccountActions.js b/js/actions/AccountActions.js
index 661c919..2fe1ce5 100644
--- a/js/actions/AccountActions.js
+++ b/js/actions/AccountActions.js
@@ -1,6 +1,6 @@
var AccountConstants = require('../constants/AccountConstants');
-var ErrorActions = require('ErrorActions');
+var ErrorActions = require('./ErrorActions');
var models = require('../models.js');
var Account = models.Account;
@@ -75,7 +75,6 @@ function fetchAll() {
url: "account/",
success: function(data, status, jqXHR) {
var e = new Error();
- var accounts = [];
e.fromJSON(data);
if (e.isError()) {
ErrorActions.serverError(e);
diff --git a/js/actions/ErrorActions.js b/js/actions/ErrorActions.js
index d75ea9b..38479b4 100644
--- a/js/actions/ErrorActions.js
+++ b/js/actions/ErrorActions.js
@@ -21,7 +21,14 @@ function ajaxError(error) {
};
}
+function clearError() {
+ return {
+ type: ErrorConstants.CLEAR_ERROR,
+ };
+}
+
module.exports = {
serverError: serverError,
- ajaxError: ajaxError
+ ajaxError: ajaxError,
+ clearError: clearError
};
diff --git a/js/actions/SecurityActions.js b/js/actions/SecurityActions.js
new file mode 100644
index 0000000..268dac4
--- /dev/null
+++ b/js/actions/SecurityActions.js
@@ -0,0 +1,52 @@
+var SecurityConstants = require('../constants/SecurityConstants');
+
+var ErrorActions = require('./ErrorActions');
+
+var models = require('../models.js');
+var Security = models.Security;
+var Error = models.Error;
+
+function fetchSecurities() {
+ return {
+ type: SecurityConstants.FETCH_SECURITIES
+ }
+}
+
+function securitiesFetched(securities) {
+ return {
+ type: SecurityConstants.SECURITIES_FETCHED,
+ securities: securities
+ }
+}
+
+function fetchAll() {
+ return function (dispatch) {
+ dispatch(fetchSecurities());
+
+ $.ajax({
+ type: "GET",
+ dataType: "json",
+ url: "security/",
+ success: function(data, status, jqXHR) {
+ var e = new Error();
+ e.fromJSON(data);
+ if (e.isError()) {
+ ErrorActions.serverError(e);
+ } else {
+ dispatch(securitiesFetched(data.securities.map(function(json) {
+ var s = new Security();
+ s.fromJSON(json);
+ return s;
+ })));
+ }
+ },
+ error: function(jqXHR, status, error) {
+ ErrorActions.ajaxError(e);
+ }
+ });
+ };
+}
+
+module.exports = {
+ fetchAll: fetchAll
+};
diff --git a/js/actions/UserActions.js b/js/actions/UserActions.js
new file mode 100644
index 0000000..abe4e62
--- /dev/null
+++ b/js/actions/UserActions.js
@@ -0,0 +1,208 @@
+var UserConstants = require('../constants/UserConstants');
+
+var AccountActions = require('./AccountActions');
+var SecurityActions = require('./SecurityActions');
+var ErrorActions = require('./ErrorActions');
+
+var models = require('../models.js');
+var User = models.User;
+var Session = models.Session;
+var Error = models.Error;
+
+function loginUser() {
+ return {
+ type: UserConstants.LOGIN_USER
+ }
+}
+
+function userLoggedIn(session) {
+ return {
+ type: UserConstants.USER_LOGGEDIN,
+ session: session
+ }
+}
+
+function logoutUser() {
+ return {
+ type: UserConstants.LOGOUT_USER
+ }
+}
+
+function userLoggedOut() {
+ return {
+ type: UserConstants.USER_LOGGEDOUT
+ }
+}
+
+function fetchUser(userId) {
+ return {
+ type: UserConstants.FETCH_USER,
+ userId: userId
+ }
+}
+
+function userFetched(user) {
+ return {
+ type: UserConstants.USER_FETCHED,
+ user: user
+ }
+}
+
+function updateUser(user) {
+ return {
+ type: UserConstants.UPDATE_USER,
+ user: user
+ }
+}
+
+function userUpdated(user) {
+ return {
+ type: UserConstants.USER_UPDATED,
+ user: user
+ }
+}
+
+function fetch(userId) {
+ return function (dispatch) {
+ dispatch(fetchUser());
+
+ $.ajax({
+ type: "GET",
+ dataType: "json",
+ url: "user/"+userId+"/",
+ success: function(data, status, jqXHR) {
+ var e = new Error();
+ e.fromJSON(data);
+ if (e.isError()) {
+ ErrorActions.serverError(e);
+ } else {
+ var u = new User();
+ u.fromJSON(data);
+ dispatch(userFetched(u));
+ }
+ },
+ error: function(jqXHR, status, error) {
+ ErrorActions.ajaxError(e);
+ }
+ });
+ };
+}
+
+function initializeSession(dispatch, session) {
+ dispatch(userLoggedIn(session));
+ dispatch(fetch(session.UserId));
+ dispatch(AccountActions.fetchAll());
+ dispatch(SecurityActions.fetchAll());
+}
+
+function login(user) {
+ return function (dispatch) {
+ dispatch(loginUser());
+
+ $.ajax({
+ type: "POST",
+ dataType: "json",
+ url: "session/",
+ data: {user: user.toJSON()},
+ success: function(data, status, jqXHR) {
+ var e = new Error();
+ e.fromJSON(data);
+ if (e.isError()) {
+ ErrorActions.serverError(e);
+ } else {
+ var s = new Session();
+ s.fromJSON(data);
+ initializeSession(dispatch, s);
+ }
+ },
+ error: function(jqXHR, status, error) {
+ ErrorActions.ajaxError(e);
+ }
+ });
+ };
+}
+
+function tryResumingSession() {
+ return function (dispatch) {
+ $.ajax({
+ type: "GET",
+ dataType: "json",
+ url: "session/",
+ success: function(data, status, jqXHR) {
+ var e = new Error();
+ e.fromJSON(data);
+ if (e.isError()) {
+ if (e.ErrorId != 1 /* Not Signed In*/)
+ ErrorActions.serverError(e);
+ } else {
+ var s = new Session();
+ s.fromJSON(data);
+ dispatch(loginUser());
+ initializeSession(dispatch, s);
+ }
+ },
+ error: function(jqXHR, status, error) {
+ ErrorActions.ajaxError(e);
+ }
+ });
+ };
+}
+
+function logout() {
+ return function (dispatch) {
+ dispatch(logoutUser());
+
+ $.ajax({
+ type: "DELETE",
+ dataType: "json",
+ url: "session/",
+ success: function(data, status, jqXHR) {
+ var e = new Error();
+ e.fromJSON(data);
+ if (e.isError()) {
+ ErrorActions.serverError(e);
+ } else {
+ dispatch(userLoggedOut());
+ }
+ },
+ error: function(jqXHR, status, error) {
+ ErrorActions.ajaxError(e);
+ }
+ });
+ };
+}
+
+function update(user) {
+ return function (dispatch) {
+ dispatch(updateUser());
+
+ $.ajax({
+ type: "PUT",
+ dataType: "json",
+ url: "user/"+user.UserId+"/",
+ data: {user: user.toJSON()},
+ success: function(data, status, jqXHR) {
+ var e = new Error();
+ e.fromJSON(data);
+ if (e.isError()) {
+ ErrorActions.serverError(e);
+ } else {
+ var u = new User();
+ u.fromJSON(data);
+ dispatch(userUpdated(u));
+ }
+ },
+ error: function(jqXHR, status, error) {
+ ErrorActions.ajaxError(e);
+ }
+ });
+ };
+}
+
+module.exports = {
+ fetch: fetch,
+ login: login,
+ logout: logout,
+ update: update,
+ tryResumingSession: tryResumingSession
+};
diff --git a/js/constants/ErrorConstants.js b/js/constants/ErrorConstants.js
index a793905..d3a0d1c 100644
--- a/js/constants/ErrorConstants.js
+++ b/js/constants/ErrorConstants.js
@@ -5,4 +5,5 @@ module.exports = keyMirror({
ERROR_SERVER: null,
ERROR_CLIENT: null,
ERROR_USER: null,
+ CLEAR_ERROR: null
});
diff --git a/js/constants/SecurityConstants.js b/js/constants/SecurityConstants.js
new file mode 100644
index 0000000..5c38b5b
--- /dev/null
+++ b/js/constants/SecurityConstants.js
@@ -0,0 +1,12 @@
+var keyMirror = require('keymirror');
+
+module.exports = keyMirror({
+ FETCH_SECURITIES: null,
+ SECURITIES_FETCHED: null,
+ CREATE_SECURITY: null,
+ SECURITY_CREATED: null,
+ UPDATE_SECURITY: null,
+ SECURITY_UPDATED: null,
+ REMOVE_SECURITY: null,
+ SECURITY_REMOVED: null
+});
diff --git a/js/constants/UserConstants.js b/js/constants/UserConstants.js
new file mode 100644
index 0000000..f090be4
--- /dev/null
+++ b/js/constants/UserConstants.js
@@ -0,0 +1,12 @@
+var keyMirror = require('keymirror');
+
+module.exports = keyMirror({
+ LOGIN_USER: null,
+ USER_LOGGEDIN: null,
+ LOGOUT_USER: null,
+ USER_LOGGEDOUT: null,
+ FETCH_USER: null,
+ USER_FETCHED: null,
+ UPDATE_USER: null,
+ USER_UPDATED: null
+});
diff --git a/js/containers/AccountSettingsModalContainer.js b/js/containers/AccountSettingsModalContainer.js
new file mode 100644
index 0000000..1dc7761
--- /dev/null
+++ b/js/containers/AccountSettingsModalContainer.js
@@ -0,0 +1,21 @@
+var connect = require('react-redux').connect;
+
+var UserActions = require('../actions/UserActions');
+var AccountSettingsModal = require('../AccountSettingsModal');
+
+function mapStateToProps(state) {
+ return {
+ user: state.user
+ }
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ onUpdateUser: function(user) {dispatch(UserActions.update(user))}
+ }
+}
+
+module.exports = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(AccountSettingsModal)
diff --git a/js/containers/AccountsTabContainer.js b/js/containers/AccountsTabContainer.js
new file mode 100644
index 0000000..5381ae4
--- /dev/null
+++ b/js/containers/AccountsTabContainer.js
@@ -0,0 +1,33 @@
+var connect = require('react-redux').connect;
+
+var AccountActions = require('../actions/AccountActions');
+var AccountsTab = require('../AccountsTab');
+
+function mapStateToProps(state) {
+ var security_list = [];
+ for (var securityId in state.securities) {
+ if (state.securities.hasOwnProperty(securityId))
+ security_list.push(state.securities[securityId]);
+ }
+ return {
+ accounts: state.accounts.map,
+ accountChildren: state.accounts.children,
+ securities: state.securities,
+ security_list: security_list,
+ selectedAccount: state.selectedAccount
+ }
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ onCreateAccount: function(account) {dispatch(AccountActions.create(account))},
+ onUpdateAccount: function(account) {dispatch(AccountActions.update(account))},
+ onDeleteAccount: function(accountId) {dispatch(AccountActions.remove(accountId))},
+ onSelectAccount: function(accountId) {dispatch(AccountActions.select(accountId))}
+ }
+}
+
+module.exports = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(AccountsTab)
diff --git a/js/containers/MoneyGoAppContainer.js b/js/containers/MoneyGoAppContainer.js
new file mode 100644
index 0000000..50e192f
--- /dev/null
+++ b/js/containers/MoneyGoAppContainer.js
@@ -0,0 +1,22 @@
+var connect = require('react-redux').connect;
+
+var UserActions = require('../actions/UserActions');
+
+var MoneyGoApp = require('../MoneyGoApp');
+
+function mapStateToProps(state) {
+ return {
+ user: state.user
+ }
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ tryResumingSession: function() {dispatch(UserActions.tryResumingSession())},
+ }
+}
+
+module.exports = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(MoneyGoApp)
diff --git a/js/containers/TopBarContainer.js b/js/containers/TopBarContainer.js
new file mode 100644
index 0000000..c683152
--- /dev/null
+++ b/js/containers/TopBarContainer.js
@@ -0,0 +1,27 @@
+var connect = require('react-redux').connect;
+
+var UserActions = require('../actions/UserActions');
+var ErrorActions = require('../actions/ErrorActions');
+
+var TopBar = require('../TopBar');
+
+function mapStateToProps(state) {
+ return {
+ user: state.user,
+ error: state.error
+ }
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ onLogin: function(user) {dispatch(UserActions.login(user))},
+ onLogout: function() {dispatch(UserActions.logout())},
+ onUpdateUser: function(user) {dispatch(UserActions.update(user))},
+ onClearError: function() {dispatch(ErrorActions.clearError())}
+ }
+}
+
+module.exports = connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(TopBar)
diff --git a/js/main.js b/js/main.js
index 976cefd..1b5ab0f 100644
--- a/js/main.js
+++ b/js/main.js
@@ -8,7 +8,7 @@ var ReduxThunk = require('redux-thunk').default;
var Globalize = require('globalize');
var globalizeLocalizer = require('react-widgets/lib/localizers/globalize');
-var MoneyGoApp = require('./MoneyGoApp.js');
+var MoneyGoAppContainer = require('./containers/MoneyGoAppContainer');
var MoneyGoReducer = require('./reducers/MoneyGoReducer');
// Setup globalization for react-widgets
@@ -33,7 +33,7 @@ $(document).ready(function() {
ReactDOM.render(
-
+
,
document.getElementById("content")
);
diff --git a/js/models.js b/js/models.js
index f9955dd..51c8a8a 100644
--- a/js/models.js
+++ b/js/models.js
@@ -157,7 +157,6 @@ function Account() {
this.ParentAccountId = -1;
this.Type = -1;
this.Name = "";
- this.Children = []; // Not sent across JSON, just used internally
}
Account.prototype.toJSON = function() {
diff --git a/js/reducers/AccountReducer.js b/js/reducers/AccountReducer.js
index 16fb033..b9171db 100644
--- a/js/reducers/AccountReducer.js
+++ b/js/reducers/AccountReducer.js
@@ -1,8 +1,26 @@
var assign = require('object-assign');
var AccountConstants = require('../constants/AccountConstants');
+var UserConstants = require('../constants/UserConstants');
-module.exports = function(state = {}, action) {
+function accountChildren(accounts) {
+ var children = {};
+ for (var accountId in accounts) {
+ if (accounts.hasOwnProperty(accountId)) {
+ var parentAccountId = accounts[accountId].ParentAccountId;
+ if (!children.hasOwnProperty(parentAccountId))
+ children[parentAccountId] = [];
+ if (!children.hasOwnProperty(accountId))
+ children[accountId] = [];
+ children[parentAccountId].push(accountId);
+ }
+ }
+ return children;
+}
+
+const initialState = {map: {}, children: {}};
+
+module.exports = function(state = initialState, action) {
switch (action.type) {
case AccountConstants.ACCOUNTS_FETCHED:
var accounts = {};
@@ -10,17 +28,29 @@ module.exports = function(state = {}, action) {
var account = action.accounts[i];
accounts[account.AccountId] = account;
}
- return accounts;
+ return {
+ map: accounts,
+ children: accountChildren(accounts)
+ };
case AccountConstants.ACCOUNT_CREATED:
case AccountConstants.ACCOUNT_UPDATED:
var account = action.account;
- return assign({}, state, {
+ var accounts = assign({}, state.map, {
[account.AccountId]: account
});
+ return {
+ map: accounts,
+ children: accountChildren(accounts)
+ };
case AccountConstants.ACCOUNT_REMOVED:
- var newstate = assign({}, state);
- delete newstate[action.accountId];
- return newstate;
+ var accounts = assign({}, state.map);
+ delete accounts[action.accountId];
+ return {
+ map: accounts,
+ children: accountChildren(accounts)
+ };
+ case UserConstants.USER_LOGGEDOUT:
+ return initialState;
default:
return state;
}
diff --git a/js/reducers/ErrorReducer.js b/js/reducers/ErrorReducer.js
new file mode 100644
index 0000000..7d93f41
--- /dev/null
+++ b/js/reducers/ErrorReducer.js
@@ -0,0 +1,17 @@
+var ErrorConstants = require('../constants/ErrorConstants');
+
+var Error = require('../models').Error;
+
+module.exports = function(state = new Error(), action) {
+ switch (action.type) {
+ case ErrorConstants.ERROR_AJAX:
+ case ErrorConstants.ERROR_SERVER:
+ case ErrorConstants.ERROR_CLIENT:
+ case ErrorConstants.ERROR_USER:
+ return action.error;
+ case ErrorConstants.CLEAR_ERROR:
+ return new Error();
+ default:
+ return state;
+ }
+};
diff --git a/js/reducers/MoneyGoReducer.js b/js/reducers/MoneyGoReducer.js
index fdd8702..0c4e12e 100644
--- a/js/reducers/MoneyGoReducer.js
+++ b/js/reducers/MoneyGoReducer.js
@@ -1,9 +1,17 @@
var Redux = require('redux');
+var UserReducer = require('./UserReducer');
+var SessionReducer = require('./SessionReducer');
var AccountReducer = require('./AccountReducer');
+var SecurityReducer = require('./SecurityReducer');
var SelectedAccountReducer = require('./SelectedAccountReducer');
+var ErrorReducer = require('./ErrorReducer');
module.exports = Redux.combineReducers({
+ user: UserReducer,
+ session: SessionReducer,
accounts: AccountReducer,
- selectedAccount: SelectedAccountReducer
+ securities: SecurityReducer,
+ selectedAccount: SelectedAccountReducer,
+ error: ErrorReducer
});
diff --git a/js/reducers/SecurityReducer.js b/js/reducers/SecurityReducer.js
new file mode 100644
index 0000000..f4e6e44
--- /dev/null
+++ b/js/reducers/SecurityReducer.js
@@ -0,0 +1,30 @@
+var assign = require('object-assign');
+
+var SecurityConstants = require('../constants/SecurityConstants');
+var UserConstants = require('../constants/UserConstants');
+
+module.exports = function(state = {}, action) {
+ switch (action.type) {
+ case SecurityConstants.SECURITIES_FETCHED:
+ var securities = {};
+ for (var i = 0; i < action.securities.length; i++) {
+ var security = action.securities[i];
+ securities[security.SecurityId] = security;
+ }
+ return securities;
+ case SecurityConstants.SECURITY_CREATED:
+ case SecurityConstants.SECURITY_UPDATED:
+ var security = action.security;
+ return assign({}, state, {
+ [security.SecurityId]: security
+ });
+ case SecurityConstants.SECURITY_REMOVED:
+ var newstate = assign({}, state);
+ delete newstate[action.securityId];
+ return newstate;
+ case UserConstants.USER_LOGGEDOUT:
+ return {};
+ default:
+ return state;
+ }
+};
diff --git a/js/reducers/SelectedAccountReducer.js b/js/reducers/SelectedAccountReducer.js
index fa2f294..504e2cd 100644
--- a/js/reducers/SelectedAccountReducer.js
+++ b/js/reducers/SelectedAccountReducer.js
@@ -1,4 +1,5 @@
var AccountConstants = require('../constants/AccountConstants');
+var UserConstants = require('../constants/UserConstants');
module.exports = function(state = -1, action) {
switch (action.type) {
@@ -14,6 +15,8 @@ module.exports = function(state = -1, action) {
return state;
case AccountConstants.ACCOUNT_SELECTED:
return action.accountId;
+ case UserConstants.USER_LOGGEDOUT:
+ return -1;
default:
return state;
}
diff --git a/js/reducers/SessionReducer.js b/js/reducers/SessionReducer.js
new file mode 100644
index 0000000..1ce65b6
--- /dev/null
+++ b/js/reducers/SessionReducer.js
@@ -0,0 +1,14 @@
+var UserConstants = require('../constants/UserConstants');
+
+var Session = require('../models').Session;
+
+module.exports = function(state = new Session(), action) {
+ switch (action.type) {
+ case UserConstants.USER_LOGGEDIN:
+ return action.session;
+ case UserConstants.USER_LOGGEDOUT:
+ return new Session();
+ default:
+ return state;
+ }
+};
diff --git a/js/reducers/UserReducer.js b/js/reducers/UserReducer.js
new file mode 100644
index 0000000..21102dc
--- /dev/null
+++ b/js/reducers/UserReducer.js
@@ -0,0 +1,15 @@
+var UserConstants = require('../constants/UserConstants');
+
+var User = require('../models').User;
+
+module.exports = function(state = new User(), action) {
+ switch (action.type) {
+ case UserConstants.USER_FETCHED:
+ case UserConstants.USER_UPDATED:
+ return action.user;
+ case UserConstants.USER_LOGGEDOUT:
+ return new User();
+ default:
+ return state;
+ }
+};
diff --git a/js/utils.js b/js/utils.js
index 358464c..0e5b296 100644
--- a/js/utils.js
+++ b/js/utils.js
@@ -1,18 +1,19 @@
-const recursiveAccountDisplayInfo = function(account, prefix) {
+const recursiveAccountDisplayInfo = function(account, account_map, accountChildren, prefix) {
var name = prefix + account.Name;
var accounts = [{AccountId: account.AccountId, Name: name}];
- for (var i = 0; i < account.Children.length; i++)
- accounts = accounts.concat(recursiveAccountDisplayInfo(account.Children[i], name + "/"));
+ for (var i = 0; i < accountChildren[account.AccountId].length; i++)
+ accounts = accounts.concat(recursiveAccountDisplayInfo(account_map[accountChildren[account.AccountId][i]], account_map, accountChildren, name + "/"));
return accounts
};
-const getAccountDisplayList = function(account_list, includeRoot, rootName) {
+const getAccountDisplayList = function(account_map, accountChildren, includeRoot, rootName) {
var accounts = []
if (includeRoot)
accounts.push({AccountId: -1, Name: rootName});
- for (var i = 0; i < account_list.length; i++) {
- if (account_list[i].isRootAccount())
- accounts = accounts.concat(recursiveAccountDisplayInfo(account_list[i], ""));
+ for (var accountId in account_map) {
+ if (account_map.hasOwnProperty(accountId) &&
+ account_map[accountId].isRootAccount())
+ accounts = accounts.concat(recursiveAccountDisplayInfo(account_map[accountId], account_map, accountChildren, ""));
}
return accounts;
};