mirror of
				https://github.com/aclindsa/moneygo.git
				synced 2025-11-04 02:23:26 -05:00 
			
		
		
		
	Add initial UI for user-editable securities
This commit is contained in:
		@@ -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() {
 | 
			
		||||
	return function (dispatch) {
 | 
			
		||||
		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 = {
 | 
			
		||||
	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 ListGroup = ReactBootstrap.ListGroup;
 | 
			
		||||
var ListGroupItem = ReactBootstrap.ListGroupItem;
 | 
			
		||||
var Collapse = ReactBootstrap.Collapse;
 | 
			
		||||
var Alert = ReactBootstrap.Alert;
 | 
			
		||||
var Modal = ReactBootstrap.Modal;
 | 
			
		||||
var Collapse = ReactBootstrap.Collapse;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ var TopBarContainer = require('../containers/TopBarContainer');
 | 
			
		||||
var NewUserForm = require('./NewUserForm');
 | 
			
		||||
var AccountSettingsModalContainer = require('../containers/AccountSettingsModalContainer');
 | 
			
		||||
var AccountsTabContainer = require('../containers/AccountsTabContainer');
 | 
			
		||||
var SecuritiesTabContainer = require('../containers/SecuritiesTabContainer');
 | 
			
		||||
 | 
			
		||||
module.exports = React.createClass({
 | 
			
		||||
	displayName: "MoneyGoApp",
 | 
			
		||||
@@ -67,9 +68,13 @@ module.exports = React.createClass({
 | 
			
		||||
						<AccountsTabContainer
 | 
			
		||||
							className="fullheight" />
 | 
			
		||||
						</Tab>
 | 
			
		||||
						<Tab title="Scheduled Transactions" eventKey={2} >Scheduled transactions go here...</Tab>
 | 
			
		||||
						<Tab title="Budgets" eventKey={3} >Budgets go here...</Tab>
 | 
			
		||||
						<Tab title="Reports" eventKey={4} >Reports go here...</Tab>
 | 
			
		||||
						<Tab title="Securities" eventKey={2} >
 | 
			
		||||
						<SecuritiesTabContainer
 | 
			
		||||
							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>);
 | 
			
		||||
			else
 | 
			
		||||
				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,
 | 
			
		||||
	SECURITY_UPDATED: 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.Precision = -1;
 | 
			
		||||
	this.Type = -1;
 | 
			
		||||
	this.AlternateId = "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Security.prototype.toJSON = function() {
 | 
			
		||||
@@ -104,6 +105,7 @@ Security.prototype.toJSON = function() {
 | 
			
		||||
	json_obj.Symbol = this.Symbol;
 | 
			
		||||
	json_obj.Precision = this.Precision;
 | 
			
		||||
	json_obj.Type = this.Type;
 | 
			
		||||
	json_obj.AlternateId = this.AlternateId;
 | 
			
		||||
	return JSON.stringify(json_obj);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -122,6 +124,8 @@ Security.prototype.fromJSON = function(json_input) {
 | 
			
		||||
		this.Precision = json_obj.Precision;
 | 
			
		||||
	if (json_obj.hasOwnProperty("Type"))
 | 
			
		||||
		this.Type = json_obj.Type;
 | 
			
		||||
	if (json_obj.hasOwnProperty("AlternateId"))
 | 
			
		||||
		this.AlternateId = json_obj.AlternateId;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Security.prototype.isSecurity = function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,9 @@ var UserReducer = require('./UserReducer');
 | 
			
		||||
var SessionReducer = require('./SessionReducer');
 | 
			
		||||
var AccountReducer = require('./AccountReducer');
 | 
			
		||||
var SecurityReducer = require('./SecurityReducer');
 | 
			
		||||
var SecurityTemplateReducer = require('./SecurityTemplateReducer');
 | 
			
		||||
var SelectedAccountReducer = require('./SelectedAccountReducer');
 | 
			
		||||
var SelectedSecurityReducer = require('./SelectedSecurityReducer');
 | 
			
		||||
var ErrorReducer = require('./ErrorReducer');
 | 
			
		||||
 | 
			
		||||
module.exports = Redux.combineReducers({
 | 
			
		||||
@@ -12,6 +14,8 @@ module.exports = Redux.combineReducers({
 | 
			
		||||
	session: SessionReducer,
 | 
			
		||||
	accounts: AccountReducer,
 | 
			
		||||
	securities: SecurityReducer,
 | 
			
		||||
	securityTemplates: SecurityTemplateReducer,
 | 
			
		||||
	selectedAccount: SelectedAccountReducer,
 | 
			
		||||
	selectedSecurity: SelectedSecurityReducer,
 | 
			
		||||
	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"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -58,7 +59,7 @@ func (sl *SecurityList) Write(w http.ResponseWriter) error {
 | 
			
		||||
	return enc.Encode(sl)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SearchSecurityTemplates(search string, _type int64) []*Security {
 | 
			
		||||
func SearchSecurityTemplates(search string, _type int64, limit int64) []*Security {
 | 
			
		||||
	upperSearch := strings.ToUpper(search)
 | 
			
		||||
	var results []*Security
 | 
			
		||||
	for i, security := range SecurityTemplates {
 | 
			
		||||
@@ -67,6 +68,9 @@ func SearchSecurityTemplates(search string, _type int64) []*Security {
 | 
			
		||||
			strings.Contains(strings.ToUpper(security.Symbol), upperSearch) {
 | 
			
		||||
			if _type == 0 || _type == security.Type {
 | 
			
		||||
				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
 | 
			
		||||
 | 
			
		||||
		query, _ := url.ParseQuery(r.URL.RawQuery)
 | 
			
		||||
 | 
			
		||||
		var limit int64 = -1
 | 
			
		||||
		search := query.Get("search")
 | 
			
		||||
		_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
 | 
			
		||||
		err := (&sl).Write(w)
 | 
			
		||||
 
 | 
			
		||||
@@ -61,7 +61,7 @@ div.accounttree-root div {
 | 
			
		||||
	height: 100%-100px;
 | 
			
		||||
	overflow: auto;
 | 
			
		||||
}
 | 
			
		||||
.account-column {
 | 
			
		||||
.account-column, .securitylist-column {
 | 
			
		||||
	padding: 15px 15px 43px 15px;
 | 
			
		||||
	border-right: 1px solid #DDD;
 | 
			
		||||
	border-left: 1px solid #DDD;
 | 
			
		||||
@@ -82,7 +82,7 @@ div.accounttree-root div {
 | 
			
		||||
	height: 100%;
 | 
			
		||||
	overflow: auto;
 | 
			
		||||
}
 | 
			
		||||
.transactions-column {
 | 
			
		||||
.transactions-column, .securities-column {
 | 
			
		||||
	padding: 15px;
 | 
			
		||||
	border-right: 1px solid #DDD;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user