Initial JavaScript UI commit

Definitely still a work in progress
This commit is contained in:
Aaron Lindsay 2015-07-03 09:33:45 -04:00
parent 524d82ecf7
commit 084ada7e6f
16 changed files with 1035 additions and 0 deletions

26
static/accounts.js Normal file
View File

@ -0,0 +1,26 @@
// Import all the objects we want to use from ReactBootstrap
var ListGroup = ReactBootstrap.ListGroup;
var ListGroupItem = ReactBootstrap.ListGroupItem;
var AccountList = React.createClass({
getInitialState: function() {
return {
};
},
render: function() {
var accounts = this.props.accounts;
var account_map = this.props.account_map;
var listGroupItems;
for (var i = 0; i < accounts.length; i++) {
listGroupItems += <ListGroupItem>{accounts[i].Name}</ListGroupItem>;
}
return (
<ListGroup>
{listGroupItems}
</ListGroup>
);
}
});

1
static/external/big/big.min.js vendored Normal file

File diff suppressed because one or more lines are too long

BIN
static/external/fonts/rw-widgets.eot vendored Normal file

Binary file not shown.

17
static/external/fonts/rw-widgets.svg vendored Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Copyright (C) 2014 by original authors @ fontello.com</metadata>
<defs>
<font id="rw-widgets" horiz-adv-x="1000" >
<font-face font-family="rw-widgets" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
<missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="up-dir" unicode="&#xe800;" d="m0 171q0 15 11 26l250 250q10 10 25 10t25-10l250-250q10-11 10-26t-10-25-25-10h-500q-15 0-25 10t-11 25z" horiz-adv-x="571.4" />
<glyph glyph-name="down-dir" unicode="&#xe801;" d="m0 457q0 15 11 25t25 11h500q14 0 25-11t10-25-10-25l-250-250q-11-11-25-11t-25 11l-250 250q-11 11-11 25z" horiz-adv-x="571.4" />
<glyph glyph-name="right-dir" unicode="&#xe806;" d="m0 100v500q0 15 11 25t25 11 25-11l250-250q10-10 10-25t-10-25l-250-250q-11-11-25-11t-25 11-11 25z" horiz-adv-x="357.1" />
<glyph glyph-name="left-dir" unicode="&#xe807;" d="m36 350q0 15 10 25l250 250q11 11 25 11t26-11 10-25v-500q0-14-10-25t-26-11-25 11l-250 250q-10 11-10 25z" horiz-adv-x="357.1" />
<glyph glyph-name="calendar" unicode="&#xe808;" d="m0-79v715q0 29 21 50t50 21h72v54q0 36 26 63t63 26h36q37 0 63-26t26-63v-54h214v54q0 36 27 63t63 26h35q37 0 63-26t27-63v-54h71q29 0 50-21t22-50v-715q0-29-22-50t-50-21h-786q-29 0-50 21t-21 50z m71 0h161v161h-161v-161z m0 197h161v178h-161v-178z m0 214h161v161h-161v-161z m143 268q0-7 6-13t12-5h36q7 0 12 5t6 13v161q0 7-6 12t-12 6h-36q-7 0-12-6t-6-12v-161z m54-679h178v161h-178v-161z m0 197h178v178h-178v-178z m0 214h178v161h-178v-161z m214-411h179v161h-179v-161z m0 197h179v178h-179v-178z m0 214h179v161h-179v-161z m161 268q0-7 5-13t13-5h35q8 0 13 5t5 13v161q0 7-5 12t-13 6h-35q-8 0-13-6t-5-12v-161z m53-679h161v161h-161v-161z m0 197h161v178h-161v-178z m0 214h161v161h-161v-161z" horiz-adv-x="928.6" />
<glyph glyph-name="clock" unicode="&#xe80c;" d="m0 350q0 117 58 215t155 156 216 58 215-58 156-156 57-215-57-215-156-156-215-58-216 58-155 156-58 215z m125 0q0-83 41-152t110-111 153-41 152 41 110 111 41 152-41 152-110 111-152 41-153-41-110-111-41-152z m161-54v36q0 8 5 13t13 5h125v196q0 8 5 13t12 5h36q8 0 13-5t5-13v-250q0-7-5-12t-13-5h-178q-8 0-13 5t-5 12z" horiz-adv-x="857.1" />
</font>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
static/external/fonts/rw-widgets.ttf vendored Normal file

