PLT-25 adding analytics panel
Этот коммит содержится в:
@@ -20,7 +20,8 @@
|
|||||||
"globals": {
|
"globals": {
|
||||||
"React": false,
|
"React": false,
|
||||||
"ReactDOM": false,
|
"ReactDOM": false,
|
||||||
"ReactBootstrap": false
|
"ReactBootstrap": false,
|
||||||
|
"Chart": false
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"comma-dangle": [2, "never"],
|
"comma-dangle": [2, "never"],
|
||||||
|
|||||||
50
web/react/components/admin_console/line_chart.jsx
Обычный файл
50
web/react/components/admin_console/line_chart.jsx
Обычный файл
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
export default class LineChart extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.initChart = this.initChart.bind(this);
|
||||||
|
this.chart = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.initChart(this.props);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.destroy();
|
||||||
|
this.initChart(nextProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initChart(props) {
|
||||||
|
var el = ReactDOM.findDOMNode(this);
|
||||||
|
var ctx = el.getContext('2d');
|
||||||
|
this.chart = new Chart(ctx).Line(props.data, props.options || {}); //eslint-disable-line new-cap
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<canvas
|
||||||
|
width={this.props.width}
|
||||||
|
height={this.props.height}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LineChart.propTypes = {
|
||||||
|
width: React.PropTypes.string,
|
||||||
|
height: React.PropTypes.string,
|
||||||
|
data: React.PropTypes.object,
|
||||||
|
options: React.PropTypes.object
|
||||||
|
};
|
||||||
@@ -2,8 +2,9 @@
|
|||||||
// See License.txt for license information.
|
// See License.txt for license information.
|
||||||
|
|
||||||
var Client = require('../../utils/client.jsx');
|
var Client = require('../../utils/client.jsx');
|
||||||
|
var LineChart = require('./line_chart.jsx');
|
||||||
|
|
||||||
export default class UserList extends React.Component {
|
export default class TeamAnalytics extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@@ -15,7 +16,9 @@ export default class UserList extends React.Component {
|
|||||||
channel_open_count: null,
|
channel_open_count: null,
|
||||||
channel_private_count: null,
|
channel_private_count: null,
|
||||||
post_count: null,
|
post_count: null,
|
||||||
post_counts_day: null
|
post_counts_day: null,
|
||||||
|
user_counts_with_posts_day: null,
|
||||||
|
recent_active_users: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,14 +34,17 @@ export default class UserList extends React.Component {
|
|||||||
for (var index in data) {
|
for (var index in data) {
|
||||||
if (data[index].name === 'channel_open_count') {
|
if (data[index].name === 'channel_open_count') {
|
||||||
this.setState({channel_open_count: data[index].value});
|
this.setState({channel_open_count: data[index].value});
|
||||||
|
this.setState({channel_open_count: 55});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[index].name === 'channel_private_count') {
|
if (data[index].name === 'channel_private_count') {
|
||||||
this.setState({channel_private_count: data[index].value});
|
this.setState({channel_private_count: data[index].value});
|
||||||
|
this.setState({channel_private_count: 12});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data[index].name === 'post_count') {
|
if (data[index].name === 'post_count') {
|
||||||
this.setState({post_count: data[index].value});
|
this.setState({post_count: data[index].value});
|
||||||
|
this.setState({post_count: 5332});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -51,8 +57,80 @@ export default class UserList extends React.Component {
|
|||||||
teamId,
|
teamId,
|
||||||
'post_counts_day',
|
'post_counts_day',
|
||||||
(data) => {
|
(data) => {
|
||||||
console.log(data);
|
data.push({name: '2015-10-24', value: 73});
|
||||||
this.setState({post_counts_day: data});
|
data.push({name: '2015-10-25', value: 84});
|
||||||
|
data.push({name: '2015-10-26', value: 61});
|
||||||
|
data.push({name: '2015-10-27', value: 97});
|
||||||
|
data.push({name: '2015-10-28', value: 73});
|
||||||
|
data.push({name: '2015-10-29', value: 84});
|
||||||
|
data.push({name: '2015-10-30', value: 61});
|
||||||
|
data.push({name: '2015-10-31', value: 97});
|
||||||
|
|
||||||
|
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.getAnalytics(
|
||||||
|
teamId,
|
||||||
|
'user_counts_with_posts_day',
|
||||||
|
(data) => {
|
||||||
|
data.push({name: '2015-10-24', value: 22});
|
||||||
|
data.push({name: '2015-10-25', value: 31});
|
||||||
|
data.push({name: '2015-10-26', value: 25});
|
||||||
|
data.push({name: '2015-10-27', value: 12});
|
||||||
|
data.push({name: '2015-10-28', value: 22});
|
||||||
|
data.push({name: '2015-10-29', value: 31});
|
||||||
|
data.push({name: '2015-10-30', value: 25});
|
||||||
|
data.push({name: '2015-10-31', value: 12});
|
||||||
|
|
||||||
|
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) => {
|
(err) => {
|
||||||
this.setState({serverError: err.message});
|
this.setState({serverError: err.message});
|
||||||
@@ -64,6 +142,13 @@ export default class UserList extends React.Component {
|
|||||||
(users) => {
|
(users) => {
|
||||||
this.setState({users});
|
this.setState({users});
|
||||||
|
|
||||||
|
var recentActive = [];
|
||||||
|
recentActive.push({email: 'corey@spinpunch.com', date: '2015-10-23'});
|
||||||
|
recentActive.push({email: 'bill@spinpunch.com', date: '2015-10-22'});
|
||||||
|
recentActive.push({email: 'bob@spinpunch.com', date: '2015-10-22'});
|
||||||
|
recentActive.push({email: 'jones@spinpunch.com', date: '2015-10-21'});
|
||||||
|
this.setState({recent_active_users: recentActive});
|
||||||
|
|
||||||
// var memberList = [];
|
// var memberList = [];
|
||||||
// for (var id in users) {
|
// for (var id in users) {
|
||||||
// if (users.hasOwnProperty(id)) {
|
// if (users.hasOwnProperty(id)) {
|
||||||
@@ -96,7 +181,9 @@ export default class UserList extends React.Component {
|
|||||||
channel_open_count: null,
|
channel_open_count: null,
|
||||||
channel_private_count: null,
|
channel_private_count: null,
|
||||||
post_count: null,
|
post_count: null,
|
||||||
post_counts_day: null
|
post_counts_day: null,
|
||||||
|
user_counts_with_posts_day: null,
|
||||||
|
recent_active_users: null
|
||||||
});
|
});
|
||||||
|
|
||||||
this.getData(newProps.team.id);
|
this.getData(newProps.team.id);
|
||||||
@@ -114,7 +201,7 @@ export default class UserList extends React.Component {
|
|||||||
var totalCount = (
|
var totalCount = (
|
||||||
<div className='total-count text-center'>
|
<div className='total-count text-center'>
|
||||||
<div>{'Total Users'}</div>
|
<div>{'Total Users'}</div>
|
||||||
<div>{this.state.users == null ? 'Loading...' : Object.keys(this.state.users).length}</div>
|
<div>{this.state.users == null ? 'Loading...' : Object.keys(this.state.users).length + 23}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -140,7 +227,7 @@ export default class UserList extends React.Component {
|
|||||||
);
|
);
|
||||||
|
|
||||||
var postCountsByDay = (
|
var postCountsByDay = (
|
||||||
<div className='total-count text-center'>
|
<div className='total-count-by-day'>
|
||||||
<div>{'Total Posts'}</div>
|
<div>{'Total Posts'}</div>
|
||||||
<div>{'Loading...'}</div>
|
<div>{'Loading...'}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -149,16 +236,92 @@ export default class UserList extends React.Component {
|
|||||||
if (this.state.post_counts_day != null) {
|
if (this.state.post_counts_day != null) {
|
||||||
postCountsByDay = (
|
postCountsByDay = (
|
||||||
<div className='total-count-by-day'>
|
<div className='total-count-by-day'>
|
||||||
<div>{'Total Posts By Day'}</div>
|
<div>{'Total Posts'}</div>
|
||||||
<LineChart
|
<LineChart
|
||||||
data={chartData}
|
data={this.state.post_counts_day}
|
||||||
width="100"
|
width='740'
|
||||||
height="250"
|
height='225'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var usersWithPostsByDay = (
|
||||||
|
<div className='total-count-by-day'>
|
||||||
|
<div>{'Total Posts'}</div>
|
||||||
|
<div>{'Loading...'}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.state.user_counts_with_posts_day != null) {
|
||||||
|
usersWithPostsByDay = (
|
||||||
|
<div className='total-count-by-day'>
|
||||||
|
<div>{'Active Users With Posts'}</div>
|
||||||
|
<LineChart
|
||||||
|
data={this.state.user_counts_with_posts_day}
|
||||||
|
width='740'
|
||||||
|
height='225'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var recentActiveUser = (
|
||||||
|
<div className='recent-active-users'>
|
||||||
|
<div>{'Recent Active Users'}</div>
|
||||||
|
<div>{'Loading...'}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.state.recent_active_users != null) {
|
||||||
|
recentActiveUser = (
|
||||||
|
<div className='recent-active-users'>
|
||||||
|
<div>{'Recent Active Users'}</div>
|
||||||
|
<table width='90%'>
|
||||||
|
<tr><td className='recent-active-users-td'>corey@spinpunch.com</td><td className='recent-active-users-td'>2015-12-23</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>bob@spinpunch.com</td><td className='recent-active-users-td'>2015-12-22</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>jimmy@spinpunch.com</td><td className='recent-active-users-td'>2015-12-22</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>jones@spinpunch.com</td><td className='recent-active-users-td'>2015-12-21</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>steve@spinpunch.com</td><td className='recent-active-users-td'>2015-12-20</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>aspen@spinpunch.com</td><td className='recent-active-users-td'>2015-12-19</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>scott@spinpunch.com</td><td className='recent-active-users-td'>2015-12-19</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>grant@spinpunch.com</td><td className='recent-active-users-td'>2015-12-19</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>sienna@spinpunch.com</td><td className='recent-active-users-td'>2015-12-18</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>jessica@spinpunch.com</td><td className='recent-active-users-td'>2015-12-18</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>davy@spinpunch.com</td><td className='recent-active-users-td'>2015-12-16</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>steve@spinpunch.com</td><td className='recent-active-users-td'>2015-12-11</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newUsers = (
|
||||||
|
<div className='recent-active-users'>
|
||||||
|
<div>{'Newly Created Users'}</div>
|
||||||
|
<div>{'Loading...'}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.state.recent_active_users != null) {
|
||||||
|
newUsers = (
|
||||||
|
<div className='recent-active-users'>
|
||||||
|
<div>{'Newly Created Users'}</div>
|
||||||
|
<table width='90%'>
|
||||||
|
<tr><td className='recent-active-users-td'>bob@spinpunch.com</td><td className='recent-active-users-td'>2015-12-11</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>corey@spinpunch.com</td><td className='recent-active-users-td'>2015-12-10</td></tr>
|
||||||
|
|
||||||
|
<tr><td className='recent-active-users-td'>jimmy@spinpunch.com</td><td className='recent-active-users-td'>2015-12-8</td></tr>
|
||||||
|
|
||||||
|
|
||||||
|
<tr><td className='recent-active-users-td'>aspen@spinpunch.com</td><td className='recent-active-users-td'>2015-12-5</td></tr>
|
||||||
|
|
||||||
|
<tr><td className='recent-active-users-td'>jones@spinpunch.com</td><td className='recent-active-users-td'>2015-12-5</td></tr>
|
||||||
|
<tr><td className='recent-active-users-td'>steve@spinpunch.com</td><td className='recent-active-users-td'>2015-12-5</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='wrapper--fixed'>
|
<div className='wrapper--fixed'>
|
||||||
<h2>{'Analytics for ' + this.props.team.name}</h2>
|
<h2>{'Analytics for ' + this.props.team.name}</h2>
|
||||||
@@ -168,11 +331,14 @@ export default class UserList extends React.Component {
|
|||||||
{openChannelCount}
|
{openChannelCount}
|
||||||
{openPrivateCount}
|
{openPrivateCount}
|
||||||
{postCountsByDay}
|
{postCountsByDay}
|
||||||
|
{usersWithPostsByDay}
|
||||||
|
{recentActiveUser}
|
||||||
|
{newUsers}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UserList.propTypes = {
|
TeamAnalytics.propTypes = {
|
||||||
team: React.PropTypes.object
|
team: React.PropTypes.object
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,6 +36,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recent-active-users {
|
||||||
|
width: 365px;
|
||||||
|
height: 375px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 5px 10px 10px 10px;
|
||||||
|
margin: 10px 10px 10px 10px;
|
||||||
|
background: #fff;
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
> table {
|
||||||
|
margin: 10px 10px 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-active-users-td {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 300;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 3px 3px 3px 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar--left {
|
.sidebar--left {
|
||||||
&.sidebar--collapsable {
|
&.sidebar--collapsable {
|
||||||
background: #333;
|
background: #333;
|
||||||
|
|||||||
11
web/static/js/Chart.min.js
поставляемый
Обычный файл
11
web/static/js/Chart.min.js
поставляемый
Обычный файл
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
@@ -4,6 +4,7 @@
|
|||||||
<html>
|
<html>
|
||||||
{{template "head" . }}
|
{{template "head" . }}
|
||||||
<body>
|
<body>
|
||||||
|
<script src="/static/js/Chart.min.js"></script>
|
||||||
|
|
||||||
<div id='error_bar'></div>
|
<div id='error_bar'></div>
|
||||||
|
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user