mirror of
https://github.com/aclindsa/moneygo.git
synced 2024-12-26 15:42:27 -05:00
Add initial UI for user-editable securities
This commit is contained in:
parent
a61e460c2f
commit
ce6660b575
@ -19,6 +19,52 @@ function securitiesFetched(securities) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createSecurity() {
|
||||||
|
return {
|
||||||
|
type: SecurityConstants.CREATE_SECURITY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function securityCreated(security) {
|
||||||
|
return {
|
||||||
|
type: SecurityConstants.SECURITY_CREATED,
|
||||||
|
security: security
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSecurity() {
|
||||||
|
return {
|
||||||
|
type: SecurityConstants.UPDATE_SECURITY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function securityUpdated(security) {
|
||||||
|
return {
|
||||||
|
type: SecurityConstants.SECURITY_UPDATED,
|
||||||
|
security: security
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeSecurity() {
|
||||||
|
return {
|
||||||
|
type: SecurityConstants.REMOVE_SECURITY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function securityRemoved(securityId) {
|
||||||
|
return {
|
||||||
|
type: SecurityConstants.SECURITY_REMOVED,
|
||||||
|
securityId: securityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function securitySelected(securityId) {
|
||||||
|
return {
|
||||||
|
type: SecurityConstants.SECURITY_SELECTED,
|
||||||
|
securityId: securityId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function fetchAll() {
|
function fetchAll() {
|
||||||
return function (dispatch) {
|
return function (dispatch) {
|
||||||
dispatch(fetchSecurities());
|
dispatch(fetchSecurities());
|
||||||
@ -47,6 +93,88 @@ function fetchAll() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function create(security) {
|
||||||
|
return function (dispatch) {
|
||||||
|
dispatch(createSecurity());
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
url: "security/",
|
||||||
|
data: {security: security.toJSON()},
|
||||||
|
success: function(data, status, jqXHR) {
|
||||||
|
var e = new Error();
|
||||||
|
e.fromJSON(data);
|
||||||
|
if (e.isError()) {
|
||||||
|
ErrorActions.serverError(e);
|
||||||
|
} else {
|
||||||
|
var s = new Security();
|
||||||
|
s.fromJSON(data);
|
||||||
|
dispatch(securityCreated(s));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(jqXHR, status, error) {
|
||||||
|
ErrorActions.ajaxError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(security) {
|
||||||
|
return function (dispatch) {
|
||||||
|
dispatch(updateSecurity());
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "PUT",
|
||||||
|
dataType: "json",
|
||||||
|
url: "security/"+security.SecurityId+"/",
|
||||||
|
data: {security: security.toJSON()},
|
||||||
|
success: function(data, status, jqXHR) {
|
||||||
|
var e = new Error();
|
||||||
|
e.fromJSON(data);
|
||||||
|
if (e.isError()) {
|
||||||
|
ErrorActions.serverError(e);
|
||||||
|
} else {
|
||||||
|
var s = new Security();
|
||||||
|
s.fromJSON(data);
|
||||||
|
dispatch(securityUpdated(s));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(jqXHR, status, error) {
|
||||||
|
ErrorActions.ajaxError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(security) {
|
||||||
|
return function(dispatch) {
|
||||||
|
dispatch(removeSecurity());
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "DELETE",
|
||||||
|
dataType: "json",
|
||||||
|
url: "security/"+security.SecurityId+"/",
|
||||||
|
success: function(data, status, jqXHR) {
|
||||||
|
var e = new Error();
|
||||||
|
e.fromJSON(data);
|
||||||
|
if (e.isError()) {
|
||||||
|
ErrorActions.serverError(e);
|
||||||
|
} else {
|
||||||
|
dispatch(securityRemoved(security.SecurityId));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(jqXHR, status, error) {
|
||||||
|
ErrorActions.ajaxError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
fetchAll: fetchAll
|
fetchAll: fetchAll,
|
||||||
|
create: create,
|
||||||
|
update: update,
|
||||||
|
remove: remove,
|
||||||
|
select: securitySelected
|
||||||
};
|
};
|
||||||
|
62
js/actions/SecurityTemplateActions.js
Normal file
62
js/actions/SecurityTemplateActions.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
var SecurityTemplateConstants = require('../constants/SecurityTemplateConstants');
|
||||||
|
|
||||||
|
var ErrorActions = require('./ErrorActions');
|
||||||
|
|
||||||
|
var models = require('../models.js');
|
||||||
|
var Security = models.Security;
|
||||||
|
var Error = models.Error;
|
||||||
|
|
||||||
|
function searchSecurityTemplates(searchString, searchType) {
|
||||||
|
return {
|
||||||
|
type: SecurityTemplateConstants.SEARCH_SECURITY_TEMPLATES,
|
||||||
|
searchString: searchString,
|
||||||
|
searchType: searchType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function securityTemplatesSearched(searchString, searchType, securities) {
|
||||||
|
return {
|
||||||
|
type: SecurityTemplateConstants.SECURITY_TEMPLATES_SEARCHED,
|
||||||
|
searchString: searchString,
|
||||||
|
searchType: searchType,
|
||||||
|
securities: securities
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function search(searchString, searchType, limit) {
|
||||||
|
return function (dispatch) {
|
||||||
|
dispatch(searchSecurityTemplates(searchString, searchType));
|
||||||
|
|
||||||
|
if (searchString == "")
|
||||||
|
return;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
url: "securitytemplate/?search="+searchString+"&type="+searchType+"&limit="+limit,
|
||||||
|
success: function(data, status, jqXHR) {
|
||||||
|
var e = new Error();
|
||||||
|
e.fromJSON(data);
|
||||||
|
if (e.isError()) {
|
||||||
|
ErrorActions.serverError(e);
|
||||||
|
} else if (data.securities == null) {
|
||||||
|
dispatch(securityTemplatesSearched(searchString, searchType, new Array()));
|
||||||
|
} else {
|
||||||
|
dispatch(securityTemplatesSearched(searchString, searchType,
|
||||||
|
data.securities.map(function(json) {
|
||||||
|
var s = new Security();
|
||||||
|
s.fromJSON(json);
|
||||||
|
return s;
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(jqXHR, status, error) {
|
||||||
|
ErrorActions.ajaxError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
search: search
|
||||||
|
};
|
@ -15,7 +15,6 @@ var ButtonGroup = ReactBootstrap.ButtonGroup;
|
|||||||
var Glyphicon = ReactBootstrap.Glyphicon;
|
var Glyphicon = ReactBootstrap.Glyphicon;
|
||||||
var ListGroup = ReactBootstrap.ListGroup;
|
var ListGroup = ReactBootstrap.ListGroup;
|
||||||
var ListGroupItem = ReactBootstrap.ListGroupItem;
|
var ListGroupItem = ReactBootstrap.ListGroupItem;
|
||||||
var Collapse = ReactBootstrap.Collapse;
|
|
||||||
var Alert = ReactBootstrap.Alert;
|
var Alert = ReactBootstrap.Alert;
|
||||||
var Modal = ReactBootstrap.Modal;
|
var Modal = ReactBootstrap.Modal;
|
||||||
var Collapse = ReactBootstrap.Collapse;
|
var Collapse = ReactBootstrap.Collapse;
|
||||||
|
@ -10,6 +10,7 @@ var TopBarContainer = require('../containers/TopBarContainer');
|
|||||||
var NewUserForm = require('./NewUserForm');
|
var NewUserForm = require('./NewUserForm');
|
||||||
var AccountSettingsModalContainer = require('../containers/AccountSettingsModalContainer');
|
var AccountSettingsModalContainer = require('../containers/AccountSettingsModalContainer');
|
||||||
var AccountsTabContainer = require('../containers/AccountsTabContainer');
|
var AccountsTabContainer = require('../containers/AccountsTabContainer');
|
||||||
|
var SecuritiesTabContainer = require('../containers/SecuritiesTabContainer');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: "MoneyGoApp",
|
displayName: "MoneyGoApp",
|
||||||
@ -67,9 +68,13 @@ module.exports = React.createClass({
|
|||||||
<AccountsTabContainer
|
<AccountsTabContainer
|
||||||
className="fullheight" />
|
className="fullheight" />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Scheduled Transactions" eventKey={2} >Scheduled transactions go here...</Tab>
|
<Tab title="Securities" eventKey={2} >
|
||||||
<Tab title="Budgets" eventKey={3} >Budgets go here...</Tab>
|
<SecuritiesTabContainer
|
||||||
<Tab title="Reports" eventKey={4} >Reports go here...</Tab>
|
className="fullheight" />
|
||||||
|
</Tab>
|
||||||
|
<Tab title="Scheduled Transactions" eventKey={3} >Scheduled transactions go here...</Tab>
|
||||||
|
<Tab title="Budgets" eventKey={4} >Budgets go here...</Tab>
|
||||||
|
<Tab title="Reports" eventKey={5} >Reports go here...</Tab>
|
||||||
</Tabs>);
|
</Tabs>);
|
||||||
else
|
else
|
||||||
mainContent = (
|
mainContent = (
|
||||||
|
363
js/components/SecuritiesTab.js
Normal file
363
js/components/SecuritiesTab.js
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
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 Form = ReactBootstrap.Form;
|
||||||
|
var FormGroup = ReactBootstrap.FormGroup;
|
||||||
|
var FormControl = ReactBootstrap.FormControl;
|
||||||
|
var ControlLabel = ReactBootstrap.ControlLabel;
|
||||||
|
var Button = ReactBootstrap.Button;
|
||||||
|
var ButtonGroup = ReactBootstrap.ButtonGroup;
|
||||||
|
var ButtonToolbar = ReactBootstrap.ButtonToolbar;
|
||||||
|
var Glyphicon = ReactBootstrap.Glyphicon;
|
||||||
|
var ListGroup = ReactBootstrap.ListGroup;
|
||||||
|
var ListGroupItem = ReactBootstrap.ListGroupItem;
|
||||||
|
var Modal = ReactBootstrap.Modal;
|
||||||
|
var Panel = ReactBootstrap.Panel;
|
||||||
|
|
||||||
|
var Combobox = require('react-widgets').Combobox;
|
||||||
|
|
||||||
|
var models = require('../models');
|
||||||
|
var Security = models.Security;
|
||||||
|
var SecurityType = models.SecurityType;
|
||||||
|
var SecurityTypeList = models.SecurityTypeList;
|
||||||
|
|
||||||
|
const SecurityTemplatePanel = React.createClass({
|
||||||
|
handleSearchChange: function(){
|
||||||
|
this.props.onSearchTemplates(ReactDOM.findDOMNode(this.refs.search).value, 0, this.props.maxResults + 1);
|
||||||
|
},
|
||||||
|
renderTemplateList: function() {
|
||||||
|
var templates = this.props.securityTemplates;
|
||||||
|
if (this.props.search != "") {
|
||||||
|
var items = [];
|
||||||
|
for (var i = 0; i < templates.length && i < 15; i++) {
|
||||||
|
var template = templates[i];
|
||||||
|
var self = this;
|
||||||
|
var onClickFn = (function() {
|
||||||
|
var j = i;
|
||||||
|
return function(){self.props.onSelectTemplate(templates[j])};
|
||||||
|
})();
|
||||||
|
var key = template.Type.toString() + template.AlternateId;
|
||||||
|
items.push((
|
||||||
|
<ListGroupItem onClick={onClickFn} key={key}>
|
||||||
|
{template.Name} - {template.Description}
|
||||||
|
</ListGroupItem>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (templates.length > this.props.maxResults) {
|
||||||
|
items.push((
|
||||||
|
<ListGroupItem disabled key="too-many-templates">
|
||||||
|
Too many templates to display, please refine your search...
|
||||||
|
</ListGroupItem>
|
||||||
|
));
|
||||||
|
} else if (templates.length == 0) {
|
||||||
|
items.push((
|
||||||
|
<ListGroupItem disabled key="no-templates">
|
||||||
|
Sorry, no templates matched your search...
|
||||||
|
</ListGroupItem>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<br />
|
||||||
|
<ControlLabel>Select a template to populate your security:</ControlLabel>
|
||||||
|
<ListGroup>
|
||||||
|
{items}
|
||||||
|
</ListGroup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<Panel collapsible header="Populate Security from Template...">
|
||||||
|
<FormControl type="text"
|
||||||
|
placeholder="Search..."
|
||||||
|
value={this.props.search}
|
||||||
|
onChange={this.handleSearchChange}
|
||||||
|
ref="search"/>
|
||||||
|
{this.renderTemplateList()}
|
||||||
|
</Panel>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const AddEditSecurityModal = React.createClass({
|
||||||
|
getInitialState: function() {
|
||||||
|
var s = {
|
||||||
|
securityid: -1,
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
symbol: "",
|
||||||
|
precision: 0,
|
||||||
|
type: 1,
|
||||||
|
alternateid: ""
|
||||||
|
};
|
||||||
|
if (this.props.editSecurity != null) {
|
||||||
|
s.securityid = this.props.editSecurity.SecurityId;
|
||||||
|
s.name = this.props.editSecurity.Name;
|
||||||
|
s.description = this.props.editSecurity.Description;
|
||||||
|
s.symbol = this.props.editSecurity.Symbol;
|
||||||
|
s.precision = this.props.editSecurity.Precision;
|
||||||
|
s.type = this.props.editSecurity.Type;
|
||||||
|
s.alternateid = this.props.editSecurity.AlternateId;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
},
|
||||||
|
onSelectTemplate: function(template) {
|
||||||
|
this.setState({
|
||||||
|
name: template.Name,
|
||||||
|
description: template.Description,
|
||||||
|
symbol: template.Symbol,
|
||||||
|
precision: template.Precision,
|
||||||
|
type: template.Type,
|
||||||
|
alternateid: template.AlternateId
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleCancel: function() {
|
||||||
|
if (this.props.onCancel != null)
|
||||||
|
this.props.onCancel();
|
||||||
|
},
|
||||||
|
handleNameChange: function() {
|
||||||
|
this.setState({
|
||||||
|
name: ReactDOM.findDOMNode(this.refs.name).value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleDescriptionChange: function() {
|
||||||
|
this.setState({
|
||||||
|
description: ReactDOM.findDOMNode(this.refs.description).value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleSymbolChange: function() {
|
||||||
|
this.setState({
|
||||||
|
symbol: ReactDOM.findDOMNode(this.refs.symbol).value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handlePrecisionChange: function() {
|
||||||
|
this.setState({
|
||||||
|
precision: +ReactDOM.findDOMNode(this.refs.precision).value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleTypeChange: function(type) {
|
||||||
|
if (type.hasOwnProperty('TypeId'))
|
||||||
|
this.setState({
|
||||||
|
type: type.TypeId
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleAlternateIdChange: function() {
|
||||||
|
this.setState({
|
||||||
|
alternateid: ReactDOM.findDOMNode(this.refs.alternateid).value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleSubmit: function() {
|
||||||
|
var s = new Security();
|
||||||
|
|
||||||
|
if (this.props.editSecurity != null)
|
||||||
|
s.SecurityId = this.state.securityid;
|
||||||
|
s.Name = this.state.name;
|
||||||
|
s.Description = this.state.description;
|
||||||
|
s.Symbol = this.state.symbol;
|
||||||
|
s.Precision = this.state.precision;
|
||||||
|
s.Type = this.state.type;
|
||||||
|
s.AlternateId = this.state.alternateid;
|
||||||
|
|
||||||
|
if (this.props.onSubmit != null)
|
||||||
|
this.props.onSubmit(s);
|
||||||
|
},
|
||||||
|
componentWillReceiveProps: function(nextProps) {
|
||||||
|
if (nextProps.show && !this.props.show) {
|
||||||
|
this.setState(this.getInitialState());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
var headerText = (this.props.editSecurity != null) ? "Edit" : "Create New";
|
||||||
|
var buttonText = (this.props.editSecurity != null) ? "Save Changes" : "Create Security";
|
||||||
|
var alternateidname = (this.state.type == SecurityType.Currency) ? "ISO 4217 Code" : "CUSIP";
|
||||||
|
return (
|
||||||
|
<Modal show={this.props.show} onHide={this.handleCancel}>
|
||||||
|
<Modal.Header closeButton>
|
||||||
|
<Modal.Title>{headerText} Security</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body>
|
||||||
|
<SecurityTemplatePanel
|
||||||
|
search={this.props.securityTemplates.search}
|
||||||
|
securityTemplates={this.props.securityTemplates.templates}
|
||||||
|
onSearchTemplates={this.props.onSearchTemplates}
|
||||||
|
maxResults={15}
|
||||||
|
onSelectTemplate={this.onSelectTemplate} />
|
||||||
|
<Form horizontal onSubmit={this.handleSubmit}>
|
||||||
|
<FormGroup>
|
||||||
|
<Col componentClass={ControlLabel} xs={3}>Name</Col>
|
||||||
|
<Col xs={9}>
|
||||||
|
<FormControl type="text"
|
||||||
|
value={this.state.name}
|
||||||
|
onChange={this.handleNameChange}
|
||||||
|
ref="name"/>
|
||||||
|
</Col>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<Col componentClass={ControlLabel} xs={3}>Description</Col>
|
||||||
|
<Col xs={9}>
|
||||||
|
<FormControl type="text"
|
||||||
|
value={this.state.description}
|
||||||
|
onChange={this.handleDescriptionChange}
|
||||||
|
ref="description"/>
|
||||||
|
</Col>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<Col componentClass={ControlLabel} xs={3}>Symbol or Ticker</Col>
|
||||||
|
<Col xs={9}>
|
||||||
|
<FormControl type="text"
|
||||||
|
value={this.state.symbol}
|
||||||
|
onChange={this.handleSymbolChange}
|
||||||
|
ref="symbol"/>
|
||||||
|
</Col>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<Col componentClass={ControlLabel} xs={3}>Smallest Fraction Traded</Col>
|
||||||
|
<Col xs={9}>
|
||||||
|
<FormControl componentClass="select"
|
||||||
|
placeholder={this.state.precision}
|
||||||
|
value={this.state.precision}
|
||||||
|
onChange={this.handlePrecisionChange}
|
||||||
|
ref="precision">
|
||||||
|
<option value={0}>1</option>
|
||||||
|
<option value={1}>0.1 (1/10)</option>
|
||||||
|
<option value={2}>0.01 (1/100)</option>
|
||||||
|
<option value={3}>0.001 (1/1000)</option>
|
||||||
|
<option value={4}>0.0001 (1/10000)</option>
|
||||||
|
<option value={5}>0.00001 (1/100000)</option>
|
||||||
|
</FormControl>
|
||||||
|
</Col>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<Col componentClass={ControlLabel} xs={3}>Security Type</Col>
|
||||||
|
<Col xs={9}>
|
||||||
|
<Combobox
|
||||||
|
suggest
|
||||||
|
data={SecurityTypeList}
|
||||||
|
valueField='TypeId'
|
||||||
|
textField='Name'
|
||||||
|
value={this.state.type}
|
||||||
|
onChange={this.handleTypeChange}
|
||||||
|
ref="type" />
|
||||||
|
</Col>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<Col componentClass={ControlLabel} xs={3}>{alternateidname}</Col>
|
||||||
|
<Col xs={9}>
|
||||||
|
<FormControl type="text"
|
||||||
|
value={this.state.alternateid}
|
||||||
|
onChange={this.handleAlternateIdChange}
|
||||||
|
ref="alternateid"/>
|
||||||
|
</Col>
|
||||||
|
</FormGroup>
|
||||||
|
</Form>
|
||||||
|
</Modal.Body>
|
||||||
|
<Modal.Footer>
|
||||||
|
<ButtonGroup className="pull-right">
|
||||||
|
<Button onClick={this.handleCancel} bsStyle="warning">Cancel</Button>
|
||||||
|
<Button onClick={this.handleSubmit} bsStyle="success">{buttonText}</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
</Modal.Footer>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const SecurityList = React.createClass({
|
||||||
|
render: function() {
|
||||||
|
var children = [];
|
||||||
|
var self = this;
|
||||||
|
for (var securityId in this.props.securities) {
|
||||||
|
if (this.props.securities.hasOwnProperty(securityId)) {
|
||||||
|
var buttonStyle = (securityId == this.props.selectedSecurity) ? "info" : "link";
|
||||||
|
var onClickFn = (function() {
|
||||||
|
var id = securityId;
|
||||||
|
return function(){self.props.onSelectSecurity(id)};
|
||||||
|
})();
|
||||||
|
children.push((<Button
|
||||||
|
bsStyle={buttonStyle}
|
||||||
|
key={securityId}
|
||||||
|
onClick={onClickFn}>
|
||||||
|
{this.props.securities[securityId].Name} - {this.props.securities[securityId].Description}
|
||||||
|
</Button>));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: "SecuritiesTab",
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
creatingNewSecurity: false,
|
||||||
|
editingSecurity: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
handleNewSecurity: function() {
|
||||||
|
this.setState({creatingNewSecurity: true});
|
||||||
|
},
|
||||||
|
handleEditSecurity: function() {
|
||||||
|
this.setState({editingSecurity: true});
|
||||||
|
},
|
||||||
|
handleCreationCancel: function() {
|
||||||
|
this.setState({creatingNewSecurity: false});
|
||||||
|
},
|
||||||
|
handleCreationSubmit: function(security) {
|
||||||
|
this.setState({creatingNewSecurity: false});
|
||||||
|
this.props.onCreateSecurity(security);
|
||||||
|
},
|
||||||
|
handleEditingCancel: function() {
|
||||||
|
this.setState({editingSecurity: false});
|
||||||
|
},
|
||||||
|
handleEditingSubmit: function(security) {
|
||||||
|
this.setState({editingSecurity: false});
|
||||||
|
this.props.onUpdateSecurity(security);
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
var editDisabled = this.props.selectedSecurity == -1;
|
||||||
|
|
||||||
|
var selectedSecurity = null;
|
||||||
|
if (this.props.securities.hasOwnProperty(this.props.selectedSecurity))
|
||||||
|
selectedSecurity = this.props.securities[this.props.selectedSecurity];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid fluid className="fullheight"><Row className="fullheight">
|
||||||
|
<Col xs={3} className="fullheight securitylist-column">
|
||||||
|
<AddEditSecurityModal
|
||||||
|
show={this.state.creatingNewSecurity}
|
||||||
|
onCancel={this.handleCreationCancel}
|
||||||
|
onSubmit={this.handleCreationSubmit}
|
||||||
|
onSearchTemplates={this.props.onSearchTemplates}
|
||||||
|
securityTemplates={this.props.securityTemplates} />
|
||||||
|
<AddEditSecurityModal
|
||||||
|
show={this.state.editingSecurity}
|
||||||
|
editSecurity={selectedSecurity}
|
||||||
|
onCancel={this.handleEditingCancel}
|
||||||
|
onSubmit={this.handleEditingSubmit}
|
||||||
|
onSearchTemplates={this.props.onSearchTemplates}
|
||||||
|
securityTemplates={this.props.securityTemplates} />
|
||||||
|
<SecurityList
|
||||||
|
selectedSecurity={this.props.selectedSecurity}
|
||||||
|
securities={this.props.securities}
|
||||||
|
onSelectSecurity={this.props.onSelectSecurity} />
|
||||||
|
</Col><Col xs={9} className="fullheight securities-column">
|
||||||
|
<ButtonToolbar className="pull-right"><ButtonGroup>
|
||||||
|
<Button onClick={this.handleEditSecurity} bsStyle="primary" disabled={editDisabled}><Glyphicon glyph='cog'/> Edit Security</Button>
|
||||||
|
<Button onClick={this.handleNewSecurity} bsStyle="success"><Glyphicon glyph='plus-sign'/> New Security</Button>
|
||||||
|
</ButtonGroup></ButtonToolbar>
|
||||||
|
</Col>
|
||||||
|
</Row></Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -8,5 +8,6 @@ module.exports = keyMirror({
|
|||||||
UPDATE_SECURITY: null,
|
UPDATE_SECURITY: null,
|
||||||
SECURITY_UPDATED: null,
|
SECURITY_UPDATED: null,
|
||||||
REMOVE_SECURITY: null,
|
REMOVE_SECURITY: null,
|
||||||
SECURITY_REMOVED: null
|
SECURITY_REMOVED: null,
|
||||||
|
SECURITY_SELECTED: null
|
||||||
});
|
});
|
||||||
|
6
js/constants/SecurityTemplateConstants.js
Normal file
6
js/constants/SecurityTemplateConstants.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
var keyMirror = require('keymirror');
|
||||||
|
|
||||||
|
module.exports = keyMirror({
|
||||||
|
SEARCH_SECURITY_TEMPLATES: null,
|
||||||
|
SECURITY_TEMPLATES_SEARCHED: null
|
||||||
|
});
|
35
js/containers/SecuritiesTabContainer.js
Normal file
35
js/containers/SecuritiesTabContainer.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
var connect = require('react-redux').connect;
|
||||||
|
|
||||||
|
var SecurityActions = require('../actions/SecurityActions');
|
||||||
|
var SecurityTemplateActions = require('../actions/SecurityTemplateActions');
|
||||||
|
var SecuritiesTab = require('../components/SecuritiesTab');
|
||||||
|
|
||||||
|
function mapStateToProps(state) {
|
||||||
|
var selectedSecurityAccounts = [];
|
||||||
|
for (var accountId in state.accounts) {
|
||||||
|
if (state.accounts.hasOwnProperty(accountId)
|
||||||
|
&& state.accounts[accountId].SecurityId == state.selectedSecurity)
|
||||||
|
selectedSecurityAccounts.push(state.accounts[accountId]);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
securities: state.securities,
|
||||||
|
selectedSecurityAccounts: selectedSecurityAccounts,
|
||||||
|
selectedSecurity: state.selectedSecurity,
|
||||||
|
securityTemplates: state.securityTemplates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch) {
|
||||||
|
return {
|
||||||
|
onCreateSecurity: function(security) {dispatch(SecurityActions.create(security))},
|
||||||
|
onUpdateSecurity: function(security) {dispatch(SecurityActions.update(security))},
|
||||||
|
onDeleteSecurity: function(securityId) {dispatch(SecurityActions.remove(securityId))},
|
||||||
|
onSelectSecurity: function(securityId) {dispatch(SecurityActions.select(securityId))},
|
||||||
|
onSearchTemplates: function(search, type, limit) {dispatch(SecurityTemplateActions.search(search, type, limit))}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(SecuritiesTab)
|
@ -94,6 +94,7 @@ function Security() {
|
|||||||
this.Symbol = "";
|
this.Symbol = "";
|
||||||
this.Precision = -1;
|
this.Precision = -1;
|
||||||
this.Type = -1;
|
this.Type = -1;
|
||||||
|
this.AlternateId = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Security.prototype.toJSON = function() {
|
Security.prototype.toJSON = function() {
|
||||||
@ -104,6 +105,7 @@ Security.prototype.toJSON = function() {
|
|||||||
json_obj.Symbol = this.Symbol;
|
json_obj.Symbol = this.Symbol;
|
||||||
json_obj.Precision = this.Precision;
|
json_obj.Precision = this.Precision;
|
||||||
json_obj.Type = this.Type;
|
json_obj.Type = this.Type;
|
||||||
|
json_obj.AlternateId = this.AlternateId;
|
||||||
return JSON.stringify(json_obj);
|
return JSON.stringify(json_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +124,8 @@ Security.prototype.fromJSON = function(json_input) {
|
|||||||
this.Precision = json_obj.Precision;
|
this.Precision = json_obj.Precision;
|
||||||
if (json_obj.hasOwnProperty("Type"))
|
if (json_obj.hasOwnProperty("Type"))
|
||||||
this.Type = json_obj.Type;
|
this.Type = json_obj.Type;
|
||||||
|
if (json_obj.hasOwnProperty("AlternateId"))
|
||||||
|
this.AlternateId = json_obj.AlternateId;
|
||||||
}
|
}
|
||||||
|
|
||||||
Security.prototype.isSecurity = function() {
|
Security.prototype.isSecurity = function() {
|
||||||
|
@ -4,7 +4,9 @@ var UserReducer = require('./UserReducer');
|
|||||||
var SessionReducer = require('./SessionReducer');
|
var SessionReducer = require('./SessionReducer');
|
||||||
var AccountReducer = require('./AccountReducer');
|
var AccountReducer = require('./AccountReducer');
|
||||||
var SecurityReducer = require('./SecurityReducer');
|
var SecurityReducer = require('./SecurityReducer');
|
||||||
|
var SecurityTemplateReducer = require('./SecurityTemplateReducer');
|
||||||
var SelectedAccountReducer = require('./SelectedAccountReducer');
|
var SelectedAccountReducer = require('./SelectedAccountReducer');
|
||||||
|
var SelectedSecurityReducer = require('./SelectedSecurityReducer');
|
||||||
var ErrorReducer = require('./ErrorReducer');
|
var ErrorReducer = require('./ErrorReducer');
|
||||||
|
|
||||||
module.exports = Redux.combineReducers({
|
module.exports = Redux.combineReducers({
|
||||||
@ -12,6 +14,8 @@ module.exports = Redux.combineReducers({
|
|||||||
session: SessionReducer,
|
session: SessionReducer,
|
||||||
accounts: AccountReducer,
|
accounts: AccountReducer,
|
||||||
securities: SecurityReducer,
|
securities: SecurityReducer,
|
||||||
|
securityTemplates: SecurityTemplateReducer,
|
||||||
selectedAccount: SelectedAccountReducer,
|
selectedAccount: SelectedAccountReducer,
|
||||||
|
selectedSecurity: SelectedSecurityReducer,
|
||||||
error: ErrorReducer
|
error: ErrorReducer
|
||||||
});
|
});
|
||||||
|
33
js/reducers/SecurityTemplateReducer.js
Normal file
33
js/reducers/SecurityTemplateReducer.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
var assign = require('object-assign');
|
||||||
|
|
||||||
|
var SecurityTemplateConstants = require('../constants/SecurityTemplateConstants');
|
||||||
|
var UserConstants = require('../constants/UserConstants');
|
||||||
|
|
||||||
|
var SecurityType = require('../models').SecurityType;
|
||||||
|
|
||||||
|
module.exports = function(state = {search: "", type: 0, templates: [], searchNumber: 0}, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case SecurityTemplateConstants.SEARCH_SECURITY_TEMPLATES:
|
||||||
|
return {
|
||||||
|
search: action.searchString,
|
||||||
|
type: action.searchType,
|
||||||
|
templates: []
|
||||||
|
};
|
||||||
|
case SecurityTemplateConstants.SECURITY_TEMPLATES_SEARCHED:
|
||||||
|
if ((action.searchString != state.search) || (action.searchType != state.type))
|
||||||
|
return state;
|
||||||
|
return {
|
||||||
|
search: action.searchString,
|
||||||
|
type: action.searchType,
|
||||||
|
templates: action.securities
|
||||||
|
};
|
||||||
|
case UserConstants.USER_LOGGEDOUT:
|
||||||
|
return {
|
||||||
|
search: "",
|
||||||
|
type: 0,
|
||||||
|
templates: []
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
23
js/reducers/SelectedSecurityReducer.js
Normal file
23
js/reducers/SelectedSecurityReducer.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
var SecurityConstants = require('../constants/SecurityConstants');
|
||||||
|
var UserConstants = require('../constants/UserConstants');
|
||||||
|
|
||||||
|
module.exports = function(state = -1, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case SecurityConstants.SECURITIES_FETCHED:
|
||||||
|
for (var i = 0; i < action.securities.length; i++) {
|
||||||
|
if (action.securities[i].SecurityId == state)
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
case SecurityConstants.SECURITY_REMOVED:
|
||||||
|
if (action.securityId == state)
|
||||||
|
return -1;
|
||||||
|
return state;
|
||||||
|
case SecurityConstants.SECURITY_SELECTED:
|
||||||
|
return action.securityId;
|
||||||
|
case UserConstants.USER_LOGGEDOUT:
|
||||||
|
return -1;
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
@ -7,6 +7,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ func (sl *SecurityList) Write(w http.ResponseWriter) error {
|
|||||||
return enc.Encode(sl)
|
return enc.Encode(sl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SearchSecurityTemplates(search string, _type int64) []*Security {
|
func SearchSecurityTemplates(search string, _type int64, limit int64) []*Security {
|
||||||
upperSearch := strings.ToUpper(search)
|
upperSearch := strings.ToUpper(search)
|
||||||
var results []*Security
|
var results []*Security
|
||||||
for i, security := range SecurityTemplates {
|
for i, security := range SecurityTemplates {
|
||||||
@ -67,6 +68,9 @@ func SearchSecurityTemplates(search string, _type int64) []*Security {
|
|||||||
strings.Contains(strings.ToUpper(security.Symbol), upperSearch) {
|
strings.Contains(strings.ToUpper(security.Symbol), upperSearch) {
|
||||||
if _type == 0 || _type == security.Type {
|
if _type == 0 || _type == security.Type {
|
||||||
results = append(results, &SecurityTemplates[i])
|
results = append(results, &SecurityTemplates[i])
|
||||||
|
if limit != -1 && int64(len(results)) >= limit {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,10 +302,22 @@ func SecurityTemplateHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
var sl SecurityList
|
var sl SecurityList
|
||||||
|
|
||||||
query, _ := url.ParseQuery(r.URL.RawQuery)
|
query, _ := url.ParseQuery(r.URL.RawQuery)
|
||||||
|
|
||||||
|
var limit int64 = -1
|
||||||
search := query.Get("search")
|
search := query.Get("search")
|
||||||
_type := GetSecurityType(query.Get("type"))
|
_type := GetSecurityType(query.Get("type"))
|
||||||
|
|
||||||
securities := SearchSecurityTemplates(search, _type)
|
limitstring := query.Get("limit")
|
||||||
|
if limitstring != "" {
|
||||||
|
limitint, err := strconv.ParseInt(limitstring, 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
WriteError(w, 3 /*Invalid Request*/)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
limit = limitint
|
||||||
|
}
|
||||||
|
|
||||||
|
securities := SearchSecurityTemplates(search, _type, limit)
|
||||||
|
|
||||||
sl.Securities = &securities
|
sl.Securities = &securities
|
||||||
err := (&sl).Write(w)
|
err := (&sl).Write(w)
|
||||||
|
@ -61,7 +61,7 @@ div.accounttree-root div {
|
|||||||
height: 100%-100px;
|
height: 100%-100px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
.account-column {
|
.account-column, .securitylist-column {
|
||||||
padding: 15px 15px 43px 15px;
|
padding: 15px 15px 43px 15px;
|
||||||
border-right: 1px solid #DDD;
|
border-right: 1px solid #DDD;
|
||||||
border-left: 1px solid #DDD;
|
border-left: 1px solid #DDD;
|
||||||
@ -82,7 +82,7 @@ div.accounttree-root div {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
.transactions-column {
|
.transactions-column, .securities-column {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
border-right: 1px solid #DDD;
|
border-right: 1px solid #DDD;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user