Binary file not shown.

BIN
static/external/fonts/rw-widgets.woff vendored Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

34
static/index.html Normal file
View File

@ -0,0 +1,34 @@
<html>
<head>
<title>MoneyGo</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="static/external/react-widgets/react-widgets.css">
<link rel="stylesheet" href="static/stylesheet.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.12.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.12.2/JSXTransformer.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/globalize/0.1.1/globalize.min.js"></script>
<script src="static/external/react-bootstrap/react-bootstrap.min.js"></script>
<script src="static/external/react-widgets/react-widgets.js"></script>
<script src="static/external/big/big.min.js"></script>
<script type="text/javascript" src="static/utils.js"></script>
<script type="text/javascript" src="static/models.js"></script>
<script type="text/jsx" src="static/top_bar.js"></script>
<script type="text/jsx" src="static/accounts.js"></script>
<script type="text/jsx" src="static/ui.js"></script>
</head>
<body>
<center>
<div id="content"></div>
</center>
</body>
</html>

274
static/models.js Normal file
View File

@ -0,0 +1,274 @@
function getJSONObj(json_input) {
if (typeof json_input == "string")
return $.parseJSON(json_input)
else if (typeof json_input == "object")
return json_input;
console.error("Unable to parse json:", json_input);
return null
}
function User() {
this.UserId = -1;
this.Name = "";
this.Username = "";
this.Password = "";
this.Email = "";
}
var BogusPassword = "password";
User.prototype.toJSON = function() {
var json_obj = {};
json_obj.UserId = this.UserId;
json_obj.Name = this.Name;
json_obj.Username = this.Username;
json_obj.Password = this.Password;
json_obj.Email = this.Email;
return JSON.stringify(json_obj);
}
User.prototype.fromJSON = function(json_input) {
var json_obj = getJSONObj(json_input);
if (json_obj.hasOwnProperty("UserId"))
this.UserId = json_obj.UserId;
if (json_obj.hasOwnProperty("Name"))
this.Name = json_obj.Name;
if (json_obj.hasOwnProperty("Username"))
this.Username = json_obj.Username;
if (json_obj.hasOwnProperty("Password"))
this.Password = json_obj.Password;
if (json_obj.hasOwnProperty("Email"))
this.Email = json_obj.Email;
}
User.prototype.isUser = function() {
var empty_user = new User();
return this.UserId != empty_user.UserId ||
this.Username != empty_user.Username;
}
function Session() {
this.SessionId = -1;
this.UserId = -1;
}
Session.prototype.toJSON = function() {
var json_obj = {};
json_obj.SessionId = this.SessionId;
json_obj.UserId = this.UserId;
return JSON.stringify(json_obj);
}
Session.prototype.fromJSON = function(json_input) {
var json_obj = getJSONObj(json_input);
if (json_obj.hasOwnProperty("SessionId"))
this.SessionId = json_obj.SessionId;
if (json_obj.hasOwnProperty("UserId"))
this.UserId = json_obj.UserId;
}
Session.prototype.isSession = function() {
var empty_session = new Session();
return this.SessionId != empty_session.SessionId ||
this.UserId != empty_session.UserId;
}
var AccountType = {
Bank: 1,
Cash: 2,
Asset: 3,
Liability: 4,
Investment: 5,
Income: 6,
Expense: 7
}
function Account() {
this.AccountId = -1;
this.UserId = -1;
this.SecurityId = -1;
this.ParentAccountId = -1;
this.Type = -1;
this.Name = "";
this.Children = []; // Not sent across JSON, just used internally
}
Account.prototype.toJSON = function() {
var json_obj = {};
json_obj.AccountId = this.AccountId;
json_obj.UserId = this.UserId;
json_obj.SecurityId = this.SecurityId;
json_obj.ParentAccountId = this.ParentAccountId;
json_obj.Type = this.Type;
json_obj.Name = this.Name;
return JSON.stringify(json_obj);
}
Account.prototype.fromJSON = function(json_input) {
var json_obj = getJSONObj(json_input);
if (json_obj.hasOwnProperty("AccountId"))
this.AccountId = json_obj.AccountId;
if (json_obj.hasOwnProperty("UserId"))
this.UserId = json_obj.UserId;
if (json_obj.hasOwnProperty("SecurityId"))
this.SecurityId = json_obj.SecurityId;
if (json_obj.hasOwnProperty("ParentAccountId"))
this.ParentAccountId = json_obj.ParentAccountId;
if (json_obj.hasOwnProperty("Type"))
this.Type = json_obj.Type;
if (json_obj.hasOwnProperty("Name"))
this.Name = json_obj.Name;
}
Account.prototype.isAccount = function() {
var empty_account = new Account();
return this.AccountId != empty_account.AccountId ||
this.UserId != empty_account.UserId;
}
Account.prototype.isRootAccount = function() {
var empty_account = new Account();
return this.ParentAccountId == empty_account.ParentAccountId;
}
function Split() {
this.SplitId = -1;
this.TransactionId = -1;
this.AccountId = -1;
this.Number = "";
this.Memo = "";
this.Amount = new Big(0.0);
this.Debit = false;
}
Split.prototype.toJSONobj = function() {
var json_obj = {};
json_obj.SplitId = this.SplitId;
json_obj.TransactionId = this.TransactionId;
json_obj.AccountId = this.AccountId;
json_obj.Number = this.Number;
json_obj.Memo = this.Memo;
json_obj.Amount = this.Amount.toFixed();
json_obj.Debit = this.Debit;
return json_obj;
}
Split.prototype.fromJSONobj = function(json_obj) {
var json_obj = getJSONObj(json_input);
if (json_obj.hasOwnProperty("SplitId"))
this.SplitId = json_obj.SplitId;
if (json_obj.hasOwnProperty("TransactionId"))
this.TransactionId = json_obj.TransactionId;
if (json_obj.hasOwnProperty("AccountId"))
this.AccountId = json_obj.AccountId;
if (json_obj.hasOwnProperty("Number"))
this.Number = json_obj.Number;
if (json_obj.hasOwnProperty("Memo"))
this.Memo = json_obj.Memo;
if (json_obj.hasOwnProperty("Amount"))
this.Amount = new Big(json_obj.Amount);
if (json_obj.hasOwnProperty("Debit"))
this.Debit = json_obj.Debit;
}
Split.prototype.isSplit = function() {
var empty_split = new Split();
return this.SplitId != empty_split.SplitId ||
this.TransactionId != empty_split.TransactionId ||
this.AccountId != empty_split.AccountId;
}
var TransactionStatus = {
Entered: 1,
Cleared: 2,
Reconciled: 3,
Voided: 4
}
function Transaction() {
this.TransactionId = -1;
this.UserId = -1;
this.Description = "";
this.Status = -1;
this.Date = new Date();
this.Splits = [];
}
Transaction.prototype.toJSON = function() {
var json_obj = {};
json_obj.TransactionId = this.TransactionId;
json_obj.UserId = this.UserId;
json_obj.Description = this.Description;
json_obj.Status = this.Status;
json_obj.Date = this.Date.toJSON();
json_obj.Splits = [];
for (var i = 0; i < this.Splits.length; i++)
json_obj.push(this.Splits[i].toJSONobj());
return json_obj;
}
Transaction.prototype.fromJSON = function(json_input) {
var json_obj = getJSONObj(json_input);
if (json_obj.hasOwnProperty("TransactionId"))
this.TransactionId = json_obj.TransactionId;
if (json_obj.hasOwnProperty("UserId"))
this.UserId = json_obj.UserId;
if (json_obj.hasOwnProperty("Description"))
this.Description = json_obj.Description;
if (json_obj.hasOwnProperty("Status"))
this.Status = json_obj.Status;
if (json_obj.hasOwnProperty("Date")) {
this.Date = json_obj.Date
if (typeof this.Date === 'string') {
var t = Date.parse(this.Date);
if (t)
this.Date = new Date(t);
else
this.Date = new Date(0);
} else
this.Date = new Date(0);
}
if (json_obj.hasOwnProperty("Splits")) {
for (var i = 0; i < json_obj.Splits.length; i++)
this.Splits.push(this.Splits[i].fromJSON());
}
}
Transaction.prototype.isTransaction = function() {
var empty_transaction = new Transaction();
return this.TransactionId != empty_transaction.TransactionId ||
this.UserId != empty_transaction.UserId;
}
function Error() {
this.ErrorId = -1;
this.ErrorString = "";
}
Error.prototype.toJSON = function() {
var json_obj = {};
json_obj.ErrorId = this.ErrorId;
json_obj.ErrorString = this.ErrorString;
return JSON.stringify(json_obj);
}
Error.prototype.fromJSON = function(json_input) {
var json_obj = getJSONObj(json_input);
if (json_obj.hasOwnProperty("ErrorId"))
this.ErrorId = json_obj.ErrorId;
if (json_obj.hasOwnProperty("ErrorString"))
this.ErrorString = json_obj.ErrorString;
}
Error.prototype.isError = function() {
var empty_error = new Error();
return this.ErrorId != empty_error.ErrorId ||
this.ErrorString != empty_error.ErrorString;
}

