From 9844785b8dedd8523cb77e10db471ed8ad636984 Mon Sep 17 00:00:00 2001 From: Aaron Lindsay Date: Sat, 17 Jun 2017 10:28:50 -0400 Subject: [PATCH] Basic Report UI complete! --- Makefile | 6 +- js/actions/ReportActions.js | 13 +- js/components/ReportsTab.js | 324 ++++++++++++++++++++++----- js/components/StackedBarChart.js | 8 +- js/containers/ReportsTabContainer.js | 8 +- js/models.js | 32 ++- js/reducers/ReportReducer.js | 11 +- package.json | 1 + reports.go | 2 + static/index.html | 1 + 10 files changed, 331 insertions(+), 75 deletions(-) diff --git a/Makefile b/Makefile index 86c3e17..1e27874 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ JS_SOURCES = $(wildcard js/*.js) $(wildcard js/*/*.js) -all: static/bundle.js static/react-widgets security_templates.go +all: static/bundle.js static/react-widgets static/codemirror/codemirror.css security_templates.go node_modules: npm install @@ -11,6 +11,10 @@ static/bundle.js: $(JS_SOURCES) node_modules static/react-widgets: node_modules/react-widgets/dist node_modules rsync -a node_modules/react-widgets/dist/ static/react-widgets/ +static/codemirror/codemirror.css: node_modules/codemirror/lib/codemirror.js node_modules + mkdir -p static/codemirror + cp node_modules/codemirror/lib/codemirror.css static/codemirror/codemirror.css + security_templates.go: cusip_list.csv ./scripts/gen_security_list.py > security_templates.go diff --git a/js/actions/ReportActions.js b/js/actions/ReportActions.js index bc344ff..cb60f89 100644 --- a/js/actions/ReportActions.js +++ b/js/actions/ReportActions.js @@ -138,9 +138,9 @@ function create(report) { if (e.isError()) { dispatch(ErrorActions.serverError(e)); } else { - var a = new Report(); - a.fromJSON(data); - dispatch(reportCreated(a)); + var r = new Report(); + r.fromJSON(data); + dispatch(reportCreated(r)); } }, error: function(jqXHR, status, error) { @@ -165,9 +165,10 @@ function update(report) { if (e.isError()) { dispatch(ErrorActions.serverError(e)); } else { - var a = new Report(); - a.fromJSON(data); - dispatch(reportUpdated(a)); + var r = new Report(); + r.fromJSON(data); + dispatch(reportUpdated(r)); + dispatch(tabulate(r)); } }, error: function(jqXHR, status, error) { diff --git a/js/components/ReportsTab.js b/js/components/ReportsTab.js index 6771528..029e9cc 100644 --- a/js/components/ReportsTab.js +++ b/js/components/ReportsTab.js @@ -1,9 +1,25 @@ var React = require('react'); +var ReactDOM = require('react-dom'); var ReactBootstrap = require('react-bootstrap'); +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 Panel = ReactBootstrap.Panel; +var Modal = ReactBootstrap.Modal; +var ProgressBar = ReactBootstrap.ProgressBar; + +var Combobox = require('react-widgets').Combobox; + +var CodeMirror = require('react-codemirror'); +require('codemirror/mode/lua/lua'); var StackedBarChart = require('../components/StackedBarChart'); var PieChart = require('../components/PieChart'); @@ -12,13 +28,121 @@ var models = require('../models') var Report = models.Report; var Tabulation = models.Tabulation; +class AddEditReportModal extends React.Component { + getInitialState(props) { + var s = { + reportid: -1, + name: "", + lua: "" + }; + if (props && props.editReport != null) { + s.reportid = props.editReport.ReportId; + s.name = props.editReport.Name; + s.lua = props.editReport.Lua; + } + return s; + } + constructor() { + super(); + this.state = this.getInitialState(); + this.onCancel = this.handleCancel.bind(this); + this.onNameChange = this.handleNameChange.bind(this); + this.onLuaChange = this.handleLuaChange.bind(this); + this.onSubmit = this.handleSubmit.bind(this); + } + componentWillReceiveProps(nextProps) { + if (nextProps.show && !this.props.show) { + this.setState(this.getInitialState(nextProps)); + } + } + handleCancel() { + if (this.props.onCancel != null) + this.props.onCancel(); + } + handleNameChange() { + this.setState({ + name: ReactDOM.findDOMNode(this.refs.name).value, + }); + } + handleLuaChange(lua) { + this.setState({ + lua: lua + }); + } + handleSubmit() { + var r = new Report(); + + if (this.props.editReport != null) + r.ReportId = this.state.reportid; + r.Name = this.state.name; + r.Lua = this.state.lua; + + if (this.props.onSubmit != null) + this.props.onSubmit(r); + } + render() { + var headerText = (this.props.editReport != null) ? "Edit" : "Create New"; + var buttonText = (this.props.editReport != null) ? "Save Changes" : "Create Report"; + + var codeMirrorOptions = { + lineNumbers: true, + mode: 'lua', + }; + return ( + + + {headerText} Report + + +
+ + Name + + + + + + Lua Code + + + + +
+
+ + + + + + +
+ ); + } +} + class ReportsTab extends React.Component { constructor() { super(); this.state = { - initialized: false + initialized: false, + creatingNewReport: false, + editingReport: false } this.onSelectSeries = this.handleSelectSeries.bind(this); + this.onSelectReport = this.handleSelectReport.bind(this); + this.onNewReport = this.handleNewReport.bind(this); + this.onEditReport = this.handleEditReport.bind(this); + this.onDeleteReport = this.handleDeleteReport.bind(this); + this.onCreationCancel = this.handleCreationCancel.bind(this); + this.onCreationSubmit = this.handleCreationSubmit.bind(this); + this.onEditingCancel = this.handleEditingCancel.bind(this); + this.onEditingSubmit = this.handleEditingSubmit.bind(this); } componentWillMount() { this.props.onFetchAllReports(); @@ -27,80 +151,164 @@ class ReportsTab extends React.Component { var selected = nextProps.reports.selected; if (!this.state.initialized) { if (selected == -1 && - nextProps.reports.list.length > 0) + nextProps.reports.list.length > 0) { nextProps.onSelectReport(nextProps.reports.map[nextProps.reports.list[0]]); - this.setState({initialized: true}); - } else if (selected != -1 && !nextProps.reports.tabulations.hasOwnProperty(selected)) { - nextProps.onTabulateReport(nextProps.reports.map[nextProps.reports.list[0]]); - } else if (selected != -1 && nextProps.reports.selectedTabulation == null) { - nextProps.onSelectSeries(nextProps.reports.tabulations[nextProps.reports.list[0]]); + nextProps.onTabulateReport(nextProps.reports.map[nextProps.reports.list[0]]); + this.setState({initialized: true}); + } + } else if (selected != -1 && + nextProps.reports.tabulations.hasOwnProperty(selected) && + nextProps.reports.selectedTabulation == null) { + nextProps.onSelectSeries(nextProps.reports.tabulations[selected]); } } handleSelectSeries(series) { if (series == Tabulation.topLevelSeriesName()) return; - var seriesTraversal = this.props.selectedTabulation.seriesTraversal.slice(); + var seriesTraversal = this.props.reports.seriesTraversal.slice(); seriesTraversal.push(series); var selectedTabulation = this.props.reports.tabulations[this.props.reports.selected]; this.props.onSelectSeries(selectedTabulation, seriesTraversal); } + handleSelectReport(report) { + this.props.onSelectReport(report); + if (!this.props.reports.tabulations.hasOwnProperty(report.ReportId)) + this.props.onTabulateReport(report); + } + handleNewReport() { + this.setState({creatingNewReport: true}); + } + handleEditReport() { + this.setState({editingReport: true}); + } + handleDeleteReport() { + this.props.onDeleteReport(this.props.reports.map[this.props.reports.selected]); + } + handleCreationCancel() { + this.setState({creatingNewReport: false}); + } + handleCreationSubmit(report) { + this.setState({creatingNewReport: false}); + this.props.onCreateReport(report); + } + handleEditingCancel() { + this.setState({editingReport: false}); + } + handleEditingSubmit(report) { + this.setState({editingReport: false}); + this.props.onUpdateReport(report); + } render() { var selectedTabulation = this.props.reports.selectedTabulation; - if (!selectedTabulation) { - return ( -
+ var reportPanel = []; + if (selectedTabulation) { + var titleTracks = []; + var seriesTraversal = []; + + for (var i = 0; i < this.props.reports.seriesTraversal.length; i++) { + var name = this.props.reports.selectedTabulation.Title; + if (i > 0) + name = this.props.reports.seriesTraversal[i-1]; + + // Make a closure for going up the food chain + var self = this; + var navOnClick = function() { + var onSelectSeries = self.props.onSelectSeries; + var tabulation = self.props.reports.tabulations[self.props.reports.selected]; + var mySeriesTraversal = seriesTraversal.slice(); + return function() { + onSelectSeries(tabulation, mySeriesTraversal); + }; + }(); + titleTracks.push(( + + )); + titleTracks.push((/)); + seriesTraversal.push(this.props.reports.seriesTraversal[i]); + } + if (titleTracks.length == 0) { + titleTracks.push(( + + )); + } else { + var i = this.props.reports.seriesTraversal.length-1; + titleTracks.push(( + + )); + } + + if (this.props.reports.selectedTabulation.Labels.length > 1) + var report = ( + + ); + else + var report = ( + + ); + + reportPanel = ( + + {report} + + ); + } else if (this.props.reports.selected != -1) { + reportPanel = ( + + + ); } - var titleTracks = []; - var seriesTraversal = []; - - for (var i = 0; i < this.props.selectedTabulation.seriesTraversal.length; i++) { - var name = this.props.selectedTabulation.tabulation.Title; - if (i > 0) - name = this.props.selectedTabulation.seriesTraversal[i-1]; - - // Make a closure for going up the food chain - var self = this; - var navOnClick = function() { - var onSelectTabulation = self.props.onSelectTabulation; - var report = self.props.reports[self.props.selectedTabulation.tabulation.ReportId]; - var mySeriesTraversal = seriesTraversal.slice(); - return function() { - onSelectTabulation(report, mySeriesTraversal); - }; - }(); - titleTracks.push(( - - )); - titleTracks.push((/)); - seriesTraversal.push(this.props.selectedTabulation.seriesTraversal[i]); - } - if (titleTracks.length == 0) { - titleTracks.push(( - - )); - } else { - var i = this.props.selectedTabulation.seriesTraversal.length-1; - titleTracks.push(( - - )); - } + var noReportSelected = this.props.reports.selected == -1; + var selectedReport = -1; + if (this.props.reports.map.hasOwnProperty(this.props.reports.selected)) + selectedReport = this.props.reports.map[this.props.reports.selected]; return ( - - - +
+ + + + + + + + typeof item === 'string' ? item : item.Name} + value={selectedReport} + onChange={this.onSelectReport} + suggest + filter='contains' + ref="report" /> + + + + + + {reportPanel} +
); } } diff --git a/js/components/StackedBarChart.js b/js/components/StackedBarChart.js index f807ddd..8fe8910 100644 --- a/js/components/StackedBarChart.js +++ b/js/components/StackedBarChart.js @@ -135,12 +135,12 @@ class StackedBarChart extends React.Component { if (value == 0) continue; if (value > 0) { - rectHeight = y(value) - y(0); + var rectHeight = y(value) - y(0); positiveSum[j] += rectHeight; - rectY = height - y(0) - positiveSum[j]; + var rectY = height - y(0) - positiveSum[j]; } else { - rectHeight = y(0) - y(value); - rectY = height - y(0) + negativeSum[j]; + var rectHeight = y(0) - y(value); + var rectY = height - y(0) + negativeSum[j]; negativeSum[j] += rectHeight; } diff --git a/js/containers/ReportsTabContainer.js b/js/containers/ReportsTabContainer.js index f4df013..84e3f93 100644 --- a/js/containers/ReportsTabContainer.js +++ b/js/containers/ReportsTabContainer.js @@ -4,8 +4,14 @@ var ReportActions = require('../actions/ReportActions'); var ReportsTab = require('../components/ReportsTab'); function mapStateToProps(state) { + var report_list = []; + for (var reportId in state.reports.map) { + if (state.reports.map.hasOwnProperty(reportId)) + report_list.push(state.reports.map[reportId]); + } return { - reports: state.reports + reports: state.reports, + report_list: report_list } } diff --git a/js/models.js b/js/models.js index 3b5cae3..2890840 100644 --- a/js/models.js +++ b/js/models.js @@ -435,6 +435,35 @@ class Error { } } +class Report { + constructor() { + this.ReportId = -1; + this.UserId = -1; + this.Name = ""; + this.Lua = ""; + } + toJSON() { + var json_obj = {}; + json_obj.ReportId = this.ReportId; + json_obj.UserId = this.UserId; + json_obj.Name = this.Name; + json_obj.Lua = this.Lua; + return JSON.stringify(json_obj); + } + fromJSON(json_input) { + var json_obj = getJSONObj(json_input) + + if (json_obj.hasOwnProperty("ReportId")) + this.ReportId = json_obj.ReportId; + if (json_obj.hasOwnProperty("UserId")) + this.UserId = json_obj.UserId; + if (json_obj.hasOwnProperty("Name")) + this.Name = json_obj.Name; + if (json_obj.hasOwnProperty("Lua")) + this.Lua = json_obj.Lua; + } +} + class Series { constructor() { this.Values = []; @@ -496,7 +525,7 @@ class Series { class Tabulation { constructor() { - this.ReportId = ""; + this.ReportId = -1; this.Title = ""; this.Subtitle = ""; this.Units = ""; @@ -578,6 +607,7 @@ module.exports = { Account: Account, Split: Split, Transaction: Transaction, + Report: Report, Tabulation: Tabulation, OFXDownload: OFXDownload, Error: Error, diff --git a/js/reducers/ReportReducer.js b/js/reducers/ReportReducer.js index ed5198a..6405ec7 100644 --- a/js/reducers/ReportReducer.js +++ b/js/reducers/ReportReducer.js @@ -63,16 +63,19 @@ module.exports = function(state = initialState, action) { selectedTabulation: null, seriesTraversal: [] }); - case ReportConstants.TABULATION_FETCHED: + case ReportConstants.REPORT_TABULATED: var tabulation = action.tabulation; - return assign({}, state, { + var tabulations = assign({}, state.tabulations, { [tabulation.ReportId]: tabulation }); + return assign({}, state, { + tabulations: tabulations + }); case ReportConstants.SERIES_SELECTED: - return { + return assign({}, state, { selectedTabulation: action.tabulation, seriesTraversal: action.seriesTraversal - }; + }); case UserConstants.USER_LOGGEDOUT: return initialState; default: diff --git a/package.json b/package.json index 5fcea6e..36bd5fc 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "react-dom": "^15.3.2", "react-redux": "^5.0.5", "react-widgets": "^3.4.4", + "react-codemirror": "^1.0.0", "redux": "^3.6.0", "redux-thunk": "^2.1.0" }, diff --git a/reports.go b/reports.go index 0701381..d84dc9d 100644 --- a/reports.go +++ b/reports.go @@ -203,6 +203,8 @@ func ReportTabulationHandler(w http.ResponseWriter, r *http.Request, user *User, return } + tabulation.ReportId = reportid + err = tabulation.Write(w) if err != nil { WriteError(w, 999 /*Internal Error*/) diff --git a/static/index.html b/static/index.html index 34b4c9c..4d0ded5 100644 --- a/static/index.html +++ b/static/index.html @@ -5,6 +5,7 @@ +