mirror of
https://github.com/aclindsa/moneygo.git
synced 2024-12-26 23:42:29 -05:00
Move to using npm/browserify to package everything
This means it now requires the Javascript to be compiled before it can be run. This move also required a massive reorganization and lots of debugging/fixups to make everything work properly again.
This commit is contained in:
parent
6856d617ec
commit
2621f64cc7
13
README
Normal file
13
README
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
MoneyGo README
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
First, install npm in your distribution:
|
||||||
|
$ sudo pacman -S npm
|
||||||
|
|
||||||
|
Install browserify globally:
|
||||||
|
$ sudo npm install -g browserify
|
||||||
|
|
||||||
|
Next, install browserify, babel, react, react-bootstrap, react-widgets, and globalize in our directory using npm:
|
||||||
|
$ cd static && npm install browserify react react-dom react-addons-update react-bootstrap react-widgets babelify babel-preset-react globalize cldr-data
|
37
static/AccountCombobox.js
Normal file
37
static/AccountCombobox.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
var React = require('react');
|
||||||
|
|
||||||
|
var Combobox = require('react-widgets').Combobox;
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: "AccountCombobox",
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {
|
||||||
|
includeRoot: true,
|
||||||
|
rootName: "New Top-level Account"
|
||||||
|
};
|
||||||
|
},
|
||||||
|
handleAccountChange: function(account) {
|
||||||
|
if (this.props.onChange != null &&
|
||||||
|
account.hasOwnProperty('AccountId') &&
|
||||||
|
(this.props.account_map.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 className = "";
|
||||||
|
if (this.props.className)
|
||||||
|
className = this.props.className;
|
||||||
|
return (
|
||||||
|
<Combobox
|
||||||
|
data={accounts}
|
||||||
|
valueField='AccountId'
|
||||||
|
textField='Name'
|
||||||
|
defaultValue={this.props.value}
|
||||||
|
onChange={this.handleAccountChange}
|
||||||
|
ref="account"
|
||||||
|
className={className} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -1,28 +1,35 @@
|
|||||||
// Import all the objects we want to use from ReactBootstrap
|
var React = require('react');
|
||||||
|
var ReactDOM = require('react-dom');
|
||||||
|
|
||||||
|
var react_update = require('react-addons-update');
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap');
|
||||||
var Alert = ReactBootstrap.Alert;
|
var Alert = ReactBootstrap.Alert;
|
||||||
var Modal = ReactBootstrap.Modal;
|
var Modal = ReactBootstrap.Modal;
|
||||||
var Pagination = ReactBootstrap.Pagination;
|
var Pagination = ReactBootstrap.Pagination;
|
||||||
|
|
||||||
var Label = ReactBootstrap.Label;
|
var Label = ReactBootstrap.Label;
|
||||||
var Table = ReactBootstrap.Table;
|
var Table = ReactBootstrap.Table;
|
||||||
var Grid = ReactBootstrap.Grid;
|
var Grid = ReactBootstrap.Grid;
|
||||||
var Row = ReactBootstrap.Row;
|
var Row = ReactBootstrap.Row;
|
||||||
var Col = ReactBootstrap.Col;
|
var Col = ReactBootstrap.Col;
|
||||||
var Panel = ReactBootstrap.Panel;
|
var Panel = ReactBootstrap.Panel;
|
||||||
|
var Input = ReactBootstrap.Input;
|
||||||
var Button = ReactBootstrap.Button;
|
var Button = ReactBootstrap.Button;
|
||||||
|
var ButtonGroup = ReactBootstrap.ButtonGroup;
|
||||||
var ButtonToolbar = ReactBootstrap.ButtonToolbar;
|
var ButtonToolbar = ReactBootstrap.ButtonToolbar;
|
||||||
|
|
||||||
var ProgressBar = ReactBootstrap.ProgressBar;
|
var ProgressBar = ReactBootstrap.ProgressBar;
|
||||||
|
var Glyphicon = ReactBootstrap.Glyphicon;
|
||||||
|
|
||||||
var DateTimePicker = ReactWidgets.DateTimePicker;
|
var DateTimePicker = require('react-widgets').DateTimePicker;
|
||||||
|
var Combobox = require('react-widgets').Combobox;
|
||||||
|
|
||||||
|
var AccountCombobox = require('./AccountCombobox.js');
|
||||||
|
|
||||||
const TransactionRow = React.createClass({
|
const TransactionRow = React.createClass({
|
||||||
handleClick: function(e) {
|
handleClick: function(e) {
|
||||||
const refs = ["date", "number", "description", "account", "status", "amount"];
|
const refs = ["date", "number", "description", "account", "status", "amount"];
|
||||||
for (var ref in refs) {
|
for (var ref in refs) {
|
||||||
if (this.refs[refs[ref]].getDOMNode() == e.target) {
|
if (this.refs[refs[ref]] == e.target) {
|
||||||
this.props.onEdit(this.props.transaction, refs[ref]);
|
this.props.onEdit(this.props.transaction, refs[ref]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -135,7 +142,7 @@ const AmountInput = React.createClass({
|
|||||||
var symbol = "?";
|
var symbol = "?";
|
||||||
if (this.props.security)
|
if (this.props.security)
|
||||||
symbol = this.props.security.Symbol;
|
symbol = this.props.security.Symbol;
|
||||||
var bsStyle = "";
|
var bsStyle = undefined;
|
||||||
if (this.props.bsStyle)
|
if (this.props.bsStyle)
|
||||||
bsStyle = this.props.bsStyle;
|
bsStyle = this.props.bsStyle;
|
||||||
|
|
||||||
@ -173,7 +180,7 @@ const AddEditTransactionModal = React.createClass({
|
|||||||
},
|
},
|
||||||
handleDescriptionChange: function() {
|
handleDescriptionChange: function() {
|
||||||
this.setState({
|
this.setState({
|
||||||
transaction: React.addons.update(this.state.transaction, {
|
transaction: react_update(this.state.transaction, {
|
||||||
Description: {$set: this.refs.description.getValue()}
|
Description: {$set: this.refs.description.getValue()}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -182,7 +189,7 @@ const AddEditTransactionModal = React.createClass({
|
|||||||
if (date == null)
|
if (date == null)
|
||||||
return;
|
return;
|
||||||
this.setState({
|
this.setState({
|
||||||
transaction: React.addons.update(this.state.transaction, {
|
transaction: react_update(this.state.transaction, {
|
||||||
Date: {$set: date}
|
Date: {$set: date}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -190,7 +197,7 @@ const AddEditTransactionModal = React.createClass({
|
|||||||
handleStatusChange: function(status) {
|
handleStatusChange: function(status) {
|
||||||
if (status.hasOwnProperty('StatusId')) {
|
if (status.hasOwnProperty('StatusId')) {
|
||||||
this.setState({
|
this.setState({
|
||||||
transaction: React.addons.update(this.state.transaction, {
|
transaction: react_update(this.state.transaction, {
|
||||||
Status: {$set: status.StatusId}
|
Status: {$set: status.StatusId}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -198,21 +205,21 @@ const AddEditTransactionModal = React.createClass({
|
|||||||
},
|
},
|
||||||
handleAddSplit: function() {
|
handleAddSplit: function() {
|
||||||
this.setState({
|
this.setState({
|
||||||
transaction: React.addons.update(this.state.transaction, {
|
transaction: react_update(this.state.transaction, {
|
||||||
Splits: {$push: [new Split()]}
|
Splits: {$push: [new Split()]}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleDeleteSplit: function(split) {
|
handleDeleteSplit: function(split) {
|
||||||
this.setState({
|
this.setState({
|
||||||
transaction: React.addons.update(this.state.transaction, {
|
transaction: react_update(this.state.transaction, {
|
||||||
Splits: {$splice: [[split, 1]]}
|
Splits: {$splice: [[split, 1]]}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleUpdateNumber: function(split) {
|
handleUpdateNumber: function(split) {
|
||||||
var transaction = this.state.transaction;
|
var transaction = this.state.transaction;
|
||||||
transaction.Splits[split] = React.addons.update(transaction.Splits[split], {
|
transaction.Splits[split] = react_update(transaction.Splits[split], {
|
||||||
Number: {$set: this.refs['number-'+split].getValue()}
|
Number: {$set: this.refs['number-'+split].getValue()}
|
||||||
});
|
});
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -221,7 +228,7 @@ const AddEditTransactionModal = React.createClass({
|
|||||||
},
|
},
|
||||||
handleUpdateMemo: function(split) {
|
handleUpdateMemo: function(split) {
|
||||||
var transaction = this.state.transaction;
|
var transaction = this.state.transaction;
|
||||||
transaction.Splits[split] = React.addons.update(transaction.Splits[split], {
|
transaction.Splits[split] = react_update(transaction.Splits[split], {
|
||||||
Memo: {$set: this.refs['memo-'+split].getValue()}
|
Memo: {$set: this.refs['memo-'+split].getValue()}
|
||||||
});
|
});
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -230,7 +237,7 @@ const AddEditTransactionModal = React.createClass({
|
|||||||
},
|
},
|
||||||
handleUpdateAccount: function(account, split) {
|
handleUpdateAccount: function(account, split) {
|
||||||
var transaction = this.state.transaction;
|
var transaction = this.state.transaction;
|
||||||
transaction.Splits[split] = React.addons.update(transaction.Splits[split], {
|
transaction.Splits[split] = react_update(transaction.Splits[split], {
|
||||||
SecurityId: {$set: -1},
|
SecurityId: {$set: -1},
|
||||||
AccountId: {$set: account.AccountId}
|
AccountId: {$set: account.AccountId}
|
||||||
});
|
});
|
||||||
@ -240,7 +247,7 @@ const AddEditTransactionModal = React.createClass({
|
|||||||
},
|
},
|
||||||
handleUpdateAmount: function(split) {
|
handleUpdateAmount: function(split) {
|
||||||
var transaction = this.state.transaction;
|
var transaction = this.state.transaction;
|
||||||
transaction.Splits[split] = React.addons.update(transaction.Splits[split], {
|
transaction.Splits[split] = react_update(transaction.Splits[split], {
|
||||||
Amount: {$set: new Big(this.refs['amount-'+split].getValue())}
|
Amount: {$set: new Big(this.refs['amount-'+split].getValue())}
|
||||||
});
|
});
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -294,7 +301,7 @@ const AddEditTransactionModal = React.createClass({
|
|||||||
var self = this;
|
var self = this;
|
||||||
var s = this.state.transaction.Splits[i];
|
var s = this.state.transaction.Splits[i];
|
||||||
var security = null;
|
var security = null;
|
||||||
var amountValidation = "";
|
var amountValidation = undefined;
|
||||||
var accountValidation = "";
|
var accountValidation = "";
|
||||||
if (s.AccountId in this.props.account_map) {
|
if (s.AccountId in this.props.account_map) {
|
||||||
security = this.props.security_map[this.props.account_map[s.AccountId].SecurityId];
|
security = this.props.security_map[this.props.account_map[s.AccountId].SecurityId];
|
||||||
@ -355,7 +362,7 @@ const AddEditTransactionModal = React.createClass({
|
|||||||
account_map={this.props.account_map}
|
account_map={this.props.account_map}
|
||||||
value={s.AccountId}
|
value={s.AccountId}
|
||||||
includeRoot={false}
|
includeRoot={false}
|
||||||
onSelect={updateAccountFn}
|
onChange={updateAccountFn}
|
||||||
ref={"account-"+i}
|
ref={"account-"+i}
|
||||||
className={accountValidation}/></Col>
|
className={accountValidation}/></Col>
|
||||||
<Col xs={2}><AmountInput type="text"
|
<Col xs={2}><AmountInput type="text"
|
||||||
@ -401,7 +408,7 @@ const AddEditTransactionModal = React.createClass({
|
|||||||
data={TransactionStatusList}
|
data={TransactionStatusList}
|
||||||
valueField='StatusId'
|
valueField='StatusId'
|
||||||
textField='Name'
|
textField='Name'
|
||||||
value={this.state.transaction.Status}
|
defaultValue={this.state.transaction.Status}
|
||||||
onSelect={this.handleStatusChange}
|
onSelect={this.handleStatusChange}
|
||||||
ref="status" />
|
ref="status" />
|
||||||
</Input>
|
</Input>
|
||||||
@ -536,7 +543,7 @@ const ImportTransactionsModal = React.createClass({
|
|||||||
panel = (<Panel header="Successfully Imported Transactions" bsStyle="success">Your import is now complete.</Panel>);
|
panel = (<Panel header="Successfully Imported Transactions" bsStyle="success">Your import is now complete.</Panel>);
|
||||||
}
|
}
|
||||||
|
|
||||||
var buttonsDisabled = (this.state.importing) ? "disabled" : "";
|
var buttonsDisabled = (this.state.importing) ? true : false;
|
||||||
var button1 = [];
|
var button1 = [];
|
||||||
var button2 = [];
|
var button2 = [];
|
||||||
if (!this.state.imported && this.state.error == null) {
|
if (!this.state.imported && this.state.error == null) {
|
||||||
@ -545,9 +552,9 @@ const ImportTransactionsModal = React.createClass({
|
|||||||
} else {
|
} else {
|
||||||
button1 = (<Button onClick={this.handleCancel} disabled={buttonsDisabled} bsStyle="success">OK</Button>);
|
button1 = (<Button onClick={this.handleCancel} disabled={buttonsDisabled} bsStyle="success">OK</Button>);
|
||||||
}
|
}
|
||||||
var inputDisabled = (this.state.importing || this.state.error != null || this.state.imported) ? "disabled" : "";
|
var inputDisabled = (this.state.importing || this.state.error != null || this.state.imported) ? true : false;
|
||||||
return (
|
return (
|
||||||
<Modal show={this.props.show} onHide={this.handleCancel} bsSize="medium">
|
<Modal show={this.props.show} onHide={this.handleCancel} bsSize="small">
|
||||||
<Modal.Header closeButton>
|
<Modal.Header closeButton>
|
||||||
<Modal.Title>Import Transactions</Modal.Title>
|
<Modal.Title>Import Transactions</Modal.Title>
|
||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
@ -577,7 +584,8 @@ const ImportTransactionsModal = React.createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const AccountRegister = React.createClass({
|
module.exports = React.createClass({
|
||||||
|
displayName: "AccountRegister",
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
importingTransactions: false,
|
importingTransactions: false,
|
||||||
@ -591,7 +599,7 @@ const AccountRegister = React.createClass({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
resize: function() {
|
resize: function() {
|
||||||
var div = React.findDOMNode(this);
|
var div = ReactDOM.findDOMNode(this);
|
||||||
this.setState({height: div.parentElement.clientHeight - 64});
|
this.setState({height: div.parentElement.clientHeight - 64});
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
@ -830,7 +838,7 @@ const AccountRegister = React.createClass({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var disabled = (this.props.selectedAccount == null) ? "disabled" : "";
|
var disabled = (this.props.selectedAccount == null) ? true : false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="transactions-container">
|
<div className="transactions-container">
|
166
static/AccountSettingsModal.js
Normal file
166
static/AccountSettingsModal.js
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
var React = require('react');
|
||||||
|
|
||||||
|
var Modal = require('react-bootstrap').Modal;
|
||||||
|
var Button = require('react-bootstrap').Button;
|
||||||
|
var ButtonGroup = require('react-bootstrap').ButtonGroup;
|
||||||
|
var Input = require('react-bootstrap').Input;
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: "AccountSettingsModal",
|
||||||
|
_getInitialState: function(props) {
|
||||||
|
return {error: "",
|
||||||
|
name: props.user.Name,
|
||||||
|
username: props.user.Username,
|
||||||
|
email: props.user.Email,
|
||||||
|
password: BogusPassword,
|
||||||
|
confirm_password: BogusPassword,
|
||||||
|
passwordChanged: false,
|
||||||
|
initial_password: BogusPassword};
|
||||||
|
},
|
||||||
|
getInitialState: function() {
|
||||||
|
return this._getInitialState(this.props);
|
||||||
|
},
|
||||||
|
componentWillReceiveProps: function(nextProps) {
|
||||||
|
if (nextProps.show && !this.props.show) {
|
||||||
|
this.setState(this._getInitialState(nextProps));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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.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),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<Modal show={this.props.show} onHide={this.handleCancel} bsSize="large">
|
||||||
|
<Modal.Header closeButton>
|
||||||
|
<Modal.Title>Edit Account Settings</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<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/>
|
||||||
|
</form>
|
||||||
|
</Modal.Body>
|
||||||
|
<Modal.Footer>
|
||||||
|
<ButtonGroup>
|
||||||
|
<Button onClick={this.handleCancel} bsStyle="warning">Cancel</Button>
|
||||||
|
<Button onClick={this.handleSubmit} bsStyle="success">Save Settings</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -1,51 +1,24 @@
|
|||||||
// Import all the objects we want to use from ReactBootstrap
|
var React = require('react');
|
||||||
var ListGroup = ReactBootstrap.ListGroup;
|
var ReactDOM = require('react-dom');
|
||||||
var ListGroupItem = ReactBootstrap.ListGroupItem;
|
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap');
|
||||||
var Grid = ReactBootstrap.Grid;
|
var Grid = ReactBootstrap.Grid;
|
||||||
var Row = ReactBootstrap.Row;
|
var Row = ReactBootstrap.Row;
|
||||||
var Col = ReactBootstrap.Col;
|
var Col = ReactBootstrap.Col;
|
||||||
|
var Input = ReactBootstrap.Input;
|
||||||
var Button = ReactBootstrap.Button;
|
var Button = ReactBootstrap.Button;
|
||||||
var ButtonGroup = ReactBootstrap.ButtonGroup;
|
var ButtonGroup = ReactBootstrap.ButtonGroup;
|
||||||
var Glyphicon = ReactBootstrap.Glyphicon;
|
var Glyphicon = ReactBootstrap.Glyphicon;
|
||||||
|
var ListGroup = ReactBootstrap.ListGroup;
|
||||||
|
var ListGroupItem = ReactBootstrap.ListGroupItem;
|
||||||
|
var Collapse = ReactBootstrap.Collapse;
|
||||||
|
var Modal = ReactBootstrap.Modal;
|
||||||
|
var Collapse = ReactBootstrap.Collapse;
|
||||||
|
|
||||||
var CollapsibleMixin = ReactBootstrap.CollapsibleMixin;
|
var Combobox = require('react-widgets').Combobox;
|
||||||
|
|
||||||
var Combobox = ReactWidgets.Combobox;
|
var AccountCombobox = require('./AccountCombobox.js');
|
||||||
|
var AccountRegister = require('./AccountRegister.js');
|
||||||
const AccountCombobox = React.createClass({
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {
|
|
||||||
includeRoot: true,
|
|
||||||
rootName: "New Top-level Account"
|
|
||||||
};
|
|
||||||
},
|
|
||||||
handleAccountChange: function(account) {
|
|
||||||
if (this.props.onSelect != null &&
|
|
||||||
account.hasOwnProperty('AccountId') &&
|
|
||||||
(this.props.account_map.hasOwnProperty([account.AccountId]) ||
|
|
||||||
account.AccountId == -1)) {
|
|
||||||
this.props.onSelect(account)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
render: function() {
|
|
||||||
var accounts = getAccountDisplayList(this.props.accounts, this.props.includeRoot, this.props.rootName);
|
|
||||||
var className = "";
|
|
||||||
if (this.props.className)
|
|
||||||
className = this.props.className;
|
|
||||||
return (
|
|
||||||
<Combobox
|
|
||||||
data={accounts}
|
|
||||||
valueField='AccountId'
|
|
||||||
textField='Name'
|
|
||||||
value={this.props.value}
|
|
||||||
onSelect={this.handleAccountChange}
|
|
||||||
ref="account"
|
|
||||||
className={className} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const AddEditAccountModal = React.createClass({
|
const AddEditAccountModal = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
@ -139,7 +112,7 @@ const AddEditAccountModal = React.createClass({
|
|||||||
account_map={this.props.account_map}
|
account_map={this.props.account_map}
|
||||||
value={this.state.parentaccountid}
|
value={this.state.parentaccountid}
|
||||||
rootName={rootName}
|
rootName={rootName}
|
||||||
onSelect={this.handleParentChange}
|
onChange={this.handleParentChange}
|
||||||
ref="parent" />
|
ref="parent" />
|
||||||
</Input>
|
</Input>
|
||||||
<Input wrapperClassName="wrapper"
|
<Input wrapperClassName="wrapper"
|
||||||
@ -151,7 +124,7 @@ const AddEditAccountModal = React.createClass({
|
|||||||
valueField='SecurityId'
|
valueField='SecurityId'
|
||||||
textField='Name'
|
textField='Name'
|
||||||
value={this.state.security}
|
value={this.state.security}
|
||||||
onSelect={this.handleSecurityChange}
|
onChange={this.handleSecurityChange}
|
||||||
ref="security" />
|
ref="security" />
|
||||||
</Input>
|
</Input>
|
||||||
<Input wrapperClassName="wrapper"
|
<Input wrapperClassName="wrapper"
|
||||||
@ -163,7 +136,7 @@ const AddEditAccountModal = React.createClass({
|
|||||||
valueField='TypeId'
|
valueField='TypeId'
|
||||||
textField='Name'
|
textField='Name'
|
||||||
value={this.state.type}
|
value={this.state.type}
|
||||||
onSelect={this.handleTypeChange}
|
onChange={this.handleTypeChange}
|
||||||
ref="type" />
|
ref="type" />
|
||||||
</Input>
|
</Input>
|
||||||
</form>
|
</form>
|
||||||
@ -179,6 +152,7 @@ const AddEditAccountModal = React.createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const DeleteAccountModal = React.createClass({
|
const DeleteAccountModal = React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
if (this.props.initialAccount != null)
|
if (this.props.initialAccount != null)
|
||||||
@ -263,7 +237,7 @@ const DeleteAccountModal = React.createClass({
|
|||||||
accounts={this.props.accounts}
|
accounts={this.props.accounts}
|
||||||
account_map={this.props.account_map}
|
account_map={this.props.account_map}
|
||||||
value={this.state.accountid}
|
value={this.state.accountid}
|
||||||
onSelect={this.handleChange}/>
|
onChange={this.handleChange}/>
|
||||||
</Input>
|
</Input>
|
||||||
{checkbox}
|
{checkbox}
|
||||||
</form>
|
</form>
|
||||||
@ -280,12 +254,8 @@ const DeleteAccountModal = React.createClass({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const AccountTreeNode = React.createClass({
|
const AccountTreeNode = React.createClass({
|
||||||
mixins: [CollapsibleMixin],
|
getInitialState: function() {
|
||||||
getCollapsibleDOMNode: function() {
|
return {expanded: false};
|
||||||
return React.findDOMNode(this.refs.children);
|
|
||||||
},
|
|
||||||
getCollapsibleDimensionValue: function() {
|
|
||||||
return React.findDOMNode(this.refs.children).scrollHeight;
|
|
||||||
},
|
},
|
||||||
handleToggle: function(e) {
|
handleToggle: function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -300,8 +270,7 @@ const AccountTreeNode = React.createClass({
|
|||||||
this.props.onSelect(this.props.account);
|
this.props.onSelect(this.props.account);
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
var styles = this.getCollapsibleClassSet();
|
var glyph = this.state.expanded ? 'minus' : 'plus';
|
||||||
var glyph = this.isExpanded() ? 'minus' : 'plus';
|
|
||||||
var active = (this.props.selectedAccount != null &&
|
var active = (this.props.selectedAccount != null &&
|
||||||
this.props.account.AccountId == this.props.selectedAccount.AccountId);
|
this.props.account.AccountId == this.props.selectedAccount.AccountId);
|
||||||
var buttonStyle = active ? "info" : "link";
|
var buttonStyle = active ? "info" : "link";
|
||||||
@ -317,7 +286,7 @@ const AccountTreeNode = React.createClass({
|
|||||||
});
|
});
|
||||||
var accounttreeClasses = "accounttree"
|
var accounttreeClasses = "accounttree"
|
||||||
var expandButton = [];
|
var expandButton = [];
|
||||||
if (children.length > 0)
|
if (children.length > 0) {
|
||||||
expandButton.push((
|
expandButton.push((
|
||||||
<Button onClick={this.handleToggle}
|
<Button onClick={this.handleToggle}
|
||||||
bsSize="xsmall"
|
bsSize="xsmall"
|
||||||
@ -326,8 +295,9 @@ const AccountTreeNode = React.createClass({
|
|||||||
<Glyphicon glyph={glyph} bsSize="xsmall"/>
|
<Glyphicon glyph={glyph} bsSize="xsmall"/>
|
||||||
</Button>
|
</Button>
|
||||||
));
|
));
|
||||||
else
|
} else {
|
||||||
accounttreeClasses += "-nochildren";
|
accounttreeClasses += "-nochildren";
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className={accounttreeClasses}>
|
<div className={accounttreeClasses}>
|
||||||
{expandButton}
|
{expandButton}
|
||||||
@ -336,9 +306,11 @@ const AccountTreeNode = React.createClass({
|
|||||||
className="accounttree-name">
|
className="accounttree-name">
|
||||||
{this.props.account.Name}
|
{this.props.account.Name}
|
||||||
</Button>
|
</Button>
|
||||||
<div ref='children' className={classNames(styles)}>
|
<Collapse in={this.state.expanded}>
|
||||||
|
<div>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
</Collapse>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -356,7 +328,7 @@ const AccountTree = React.createClass({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
resize: function() {
|
resize: function() {
|
||||||
var div = React.findDOMNode(this);
|
var div = ReactDOM.findDOMNode(this);
|
||||||
this.setState({height: div.parentElement.clientHeight - 73});
|
this.setState({height: div.parentElement.clientHeight - 73});
|
||||||
},
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
@ -386,7 +358,8 @@ const AccountTree = React.createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const AccountsTab = React.createClass({
|
module.exports = React.createClass({
|
||||||
|
displayName: "AccountsTab",
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
selectedAccount: null,
|
selectedAccount: null,
|
||||||
@ -436,7 +409,7 @@ const AccountsTab = React.createClass({
|
|||||||
var accounts = this.props.accounts;
|
var accounts = this.props.accounts;
|
||||||
var account_map = this.props.account_map;
|
var account_map = this.props.account_map;
|
||||||
|
|
||||||
var disabled = (this.state.selectedAccount == null) ? "disabled" : "";
|
var disabled = (this.state.selectedAccount == null) ? true : false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid fluid className="fullheight"><Row className="fullheight">
|
<Grid fluid className="fullheight"><Row className="fullheight">
|
4
static/Makefile
Normal file
4
static/Makefile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
all:
|
||||||
|
browserify -t [ babelify --presets [ react ] ] main.js -o bundle.js
|
||||||
|
|
||||||
|
.PHONY = all
|
324
static/MoneyGoApp.js
Normal file
324
static/MoneyGoApp.js
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
var React = require('react');
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap');
|
||||||
|
var Jumbotron = ReactBootstrap.Jumbotron;
|
||||||
|
var Tabs = ReactBootstrap.Tabs;
|
||||||
|
var Tab = ReactBootstrap.Tab;
|
||||||
|
var Modal = ReactBootstrap.Modal;
|
||||||
|
|
||||||
|
var TopBar = require('./TopBar.js');
|
||||||
|
var NewUserForm = require('./NewUserForm.js');
|
||||||
|
var AccountSettingsModal = require('./AccountSettingsModal.js');
|
||||||
|
var AccountsTab = require('./AccountsTab.js');
|
||||||
|
|
||||||
|
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.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();
|
||||||
|
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) {
|
||||||
|
this.setState({showAccountSettingsModal: false});
|
||||||
|
},
|
||||||
|
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.user.isUser())
|
||||||
|
mainContent = (
|
||||||
|
<Tabs defaultActiveKey={1}>
|
||||||
|
<Tab title="Accounts" eventKey={1} tabClassName="fullheight">
|
||||||
|
<AccountsTab
|
||||||
|
className="fullheight"
|
||||||
|
accounts={this.state.accounts}
|
||||||
|
account_map={this.state.account_map}
|
||||||
|
securities={this.state.securities}
|
||||||
|
security_map={this.state.security_map}
|
||||||
|
onCreateAccount={this.handleCreateAccount}
|
||||||
|
onUpdateAccount={this.handleUpdateAccount}
|
||||||
|
onDeleteAccount={this.handleDeleteAccount} />
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Scheduled Transactions" eventKey={2} tabClassName="fullheight">Scheduled transactions go here...</Tab>
|
||||||
|
<Tab title="Budgets" eventKey={3} tabClassName="fullheight">Budgets go here...</Tab>
|
||||||
|
<Tab title="Reports" eventKey={4} tabClassName="fullheight">Reports go here...</Tab>
|
||||||
|
</Tabs>);
|
||||||
|
else
|
||||||
|
mainContent = (
|
||||||
|
<Jumbotron>
|
||||||
|
<center>
|
||||||
|
<h1>Money<i>Go</i></h1>
|
||||||
|
<p><i>Go</i> manage your money.</p>
|
||||||
|
</center>
|
||||||
|
</Jumbotron>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fullheight ui">
|
||||||
|
<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}
|
||||||
|
<AccountSettingsModal
|
||||||
|
show={this.state.showAccountSettingsModal}
|
||||||
|
user={this.state.user}
|
||||||
|
onSubmit={this.handleSettingsSubmitted}
|
||||||
|
onCancel={this.handleSettingsCanceled}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
147
static/NewUserForm.js
Normal file
147
static/NewUserForm.js
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
var React = require('react');
|
||||||
|
|
||||||
|
var Panel = require('react-bootstrap').Panel;
|
||||||
|
var Input = require('react-bootstrap').Input;
|
||||||
|
var Button = require('react-bootstrap').Button;
|
||||||
|
var ButtonGroup = require('react-bootstrap').ButtonGroup;
|
||||||
|
|
||||||
|
module.exports = 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -1,11 +1,11 @@
|
|||||||
// Import all the objects we want to use from ReactBootstrap
|
var React = require('react');
|
||||||
var Alert = ReactBootstrap.Alert;
|
|
||||||
|
|
||||||
|
var ReactBootstrap = require('react-bootstrap');
|
||||||
|
var Alert = ReactBootstrap.Alert;
|
||||||
var Input = ReactBootstrap.Input;
|
var Input = ReactBootstrap.Input;
|
||||||
var Button = ReactBootstrap.Button;
|
var Button = ReactBootstrap.Button;
|
||||||
var DropdownButton = ReactBootstrap.DropdownButton;
|
var DropdownButton = ReactBootstrap.DropdownButton;
|
||||||
var MenuItem = ReactBootstrap.MenuItem;
|
var MenuItem = ReactBootstrap.MenuItem;
|
||||||
|
|
||||||
var Row = ReactBootstrap.Row;
|
var Row = ReactBootstrap.Row;
|
||||||
var Col = ReactBootstrap.Col;
|
var Col = ReactBootstrap.Col;
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ const LoginBar = React.createClass({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const LogoutBar = React.createClass({
|
const LogoutBar = React.createClass({
|
||||||
handleOnSelect: function(key) {
|
handleOnSelect: function(e, key) {
|
||||||
if (key == 1) {
|
if (key == 1) {
|
||||||
if (this.props.onAccountSettings != null)
|
if (this.props.onAccountSettings != null)
|
||||||
this.props.onAccountSettings();
|
this.props.onAccountSettings();
|
||||||
@ -79,7 +79,7 @@ const LogoutBar = React.createClass({
|
|||||||
<Col xs={6}></Col>
|
<Col xs={6}></Col>
|
||||||
<Col xs={4}>
|
<Col xs={4}>
|
||||||
<div className="pull-right">
|
<div className="pull-right">
|
||||||
<DropdownButton title={signedInString} onSelect={this.handleOnSelect} bsStyle="info">
|
<DropdownButton id="logout-settings-dropdown" title={signedInString} onSelect={this.handleOnSelect} bsStyle="info">
|
||||||
<MenuItem eventKey="1">Account Settings</MenuItem>
|
<MenuItem eventKey="1">Account Settings</MenuItem>
|
||||||
<MenuItem eventKey="2">Logout</MenuItem>
|
<MenuItem eventKey="2">Logout</MenuItem>
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
@ -91,7 +91,8 @@ const LogoutBar = React.createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const TopBar = React.createClass({
|
module.exports = React.createClass({
|
||||||
|
displayName: "TopBar",
|
||||||
render: function() {
|
render: function() {
|
||||||
var barContents;
|
var barContents;
|
||||||
var errorAlert;
|
var errorAlert;
|
BIN
static/external/fonts/rw-widgets.eot
vendored
BIN
static/external/fonts/rw-widgets.eot
vendored
Binary file not shown.
18
static/external/fonts/rw-widgets.svg
vendored
18
static/external/fonts/rw-widgets.svg
vendored
@ -1,18 +0,0 @@
|
|||||||
<?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) 2015 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="" d="m571 171q0-14-10-25t-25-10h-500q-15 0-25 10t-11 25 11 26l250 250q10 10 25 10t25-10l250-250q10-11 10-26z" horiz-adv-x="571.4" />
|
|
||||||
<glyph glyph-name="left-dir" unicode="" d="m357 600v-500q0-14-10-25t-26-11-25 11l-250 250q-10 11-10 25t10 25l250 250q11 11 25 11t26-11 10-25z" horiz-adv-x="357.1" />
|
|
||||||
<glyph glyph-name="right-dir" unicode="" d="m321 350q0-14-10-25l-250-250q-11-11-25-11t-25 11-11 25v500q0 15 11 25t25 11 25-11l250-250q10-10 10-25z" horiz-adv-x="357.1" />
|
|
||||||
<glyph glyph-name="down-dir" unicode="" d="m571 457q0-14-10-25l-250-250q-11-11-25-11t-25 11l-250 250q-11 11-11 25t11 25 25 11h500q14 0 25-11t10-25z" horiz-adv-x="571.4" />
|
|
||||||
<glyph glyph-name="calendar" unicode="" d="m71-79h161v161h-161v-161z m197 0h178v161h-178v-161z m-197 197h161v178h-161v-178z m197 0h178v178h-178v-178z m-197 214h161v161h-161v-161z m411-411h179v161h-179v-161z m-214 411h178v161h-178v-161z m428-411h161v161h-161v-161z m-214 197h179v178h-179v-178z m-196 482v161q0 7-6 12t-12 6h-36q-7 0-12-6t-6-12v-161q0-7 6-13t12-5h36q7 0 12 5t6 13z m410-482h161v178h-161v-178z m-214 214h179v161h-179v-161z m214 0h161v161h-161v-161z m18 268v161q0 7-5 12t-13 6h-35q-8 0-13-6t-5-12v-161q0-7 5-13t13-5h35q8 0 13 5t5 13z m215 36v-715q0-29-22-50t-50-21h-786q-29 0-50 21t-21 50v715q0 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-50z" horiz-adv-x="928.6" />
|
|
||||||
<glyph glyph-name="clock" unicode="" d="m500 546v-250q0-7-5-12t-13-5h-178q-8 0-13 5t-5 12v36q0 8 5 13t13 5h125v196q0 8 5 13t12 5h36q8 0 13-5t5-13z m232-196q0 83-41 152t-110 111-152 41-153-41-110-111-41-152 41-152 110-111 153-41 152 41 110 111 41 152z m125 0q0-117-57-215t-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58 215-58 156-156 57-215z" horiz-adv-x="857.1" />
|
|
||||||
<glyph glyph-name="search" unicode="" d="m643 386q0 103-74 176t-176 74-177-74-73-176 73-177 177-73 176 73 74 177z m286-465q0-29-22-50t-50-21q-30 0-50 21l-191 191q-100-69-223-69-80 0-153 31t-125 84-84 125-31 153 31 152 84 126 125 84 153 31 152-31 126-84 84-126 31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
|
|
||||||
</font>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.6 KiB |
BIN
static/external/fonts/rw-widgets.ttf
vendored
BIN
static/external/fonts/rw-widgets.ttf
vendored
Binary file not shown.
BIN
static/external/fonts/rw-widgets.woff
vendored
BIN
static/external/fonts/rw-widgets.woff
vendored
Binary file not shown.
File diff suppressed because one or more lines are too long
307
static/external/react-widgets/core.css
vendored
307
static/external/react-widgets/core.css
vendored
@ -1,307 +0,0 @@
|
|||||||
/* for debugging */
|
|
||||||
.rw-widget {
|
|
||||||
outline: 0;
|
|
||||||
-moz-background-clip: border-box;
|
|
||||||
-webkit-background-clip: border-box;
|
|
||||||
background-clip: border-box;
|
|
||||||
}
|
|
||||||
.rw-btn {
|
|
||||||
color: #333333;
|
|
||||||
line-height: 2.286em;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
background: none;
|
|
||||||
background-image: none;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
padding: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.rw-rtl {
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
.rw-input {
|
|
||||||
color: #555555;
|
|
||||||
height: 2.286em;
|
|
||||||
line-height: 2.286em;
|
|
||||||
padding: 0.429em 0.857em;
|
|
||||||
}
|
|
||||||
.rw-input[disabled] {
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
opacity: 1;
|
|
||||||
background-color: #eeeeee;
|
|
||||||
border-color: #cccccc;
|
|
||||||
}
|
|
||||||
.rw-input[readonly] {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
.rw-i.rw-loading {
|
|
||||||
background: url("../img/loading.gif") no-repeat center;
|
|
||||||
width: 16px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.rw-i.rw-loading:before {
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
.rw-loading-mask {
|
|
||||||
border-radius: 4px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.rw-loading-mask:after {
|
|
||||||
content: '';
|
|
||||||
background: url("../img/loader-big.gif") no-repeat center;
|
|
||||||
position: absolute;
|
|
||||||
background-color: #fff;
|
|
||||||
opacity: 0.7;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.rw-now {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
.rw-state-focus {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: #66afe9 1px solid;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
.rw-state-selected {
|
|
||||||
background-color: #adadad;
|
|
||||||
border: #adadad 1px solid;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
.rw-state-disabled {
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.rw-btn,
|
|
||||||
.rw-dropdownlist {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.rw-btn[disabled],
|
|
||||||
.rw-state-disabled .rw-btn,
|
|
||||||
.rw-state-readonly .rw-btn {
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
pointer-events: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
filter: alpha(opacity=65);
|
|
||||||
opacity: .65;
|
|
||||||
}
|
|
||||||
ul.rw-list,
|
|
||||||
ul.rw-selectlist {
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
list-style: none;
|
|
||||||
padding: 5px 0;
|
|
||||||
overflow: auto;
|
|
||||||
outline: 0;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
ul.rw-list > li.rw-list-optgroup,
|
|
||||||
ul.rw-selectlist > li.rw-list-optgroup {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
ul.rw-list > li.rw-list-option,
|
|
||||||
ul.rw-selectlist > li.rw-list-option {
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 10px;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
ul.rw-list > li.rw-list-option:hover,
|
|
||||||
ul.rw-selectlist > li.rw-list-option:hover {
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
border-color: #adadad;
|
|
||||||
}
|
|
||||||
ul.rw-list > li.rw-list-option.rw-state-focus,
|
|
||||||
ul.rw-selectlist > li.rw-list-option.rw-state-focus {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: #66afe9 1px solid;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
ul.rw-list > li.rw-list-option.rw-state-selected,
|
|
||||||
ul.rw-selectlist > li.rw-list-option.rw-state-selected {
|
|
||||||
background-color: #adadad;
|
|
||||||
border: #adadad 1px solid;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
ul.rw-list.rw-list-grouped > li.rw-list-optgroup {
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
ul.rw-list.rw-list-grouped > li.rw-list-option {
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
.rw-widget {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.rw-open.rw-widget,
|
|
||||||
.rw-open > .rw-multiselect-wrapper {
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
}
|
|
||||||
.rw-open-up.rw-widget,
|
|
||||||
.rw-open-up > .rw-multiselect-wrapper {
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
}
|
|
||||||
.rw-combobox .rw-list,
|
|
||||||
.rw-datetimepicker .rw-list,
|
|
||||||
.rw-numberpicker .rw-list,
|
|
||||||
.rw-dropdownlist .rw-list,
|
|
||||||
.rw-multiselect .rw-list {
|
|
||||||
max-height: 200px;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
.rw-widget {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: #cccccc 1px solid;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.rw-widget .rw-input {
|
|
||||||
border-bottom-left-radius: 4px;
|
|
||||||
border-top-left-radius: 4px;
|
|
||||||
}
|
|
||||||
.rw-rtl.rw-widget .rw-input {
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
border-bottom-right-radius: 4px;
|
|
||||||
border-top-right-radius: 4px;
|
|
||||||
}
|
|
||||||
.rw-widget > .rw-select {
|
|
||||||
border-left: #cccccc 1px solid;
|
|
||||||
}
|
|
||||||
.rw-rtl.rw-widget > .rw-select {
|
|
||||||
border-right: #cccccc 1px solid;
|
|
||||||
border-left: none;
|
|
||||||
}
|
|
||||||
.rw-widget.rw-state-focus,
|
|
||||||
.rw-widget.rw-state-focus:hover {
|
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
|
||||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
|
||||||
border-color: #66afe9;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
.rw-widget.rw-state-readonly,
|
|
||||||
.rw-widget.rw-state-readonly > .rw-multiselect-wrapper {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
.rw-widget.rw-state-disabled,
|
|
||||||
.rw-widget.rw-state-disabled:hover,
|
|
||||||
.rw-widget.rw-state-disabled:active {
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
background-color: #eeeeee;
|
|
||||||
border-color: #cccccc;
|
|
||||||
}
|
|
||||||
.rw-combobox,
|
|
||||||
.rw-datetimepicker,
|
|
||||||
.rw-numberpicker,
|
|
||||||
.rw-dropdownlist {
|
|
||||||
padding-right: 1.9em;
|
|
||||||
}
|
|
||||||
.rw-combobox.rw-rtl,
|
|
||||||
.rw-datetimepicker.rw-rtl,
|
|
||||||
.rw-numberpicker.rw-rtl,
|
|
||||||
.rw-dropdownlist.rw-rtl {
|
|
||||||
padding-right: 0;
|
|
||||||
padding-left: 1.9em;
|
|
||||||
}
|
|
||||||
.rw-combobox > .rw-input,
|
|
||||||
.rw-datetimepicker > .rw-input,
|
|
||||||
.rw-numberpicker > .rw-input,
|
|
||||||
.rw-dropdownlist > .rw-input {
|
|
||||||
width: 100%;
|
|
||||||
border: none;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
.rw-combobox > .rw-input::-moz-placeholder,
|
|
||||||
.rw-datetimepicker > .rw-input::-moz-placeholder,
|
|
||||||
.rw-numberpicker > .rw-input::-moz-placeholder,
|
|
||||||
.rw-dropdownlist > .rw-input::-moz-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.rw-combobox > .rw-input:-ms-input-placeholder,
|
|
||||||
.rw-datetimepicker > .rw-input:-ms-input-placeholder,
|
|
||||||
.rw-numberpicker > .rw-input:-ms-input-placeholder,
|
|
||||||
.rw-dropdownlist > .rw-input:-ms-input-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.rw-combobox > .rw-input::-webkit-input-placeholder,
|
|
||||||
.rw-datetimepicker > .rw-input::-webkit-input-placeholder,
|
|
||||||
.rw-numberpicker > .rw-input::-webkit-input-placeholder,
|
|
||||||
.rw-dropdownlist > .rw-input::-webkit-input-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.rw-select {
|
|
||||||
position: absolute;
|
|
||||||
width: 1.9em;
|
|
||||||
height: 100%;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
.rw-select.rw-btn,
|
|
||||||
.rw-select > .rw-btn {
|
|
||||||
height: 100%;
|
|
||||||
vertical-align: middle;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
.rw-rtl .rw-select {
|
|
||||||
left: 0;
|
|
||||||
right: auto;
|
|
||||||
}
|
|
||||||
.rw-multiselect,
|
|
||||||
.rw-combobox input.rw-input,
|
|
||||||
.rw-datetimepicker input.rw-input,
|
|
||||||
.rw-numberpicker input.rw-input {
|
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
|
|
||||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
|
|
||||||
}
|
|
||||||
.rw-combobox:active,
|
|
||||||
.rw-datetimepicker:active,
|
|
||||||
.rw-dropdownlist:active,
|
|
||||||
.rw-header > .rw-btn:active,
|
|
||||||
.rw-numberpicker .rw-btn.rw-state-active,
|
|
||||||
.rw-combobox:active.rw-state-focus,
|
|
||||||
.rw-datetimepicker:active.rw-state-focus,
|
|
||||||
.rw-dropdownlist:active.rw-state-focus,
|
|
||||||
.rw-header > .rw-btn:active.rw-state-focus,
|
|
||||||
.rw-numberpicker .rw-btn.rw-state-active.rw-state-focus {
|
|
||||||
background-image: none;
|
|
||||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
|
||||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
|
||||||
}
|
|
||||||
.rw-combobox:hover,
|
|
||||||
.rw-datetimepicker:hover,
|
|
||||||
.rw-numberpicker:hover,
|
|
||||||
.rw-dropdownlist:hover {
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
border-color: #adadad;
|
|
||||||
}
|
|
||||||
.rw-dropdownlist.rw-state-disabled,
|
|
||||||
.rw-dropdownlist.rw-state-readonly {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
.rw-dropdownlist > .rw-input {
|
|
||||||
background-color: transparent;
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
.rw-dropdownlist > .rw-select,
|
|
||||||
.rw-dropdownlist > .rw-select.rw-rtl {
|
|
||||||
border-width: 0;
|
|
||||||
}
|
|
||||||
.rw-numberpicker .rw-btn {
|
|
||||||
display: block;
|
|
||||||
height: 1.143em;
|
|
||||||
line-height: 1.143em;
|
|
||||||
width: 100%;
|
|
||||||
border-width: 0;
|
|
||||||
}
|
|
BIN
static/external/react-widgets/loader-big.gif
vendored
BIN
static/external/react-widgets/loader-big.gif
vendored
Binary file not shown.
Before Width: | Height: | Size: 23 KiB |
BIN
static/external/react-widgets/loading.gif
vendored
BIN
static/external/react-widgets/loading.gif
vendored
Binary file not shown.
Before Width: | Height: | Size: 673 B |
730
static/external/react-widgets/react-widgets.css
vendored
730
static/external/react-widgets/react-widgets.css
vendored
@ -1,730 +0,0 @@
|
|||||||
/* Noramlize.css */
|
|
||||||
.rw-btn,
|
|
||||||
.rw-input {
|
|
||||||
color: inherit;
|
|
||||||
font: inherit;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
button.rw-input {
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
button.rw-input,
|
|
||||||
select.rw-input {
|
|
||||||
text-transform: none;
|
|
||||||
}
|
|
||||||
button.rw-input,
|
|
||||||
html input[type="button"].rw-input,
|
|
||||||
input[type="reset"].rw-input,
|
|
||||||
input[type="submit"].rw-input {
|
|
||||||
-webkit-appearance: button;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
button[disabled].rw-input,
|
|
||||||
html input[disabled].rw-input {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
button.rw-input::-moz-focus-inner,
|
|
||||||
input.rw-input::-moz-focus-inner {
|
|
||||||
border: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
/* -------------- */
|
|
||||||
.rw-sr {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
margin: -1px;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
.rw-widget,
|
|
||||||
.rw-widget * {
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.rw-widget:before,
|
|
||||||
.rw-widget *:before,
|
|
||||||
.rw-widget:after,
|
|
||||||
.rw-widget *:after {
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: 'RwWidgets';
|
|
||||||
src: url('../fonts/rw-widgets.eot?v=4.1.0');
|
|
||||||
src: url('../fonts/rw-widgets.eot?#iefix&v=4.1.0') format('embedded-opentype'), url('../fonts/rw-widgets.woff?v=4.1.0') format('woff'), url('../fonts/rw-widgets.ttf?v=4.1.0') format('truetype'), url('../fonts/rw-widgets.svg?v=4.1.0#fontawesomeregular') format('svg');
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
.rw-i {
|
|
||||||
display: inline-block;
|
|
||||||
font-family: RwWidgets;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1em;
|
|
||||||
font-variant: normal;
|
|
||||||
text-transform: none;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
.rw-i-caret-down:before {
|
|
||||||
content: '\e803';
|
|
||||||
}
|
|
||||||
.rw-i-caret-up:before {
|
|
||||||
content: '\e800';
|
|
||||||
}
|
|
||||||
.rw-i-caret-left:before {
|
|
||||||
content: '\e801';
|
|
||||||
}
|
|
||||||
.rw-i-caret-right:before {
|
|
||||||
content: '\e802';
|
|
||||||
}
|
|
||||||
.rw-i-clock-o:before {
|
|
||||||
content: '\e805';
|
|
||||||
}
|
|
||||||
.rw-i-calendar:before {
|
|
||||||
content: '\e804';
|
|
||||||
}
|
|
||||||
.rw-i-search:before {
|
|
||||||
content: '\e806';
|
|
||||||
}
|
|
||||||
/* for debugging */
|
|
||||||
.rw-widget {
|
|
||||||
outline: 0;
|
|
||||||
-moz-background-clip: border-box;
|
|
||||||
-webkit-background-clip: border-box;
|
|
||||||
background-clip: border-box;
|
|
||||||
}
|
|
||||||
.rw-btn {
|
|
||||||
color: #333333;
|
|
||||||
line-height: 2.286em;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
background: none;
|
|
||||||
background-image: none;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
padding: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.rw-rtl {
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
.rw-input {
|
|
||||||
color: #555555;
|
|
||||||
height: 2.286em;
|
|
||||||
padding: 0.429em 0.857em;
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
.rw-input[disabled] {
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
opacity: 1;
|
|
||||||
background-color: #eeeeee;
|
|
||||||
border-color: #cccccc;
|
|
||||||
}
|
|
||||||
.rw-input[readonly] {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
.rw-filter-input {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
padding-right: 1.9em;
|
|
||||||
border: #cccccc 1px solid;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
.rw-rtl .rw-filter-input {
|
|
||||||
padding-left: 1.9em;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
.rw-filter-input > .rw-input {
|
|
||||||
width: 100%;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
.rw-filter-input > span {
|
|
||||||
margin-top: -2px;
|
|
||||||
}
|
|
||||||
.rw-i.rw-loading {
|
|
||||||
background: url("../img/loading.gif") no-repeat center;
|
|
||||||
width: 16px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.rw-i.rw-loading:before {
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
.rw-loading-mask {
|
|
||||||
border-radius: 4px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.rw-loading-mask:after {
|
|
||||||
content: '';
|
|
||||||
background: url("../img/loader-big.gif") no-repeat center;
|
|
||||||
position: absolute;
|
|
||||||
background-color: #fff;
|
|
||||||
opacity: 0.7;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.rw-now {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
.rw-state-focus {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: #66afe9 1px solid;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
.rw-state-selected {
|
|
||||||
background-color: #adadad;
|
|
||||||
border: #adadad 1px solid;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
.rw-state-disabled {
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.rw-btn,
|
|
||||||
.rw-dropdownlist {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.rw-btn[disabled],
|
|
||||||
.rw-state-disabled .rw-btn,
|
|
||||||
.rw-state-readonly .rw-btn {
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
pointer-events: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
filter: alpha(opacity=65);
|
|
||||||
opacity: .65;
|
|
||||||
}
|
|
||||||
ul.rw-list,
|
|
||||||
.rw-selectlist {
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
list-style: none;
|
|
||||||
padding: 5px 0;
|
|
||||||
overflow: auto;
|
|
||||||
outline: 0;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
ul.rw-list > li,
|
|
||||||
.rw-selectlist > li {
|
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
ul.rw-list > li.rw-list-optgroup,
|
|
||||||
.rw-selectlist > li.rw-list-optgroup {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
ul.rw-list > li.rw-list-option,
|
|
||||||
ul.rw-list > li.rw-list-empty,
|
|
||||||
.rw-selectlist > li.rw-list-option,
|
|
||||||
.rw-selectlist > li.rw-list-empty {
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
ul.rw-list > li.rw-list-option,
|
|
||||||
.rw-selectlist > li.rw-list-option {
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
ul.rw-list > li.rw-list-option:hover,
|
|
||||||
.rw-selectlist > li.rw-list-option:hover {
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
border-color: #adadad;
|
|
||||||
}
|
|
||||||
ul.rw-list > li.rw-list-option.rw-state-focus,
|
|
||||||
.rw-selectlist > li.rw-list-option.rw-state-focus {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: #66afe9 1px solid;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
ul.rw-list > li.rw-list-option.rw-state-selected,
|
|
||||||
.rw-selectlist > li.rw-list-option.rw-state-selected {
|
|
||||||
background-color: #adadad;
|
|
||||||
border: #adadad 1px solid;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
ul.rw-list.rw-list-grouped > li.rw-list-optgroup,
|
|
||||||
.rw-selectlist.rw-list-grouped > li.rw-list-optgroup {
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
ul.rw-list.rw-list-grouped > li.rw-list-option,
|
|
||||||
.rw-selectlist.rw-list-grouped > li.rw-list-option {
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
.rw-widget {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.rw-open.rw-widget,
|
|
||||||
.rw-open > .rw-multiselect-wrapper {
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
}
|
|
||||||
.rw-open-up.rw-widget,
|
|
||||||
.rw-open-up > .rw-multiselect-wrapper {
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
}
|
|
||||||
.rw-combobox .rw-list,
|
|
||||||
.rw-datetimepicker .rw-list,
|
|
||||||
.rw-numberpicker .rw-list,
|
|
||||||
.rw-dropdownlist .rw-list,
|
|
||||||
.rw-multiselect .rw-list {
|
|
||||||
max-height: 200px;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
.rw-widget {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: #cccccc 1px solid;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.rw-widget .rw-input {
|
|
||||||
border-bottom-left-radius: 4px;
|
|
||||||
border-top-left-radius: 4px;
|
|
||||||
}
|
|
||||||
.rw-rtl.rw-widget .rw-input {
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
border-bottom-right-radius: 4px;
|
|
||||||
border-top-right-radius: 4px;
|
|
||||||
}
|
|
||||||
.rw-widget > .rw-select {
|
|
||||||
border-left: #cccccc 1px solid;
|
|
||||||
}
|
|
||||||
.rw-rtl.rw-widget > .rw-select {
|
|
||||||
border-right: #cccccc 1px solid;
|
|
||||||
border-left: none;
|
|
||||||
}
|
|
||||||
.rw-widget.rw-state-focus,
|
|
||||||
.rw-widget.rw-state-focus:hover {
|
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
|
||||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
|
||||||
border-color: #66afe9;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
.rw-widget.rw-state-readonly,
|
|
||||||
.rw-widget.rw-state-readonly > .rw-multiselect-wrapper {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
.rw-widget.rw-state-disabled,
|
|
||||||
.rw-widget.rw-state-disabled:hover,
|
|
||||||
.rw-widget.rw-state-disabled:active {
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
background-color: #eeeeee;
|
|
||||||
border-color: #cccccc;
|
|
||||||
}
|
|
||||||
.rw-combobox,
|
|
||||||
.rw-datetimepicker,
|
|
||||||
.rw-numberpicker,
|
|
||||||
.rw-dropdownlist {
|
|
||||||
padding-right: 1.9em;
|
|
||||||
}
|
|
||||||
.rw-combobox.rw-rtl,
|
|
||||||
.rw-datetimepicker.rw-rtl,
|
|
||||||
.rw-numberpicker.rw-rtl,
|
|
||||||
.rw-dropdownlist.rw-rtl {
|
|
||||||
padding-right: 0;
|
|
||||||
padding-left: 1.9em;
|
|
||||||
}
|
|
||||||
.rw-combobox > .rw-input,
|
|
||||||
.rw-datetimepicker > .rw-input,
|
|
||||||
.rw-numberpicker > .rw-input,
|
|
||||||
.rw-dropdownlist > .rw-input {
|
|
||||||
width: 100%;
|
|
||||||
border: none;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
.rw-combobox > .rw-input::-moz-placeholder,
|
|
||||||
.rw-datetimepicker > .rw-input::-moz-placeholder,
|
|
||||||
.rw-numberpicker > .rw-input::-moz-placeholder,
|
|
||||||
.rw-dropdownlist > .rw-input::-moz-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.rw-combobox > .rw-input:-ms-input-placeholder,
|
|
||||||
.rw-datetimepicker > .rw-input:-ms-input-placeholder,
|
|
||||||
.rw-numberpicker > .rw-input:-ms-input-placeholder,
|
|
||||||
.rw-dropdownlist > .rw-input:-ms-input-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.rw-combobox > .rw-input::-webkit-input-placeholder,
|
|
||||||
.rw-datetimepicker > .rw-input::-webkit-input-placeholder,
|
|
||||||
.rw-numberpicker > .rw-input::-webkit-input-placeholder,
|
|
||||||
.rw-dropdownlist > .rw-input::-webkit-input-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.rw-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.rw-select {
|
|
||||||
position: absolute;
|
|
||||||
width: 1.9em;
|
|
||||||
height: 100%;
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
.rw-select.rw-btn,
|
|
||||||
.rw-select > .rw-btn {
|
|
||||||
height: 100%;
|
|
||||||
vertical-align: middle;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
.rw-rtl .rw-select {
|
|
||||||
left: 0;
|
|
||||||
right: auto;
|
|
||||||
}
|
|
||||||
.rw-multiselect,
|
|
||||||
.rw-combobox input.rw-input,
|
|
||||||
.rw-datetimepicker input.rw-input,
|
|
||||||
.rw-numberpicker input.rw-input {
|
|
||||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
|
|
||||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
|
|
||||||
}
|
|
||||||
.rw-combobox:active,
|
|
||||||
.rw-datetimepicker:active,
|
|
||||||
.rw-dropdownlist:active,
|
|
||||||
.rw-header > .rw-btn:active,
|
|
||||||
.rw-numberpicker .rw-btn.rw-state-active,
|
|
||||||
.rw-combobox:active.rw-state-focus,
|
|
||||||
.rw-datetimepicker:active.rw-state-focus,
|
|
||||||
.rw-dropdownlist:active.rw-state-focus,
|
|
||||||
.rw-header > .rw-btn:active.rw-state-focus,
|
|
||||||
.rw-numberpicker .rw-btn.rw-state-active.rw-state-focus {
|
|
||||||
background-image: none;
|
|
||||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
|
||||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
|
||||||
}
|
|
||||||
.rw-combobox:hover,
|
|
||||||
.rw-datetimepicker:hover,
|
|
||||||
.rw-numberpicker:hover,
|
|
||||||
.rw-dropdownlist:hover {
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
border-color: #adadad;
|
|
||||||
}
|
|
||||||
.rw-dropdownlist.rw-state-disabled,
|
|
||||||
.rw-dropdownlist.rw-state-readonly {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
.rw-dropdownlist > .rw-input {
|
|
||||||
line-height: 2.286em;
|
|
||||||
background-color: transparent;
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.rw-dropdownlist.rw-rtl > .rw-input {
|
|
||||||
padding: 0.429em 0.857em;
|
|
||||||
padding-top: 0;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
.rw-dropdownlist > .rw-select,
|
|
||||||
.rw-dropdownlist.rw-rtl > .rw-select {
|
|
||||||
border-width: 0;
|
|
||||||
}
|
|
||||||
.rw-numberpicker .rw-btn {
|
|
||||||
display: block;
|
|
||||||
height: 1.143em;
|
|
||||||
line-height: 1.143em;
|
|
||||||
width: 100%;
|
|
||||||
border-width: 0;
|
|
||||||
}
|
|
||||||
.rw-popup {
|
|
||||||
position: absolute;
|
|
||||||
-webkit-box-shadow: 0 5px 6px rgba(0, 0, 0, 0.2);
|
|
||||||
box-shadow: 0 5px 6px rgba(0, 0, 0, 0.2);
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
border-bottom-right-radius: 3px;
|
|
||||||
border-bottom-left-radius: 3px;
|
|
||||||
border: #cccccc 1px solid;
|
|
||||||
background: #ffffff;
|
|
||||||
padding: 2px;
|
|
||||||
overflow: auto;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
left: 10px;
|
|
||||||
right: 10px;
|
|
||||||
}
|
|
||||||
.rw-dropup > .rw-popup {
|
|
||||||
margin-bottom: 0;
|
|
||||||
margin-top: 10px;
|
|
||||||
border-top-right-radius: 3px;
|
|
||||||
border-top-left-radius: 3px;
|
|
||||||
border-bottom-right-radius: 0;
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
-webkit-box-shadow: 0 0 6px rgba(0, 0, 0, 0.2);
|
|
||||||
box-shadow: 0 0 6px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
.rw-popup-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
margin-top: 1px;
|
|
||||||
z-index: 1005;
|
|
||||||
left: -11px;
|
|
||||||
right: -11px;
|
|
||||||
}
|
|
||||||
.rw-popup-container.rw-dropup {
|
|
||||||
top: auto;
|
|
||||||
bottom: 100%;
|
|
||||||
}
|
|
||||||
.rw-popup-container.rw-calendar-popup {
|
|
||||||
right: auto;
|
|
||||||
width: 18em;
|
|
||||||
}
|
|
||||||
.rw-datetimepicker .rw-btn {
|
|
||||||
width: 1.8em;
|
|
||||||
}
|
|
||||||
.rw-datetimepicker.rw-has-neither {
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
.rw-datetimepicker.rw-has-neither .rw-input {
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.rw-datetimepicker.rw-has-both {
|
|
||||||
padding-right: 3.8em;
|
|
||||||
}
|
|
||||||
.rw-datetimepicker.rw-has-both.rw-rtl {
|
|
||||||
padding-right: 0;
|
|
||||||
padding-left: 3.8em;
|
|
||||||
}
|
|
||||||
.rw-datetimepicker.rw-has-both > .rw-select {
|
|
||||||
width: 3.8em;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.rw-calendar {
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
.rw-calendar thead > tr {
|
|
||||||
border-bottom: 2px solid #cccccc;
|
|
||||||
}
|
|
||||||
.rw-calendar .rw-header {
|
|
||||||
padding-bottom: 5px;
|
|
||||||
}
|
|
||||||
.rw-calendar .rw-header .rw-btn-left,
|
|
||||||
.rw-calendar .rw-header .rw-btn-right {
|
|
||||||
width: 12.5%;
|
|
||||||
}
|
|
||||||
.rw-calendar .rw-header .rw-btn-view {
|
|
||||||
width: 75%;
|
|
||||||
background-color: #eeeeee;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.rw-calendar .rw-header .rw-btn-view[disabled] {
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
.rw-calendar .rw-footer {
|
|
||||||
border-top: 1px solid #cccccc;
|
|
||||||
}
|
|
||||||
.rw-calendar .rw-footer .rw-btn {
|
|
||||||
width: 100%;
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
.rw-calendar .rw-footer .rw-btn:hover {
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
}
|
|
||||||
.rw-calendar .rw-footer .rw-btn[disabled] {
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
.rw-calendar-grid {
|
|
||||||
height: 14.28571429em;
|
|
||||||
table-layout: fixed;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.rw-calendar-grid th {
|
|
||||||
text-align: right;
|
|
||||||
padding: 0 .4em 0 .1em;
|
|
||||||
}
|
|
||||||
.rw-calendar-grid .rw-btn {
|
|
||||||
width: 100%;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
.rw-calendar-grid td .rw-btn {
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 0 .4em 0 .1em;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
.rw-calendar-grid td .rw-btn:hover {
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
}
|
|
||||||
.rw-calendar-grid td .rw-btn.rw-off-range {
|
|
||||||
color: #b3b3b3;
|
|
||||||
}
|
|
||||||
.rw-calendar-grid.rw-nav-view .rw-btn {
|
|
||||||
padding: .25em 0 .3em;
|
|
||||||
display: block;
|
|
||||||
overflow: hidden;
|
|
||||||
text-align: center;
|
|
||||||
white-space: normal;
|
|
||||||
}
|
|
||||||
.rw-selectlist {
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
.rw-selectlist > ul {
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
.rw-selectlist > ul > li.rw-list-option {
|
|
||||||
position: relative;
|
|
||||||
min-height: 27px;
|
|
||||||
cursor: auto;
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
.rw-selectlist > ul > li.rw-list-option > label > input {
|
|
||||||
position: absolute;
|
|
||||||
margin: 4px 0 0 -20px;
|
|
||||||
}
|
|
||||||
.rw-selectlist > ul > li.rw-list-option > label {
|
|
||||||
padding-left: 20px;
|
|
||||||
line-height: 1.423em;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
.rw-selectlist.rw-rtl > ul > li.rw-list-option {
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
.rw-selectlist.rw-rtl > ul > li.rw-list-option > label > input {
|
|
||||||
margin: 4px -20px 0 0px;
|
|
||||||
}
|
|
||||||
.rw-selectlist.rw-rtl > ul > li.rw-list-option > label {
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
|
||||||
.rw-selectlist.rw-rtl > ul > li.rw-list-option {
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
.rw-selectlist.rw-rtl > ul > li.rw-list-option > label > input {
|
|
||||||
margin: 4px -20px 0 0px;
|
|
||||||
}
|
|
||||||
.rw-selectlist.rw-rtl > ul > li.rw-list-option > label {
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
|
||||||
.rw-selectlist.rw-state-disabled > ul > li:hover,
|
|
||||||
.rw-selectlist.rw-state-readonly > ul > li:hover {
|
|
||||||
background: none;
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
.rw-multiselect {
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
.rw-multiselect:hover {
|
|
||||||
border-color: #adadad;
|
|
||||||
}
|
|
||||||
.rw-multiselect-wrapper {
|
|
||||||
border-radius: 4px;
|
|
||||||
position: relative;
|
|
||||||
cursor: text;
|
|
||||||
}
|
|
||||||
.rw-multiselect-wrapper:before,
|
|
||||||
.rw-multiselect-wrapper:after {
|
|
||||||
content: " ";
|
|
||||||
display: table;
|
|
||||||
}
|
|
||||||
.rw-multiselect-wrapper:after {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
.rw-multiselect-wrapper i.rw-loading {
|
|
||||||
position: absolute;
|
|
||||||
right: 3px;
|
|
||||||
}
|
|
||||||
.rw-multiselect-wrapper > .rw-input {
|
|
||||||
float: left;
|
|
||||||
outline: 0;
|
|
||||||
border-width: 0;
|
|
||||||
line-height: normal;
|
|
||||||
width: auto;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
.rw-multiselect-wrapper > .rw-input::-moz-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.rw-multiselect-wrapper > .rw-input:-ms-input-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.rw-multiselect-wrapper > .rw-input::-webkit-input-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.rw-state-readonly > .rw-multiselect-wrapper,
|
|
||||||
.rw-state-disabled > .rw-multiselect-wrapper {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
.rw-rtl .rw-multiselect-wrapper > .rw-input {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
.rw-multiselect-create-tag {
|
|
||||||
border-top: 1px #cccccc solid;
|
|
||||||
padding-top: 5px;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
.rw-multiselect-taglist {
|
|
||||||
margin: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
list-style: none;
|
|
||||||
padding-right: 0;
|
|
||||||
}
|
|
||||||
.rw-multiselect-taglist > li {
|
|
||||||
display: inline-block;
|
|
||||||
padding-left: 5px;
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
.rw-multiselect-taglist > li {
|
|
||||||
float: left;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 1px;
|
|
||||||
padding: 0.214em 0.15em 0.214em 0.4em;
|
|
||||||
line-height: 1.4em;
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
white-space: nowrap;
|
|
||||||
border-radius: 3px;
|
|
||||||
border: 1px solid #cccccc;
|
|
||||||
background-color: #cccccc;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.rw-multiselect-taglist > li.rw-state-focus {
|
|
||||||
background-color: #ffffff;
|
|
||||||
border: #66afe9 1px solid;
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
.rw-multiselect-taglist > li.rw-state-readonly,
|
|
||||||
.rw-multiselect-taglist > li.rw-state-disabled,
|
|
||||||
.rw-multiselect.rw-state-readonly .rw-multiselect-taglist > li,
|
|
||||||
.rw-multiselect.rw-state-disabled .rw-multiselect-taglist > li {
|
|
||||||
cursor: not-allowed;
|
|
||||||
filter: alpha(opacity=65);
|
|
||||||
opacity: .65;
|
|
||||||
}
|
|
||||||
.rw-multiselect-taglist > li .rw-btn {
|
|
||||||
outline: 0;
|
|
||||||
font-size: 115%;
|
|
||||||
line-height: normal;
|
|
||||||
}
|
|
||||||
.rw-rtl .rw-multiselect-taglist > li {
|
|
||||||
float: right;
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
@ -2,28 +2,18 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>MoneyGo</title>
|
<title>MoneyGo</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css">
|
||||||
<link rel="stylesheet" href="static/external/react-widgets/react-widgets.css">
|
<link rel="stylesheet" href="static/node_modules/react-widgets/dist/css/react-widgets.css">
|
||||||
<link rel="stylesheet" href="static/stylesheet.css">
|
<link rel="stylesheet" href="static/stylesheet.css">
|
||||||
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
||||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react-with-addons.js"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/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 src="static/external/big/big.min.js"></script>
|
||||||
<script src="static/external/classnames/index.js"></script>
|
<script src="static/external/classnames/index.js"></script>
|
||||||
|
|
||||||
<script type="text/javascript" src="static/utils.js"></script>
|
<script type="text/javascript" src="static/utils.js"></script>
|
||||||
<script type="text/javascript" src="static/models.js"></script>
|
<script type="text/javascript" src="static/models.js"></script>
|
||||||
|
<script type="text/javascript" src="static/bundle.js"></script>
|
||||||
<script type="text/jsx" src="static/top_bar.js"></script>
|
|
||||||
<script type="text/jsx" src="static/account_register.js"></script>
|
|
||||||
<script type="text/jsx" src="static/accounts.js"></script>
|
|
||||||
<script type="text/jsx" src="static/ui.js"></script>
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
23
static/main.js
Normal file
23
static/main.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
var React = require('react');
|
||||||
|
var ReactDOM = require('react-dom');
|
||||||
|
|
||||||
|
var Globalize = require('globalize');
|
||||||
|
var globalizeLocalizer = require('react-widgets/lib/localizers/globalize');
|
||||||
|
|
||||||
|
var MoneyGoApp = require('./MoneyGoApp.js');
|
||||||
|
|
||||||
|
// Setup globalization for react-widgets
|
||||||
|
//Globalize.load(require("cldr-data").entireSupplemental());
|
||||||
|
Globalize.load(
|
||||||
|
require("cldr-data/main/en/ca-gregorian"),
|
||||||
|
require("cldr-data/main/en/numbers"),
|
||||||
|
require("cldr-data/supplemental/likelySubtags"),
|
||||||
|
require("cldr-data/supplemental/timeData"),
|
||||||
|
require("cldr-data/supplemental/weekData")
|
||||||
|
);
|
||||||
|
Globalize.locale('en');
|
||||||
|
globalizeLocalizer(Globalize);
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
ReactDOM.render(<MoneyGoApp />, document.getElementById("content"));
|
||||||
|
});
|
620
static/ui.js
620
static/ui.js
@ -1,620 +0,0 @@
|
|||||||
// Import all the objects we want to use from ReactBootstrap
|
|
||||||
var Jumbotron = ReactBootstrap.Jumbotron;
|
|
||||||
var TabbedArea = ReactBootstrap.TabbedArea;
|
|
||||||
var TabPane = ReactBootstrap.TabPane;
|
|
||||||
var Panel = ReactBootstrap.Panel;
|
|
||||||
var ButtonGroup = ReactBootstrap.ButtonGroup;
|
|
||||||
var Modal = ReactBootstrap.Modal;
|
|
||||||
|
|
||||||
const 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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const AccountSettingsModal = React.createClass({
|
|
||||||
_getInitialState: function(props) {
|
|
||||||
return {error: "",
|
|
||||||
name: props.user.Name,
|
|
||||||
username: props.user.Username,
|
|
||||||
email: props.user.Email,
|
|
||||||
password: BogusPassword,
|
|
||||||
confirm_password: BogusPassword,
|
|
||||||
passwordChanged: false,
|
|
||||||
initial_password: BogusPassword};
|
|
||||||
},
|
|
||||||
getInitialState: function() {
|
|
||||||
return this._getInitialState(this.props);
|
|
||||||
},
|
|
||||||
componentWillReceiveProps: function(nextProps) {
|
|
||||||
if (nextProps.show && !this.props.show) {
|
|
||||||
this.setState(this._getInitialState(nextProps));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
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.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),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<Modal show={this.props.show} onHide={this.handleCancel} bsSize="large">
|
|
||||||
<Modal.Header closeButton>
|
|
||||||
<Modal.Title>Edit Account Settings</Modal.Title>
|
|
||||||
</Modal.Header>
|
|
||||||
<Modal.Body>
|
|
||||||
<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/>
|
|
||||||
</form>
|
|
||||||
</Modal.Body>
|
|
||||||
<Modal.Footer>
|
|
||||||
<ButtonGroup>
|
|
||||||
<Button onClick={this.handleCancel} bsStyle="warning">Cancel</Button>
|
|
||||||
<Button onClick={this.handleSubmit} bsStyle="success">Save Settings</Button>
|
|
||||||
</ButtonGroup>
|
|
||||||
</Modal.Footer>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const MoneyGoApp = React.createClass({
|
|
||||||
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.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();
|
|
||||||
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) {
|
|
||||||
this.setState({showAccountSettingsModal: false});
|
|
||||||
},
|
|
||||||
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.user.isUser())
|
|
||||||
mainContent =
|
|
||||||
<TabbedArea defaultActiveKey='1' className="">
|
|
||||||
<TabPane tab="Accounts" eventKey='1' className="fullheight">
|
|
||||||
<AccountsTab
|
|
||||||
className="fullheight"
|
|
||||||
accounts={this.state.accounts}
|
|
||||||
account_map={this.state.account_map}
|
|
||||||
securities={this.state.securities}
|
|
||||||
security_map={this.state.security_map}
|
|
||||||
onCreateAccount={this.handleCreateAccount}
|
|
||||||
onUpdateAccount={this.handleUpdateAccount}
|
|
||||||
onDeleteAccount={this.handleDeleteAccount} />
|
|
||||||
</TabPane>
|
|
||||||
<TabPane tab="Scheduled Transactions" eventKey='2' className="fullheight">Scheduled transactions go here...</TabPane>
|
|
||||||
<TabPane tab="Budgets" eventKey='3' className="fullheight">Budgets go here...</TabPane>
|
|
||||||
<TabPane tab="Reports" eventKey='4' className="fullheight">Reports go here...</TabPane>
|
|
||||||
</TabbedArea>
|
|
||||||
else
|
|
||||||
mainContent =
|
|
||||||
<Jumbotron>
|
|
||||||
<center>
|
|
||||||
<h1>Money<i>Go</i></h1>
|
|
||||||
<p><i>Go</i> manage your money.</p>
|
|
||||||
</center>
|
|
||||||
</Jumbotron>
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="fullheight ui">
|
|
||||||
<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}
|
|
||||||
<AccountSettingsModal
|
|
||||||
show={this.state.showAccountSettingsModal}
|
|
||||||
user={this.state.user}
|
|
||||||
onSubmit={this.handleSettingsSubmitted}
|
|
||||||
onCancel={this.handleSettingsCanceled}/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
React.render(<MoneyGoApp />, document.getElementById("content"));
|
|
Loading…
Reference in New Issue
Block a user