5
static/stylesheet.css Normal file
View File

@ -0,0 +1,5 @@
div#content {
width: 95%;
min-width: 75em;
max-width: 100em;
}

117
static/top_bar.js Normal file
View File

@ -0,0 +1,117 @@
// Import all the objects we want to use from ReactBootstrap
var Alert = ReactBootstrap.Alert;
var Input = ReactBootstrap.Input;
var Button = ReactBootstrap.Button;
var DropdownButton = ReactBootstrap.DropdownButton;
var MenuItem = ReactBootstrap.MenuItem;
var Row = ReactBootstrap.Row;
var Col = ReactBootstrap.Col;
var LoginBar = React.createClass({
getInitialState: function() {
return {username: '', password: ''};
},
onUsernameChange: function(e) {
this.setState({username: e.target.value});
},
onPasswordChange: function(e) {
this.setState({password: e.target.value});
},
handleSubmit: function(e) {
var user = new User();
e.preventDefault();
user.Username = this.refs.username.getValue();
user.Password = this.refs.password.getValue();
this.props.onLoginSubmit(user);
},
handleNewUserSubmit: function(e) {
e.preventDefault();
this.props.onCreateNewUser();
},
render: function() {
return (
<form onSubmit={this.handleSubmit}>
<Input wrapperClassName="wrapper">
<Row>
<Col xs={4}></Col>
<Col xs={2}>
<Button bsStyle="link"
onClick={this.handleNewUserSubmit}>Create New User</Button>
</Col>
<Col xs={2}>
<Input type="text"
placeholder="Username..."
ref="username"/>
</Col>
<Col xs={2}>
<Input type="password"
placeholder="Password..."
ref="password" block/>
</Col>
<Col xs={2}>
<Button type="submit" bsStyle="primary" block>
Login</Button>
</Col>
</Row>
</Input>
</form>
);
}
});
var LogoutBar = React.createClass({
handleOnSelect: function(key) {
if (key == 1) {
if (this.props.onAccountSettings != null)
this.props.onAccountSettings();
} else if (key == 2) {
this.props.onLogoutSubmit();
}
},
render: function() {
var signedInString = "Signed in as "+this.props.user.Name;
return (
<Input wrapperClassName="wrapper">
<Row>
<Col xs={2}><label className="control-label pull-left">Money<i>Go</i></label></Col>
<Col xs={6}></Col>
<Col xs={4}>
<div className="pull-right">
<DropdownButton title={signedInString} onSelect={this.handleOnSelect} bsStyle="info">
<MenuItem eventKey="1">Account Settings</MenuItem>
<MenuItem eventKey="2">Logout</MenuItem>
</DropdownButton>
</div>
</Col>
</Row>
</Input>
);
}
});
var TopBar = React.createClass({
render: function() {
var barContents;
var errorAlert;
if (!this.props.user.isUser())
barContents = <LoginBar onLoginSubmit={this.props.onLoginSubmit} onCreateNewUser={this.props.onCreateNewUser} />;
else
barContents = <LogoutBar user={this.props.user} onLogoutSubmit={this.props.onLogoutSubmit} onAccountSettings={this.props.onAccountSettings}/>;
if (this.props.error.isError())
errorAlert =
<Alert bsStyle="danger" onDismiss={this.props.onErrorClear}>
<h4>Error!</h4>
<p>Error {this.props.error.ErrorId}: {this.props.error.ErrorString}</p>
<Button onClick={this.props.onErrorClear}>Clear</Button>
</Alert>;
return (
<div>
{barContents}
{errorAlert}
</div>
);
}
});

