summaryrefslogtreecommitdiffstats
path: root/webapp/components/analytics
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/components/analytics')
-rw-r--r--webapp/components/analytics/doughnut_chart.jsx90
-rw-r--r--webapp/components/analytics/line_chart.jsx121
-rw-r--r--webapp/components/analytics/statistic_count.jsx35
-rw-r--r--webapp/components/analytics/system_analytics.jsx492
-rw-r--r--webapp/components/analytics/table_chart.jsx62
-rw-r--r--webapp/components/analytics/team_analytics/index.js36
-rw-r--r--webapp/components/analytics/team_analytics/team_analytics.jsx369
7 files changed, 0 insertions, 1205 deletions
diff --git a/webapp/components/analytics/doughnut_chart.jsx b/webapp/components/analytics/doughnut_chart.jsx
deleted file mode 100644
index 598922acd..000000000
--- a/webapp/components/analytics/doughnut_chart.jsx
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'react-intl';
-
-import * as Utils from 'utils/utils.jsx';
-
-import PropTypes from 'prop-types';
-
-import React from 'react';
-import ReactDOM from 'react-dom';
-import Chart from 'chart.js';
-
-export default class DoughnutChart extends React.Component {
- constructor(props) {
- super(props);
-
- this.initChart = this.initChart.bind(this);
- this.chart = null;
- }
-
- componentDidMount() {
- this.initChart();
- }
-
- componentDidUpdate(prevProps) {
- if (!Utils.areObjectsEqual(prevProps.data, this.props.data) || !Utils.areObjectsEqual(prevProps.options, this.props.options)) {
- this.initChart(true);
- }
- }
-
- componentWillUnmount() {
- if (this.chart && this.refs.canvas) {
- this.chart.destroy();
- }
- }
-
- initChart(update) {
- if (!this.refs.canvas) {
- return;
- }
- var el = ReactDOM.findDOMNode(this.refs.canvas);
- var ctx = el.getContext('2d');
- this.chart = new Chart(ctx, {type: 'doughnut', data: this.props.data, options: this.props.options || {}}); //eslint-disable-line new-cap
- if (update) {
- this.chart.update();
- }
- }
-
- render() {
- let content;
- if (this.props.data == null) {
- content = (
- <FormattedMessage
- id='analytics.chart.loading'
- defaultMessage='Loading...'
- />
- );
- } else {
- content = (
- <canvas
- ref='canvas'
- width={this.props.width}
- height={this.props.height}
- />
- );
- }
-
- return (
- <div className='col-sm-6'>
- <div className='total-count'>
- <div className='title'>
- {this.props.title}
- </div>
- <div className='content'>
- {content}
- </div>
- </div>
- </div>
- );
- }
-}
-
-DoughnutChart.propTypes = {
- title: PropTypes.node,
- width: PropTypes.string,
- height: PropTypes.string,
- data: PropTypes.object,
- options: PropTypes.object
-};
diff --git a/webapp/components/analytics/line_chart.jsx b/webapp/components/analytics/line_chart.jsx
deleted file mode 100644
index 67a1162fc..000000000
--- a/webapp/components/analytics/line_chart.jsx
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'react-intl';
-
-import * as Utils from 'utils/utils.jsx';
-
-import PropTypes from 'prop-types';
-
-import React from 'react';
-import ReactDOM from 'react-dom';
-import Chart from 'chart.js';
-
-export default class LineChart extends React.Component {
- constructor(props) {
- super(props);
-
- this.initChart = this.initChart.bind(this);
- this.chart = null;
- }
-
- componentDidMount() {
- this.initChart();
- }
-
- componentWillUpdate(nextProps) {
- const willHaveData = nextProps.data && nextProps.data.labels.length > 0;
- const hasChart = Boolean(this.chart);
-
- if (!willHaveData && hasChart) {
- // Clean up the rendered chart before we render and destroy its context
- this.chart.destroy();
- this.chart = null;
- }
- }
-
- componentDidUpdate(prevProps) {
- if (Utils.areObjectsEqual(prevProps.data, this.props.data) && Utils.areObjectsEqual(prevProps.options, this.props.options)) {
- return;
- }
-
- const hasData = this.props.data && this.props.data.labels.length > 0;
- const hasChart = Boolean(this.chart);
-
- if (hasData) {
- // Update the rendered chart or initialize it as necessary
- this.initChart(hasChart);
- }
- }
-
- componentWillUnmount() {
- if (this.chart) {
- this.chart.destroy();
- }
- }
-
- initChart(update) {
- if (!this.refs.canvas) {
- return;
- }
-
- var el = ReactDOM.findDOMNode(this.refs.canvas);
- var ctx = el.getContext('2d');
- this.chart = new Chart(ctx, {type: 'line', data: this.props.data, options: this.props.options || {}}); // eslint-disable-line new-cap
-
- if (update) {
- this.chart.update();
- }
- }
-
- render() {
- let content;
- if (this.props.data == null) {
- content = (
- <FormattedMessage
- id='analytics.chart.loading'
- defaultMessage='Loading...'
- />
- );
- } else if (this.props.data.labels.length === 0) {
- content = (
- <h5>
- <FormattedMessage
- id='analytics.chart.meaningful'
- defaultMessage='Not enough data for a meaningful representation.'
- />
- </h5>
- );
- } else {
- content = (
- <canvas
- ref='canvas'
- width={this.props.width}
- height={this.props.height}
- />
- );
- }
-
- return (
- <div className='col-sm-12'>
- <div className='total-count by-day'>
- <div className='title'>
- {this.props.title}
- </div>
- <div className='content'>
- {content}
- </div>
- </div>
- </div>
- );
- }
-}
-
-LineChart.propTypes = {
- title: PropTypes.node.isRequired,
- width: PropTypes.string.isRequired,
- height: PropTypes.string.isRequired,
- data: PropTypes.object,
- options: PropTypes.object
-};
-
diff --git a/webapp/components/analytics/statistic_count.jsx b/webapp/components/analytics/statistic_count.jsx
deleted file mode 100644
index 4b505bb3f..000000000
--- a/webapp/components/analytics/statistic_count.jsx
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {FormattedMessage} from 'react-intl';
-
-import PropTypes from 'prop-types';
-
-import React from 'react';
-
-export default function StatisticCount(props) {
- const loading = (
- <FormattedMessage
- id='analytics.chart.loading'
- defaultMessage='Loading...'
- />
- );
-
- return (
- <div className='col-md-3 col-sm-6'>
- <div className='total-count'>
- <div className='title'>
- {props.title}
- <i className={'fa ' + props.icon}/>
- </div>
- <div className='content'>{props.count == null ? loading : props.count}</div>
- </div>
- </div>
- );
-}
-
-StatisticCount.propTypes = {
- title: PropTypes.node.isRequired,
- icon: PropTypes.string.isRequired,
- count: PropTypes.number
-};
diff --git a/webapp/components/analytics/system_analytics.jsx b/webapp/components/analytics/system_analytics.jsx
deleted file mode 100644
index 9aa4c26c8..000000000
--- a/webapp/components/analytics/system_analytics.jsx
+++ /dev/null
@@ -1,492 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import LineChart from './line_chart.jsx';
-import DoughnutChart from './doughnut_chart.jsx';
-import StatisticCount from './statistic_count.jsx';
-
-import AnalyticsStore from 'stores/analytics_store.jsx';
-
-import * as Utils from 'utils/utils.jsx';
-import * as AdminActions from 'actions/admin_actions.jsx';
-import Constants from 'utils/constants.jsx';
-const StatTypes = Constants.StatTypes;
-
-import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
-
-import React from 'react';
-
-export default class SystemAnalytics extends React.Component {
- constructor(props) {
- super(props);
-
- this.onChange = this.onChange.bind(this);
-
- this.state = {stats: AnalyticsStore.getAllSystem()};
- }
-
- componentDidMount() {
- AnalyticsStore.addChangeListener(this.onChange);
-
- AdminActions.getStandardAnalytics();
- AdminActions.getPostsPerDayAnalytics();
- AdminActions.getUsersPerDayAnalytics();
-
- if (global.window.mm_license.IsLicensed === 'true') {
- AdminActions.getAdvancedAnalytics();
- }
- }
-
- componentWillUnmount() {
- AnalyticsStore.removeChangeListener(this.onChange);
- }
-
- shouldComponentUpdate(nextProps, nextState) {
- if (!Utils.areObjectsEqual(nextState.stats, this.state.stats)) {
- return true;
- }
-
- return false;
- }
-
- onChange() {
- this.setState({stats: AnalyticsStore.getAllSystem()});
- }
-
- render() {
- const stats = this.state.stats;
- const isLicensed = global.window.mm_license.IsLicensed === 'true';
- const skippedIntensiveQueries = stats[StatTypes.TOTAL_POSTS] === -1;
- const postCountsDay = formatPostsPerDayData(stats[StatTypes.POST_PER_DAY]);
- const userCountsWithPostsDay = formatUsersWithPostsPerDayData(stats[StatTypes.USERS_WITH_POSTS_PER_DAY]);
-
- let banner;
- let postCount;
- let postTotalGraph;
- let activeUserGraph;
- if (skippedIntensiveQueries) {
- banner = (
- <div className='banner'>
- <div className='banner__content'>
- <FormattedHTMLMessage
- id='analytics.system.skippedIntensiveQueries'
- defaultMessage="To maximize performance, some statistics are disabled. You can re-enable them in config.json. See: <a href='https://docs.mattermost.com/administration/statistics.html' target='_blank'>https://docs.mattermost.com/administration/statistics.html</a>"
- />
- </div>
- </div>
- );
- } else {
- postCount = (
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalPosts'
- defaultMessage='Total Posts'
- />
- }
- icon='fa-comment'
- count={stats[StatTypes.TOTAL_POSTS]}
- />
- );
-
- postTotalGraph = (
- <div className='row'>
- <LineChart
- title={
- <FormattedMessage
- id='analytics.system.totalPosts'
- defaultMessage='Total Posts'
- />
- }
- data={postCountsDay}
- options={{
- legend: {
- display: false
- }
- }}
- width='740'
- height='225'
- />
- </div>
- );
-
- activeUserGraph = (
- <div className='row'>
- <LineChart
- title={
- <FormattedMessage
- id='analytics.system.activeUsers'
- defaultMessage='Active Users With Posts'
- />
- }
- data={userCountsWithPostsDay}
- options={{
- legend: {
- display: false
- }
- }}
- width='740'
- height='225'
- />
- </div>
- );
- }
-
- let advancedStats;
- let advancedGraphs;
- let sessionCount;
- let commandCount;
- let incomingCount;
- let outgoingCount;
- if (global.window.mm_license.IsLicensed === 'true') {
- sessionCount = (
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalSessions'
- defaultMessage='Total Sessions'
- />
- }
- icon='fa-signal'
- count={stats[StatTypes.TOTAL_SESSIONS]}
- />
- );
-
- commandCount = (
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalCommands'
- defaultMessage='Total Commands'
- />
- }
- icon='fa-terminal'
- count={stats[StatTypes.TOTAL_COMMANDS]}
- />
- );
-
- incomingCount = (
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalIncomingWebhooks'
- defaultMessage='Incoming Webhooks'
- />
- }
- icon='fa-arrow-down'
- count={stats[StatTypes.TOTAL_IHOOKS]}
- />
- );
-
- outgoingCount = (
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalOutgoingWebhooks'
- defaultMessage='Outgoing Webhooks'
- />
- }
- icon='fa-arrow-up'
- count={stats[StatTypes.TOTAL_OHOOKS]}
- />
- );
-
- advancedStats = (
- <div>
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalWebsockets'
- defaultMessage='WebSocket Conns'
- />
- }
- icon='fa-user'
- count={stats[StatTypes.TOTAL_WEBSOCKET_CONNECTIONS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalMasterDbConnections'
- defaultMessage='Master DB Conns'
- />
- }
- icon='fa-terminal'
- count={stats[StatTypes.TOTAL_MASTER_DB_CONNECTIONS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalReadDbConnections'
- defaultMessage='Replica DB Conns'
- />
- }
- icon='fa-terminal'
- count={stats[StatTypes.TOTAL_READ_DB_CONNECTIONS]}
- />
- </div>
- );
-
- const channelTypeData = formatChannelDoughtnutData(stats[StatTypes.TOTAL_PUBLIC_CHANNELS], stats[StatTypes.TOTAL_PRIVATE_GROUPS]);
- const postTypeData = formatPostDoughtnutData(stats[StatTypes.TOTAL_FILE_POSTS], stats[StatTypes.TOTAL_HASHTAG_POSTS], stats[StatTypes.TOTAL_POSTS]);
-
- let postTypeGraph;
- if (stats[StatTypes.TOTAL_POSTS] !== -1) {
- postTypeGraph = (
- <DoughnutChart
- title={
- <FormattedMessage
- id='analytics.system.postTypes'
- defaultMessage='Posts, Files and Hashtags'
- />
- }
- data={postTypeData}
- width='300'
- height='225'
- />
- );
- }
-
- advancedGraphs = (
- <div className='row'>
- <DoughnutChart
- title={
- <FormattedMessage
- id='analytics.system.channelTypes'
- defaultMessage='Channel Types'
- />
- }
- data={channelTypeData}
- width='300'
- height='225'
- />
- {postTypeGraph}
- </div>
- );
- }
-
- const userCount = (
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalUsers'
- defaultMessage='Total Users'
- />
- }
- icon='fa-user'
- count={stats[StatTypes.TOTAL_USERS]}
- />
- );
-
- const teamCount = (
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalTeams'
- defaultMessage='Total Teams'
- />
- }
- icon='fa-users'
- count={stats[StatTypes.TOTAL_TEAMS]}
- />
- );
-
- const channelCount = (
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.totalChannels'
- defaultMessage='Total Channels'
- />
- }
- icon='fa-globe'
- count={stats[StatTypes.TOTAL_PUBLIC_CHANNELS] + stats[StatTypes.TOTAL_PRIVATE_GROUPS]}
- />
- );
-
- const dailyActiveUsers = (
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.dailyActiveUsers'
- defaultMessage='Daily Active Users'
- />
- }
- icon='fa-users'
- count={stats[StatTypes.DAILY_ACTIVE_USERS]}
- />
- );
-
- const monthlyActiveUsers = (
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.system.monthlyActiveUsers'
- defaultMessage='Monthly Active Users'
- />
- }
- icon='fa-users'
- count={stats[StatTypes.MONTHLY_ACTIVE_USERS]}
- />
- );
-
- let firstRow;
- let secondRow;
- if (isLicensed && skippedIntensiveQueries) {
- firstRow = (
- <div>
- {userCount}
- {teamCount}
- {channelCount}
- {sessionCount}
- </div>
- );
-
- secondRow = (
- <div>
- {commandCount}
- {incomingCount}
- {outgoingCount}
- </div>
- );
- } else if (isLicensed && !skippedIntensiveQueries) {
- firstRow = (
- <div>
- {userCount}
- {teamCount}
- {channelCount}
- {postCount}
- </div>
- );
-
- secondRow = (
- <div>
- {sessionCount}
- {commandCount}
- {incomingCount}
- {outgoingCount}
- </div>
- );
- } else if (!isLicensed) {
- firstRow = (
- <div>
- {userCount}
- {teamCount}
- {channelCount}
- {postCount}
- </div>
- );
- }
-
- const thirdRow = (
- <div>
- {dailyActiveUsers}
- {monthlyActiveUsers}
- </div>
- );
-
- return (
- <div className='wrapper--fixed team_statistics'>
- <h3 className='admin-console-header'>
- <FormattedMessage
- id='analytics.system.title'
- defaultMessage='System Statistics'
- />
- </h3>
- {banner}
- <div className='row'>
- {firstRow}
- {secondRow}
- {thirdRow}
- {advancedStats}
- </div>
- {advancedGraphs}
- {postTotalGraph}
- {activeUserGraph}
- </div>
- );
- }
-}
-
-export function formatChannelDoughtnutData(totalPublic, totalPrivate) {
- const channelTypeData = {
- labels: [
- Utils.localizeMessage('analytics.system.publicChannels', 'Public Channels'),
- Utils.localizeMessage('analytics.system.privateGroups', 'Private Channels')
- ],
- datasets: [{
- data: [totalPublic, totalPrivate],
- backgroundColor: ['#46BFBD', '#FDB45C'],
- hoverBackgroundColor: ['#5AD3D1', '#FFC870']
- }]
- };
-
- return channelTypeData;
-}
-
-export function formatPostDoughtnutData(filePosts, hashtagPosts, totalPosts) {
- const postTypeData = {
- labels: [
- Utils.localizeMessage('analytics.system.totalFilePosts', 'Posts with Files'),
- Utils.localizeMessage('analytics.system.totalHashtagPosts', 'Posts with Hashtags'),
- Utils.localizeMessage('analytics.system.textPosts', 'Posts with Text-only')
- ],
- datasets: [{
- data: [filePosts, hashtagPosts, (totalPosts - filePosts - hashtagPosts)],
- backgroundColor: ['#46BFBD', '#F7464A', '#FDB45C'],
- hoverBackgroundColor: ['#5AD3D1', '#FF5A5E', '#FFC870']
- }]
- };
-
- return postTypeData;
-}
-
-export function formatPostsPerDayData(data) {
- var chartData = {
- labels: [],
- datasets: [{
- fillColor: 'rgba(151,187,205,0.2)',
- borderColor: 'rgba(151,187,205,1)',
- pointBackgroundColor: 'rgba(151,187,205,1)',
- pointBorderColor: '#fff',
- pointHoverBackgroundColor: '#fff',
- pointHoverBorderColor: '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);
- }
- }
-
- return chartData;
-}
-
-export function formatUsersWithPostsPerDayData(data) {
- var chartData = {
- labels: [],
- datasets: [{
- label: '',
- fillColor: 'rgba(151,187,205,0.2)',
- borderColor: 'rgba(151,187,205,1)',
- pointBackgroundColor: 'rgba(151,187,205,1)',
- pointBorderColor: '#fff',
- pointHoverBackgroundColor: '#fff',
- pointHoverBorderColor: '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);
- }
- }
-
- return chartData;
-}
diff --git a/webapp/components/analytics/table_chart.jsx b/webapp/components/analytics/table_chart.jsx
deleted file mode 100644
index 5836e09a7..000000000
--- a/webapp/components/analytics/table_chart.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import Constants from 'utils/constants.jsx';
-
-import {Tooltip, OverlayTrigger} from 'react-bootstrap';
-
-import PropTypes from 'prop-types';
-
-import React from 'react';
-
-export default function TableChart(props) {
- return (
- <div className='col-sm-6'>
- <div className='total-count recent-active-users'>
- <div className='title'>
- {props.title}
- </div>
- <div className='content'>
- <table>
- <tbody>
- {
- props.data.map((item) => {
- const tooltip = (
- <Tooltip id={'tip-table-entry-' + item.name}>
- {item.tip}
- </Tooltip>
- );
-
- return (
- <tr key={'table-entry-' + item.name}>
- <td>
- <OverlayTrigger
- trigger={['hover', 'focus']}
- delayShow={Constants.OVERLAY_TIME_DELAY}
- placement='top'
- overlay={tooltip}
- >
- <time>
- {item.name}
- </time>
- </OverlayTrigger>
- </td>
- <td>
- {item.value}
- </td>
- </tr>
- );
- })
- }
- </tbody>
- </table>
- </div>
- </div>
- </div>
- );
-}
-
-TableChart.propTypes = {
- title: PropTypes.node,
- data: PropTypes.array
-};
diff --git a/webapp/components/analytics/team_analytics/index.js b/webapp/components/analytics/team_analytics/index.js
deleted file mode 100644
index fe53a1559..000000000
--- a/webapp/components/analytics/team_analytics/index.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import {connect} from 'react-redux';
-import {bindActionCreators} from 'redux';
-import {getTeams} from 'mattermost-redux/actions/teams';
-import {getProfilesInTeam} from 'mattermost-redux/actions/users';
-
-import {getTeamsList} from 'mattermost-redux/selectors/entities/teams';
-import BrowserStore from 'stores/browser_store.jsx';
-
-import TeamAnalytics from './team_analytics.jsx';
-
-const LAST_ANALYTICS_TEAM = 'last_analytics_team';
-
-function mapStateToProps(state, ownProps) {
- const teams = getTeamsList(state);
- const teamId = BrowserStore.getGlobalItem(LAST_ANALYTICS_TEAM, teams.length > 0 ? teams[0].id : '');
-
- return {
- initialTeam: state.entities.teams.teams[teamId],
- teams,
- ...ownProps
- };
-}
-
-function mapDispatchToProps(dispatch) {
- return {
- actions: bindActionCreators({
- getTeams,
- getProfilesInTeam
- }, dispatch)
- };
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(TeamAnalytics);
diff --git a/webapp/components/analytics/team_analytics/team_analytics.jsx b/webapp/components/analytics/team_analytics/team_analytics.jsx
deleted file mode 100644
index f2f12d938..000000000
--- a/webapp/components/analytics/team_analytics/team_analytics.jsx
+++ /dev/null
@@ -1,369 +0,0 @@
-// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import React from 'react';
-import PropTypes from 'prop-types';
-import {FormattedDate, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
-
-import Banner from 'components/admin_console/banner.jsx';
-import LoadingScreen from 'components/loading_screen.jsx';
-
-import AnalyticsStore from 'stores/analytics_store.jsx';
-import BrowserStore from 'stores/browser_store.jsx';
-
-import * as AdminActions from 'actions/admin_actions.jsx';
-import {StatTypes} from 'utils/constants.jsx';
-import {General} from 'mattermost-redux/constants';
-
-import LineChart from 'components/analytics/line_chart.jsx';
-import StatisticCount from 'components/analytics/statistic_count.jsx';
-import TableChart from 'components/analytics/table_chart.jsx';
-import {formatPostsPerDayData, formatUsersWithPostsPerDayData} from 'components/analytics/system_analytics.jsx';
-
-const LAST_ANALYTICS_TEAM = 'last_analytics_team';
-
-export default class TeamAnalytics extends React.Component {
- static propTypes = {
-
- /*
- * Array of team objects
- */
- teams: PropTypes.arrayOf(PropTypes.object).isRequired,
-
- /*
- * Initial team to load analytics for
- */
- initialTeam: PropTypes.object,
-
- actions: PropTypes.shape({
-
- /*
- * Function to get teams
- */
- getTeams: PropTypes.func.isRequired,
-
- /*
- * Function to get users in a team
- */
- getProfilesInTeam: PropTypes.func.isRequired
- }).isRequired
- }
-
- constructor(props) {
- super(props);
-
- const teamId = props.initialTeam ? props.initialTeam.id : '';
-
- this.state = {
- team: props.initialTeam,
- stats: AnalyticsStore.getAllTeam(teamId),
- recentlyActiveUsers: [],
- newUsers: []
- };
- }
-
- componentDidMount() {
- AnalyticsStore.addChangeListener(this.onChange);
-
- if (this.state.team) {
- this.getData(this.state.team.id);
- }
-
- this.props.actions.getTeams(0, 1000);
- }
-
- componentWillUpdate(nextProps, nextState) {
- if (nextState.team && nextState.team !== this.state.team) {
- this.getData(nextState.team.id);
- }
- }
-
- getData = async (id) => {
- AdminActions.getStandardAnalytics(id);
- AdminActions.getPostsPerDayAnalytics(id);
- AdminActions.getUsersPerDayAnalytics(id);
- const recentlyActiveUsers = await this.props.actions.getProfilesInTeam(id, 0, General.PROFILE_CHUNK_SIZE, 'last_activity_at');
- const newUsers = await this.props.actions.getProfilesInTeam(id, 0, General.PROFILE_CHUNK_SIZE, 'create_at');
-
- this.setState({
- recentlyActiveUsers,
- newUsers
- });
- }
-
- componentWillUnmount() {
- AnalyticsStore.removeChangeListener(this.onChange);
- }
-
- onChange = () => {
- const teamId = this.state.team ? this.state.team.id : '';
- this.setState({
- stats: AnalyticsStore.getAllTeam(teamId)
- });
- }
-
- handleTeamChange = (e) => {
- const teamId = e.target.value;
-
- let team;
- this.props.teams.forEach((t) => {
- if (t.id === teamId) {
- team = t;
- }
- });
-
- this.setState({
- team
- });
-
- BrowserStore.setGlobalItem(LAST_ANALYTICS_TEAM, teamId);
- }
-
- render() {
- if (this.props.teams.length === 0 || !this.state.team || !this.state.stats) {
- return <LoadingScreen/>;
- }
-
- if (this.state.team == null) {
- return (
- <Banner
- description={
- <FormattedMessage
- id='analytics.team.noTeams'
- defaultMessage='There are no teams on this server for which to view statistics.'
- />
- }
- />
- );
- }
-
- const stats = this.state.stats;
- const postCountsDay = formatPostsPerDayData(stats[StatTypes.POST_PER_DAY]);
- const userCountsWithPostsDay = formatUsersWithPostsPerDayData(stats[StatTypes.USERS_WITH_POSTS_PER_DAY]);
-
- let banner;
- let totalPostsCount;
- let postTotalGraph;
- let userActiveGraph;
- if (stats[StatTypes.TOTAL_POSTS] === -1) {
- banner = (
- <div className='banner'>
- <div className='banner__content'>
- <FormattedHTMLMessage
- id='analytics.system.skippedIntensiveQueries'
- defaultMessage="Some statistics have been omitted because they put too much load on the system to calculate. See <a href='https://docs.mattermost.com/administration/statistics.html' target='_blank'>https://docs.mattermost.com/administration/statistics.html</a> for more details."
- />
- </div>
- </div>
- );
- } else {
- totalPostsCount = (
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.team.totalPosts'
- defaultMessage='Total Posts'
- />
- }
- icon='fa-comment'
- count={stats[StatTypes.TOTAL_POSTS]}
- />
- );
-
- postTotalGraph = (
- <div className='row'>
- <LineChart
- key={this.state.team.id}
- title={
- <FormattedMessage
- id='analytics.team.totalPosts'
- defaultMessage='Total Posts'
- />
- }
- data={postCountsDay}
- options={{
- legend: {
- display: false
- }
- }}
- width='740'
- height='225'
- />
- </div>
- );
-
- userActiveGraph = (
- <div className='row'>
- <LineChart
- key={this.state.team.id}
- title={
- <FormattedMessage
- id='analytics.team.activeUsers'
- defaultMessage='Active Users With Posts'
- />
- }
- data={userCountsWithPostsDay}
- options={{
- legend: {
- display: false
- }
- }}
- width='740'
- height='225'
- />
- </div>
- );
- }
-
- const recentActiveUsers = formatRecentUsersData(this.state.recentlyActiveUsers);
- const newlyCreatedUsers = formatNewUsersData(this.state.newUsers);
-
- const teams = this.props.teams.map((team) => {
- return (
- <option
- key={team.id}
- value={team.id}
- >
- {team.display_name}
- </option>
- );
- });
-
- return (
- <div className='wrapper--fixed team_statistics'>
- <div className='admin-console-header team-statistics__header-row'>
- <div className='team-statistics__header'>
- <h3>
- <FormattedMessage
- id='analytics.team.title'
- defaultMessage='Team Statistics for {team}'
- values={{
- team: this.state.team.display_name
- }}
- />
- </h3>
- </div>
- <div className='team-statistics__team-filter'>
- <select
- className='form-control team-statistics__team-filter__dropdown'
- onChange={this.handleTeamChange}
- value={this.state.team.id}
- >
- {teams}
- </select>
- </div>
- </div>
- {banner}
- <div className='row'>
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.team.totalUsers'
- defaultMessage='Total Users'
- />
- }
- icon='fa-user'
- count={stats[StatTypes.TOTAL_USERS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.team.publicChannels'
- defaultMessage='Public Channels'
- />
- }
- icon='fa-users'
- count={stats[StatTypes.TOTAL_PUBLIC_CHANNELS]}
- />
- <StatisticCount
- title={
- <FormattedMessage
- id='analytics.team.privateGroups'
- defaultMessage='Private Channels'
- />
- }
- icon='fa-globe'
- count={stats[StatTypes.TOTAL_PRIVATE_GROUPS]}
- />
- {totalPostsCount}
- </div>
- {postTotalGraph}
- {userActiveGraph}
- <div className='row'>
- <TableChart
- title={
- <FormattedMessage
- id='analytics.team.recentUsers'
- defaultMessage='Recent Active Users'
- />
- }
- data={recentActiveUsers}
- />
- <TableChart
- title={
- <FormattedMessage
- id='analytics.team.newlyCreated'
- defaultMessage='Newly Created Users'
- />
- }
- data={newlyCreatedUsers}
- />
- </div>
- </div>
- );
- }
-}
-
-export function formatRecentUsersData(data) {
- if (data == null) {
- return [];
- }
-
- const formattedData = data.map((user) => {
- const item = {};
- item.name = user.username;
- item.value = (
- <FormattedDate
- value={user.last_activity_at}
- day='numeric'
- month='long'
- year='numeric'
- hour12={true}
- hour='2-digit'
- minute='2-digit'
- />
- );
- item.tip = user.email;
-
- return item;
- });
-
- return formattedData;
-}
-
-export function formatNewUsersData(data) {
- if (data == null) {
- return [];
- }
-
- const formattedData = data.map((user) => {
- const item = {};
- item.name = user.username;
- item.value = (
- <FormattedDate
- value={user.create_at}
- day='numeric'
- month='long'
- year='numeric'
- hour12={true}
- hour='2-digit'
- minute='2-digit'
- />
- );
- item.tip = user.email;
-
- return item;
- });
-
- return formattedData;
-}