diff --git a/package.json b/package.json index 6197f200..f7e390aa 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "build": "./node_modules/.bin/webpack", "dev-client": "./node_modules/.bin/webpack-dev-server --port 3002 --content-base public/", "fullStart": "./node_modules/.bin/webpack && node ./bin/start", - "debug": "node $NODE_DEBUG_OPTION ./bin/start" + "debug": "node $NODE_DEBUG_OPTION ./bin/start", + "test": "jest" }, "repository": { "type": "git", @@ -113,10 +114,12 @@ }, "devDependencies": { "babel-plugin-transform-async-to-generator": "^6.24.1", + "jest": "^22.4.2", "react-hot-loader": "^3.0.0-beta.7", + "servicebot-client": "^1.0.3", "supertest": "^3.0.0", "tape": "^4.8.0", - "uglifyjs-webpack-plugin": "^1.0.1", + "uglifyjs-webpack-plugin": "^1.2.4", "webpack-dev-server": "^1.14.1" } } diff --git a/views/components/elements/dashboard/css/style.css b/views/components/elements/dashboard/css/style.css index 9fa04f15..ab280011 100644 --- a/views/components/elements/dashboard/css/style.css +++ b/views/components/elements/dashboard/css/style.css @@ -26,4 +26,30 @@ font-size: 2em; font-weight: 100; padding: 20px 0px; +} + +.offerings-widgets{ + background: white; + margin: 15px 0; + border-radius: 5px; +} + +.offerings-widget { + padding: 5px 15px; + text-align: center; +} + +.offerings-widget.master { + border-bottom-left-radius: 5px; + border-top-left-radius: 5px; + background: #000000; + color: white; + text-align: left; +} + +.customer-chart-widget{ + background: #ffffff; + padding: 5px; + border-radius: 5px; + margin: 5px 10px; } \ No newline at end of file diff --git a/views/components/elements/dashboard/dashboard-chart.jsx b/views/components/elements/dashboard/dashboard-chart.jsx index c5edb5eb..66314b3b 100644 --- a/views/components/elements/dashboard/dashboard-chart.jsx +++ b/views/components/elements/dashboard/dashboard-chart.jsx @@ -160,4 +160,76 @@ class ServiceStatusChart extends React.Component { } -export {ServiceOverTimeChart, ServiceStatusChart}; + + +class CustomerStatusChart extends React.Component { + constructor(props){ + super(props); + this.state = { + loading: true, + instances: {}, + containerWidth: this.props.containerWidth, + chartData: {}, + chartOption: {} + }; + + this.fetchInstances = this.fetchInstances.bind(this); + } + + componentDidMount(){ + this.fetchInstances(); + } + + fetchInstances(){ + let self = this; + let url = '/api/v1/service-instances'; + Fetcher(url).then(function (response) { + + if(!response.error) { + let statuses = _.uniq(_.map(response, (instance) => instance.status)); + let groupByStatus = _.groupBy(response, (instance) => { + return instance.status ? instance.status : other + }); + let serviceCountByStatus = _.map(groupByStatus, (group)=>{return(group.length)}); + + let data = { + labels: statuses, + datasets: [{ + data: serviceCountByStatus, + backgroundColor: [ "#FF6384", "#36A2EB", "#FFCE56", "#B388FF" ], + hoverBackgroundColor: [ "#FF6384", "#36A2EB", "#FFCE56", "#B388FF" ] }] + }; + let options = { + animation: { + animateRotate: true, + animateScale: true + } + }; + + self.setState({loading: false, instances: response, chartData: data, chartOptions: options}); + }else{ + self.setState({loading: false}); + } + }) + } + + render(){ + if(this.state.loading){ + return( +
+ +
+ ); + }else{ + return( +
+

Customer Stats

+ +
+ ); + } + } + +} + +export {ServiceOverTimeChart, ServiceStatusChart, CustomerStatusChart}; diff --git a/views/components/elements/dashboard/dashboard-widgets.jsx b/views/components/elements/dashboard/dashboard-widgets.jsx index 2c7b6d44..899cee7c 100644 --- a/views/components/elements/dashboard/dashboard-widgets.jsx +++ b/views/components/elements/dashboard/dashboard-widgets.jsx @@ -70,13 +70,13 @@ class Widget extends React.Component { return (
{this.state.data.label}
- {this.state.data.value &&
{this.getFormatted(this.state.data.value)}{this.props.postFix}
} + {this.state.data.value !== null &&
{this.getFormatted(this.state.data.value)}{this.props.postFix}
} {this.state.data.list && -
+
{this.state.data.list.map(listing => -
- {listing.label} - {listing.value} +
+
{listing.label}
+
{listing.value}
)}
@@ -115,12 +115,20 @@ class DashboardWidgets extends React.Component { saleStat.push({label:'Requested Sales', value:this.state.data.salesStats.overall.requested}); saleStat.push({label:'Waiting Cancellations', value:this.state.data.salesStats.overall.waitingCancellation}); saleStat.push({label:'Cancelled Sales', value:this.state.data.salesStats.overall.cancelled}); + + //Unpaid charge logic + let orange = false; + let green = true; + if(this.state.data.salesStats.overall.remainingCharges > 0) { + orange = true; + green = false; + } return (
- +
@@ -135,4 +143,4 @@ class DashboardWidgets extends React.Component { } -export {DashboardWidgets}; \ No newline at end of file +export {DashboardWidgets, Widget}; \ No newline at end of file diff --git a/views/components/elements/dashboard/offerings-stats-widgets.jsx b/views/components/elements/dashboard/offerings-stats-widgets.jsx new file mode 100644 index 00000000..c4b96cb7 --- /dev/null +++ b/views/components/elements/dashboard/offerings-stats-widgets.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import './css/style.css'; + +class OfferingsStatsWidgets extends React.Component { + + constructor(props){ + super(props); + } + + render(){ + let self = this; + let analytics = self.props.data; + return ( +
+
Total Offerings: {analytics.offeringStats.total}
+
Subscriptions: {analytics.offeringStats.totalSubscription}
+
Scheduled Payments: {analytics.offeringStats.totalSplit}
+
One Times: {analytics.offeringStats.totalOneTime}
+
Quotes: {analytics.offeringStats.totalQuote}
+
+ ); + } + +} + +export default OfferingsStatsWidgets; \ No newline at end of file diff --git a/views/components/elements/dashboard/overall-stats-widgets.jsx b/views/components/elements/dashboard/overall-stats-widgets.jsx new file mode 100644 index 00000000..c30d43cd --- /dev/null +++ b/views/components/elements/dashboard/overall-stats-widgets.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import './css/style.css'; +import {Widget} from "../../elements/dashboard/dashboard-widgets.jsx"; +import {CustomerStatusChart} from "../../elements/dashboard/dashboard-chart.jsx"; + +class OverallStatsWidgets extends React.Component { + + + constructor(props){ + super(props); + } + + render(){ + let self = this; + let analytics = self.props.data; + let saleStat = []; + saleStat.push({label:'Total Sales', value: analytics.salesStats.overall.total}); + saleStat.push({label:'Customers who Purchased', value: analytics.salesStats.overall.customersWithOfferings}); + saleStat.push({label:'Active Sales', value: analytics.salesStats.overall.activeSales}); + saleStat.push({label:'Requested Sales', value: analytics.salesStats.overall.requested}); + saleStat.push({label:'Waiting Cancellations', value: analytics.salesStats.overall.waitingCancellation}); + saleStat.push({label:'Cancelled Sales', value: analytics.salesStats.overall.cancelled}); + return ( +
+
+ +
+
+ +
+
+ ); + } + +} + +export default OverallStatsWidgets; \ No newline at end of file diff --git a/views/components/elements/forms/login.jsx b/views/components/elements/forms/login.jsx index ab2449fe..3fdb364f 100644 --- a/views/components/elements/forms/login.jsx +++ b/views/components/elements/forms/login.jsx @@ -183,14 +183,16 @@ class Login extends React.Component { +

+ Forgot + Password +

{(this.props.options && this.props.options.allow_registration.value == 'true') &&

Don't have an account? Sign up here or - Forgot - Password + }}> Sign up here

} diff --git a/views/components/pages/dashboard.jsx b/views/components/pages/dashboard.jsx index 74079ae6..5aaa84d8 100644 --- a/views/components/pages/dashboard.jsx +++ b/views/components/pages/dashboard.jsx @@ -14,7 +14,8 @@ import SVGIcons from "../utilities/svg-icons.jsx"; import ServiceBotTableNoData from '../elements/bootstrap-tables/servicebot-table-no-data.jsx'; let _ = require("lodash"); import { connect } from "react-redux"; -//import OverallStatsWidgets from '../elements/dashboard/overall-stats-widgets.jsx'; +import OfferingsStatsWidgets from '../elements/dashboard/offerings-stats-widgets.jsx'; +import OverallStatsWidgets from '../elements/dashboard/overall-stats-widgets.jsx'; //import SubscriptionStatsWidgets from '../elements/dashboard/subscription-widgets.jsx'; //import UnSubscriptionStatsWidgets from '../elements/dashboard/un-subscription-widgets.jsx'; @@ -82,7 +83,8 @@ class Dashboard extends React.Component {
- {/**/} + + {/**/} {/**/}