551
static/ui.js Normal file
View File

@ -0,0 +1,551 @@
// Import all the objects we want to use from ReactBootstrap
var Jumbotron = ReactBootstrap.Jumbotron;
var Panel = ReactBootstrap.Panel;
var ButtonGroup = ReactBootstrap.ButtonGroup;
var NewUserForm = React.createClass({
getInitialState: function() {
return {error: "",
name: "",
username: "",
email: "",
password: "",
confirm_password: "",
passwordChanged: false,
initial_password: ""};
},
passwordValidationState: function() {
if (this.state.passwordChanged) {
if (this.state.password.length >= 10)
return "success";
else if (this.state.password.length >= 6)
return "warning";
else
return "error";
}
},
confirmPasswordValidationState: function() {
if (this.state.confirm_password.length > 0) {
if (this.state.confirm_password == this.state.password)
return "success";
else
return "error";
}
},
handleCancel: function() {
if (this.props.onCancel != null)
this.props.onCancel();
},
handleChange: function() {
if (this.refs.password.getValue() != this.state.initial_password)
this.setState({passwordChanged: true});
this.setState({
name: this.refs.name.getValue(),
username: this.refs.username.getValue(),
email: this.refs.email.getValue(),
password: this.refs.password.getValue(),
confirm_password: this.refs.confirm_password.getValue()
});
},
handleSubmit: function(e) {
var u = new User();
var error = "";
e.preventDefault();
u.Name = this.state.name;
u.Username = this.state.username;
u.Email = this.state.email;
u.Password = this.state.password;
if (u.Password != this.state.confirm_password) {
this.setState({error: "Error: password do not match"});
return;
}
this.handleCreateNewUser(u);
},
handleCreateNewUser: function(user) {
$.ajax({
type: "POST",
dataType: "json",
url: "user/",
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.props.onNewUser();
}
}.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),
});
},
render: function() {
var title = <h3>Create New User</h3>;
return (
<Panel header={title} bsStyle="info">
<span color="red">{this.state.error}</span>
<form onSubmit={this.handleSubmit}
className="form-horizontal">
<Input type="text"
label="Name"
value={this.state.name}
onChange={this.handleChange}
ref="name"
labelClassName="col-xs-2"
wrapperClassName="col-xs-10"/>
<Input type="text"
label="Username"
value={this.state.username}
onChange={this.handleChange}
ref="username"
labelClassName="col-xs-2"
wrapperClassName="col-xs-10"/>
<Input type="email"
label="Email"
value={this.state.email}
onChange={this.handleChange}
ref="email"
labelClassName="col-xs-2"
wrapperClassName="col-xs-10"/>
<Input type="password"
label="Password"
value={this.state.password}
onChange={this.handleChange}
ref="password"
labelClassName="col-xs-2"
wrapperClassName="col-xs-10"
bsStyle={this.passwordValidationState()}
hasFeedback/>
<Input type="password"
label="Confirm Password"
value={this.state.confirm_password}
onChange={this.handleChange}
ref="confirm_password"
labelClassName="col-xs-2"
wrapperClassName="col-xs-10"
bsStyle={this.confirmPasswordValidationState()}
hasFeedback/>
<ButtonGroup className="pull-right">
<Button onClick={this.handleCancel}
bsStyle="warning">Cancel</Button>
<Button type="submit"
bsStyle="success">Create New User</Button>
</ButtonGroup>
</form>
</Panel>
);
}
});
var AccountSettings = React.createClass({
getInitialState: function() {
return {error: "",
name: this.props.user.Name,
username: this.props.user.Username,
email: this.props.user.Email,
password: BogusPassword,
confirm_password: BogusPassword,
passwordChanged: false,
initial_password: BogusPassword};
},
passwordValidationState: function() {
if (this.state.passwordChanged) {
if (this.state.password.length >= 10)
return "success";
else if (this.state.password.length >= 6)
return "warning";
else
return "error";
}
},
confirmPasswordValidationState: function() {
if (this.state.confirm_password.length > 0) {
if (this.state.confirm_password == this.state.password)
return "success";
else
return "error";
}
},
handleCancel: function() {
if (this.props.onCancel != null)
this.props.onCancel();
},
handleChange: function() {
if (this.refs.password.getValue() != this.state.initial_password)
this.setState({passwordChanged: true});
this.setState({
name: this.refs.name.getValue(),
username: this.refs.username.getValue(),
email: this.refs.email.getValue(),
password: this.refs.password.getValue(),
confirm_password: this.refs.confirm_password.getValue()
});
},
handleSubmit: function(e) {
var u = new User();
var error = "";
e.preventDefault();
u.UserId = this.props.user.UserId;
u.Name = this.state.name;
u.Username = this.state.username;
u.Email = this.state.email;
if (this.state.passwordChanged) {
u.Password = this.state.password;
if (u.Password != this.state.confirm_password) {
this.setState({error: "Error: password do not match"});
return;
}
} else {
u.Password = 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.onSettingsSubmitted(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),
});
},
render: function() {
var title = <h3>Edit Account Settings</h3>;
return (
<Panel header={title} bsStyle="info">
<span color="red">{this.state.error}</span>
<form onSubmit={this.handleSubmit}
className="form-horizontal">
<Input type="text"
label="Name"
value={this.state.name}
onChange={this.handleChange}
ref="name"
labelClassName="col-xs-2"
wrapperClassName="col-xs-10"/>
<Input type="text"
label="Username"
value={this.state.username}
onChange={this.handleChange}
ref="username"
labelClassName="col-xs-2"
wrapperClassName="col-xs-10"/>
<Input type="email"
label="Email"
value={this.state.email}
onChange={this.handleChange}
ref="email"
labelClassName="col-xs-2"
wrapperClassName="col-xs-10"/>
<Input type="password"
label="Password"
value={this.state.password}
onChange={this.handleChange}
ref="password"
labelClassName="col-xs-2"
wrapperClassName="col-xs-10"
bsStyle={this.passwordValidationState()}
hasFeedback/>
<Input type="password"
label="Confirm Password"
value={this.state.confirm_password}
onChange={this.handleChange}
ref="confirm_password"
labelClassName="col-xs-2"
wrapperClassName="col-xs-10"
bsStyle={this.confirmPasswordValidationState()}
hasFeedback/>
<ButtonGroup className="pull-right">
<Button onClick={this.handleCancel}
bsStyle="warning">Cancel</Button>
<Button type="submit"
bsStyle="success">Save Settings</Button>
</ButtonGroup>
</form>
</Panel>
);
}
});
var MoneyGoApp = React.createClass({
getInitialState: function() {
return {
hash: "home",
session: new Session(),
user: new User(),
accounts: [],
account_map: {},
error: new Error()
};
},
componentDidMount: function() {
this.getSession();
this.handleHashChange();
if ("onhashchange" in window) {
window.onhashchange = this.handleHashChange;
}
},
handleHashChange: function() {
var hash = location.hash.replace(/^#/, '');
if (hash.length == 0)
hash = "home";
if (hash != this.state.hash)
this.setHash(hash);
},
setHash: function(hash) {
location.hash = hash;
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();
}.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
});
},
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.setHash("account");
},
handleSettingsSubmitted: function(user) {
this.setState({user: user});
this.setHash("home");
},
handleCreateNewUser: function() {
this.setHash("new_user");
},
handleGoHome: function(user) {
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 = <NewUserForm onNewUser={this.handleGoHome} onCancel={this.handleGoHome}/>
} else if (this.state.hash == "account" && this.state.user.isUser()) {
mainContent = <AccountSettings user={this.state.user} onSettingsSubmitted={this.handleSettingsSubmitted} onCancel={this.handleGoHome}/>
} else {
if (this.state.user.isUser())
mainContent = <AccountList
accounts={this.state.accounts}
account_map={this.state.account_map}
onCreateAccount={this.handleCreateAccount}
onUpdateAccount={this.handleUpdateAccount}
onDeleteAccount={this.handleDeleteAccount} />
else
mainContent =
<Jumbotron>
<h1>Money<i>Go</i></h1>
<p><i>Go</i> manage your money.</p>
</Jumbotron>
}
return (
<div>
<TopBar
error={this.state.error}
onErrorClear={this.handleErrorClear}
onLoginSubmit={this.handleLoginSubmit}
onCreateNewUser={this.handleCreateNewUser}
user={this.state.user}
onAccountSettings={this.handleAccountSettings}
onLogoutSubmit={this.handleLogoutSubmit} />
{mainContent}
</div>
);
}
});
React.render(<MoneyGoApp />, document.getElementById("content"));