From cefdad6d8c71ff6adf0ae919bd9f9139e02a6caa Mon Sep 17 00:00:00 2001 From: JoramWilander Date: Thu, 21 Jan 2016 12:59:32 -0500 Subject: Add system analytics page --- .../components/admin_console/admin_controller.jsx | 5 +- .../components/admin_console/admin_sidebar.jsx | 19 +++ web/react/components/admin_console/analytics.jsx | 158 ++++++++++---------- .../components/admin_console/system_analytics.jsx | 161 +++++++++++++++++++++ web/react/utils/client.jsx | 4 +- 5 files changed, 266 insertions(+), 81 deletions(-) create mode 100644 web/react/components/admin_console/system_analytics.jsx (limited to 'web/react') diff --git a/web/react/components/admin_console/admin_controller.jsx b/web/react/components/admin_console/admin_controller.jsx index 0f85c238d..db98d8f35 100644 --- a/web/react/components/admin_console/admin_controller.jsx +++ b/web/react/components/admin_console/admin_controller.jsx @@ -1,4 +1,4 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. import AdminSidebar from './admin_sidebar.jsx'; @@ -23,6 +23,7 @@ import TeamUsersTab from './team_users.jsx'; import TeamAnalyticsTab from './team_analytics.jsx'; import LdapSettingsTab from './ldap_settings.jsx'; import LicenseSettingsTab from './license_settings.jsx'; +import SystemAnalyticsTab from './system_analytics.jsx'; export default class AdminController extends React.Component { constructor(props) { @@ -165,6 +166,8 @@ export default class AdminController extends React.Component { if (this.state.teams) { tab = ; } + } else if (this.state.selected === 'system_analytics') { + tab = ; } } diff --git a/web/react/components/admin_console/admin_sidebar.jsx b/web/react/components/admin_console/admin_sidebar.jsx index 5a5eaa055..66f82c55b 100644 --- a/web/react/components/admin_console/admin_sidebar.jsx +++ b/web/react/components/admin_console/admin_sidebar.jsx @@ -192,6 +192,25 @@ export default class AdminSidebar extends React.Component {
  • +
      +
    • +

      + + {'SITE REPORTS'} +

      +
    • +
    +
    • diff --git a/web/react/components/admin_console/analytics.jsx b/web/react/components/admin_console/analytics.jsx index 4349719c1..8fdf538f8 100644 --- a/web/react/components/admin_console/analytics.jsx +++ b/web/react/components/admin_console/analytics.jsx @@ -94,8 +94,8 @@ export default class Analytics extends React.Component { var usersWithPostsByDay = (
      -
      {'Total Posts'}
      -
      {'Loading...'}
      +
      {'Active Users With Posts'}
      +
      {'Loading...'}
      ); @@ -125,98 +125,102 @@ export default class Analytics extends React.Component { ); } - var recentActiveUser = ( -
      -
      {'Recent Active Users'}
      -
      {'Loading...'}
      -
      - ); - + let recentActiveUser; if (this.props.recentActiveUsers != null) { + let content; + if (this.props.recentActiveUsers.length === 0) { + content = 'Loading...'; + } else { + content = ( + + + { + this.props.recentActiveUsers.map((user) => { + const tooltip = ( + + {user.email} + + ); + + return ( + + + + + ); + }) + } + +
      + + + + {Utils.displayDateTime(user.last_activity_at)}
      + ); + } recentActiveUser = (
      {'Recent Active Users'}
      - - - { - this.props.recentActiveUsers.map((user) => { - const tooltip = ( - - {user.email} - - ); - - return ( - - - - - ); - }) - } - -
      - - - - {Utils.displayDateTime(user.last_activity_at)}
      + {content}
      ); } - var newUsers = ( -
      -
      {'Newly Created Users'}
      -
      {'Loading...'}
      -
      - ); - + let newUsers; if (this.props.newlyCreatedUsers != null) { + let content; + if (this.props.newlyCreatedUsers.length === 0) { + content = 'Loading...'; + } else { + content = ( + + + { + this.props.newlyCreatedUsers.map((user) => { + const tooltip = ( + + {user.email} + + ); + + return ( + + + + + ); + }) + } + +
      + + + + {Utils.displayDateTime(user.create_at)}
      + ); + } newUsers = (
      {'Newly Created Users'}
      - - - { - this.props.newlyCreatedUsers.map((user) => { - const tooltip = ( - - {user.email} - - ); - - return ( - - - - - ); - }) - } - -
      - - - - {Utils.displayDateTime(user.create_at)}
      + {content}
      @@ -250,7 +254,6 @@ export default class Analytics extends React.Component { Analytics.defaultProps = { title: null, - users: null, channelOpenCount: null, channelPrivateCount: null, postCount: null, @@ -264,7 +267,6 @@ Analytics.defaultProps = { Analytics.propTypes = { title: React.PropTypes.string, - users: React.PropTypes.object, channelOpenCount: React.PropTypes.number, channelPrivateCount: React.PropTypes.number, postCount: React.PropTypes.number, diff --git a/web/react/components/admin_console/system_analytics.jsx b/web/react/components/admin_console/system_analytics.jsx new file mode 100644 index 000000000..640f17ff0 --- /dev/null +++ b/web/react/components/admin_console/system_analytics.jsx @@ -0,0 +1,161 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import Analytics from './analytics.jsx'; +import * as Client from '../../utils/client.jsx'; + +export default class SystemAnalytics extends React.Component { + constructor(props) { + super(props); + + this.getData = this.getData.bind(this); + + this.state = { // most of this state should be from a store in the future + users: null, + serverError: null, + channel_open_count: null, + channel_private_count: null, + post_count: null, + post_counts_day: null, + user_counts_with_posts_day: null, + recent_active_users: null, + newly_created_users: null, + unique_user_count: null + }; + } + + componentDidMount() { + this.getData(); + } + + getData() { // should be moved to an action creator eventually + Client.getSystemAnalytics( + 'standard', + (data) => { + for (var index in data) { + if (data[index].name === 'channel_open_count') { + this.setState({channel_open_count: data[index].value}); + } + + if (data[index].name === 'channel_private_count') { + this.setState({channel_private_count: data[index].value}); + } + + if (data[index].name === 'post_count') { + this.setState({post_count: data[index].value}); + } + + if (data[index].name === 'unique_user_count') { + this.setState({unique_user_count: data[index].value}); + } + } + }, + (err) => { + this.setState({serverError: err.message}); + } + ); + + Client.getSystemAnalytics( + 'post_counts_day', + (data) => { + data.reverse(); + + var chartData = { + labels: [], + datasets: [{ + label: 'Total Posts', + fillColor: 'rgba(151,187,205,0.2)', + strokeColor: 'rgba(151,187,205,1)', + pointColor: 'rgba(151,187,205,1)', + pointStrokeColor: '#fff', + pointHighlightFill: '#fff', + pointHighlightStroke: 'rgba(151,187,205,1)', + data: [] + }] + }; + + for (var index in data) { + if (data[index]) { + var row = data[index]; + chartData.labels.push(row.name); + chartData.datasets[0].data.push(row.value); + } + } + + this.setState({post_counts_day: chartData}); + }, + (err) => { + this.setState({serverError: err.message}); + } + ); + + Client.getSystemAnalytics( + 'user_counts_with_posts_day', + (data) => { + data.reverse(); + + var chartData = { + labels: [], + datasets: [{ + label: 'Active Users With Posts', + fillColor: 'rgba(151,187,205,0.2)', + strokeColor: 'rgba(151,187,205,1)', + pointColor: 'rgba(151,187,205,1)', + pointStrokeColor: '#fff', + pointHighlightFill: '#fff', + pointHighlightStroke: 'rgba(151,187,205,1)', + data: [] + }] + }; + + for (var index in data) { + if (data[index]) { + var row = data[index]; + chartData.labels.push(row.name); + chartData.datasets[0].data.push(row.value); + } + } + + this.setState({user_counts_with_posts_day: chartData}); + }, + (err) => { + this.setState({serverError: err.message}); + } + ); + } + + componentWillReceiveProps(newProps) { + this.setState({ + serverError: null, + channel_open_count: null, + channel_private_count: null, + post_count: null, + post_counts_day: null, + user_counts_with_posts_day: null, + unique_user_count: null + }); + + this.getData(); + } + + render() { + return ( +
      + +
      + ); + } +} + +SystemAnalytics.propTypes = { + team: React.PropTypes.object +}; diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx index 8484f3cce..96fa342a3 100644 --- a/web/react/utils/client.jsx +++ b/web/react/utils/client.jsx @@ -401,7 +401,7 @@ export function getTeamAnalytics(teamId, name, success, error) { }); } -export function getServerAnalytics(name, success, error) { +export function getSystemAnalytics(name, success, error) { $.ajax({ url: '/api/v1/admin/analytics/' + name, dataType: 'json', @@ -409,7 +409,7 @@ export function getServerAnalytics(name, success, error) { type: 'GET', success, error: (xhr, status, err) => { - var e = handleError('getServerAnalytics', xhr, status, err); + var e = handleError('getSystemAnalytics', xhr, status, err); error(e); } }); -- cgit v1.2.3-1-g7c22