mirror of
https://github.com/aclindsa/moneygo.git
synced 2024-12-26 15:42:27 -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 Modal = ReactBootstrap.Modal;
|
||||
var Pagination = ReactBootstrap.Pagination;
|
||||
|
||||
var Label = ReactBootstrap.Label;
|
||||
var Table = ReactBootstrap.Table;
|
||||
var Grid = ReactBootstrap.Grid;
|
||||
var Row = ReactBootstrap.Row;
|
||||
var Col = ReactBootstrap.Col;
|
||||
var Panel = ReactBootstrap.Panel;
|
||||
|
||||
var Input = ReactBootstrap.Input;
|
||||
var Button = ReactBootstrap.Button;
|
||||
var ButtonGroup = ReactBootstrap.ButtonGroup;
|
||||
var ButtonToolbar = ReactBootstrap.ButtonToolbar;
|
||||
|
||||
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({
|
||||
handleClick: function(e) {
|
||||
const refs = ["date", "number", "description", "account", "status", "amount"];
|
||||
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]);
|
||||
return;
|
||||
}
|
||||
@ -135,7 +142,7 @@ const AmountInput = React.createClass({
|
||||
var symbol = "?";
|
||||
if (this.props.security)
|
||||
symbol = this.props.security.Symbol;
|
||||
var bsStyle = "";
|
||||
var bsStyle = undefined;
|
||||
if (this.props.bsStyle)
|
||||
bsStyle = this.props.bsStyle;
|
||||
|
||||
@ -173,7 +180,7 @@ const AddEditTransactionModal = React.createClass({
|
||||
},
|
||||
handleDescriptionChange: function() {
|
||||
this.setState({
|
||||
transaction: React.addons.update(this.state.transaction, {
|
||||
transaction: react_update(this.state.transaction, {
|
||||
Description: {$set: this.refs.description.getValue()}
|
||||
})
|
||||
});
|
||||
@ -182,7 +189,7 @@ const AddEditTransactionModal = React.createClass({
|
||||
if (date == null)
|
||||
return;
|
||||
this.setState({
|
||||
transaction: React.addons.update(this.state.transaction, {
|
||||
transaction: react_update(this.state.transaction, {
|
||||
Date: {$set: date}
|
||||
})
|
||||
});
|
||||
@ -190,7 +197,7 @@ const AddEditTransactionModal = React.createClass({
|
||||
handleStatusChange: function(status) {
|
||||
if (status.hasOwnProperty('StatusId')) {
|
||||
this.setState({
|
||||
transaction: React.addons.update(this.state.transaction, {
|
||||
transaction: react_update(this.state.transaction, {
|
||||
Status: {$set: status.StatusId}
|
||||
})
|
||||
});
|
||||
@ -198,21 +205,21 @@ const AddEditTransactionModal = React.createClass({
|
||||
},
|
||||
handleAddSplit: function() {
|
||||
this.setState({
|
||||
transaction: React.addons.update(this.state.transaction, {
|
||||
transaction: react_update(this.state.transaction, {
|
||||
Splits: {$push: [new Split()]}
|
||||
})
|
||||
});
|
||||
},
|
||||
handleDeleteSplit: function(split) {
|
||||
this.setState({
|
||||
transaction: React.addons.update(this.state.transaction, {
|
||||
transaction: react_update(this.state.transaction, {
|
||||
Splits: {$splice: [[split, 1]]}
|
||||
})
|
||||
});
|
||||
},
|
||||
handleUpdateNumber: function(split) {
|
||||
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()}
|
||||
});
|
||||
this.setState({
|
||||
@ -221,7 +228,7 @@ const AddEditTransactionModal = React.createClass({
|
||||
},
|
||||
handleUpdateMemo: function(split) {
|
||||
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()}
|
||||
});
|
||||
this.setState({
|
||||
@ -230,7 +237,7 @@ const AddEditTransactionModal = React.createClass({
|
||||
},
|
||||
handleUpdateAccount: function(account, split) {
|
||||
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},
|
||||
AccountId: {$set: account.AccountId}
|
||||
});
|
||||
@ -240,7 +247,7 @@ const AddEditTransactionModal = React.createClass({
|
||||
},
|
||||
handleUpdateAmount: function(split) {
|
||||
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())}
|
||||
});
|
||||
this.setState({
|
||||
@ -294,7 +301,7 @@ const AddEditTransactionModal = React.createClass({
|
||||
var self = this;
|
||||
var s = this.state.transaction.Splits[i];
|
||||
var security = null;
|
||||
var amountValidation = "";
|
||||
var amountValidation = undefined;
|
||||
var accountValidation = "";
|
||||
if (s.AccountId in this.props.account_map) {
|
||||
security = this.props.security_map[this.props.account_map[s.AccountId].SecurityId];
|
||||
@ -355,7 +362,7 @@ const AddEditTransactionModal = React.createClass({
|
||||
account_map={this.props.account_map}
|
||||
value={s.AccountId}
|
||||
includeRoot={false}
|
||||
onSelect={updateAccountFn}
|
||||
onChange={updateAccountFn}
|
||||
ref={"account-"+i}
|
||||
className={accountValidation}/></Col>
|
||||
<Col xs={2}><AmountInput type="text"
|
||||
@ -401,7 +408,7 @@ const AddEditTransactionModal = React.createClass({
|
||||
data={TransactionStatusList}
|
||||
valueField='StatusId'
|
||||
textField='Name'
|
||||
value={this.state.transaction.Status}
|
||||
defaultValue={this.state.transaction.Status}
|
||||
onSelect={this.handleStatusChange}
|
||||
ref="status" />
|
||||
</Input>
|
||||
@ -536,7 +543,7 @@ const ImportTransactionsModal = React.createClass({
|
||||
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 button2 = [];
|
||||
if (!this.state.imported && this.state.error == null) {
|
||||
@ -545,9 +552,9 @@ const ImportTransactionsModal = React.createClass({
|
||||
} else {
|
||||
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 (
|
||||
<Modal show={this.props.show} onHide={this.handleCancel} bsSize="medium">
|
||||
<Modal show={this.props.show} onHide={this.handleCancel} bsSize="small">
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>Import Transactions</Modal.Title>
|
||||
</Modal.Header>
|
||||
@ -577,7 +584,8 @@ const ImportTransactionsModal = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
const AccountRegister = React.createClass({
|
||||
module.exports = React.createClass({
|
||||
displayName: "AccountRegister",
|
||||
getInitialState: function() {
|
||||
return {
|
||||
importingTransactions: false,
|
||||
@ -591,7 +599,7 @@ const AccountRegister = React.createClass({
|
||||
};
|
||||
},
|
||||
resize: function() {
|
||||
var div = React.findDOMNode(this);
|
||||
var div = ReactDOM.findDOMNode(this);
|
||||
this.setState({height: div.parentElement.clientHeight - 64});
|
||||
},
|
||||
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 (
|
||||
<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 ListGroup = ReactBootstrap.ListGroup;
|
||||
var ListGroupItem = ReactBootstrap.ListGroupItem;
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
|
||||
var ReactBootstrap = require('react-bootstrap');
|
||||
var Grid = ReactBootstrap.Grid;
|
||||
var Row = ReactBootstrap.Row;
|
||||
var Col = ReactBootstrap.Col;
|
||||
|
||||
var Input = ReactBootstrap.Input;
|
||||
var Button = ReactBootstrap.Button;
|
||||
var ButtonGroup = ReactBootstrap.ButtonGroup;
|
||||
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;
|
||||
|
||||
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} />
|
||||
);
|
||||
}
|
||||
});
|
||||
var AccountCombobox = require('./AccountCombobox.js');
|
||||
var AccountRegister = require('./AccountRegister.js');
|
||||
|
||||
const AddEditAccountModal = React.createClass({
|
||||
getInitialState: function() {
|
||||
@ -139,7 +112,7 @@ const AddEditAccountModal = React.createClass({
|
||||
account_map={this.props.account_map}
|
||||
value={this.state.parentaccountid}
|
||||
rootName={rootName}
|
||||
onSelect={this.handleParentChange}
|
||||
onChange={this.handleParentChange}
|
||||
ref="parent" />
|
||||
</Input>
|
||||
<Input wrapperClassName="wrapper"
|
||||
@ -151,7 +124,7 @@ const AddEditAccountModal = React.createClass({
|
||||
valueField='SecurityId'
|
||||
textField='Name'
|
||||
value={this.state.security}
|
||||
onSelect={this.handleSecurityChange}
|
||||
onChange={this.handleSecurityChange}
|
||||
ref="security" />
|
||||
</Input>
|
||||
<Input wrapperClassName="wrapper"
|
||||
@ -163,7 +136,7 @@ const AddEditAccountModal = React.createClass({
|
||||
valueField='TypeId'
|
||||
textField='Name'
|
||||
value={this.state.type}
|
||||
onSelect={this.handleTypeChange}
|
||||
onChange={this.handleTypeChange}
|
||||
ref="type" />
|
||||
</Input>
|
||||
</form>
|
||||
@ -179,6 +152,7 @@ const AddEditAccountModal = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const DeleteAccountModal = React.createClass({
|
||||
getInitialState: function() {
|
||||
if (this.props.initialAccount != null)
|
||||
@ -263,7 +237,7 @@ const DeleteAccountModal = React.createClass({
|
||||
accounts={this.props.accounts}
|
||||
account_map={this.props.account_map}
|
||||
value={this.state.accountid}
|
||||
onSelect={this.handleChange}/>
|
||||
onChange={this.handleChange}/>
|
||||
</Input>
|
||||
{checkbox}
|
||||
</form>
|
||||
@ -280,12 +254,8 @@ const DeleteAccountModal = React.createClass({
|
||||
});
|
||||
|
||||
const AccountTreeNode = React.createClass({
|
||||
mixins: [CollapsibleMixin],
|
||||
getCollapsibleDOMNode: function() {
|
||||
return React.findDOMNode(this.refs.children);
|
||||
},
|
||||
getCollapsibleDimensionValue: function() {
|
||||
return React.findDOMNode(this.refs.children).scrollHeight;
|
||||
getInitialState: function() {
|
||||
return {expanded: false};
|
||||
},
|
||||
handleToggle: function(e) {
|
||||
e.preventDefault();
|
||||
@ -300,8 +270,7 @@ const AccountTreeNode = React.createClass({
|
||||
this.props.onSelect(this.props.account);
|
||||
},
|
||||
render: function() {
|
||||
var styles = this.getCollapsibleClassSet();
|
||||
var glyph = this.isExpanded() ? 'minus' : 'plus';
|
||||
var glyph = this.state.expanded ? 'minus' : 'plus';
|
||||
var active = (this.props.selectedAccount != null &&
|
||||
this.props.account.AccountId == this.props.selectedAccount.AccountId);
|
||||
var buttonStyle = active ? "info" : "link";
|
||||
@ -317,7 +286,7 @@ const AccountTreeNode = React.createClass({
|
||||
});
|
||||
var accounttreeClasses = "accounttree"
|
||||
var expandButton = [];
|
||||
if (children.length > 0)
|
||||
if (children.length > 0) {
|
||||
expandButton.push((
|
||||
<Button onClick={this.handleToggle}
|
||||
bsSize="xsmall"
|
||||
@ -326,8 +295,9 @@ const AccountTreeNode = React.createClass({
|
||||
<Glyphicon glyph={glyph} bsSize="xsmall"/>
|
||||
</Button>
|
||||
));
|
||||
else
|
||||
} else {
|
||||
accounttreeClasses += "-nochildren";
|
||||
}
|
||||
return (
|
||||
<div className={accounttreeClasses}>
|
||||
{expandButton}
|
||||
@ -336,9 +306,11 @@ const AccountTreeNode = React.createClass({
|
||||
className="accounttree-name">
|
||||
{this.props.account.Name}
|
||||
</Button>
|
||||
<div ref='children' className={classNames(styles)}>
|
||||
{children}
|
||||
</div>
|
||||
<Collapse in={this.state.expanded}>
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -356,7 +328,7 @@ const AccountTree = React.createClass({
|
||||
}
|
||||
},
|
||||
resize: function() {
|
||||
var div = React.findDOMNode(this);
|
||||
var div = ReactDOM.findDOMNode(this);
|
||||
this.setState({height: div.parentElement.clientHeight - 73});
|
||||
},
|
||||
componentDidMount: function() {
|
||||
@ -386,7 +358,8 @@ const AccountTree = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
const AccountsTab = React.createClass({
|
||||
module.exports = React.createClass({
|
||||
displayName: "AccountsTab",
|
||||
getInitialState: function() {
|
||||
return {
|
||||
selectedAccount: null,
|
||||
@ -436,7 +409,7 @@ const AccountsTab = React.createClass({
|
||||
var accounts = this.props.accounts;
|
||||
var account_map = this.props.account_map;
|
||||
|
||||
var disabled = (this.state.selectedAccount == null) ? "disabled" : "";
|
||||
var disabled = (this.state.selectedAccount == null) ? true : false;
|
||||
|
||||
return (
|
||||
<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 Alert = ReactBootstrap.Alert;
|
||||
var React = require('react');
|
||||
|
||||
var ReactBootstrap = require('react-bootstrap');
|
||||
var Alert = ReactBootstrap.Alert;
|
||||
var Input = ReactBootstrap.Input;
|
||||
var Button = ReactBootstrap.Button;
|
||||
var DropdownButton = ReactBootstrap.DropdownButton;
|
||||
var MenuItem = ReactBootstrap.MenuItem;
|
||||
|
||||
var Row = ReactBootstrap.Row;
|
||||
var Col = ReactBootstrap.Col;
|
||||
|
||||
@ -62,7 +62,7 @@ const LoginBar = React.createClass({
|
||||
});
|
||||
|
||||
const LogoutBar = React.createClass({
|
||||
handleOnSelect: function(key) {
|
||||
handleOnSelect: function(e, key) {
|
||||
if (key == 1) {
|
||||
if (this.props.onAccountSettings != null)
|
||||
this.props.onAccountSettings();
|
||||
@ -79,7 +79,7 @@ const LogoutBar = React.createClass({
|
||||
<Col xs={6}></Col>
|
||||
<Col xs={4}>
|
||||
<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="2">Logout</MenuItem>
|
||||
</DropdownButton>
|
||||
@ -91,7 +91,8 @@ const LogoutBar = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
const TopBar = React.createClass({
|
||||
module.exports = React.createClass({
|
||||
displayName: "TopBar",
|
||||
render: function() {
|
||||
var barContents;
|
||||
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>
|
||||
<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.5/css/bootstrap-theme.min.css">
|
||||
<link rel="stylesheet" href="static/external/react-widgets/react-widgets.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.6/css/bootstrap-theme.min.css">
|
||||
<link rel="stylesheet" href="static/node_modules/react-widgets/dist/css/react-widgets.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://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/classnames/index.js"></script>
|
||||
|
||||
<script type="text/javascript" src="static/utils.js"></script>
|
||||
<script type="text/javascript" src="static/models.js"></script>
|
||||
|
||||
<script type="text/jsx" src="static/top_bar.js"></script>
|
||||
<script type="text/jsx" src="static/account_register.js"></script>
|
||||
<script type="text/jsx" src="static/accounts.js"></script>
|
||||
<script type="text/jsx" src="static/ui.js"></script>
|
||||
<script type="text/javascript" src="static/bundle.js"></script>
|
||||
|
||||
</head>
|
||||
<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