diff options
Diffstat (limited to 'web/react/components/admin_console')
-rw-r--r-- | web/react/components/admin_console/admin_controller.jsx | 4 | ||||
-rw-r--r-- | web/react/components/admin_console/analytics.jsx | 489 | ||||
-rw-r--r-- | web/react/components/admin_console/doughnut_chart.jsx | 77 | ||||
-rw-r--r-- | web/react/components/admin_console/line_chart.jsx | 50 | ||||
-rw-r--r-- | web/react/components/admin_console/service_settings.jsx | 35 | ||||
-rw-r--r-- | web/react/components/admin_console/statistic_count.jsx | 33 | ||||
-rw-r--r-- | web/react/components/admin_console/system_analytics.jsx | 216 | ||||
-rw-r--r-- | web/react/components/admin_console/team_analytics.jsx | 253 | ||||
-rw-r--r-- | web/react/components/admin_console/user_item.jsx | 61 |
9 files changed, 71 insertions, 1147 deletions
diff --git a/web/react/components/admin_console/admin_controller.jsx b/web/react/components/admin_console/admin_controller.jsx index de0b085bc..32ed70a99 100644 --- a/web/react/components/admin_console/admin_controller.jsx +++ b/web/react/components/admin_console/admin_controller.jsx @@ -21,10 +21,10 @@ import TeamSettingsTab from './team_settings.jsx'; import ServiceSettingsTab from './service_settings.jsx'; import LegalAndSupportSettingsTab from './legal_and_support_settings.jsx'; import TeamUsersTab from './team_users.jsx'; -import TeamAnalyticsTab from './team_analytics.jsx'; +import TeamAnalyticsTab from '../analytics/team_analytics.jsx'; import LdapSettingsTab from './ldap_settings.jsx'; import LicenseSettingsTab from './license_settings.jsx'; -import SystemAnalyticsTab from './system_analytics.jsx'; +import SystemAnalyticsTab from '../analytics/system_analytics.jsx'; export default class AdminController extends React.Component { constructor(props) { diff --git a/web/react/components/admin_console/analytics.jsx b/web/react/components/admin_console/analytics.jsx deleted file mode 100644 index ec9ad4da0..000000000 --- a/web/react/components/admin_console/analytics.jsx +++ /dev/null @@ -1,489 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import Constants from '../../utils/constants.jsx'; -import LineChart from './line_chart.jsx'; -import DoughnutChart from './doughnut_chart.jsx'; -import StatisticCount from './statistic_count.jsx'; - -var Tooltip = ReactBootstrap.Tooltip; -var OverlayTrigger = ReactBootstrap.OverlayTrigger; - -import {injectIntl, intlShape, defineMessages, FormattedMessage, FormattedDate} from 'mm-intl'; - -const holders = defineMessages({ - analyticsTotalUsers: { - id: 'admin.analytics.totalUsers', - defaultMessage: 'Total Users' - }, - analyticsPublicChannels: { - id: 'admin.analytics.publicChannels', - defaultMessage: 'Public Channels' - }, - analyticsPrivateGroups: { - id: 'admin.analytics.privateGroups', - defaultMessage: 'Private Groups' - }, - analyticsTotalPosts: { - id: 'admin.analytics.totalPosts', - defaultMessage: 'Total Posts' - }, - analyticsFilePosts: { - id: 'admin.analytics.totalFilePosts', - defaultMessage: 'Posts with Files' - }, - analyticsHashtagPosts: { - id: 'admin.analytics.totalHashtagPosts', - defaultMessage: 'Posts with Hashtags' - }, - analyticsIncomingHooks: { - id: 'admin.analytics.totalIncomingWebhooks', - defaultMessage: 'Incoming Webhooks' - }, - analyticsOutgoingHooks: { - id: 'admin.analytics.totalOutgoingWebhooks', - defaultMessage: 'Outgoing Webhooks' - }, - analyticsChannelTypes: { - id: 'admin.analytics.channelTypes', - defaultMessage: 'Channel Types' - }, - analyticsTextPosts: { - id: 'admin.analytics.textPosts', - defaultMessage: 'Posts with Text-only' - }, - analyticsPostTypes: { - id: 'admin.analytics.postTypes', - defaultMessage: 'Posts, Files and Hashtags' - } -}); - -export default class Analytics extends React.Component { - constructor(props) { - super(props); - - this.state = {}; - } - - render() { // in the future, break down these into smaller components - const {formatMessage} = this.props.intl; - - var serverError = ''; - if (this.props.serverError) { - serverError = <div className='form-group has-error'><label className='control-label'>{this.props.serverError}</label></div>; - } - - let loading = ( - <h5> - <FormattedMessage - id='admin.analytics.loading' - defaultMessage='Loading...' - /> - </h5> - ); - - let firstRow; - let extraGraphs; - if (this.props.showAdvanced) { - firstRow = ( - <div className='row'> - <StatisticCount - title={formatMessage(holders.analyticsTotalUsers)} - icon='fa-users' - count={this.props.uniqueUserCount} - /> - <StatisticCount - title={formatMessage(holders.analyticsTotalPosts)} - icon='fa-comment' - count={this.props.postCount} - /> - <StatisticCount - title={formatMessage(holders.analyticsIncomingHooks)} - icon='fa-arrow-down' - count={this.props.incomingWebhookCount} - /> - <StatisticCount - title={formatMessage(holders.analyticsOutgoingHooks)} - icon='fa-arrow-up' - count={this.props.outgoingWebhookCount} - /> - </div> - ); - - const channelTypeData = [ - { - value: this.props.channelOpenCount, - color: '#46BFBD', - highlight: '#5AD3D1', - label: formatMessage(holders.analyticsPublicChannels) - }, - { - value: this.props.channelPrivateCount, - color: '#FDB45C', - highlight: '#FFC870', - label: formatMessage(holders.analyticsPrivateGroups) - } - ]; - - const postTypeData = [ - { - value: this.props.filePostCount, - color: '#46BFBD', - highlight: '#5AD3D1', - label: formatMessage(holders.analyticsFilePosts) - }, - { - value: this.props.filePostCount, - color: '#F7464A', - highlight: '#FF5A5E', - label: formatMessage(holders.analyticsHashtagPosts) - }, - { - value: this.props.postCount - this.props.filePostCount - this.props.hashtagPostCount, - color: '#FDB45C', - highlight: '#FFC870', - label: formatMessage(holders.analyticsTextPosts) - } - ]; - - extraGraphs = ( - <div className='row'> - <DoughnutChart - title={formatMessage(holders.analyticsChannelTypes)} - data={channelTypeData} - width='300' - height='225' - /> - <DoughnutChart - title={formatMessage(holders.analyticsPostTypes)} - data={postTypeData} - width='300' - height='225' - /> - </div> - ); - } else { - firstRow = ( - <div className='row'> - <StatisticCount - title={formatMessage(holders.analyticsTotalUsers)} - icon='fa-users' - count={this.props.uniqueUserCount} - /> - <StatisticCount - title={formatMessage(holders.analyticsPublicChannels)} - icon='fa-globe' - count={this.props.channelOpenCount} - /> - <StatisticCount - title={formatMessage(holders.analyticsPrivateGroups)} - icon='fa-lock' - count={this.props.channelPrivateCount} - /> - <StatisticCount - title={formatMessage(holders.analyticsTotalPosts)} - icon='fa-comment' - count={this.props.postCount} - /> - </div> - ); - } - - let postCountsByDay; - if (this.props.postCountsDay == null) { - postCountsByDay = ( - <div className='col-sm-12'> - <div className='total-count by-day'> - <div className='title'> - <FormattedMessage - id='admin.analytics.totalPosts' - defaultMessage='Total Posts' - /> - </div> - <div className='content'>{loading}</div> - </div> - </div> - ); - } else { - let content; - if (this.props.postCountsDay.labels.length === 0) { - content = ( - <h5> - <FormattedMessage - id='admin.analytics.meaningful' - defaultMessage='Not enough data for a meaningful representation.' - /> - </h5> - ); - } else { - content = ( - <LineChart - data={this.props.postCountsDay} - width='740' - height='225' - /> - ); - } - postCountsByDay = ( - <div className='col-sm-12'> - <div className='total-count by-day'> - <div className='title'> - <FormattedMessage - id='admin.analytics.totalPosts' - defaultMessage='Total Posts' - /> - </div> - <div className='content'> - {content} - </div> - </div> - </div> - ); - } - - let usersWithPostsByDay; - if (this.props.userCountsWithPostsDay == null) { - usersWithPostsByDay = ( - <div className='col-sm-12'> - <div className='total-count by-day'> - <div className='title'> - <FormattedMessage - id='admin.analytics.activeUsers' - defaultMessage='Active Users With Posts' - /> - </div> - <div className='content'>{loading}</div> - </div> - </div> - ); - } else { - let content; - if (this.props.userCountsWithPostsDay.labels.length === 0) { - content = ( - <h5> - <FormattedMessage - id='admin.analytics.meaningful' - defaultMessage='Not enough data for a meaningful representation.' - /> - </h5> - ); - } else { - content = ( - <LineChart - data={this.props.userCountsWithPostsDay} - width='740' - height='225' - /> - ); - } - usersWithPostsByDay = ( - <div className='col-sm-12'> - <div className='total-count by-day'> - <div className='title'> - <FormattedMessage - id='admin.analytics.activeUsers' - defaultMessage='Active Users With Posts' - /> - </div> - <div className='content'> - {content} - </div> - </div> - </div> - ); - } - - let recentActiveUser; - if (this.props.recentActiveUsers != null) { - let content; - if (this.props.recentActiveUsers.length === 0) { - content = loading; - } else { - content = ( - <table> - <tbody> - { - this.props.recentActiveUsers.map((user) => { - const tooltip = ( - <Tooltip id={'recent-user-email-tooltip-' + user.id}> - {user.email} - </Tooltip> - ); - - return ( - <tr key={'recent-user-table-entry-' + user.id}> - <td> - <OverlayTrigger - delayShow={Constants.OVERLAY_TIME_DELAY} - placement='top' - overlay={tooltip} - > - <time> - {user.username} - </time> - </OverlayTrigger> - </td> - <td> - <FormattedDate - value={user.last_activity_at} - day='numeric' - month='long' - year='numeric' - hour12={true} - hour='2-digit' - minute='2-digit' - /> - </td> - </tr> - ); - }) - } - </tbody> - </table> - ); - } - recentActiveUser = ( - <div className='col-sm-6'> - <div className='total-count recent-active-users'> - <div className='title'> - <FormattedMessage - id='admin.analytics.recentActive' - defaultMessage='Recent Active Users' - /> - </div> - <div className='content'> - {content} - </div> - </div> - </div> - ); - } - - let newUsers; - if (this.props.newlyCreatedUsers != null) { - let content; - if (this.props.newlyCreatedUsers.length === 0) { - content = loading; - } else { - content = ( - <table> - <tbody> - { - this.props.newlyCreatedUsers.map((user) => { - const tooltip = ( - <Tooltip id={'new-user-email-tooltip-' + user.id}> - {user.email} - </Tooltip> - ); - - return ( - <tr key={'new-user-table-entry-' + user.id}> - <td> - <OverlayTrigger - delayShow={Constants.OVERLAY_TIME_DELAY} - placement='top' - overlay={tooltip} - > - <time> - {user.username} - </time> - </OverlayTrigger> - </td> - <td> - <FormattedDate - value={user.create_at} - day='numeric' - month='long' - year='numeric' - hour12={true} - hour='2-digit' - minute='2-digit' - /> - </td> - </tr> - ); - }) - } - </tbody> - </table> - ); - } - newUsers = ( - <div className='col-sm-6'> - <div className='total-count recent-active-users'> - <div className='title'> - <FormattedMessage - id='admin.analytics.newlyCreated' - defaultMessage='Newly Created Users' - /> - </div> - <div className='content'> - {content} - </div> - </div> - </div> - ); - } - - return ( - <div className='wrapper--fixed team_statistics'> - <h3> - <FormattedMessage - id='admin.analytics.title' - defaultMessage='Statistics for {title}' - values={{ - title: this.props.title - }} - /> - </h3> - {serverError} - {firstRow} - {extraGraphs} - <div className='row'> - {postCountsByDay} - </div> - <div className='row'> - {usersWithPostsByDay} - </div> - <div className='row'> - {recentActiveUser} - {newUsers} - </div> - </div> - ); - } -} - -Analytics.defaultProps = { - title: null, - channelOpenCount: null, - channelPrivateCount: null, - postCount: null, - postCountsDay: null, - userCountsWithPostsDay: null, - recentActiveUsers: null, - newlyCreatedUsers: null, - uniqueUserCount: null, - serverError: null -}; - -Analytics.propTypes = { - intl: intlShape.isRequired, - title: React.PropTypes.string, - channelOpenCount: React.PropTypes.number, - channelPrivateCount: React.PropTypes.number, - postCount: React.PropTypes.number, - showAdvanced: React.PropTypes.bool, - filePostCount: React.PropTypes.number, - hashtagPostCount: React.PropTypes.number, - incomingWebhookCount: React.PropTypes.number, - outgoingWebhookCount: React.PropTypes.number, - postCountsDay: React.PropTypes.object, - userCountsWithPostsDay: React.PropTypes.object, - recentActiveUsers: React.PropTypes.array, - newlyCreatedUsers: React.PropTypes.array, - uniqueUserCount: React.PropTypes.number, - serverError: React.PropTypes.string -}; - -export default injectIntl(Analytics); diff --git a/web/react/components/admin_console/doughnut_chart.jsx b/web/react/components/admin_console/doughnut_chart.jsx deleted file mode 100644 index e2dc01528..000000000 --- a/web/react/components/admin_console/doughnut_chart.jsx +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import {FormattedMessage} from 'mm-intl'; - -export default class DoughnutChart 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.refs.canvas); - var ctx = el.getContext('2d'); - this.chart = new Chart(ctx).Doughnut(props.data, props.options || {}); //eslint-disable-line new-cap - } - - render() { - let content; - if (this.props.data == null) { - content = ( - <FormattedMessage - id='admin.analytics.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: React.PropTypes.string, - width: React.PropTypes.string, - height: React.PropTypes.string, - data: React.PropTypes.array, - options: React.PropTypes.object -}; diff --git a/web/react/components/admin_console/line_chart.jsx b/web/react/components/admin_console/line_chart.jsx deleted file mode 100644 index 7e2f95c84..000000000 --- a/web/react/components/admin_console/line_chart.jsx +++ /dev/null @@ -1,50 +0,0 @@ -// 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 -}; diff --git a/web/react/components/admin_console/service_settings.jsx b/web/react/components/admin_console/service_settings.jsx index 047c7eb8d..9ed81b6a3 100644 --- a/web/react/components/admin_console/service_settings.jsx +++ b/web/react/components/admin_console/service_settings.jsx @@ -31,6 +31,10 @@ var holders = defineMessages({ id: 'admin.service.sessionDaysEx', defaultMessage: 'Ex "30"' }, + corsExample: { + id: 'admin.service.corsEx', + defaultMessage: 'http://example.com' + }, saving: { id: 'admin.service.saving', defaultMessage: 'Saving Config...' @@ -131,6 +135,8 @@ class ServiceSettings extends React.Component { config.ServiceSettings.SessionCacheInMinutes = SessionCacheInMinutes; ReactDOM.findDOMNode(this.refs.SessionCacheInMinutes).value = SessionCacheInMinutes; + config.ServiceSettings.AllowCorsFrom = ReactDOM.findDOMNode(this.refs.AllowCorsFrom).value.trim(); + Client.saveConfig( config, () => { @@ -766,6 +772,35 @@ class ServiceSettings extends React.Component { <div className='form-group'> <label className='control-label col-sm-4' + htmlFor='AllowCorsFrom' + > + <FormattedMessage + id='admin.service.corsTitle' + defaultMessage='Allow Cross-origin Requests from:' + /> + </label> + <div className='col-sm-8'> + <input + type='text' + className='form-control' + id='AllowCorsFrom' + ref='AllowCorsFrom' + placeholder={formatMessage(holders.corsExample)} + defaultValue={this.props.config.ServiceSettings.AllowCorsFrom} + onChange={this.handleChange} + /> + <p className='help-text'> + <FormattedMessage + id='admin.service.corsDescription' + defaultMessage='Enable HTTP Cross origin request from a specific domain. Use "*" if you want to allow CORS from any domain or leave it blank to disable it.' + /> + </p> + </div> + </div> + + <div className='form-group'> + <label + className='control-label col-sm-4' htmlFor='SessionLengthWebInDays' > <FormattedMessage diff --git a/web/react/components/admin_console/statistic_count.jsx b/web/react/components/admin_console/statistic_count.jsx deleted file mode 100644 index 118a0ad31..000000000 --- a/web/react/components/admin_console/statistic_count.jsx +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -import {FormattedMessage} from 'mm-intl'; - -export default class StatisticCount extends React.Component { - render() { - let loading = ( - <FormattedMessage - id='admin.analytics.loading' - defaultMessage='Loading...' - /> - ); - - return ( - <div className='col-sm-3'> - <div className='total-count'> - <div className='title'> - {this.props.title} - <i className={'fa ' + this.props.icon}/> - </div> - <div className='content'>{this.props.count == null ? loading : this.props.count}</div> - </div> - </div> - ); - } -} - -StatisticCount.propTypes = { - title: React.PropTypes.string.isRequired, - icon: React.PropTypes.string.isRequired, - count: React.PropTypes.number -}; diff --git a/web/react/components/admin_console/system_analytics.jsx b/web/react/components/admin_console/system_analytics.jsx deleted file mode 100644 index f983db177..000000000 --- a/web/react/components/admin_console/system_analytics.jsx +++ /dev/null @@ -1,216 +0,0 @@ -// 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'; - -import {injectIntl, intlShape, defineMessages} from 'mm-intl'; - -const labels = defineMessages({ - totalPosts: { - id: 'admin.system_analytics.totalPosts', - defaultMessage: 'Total Posts' - }, - activeUsers: { - id: 'admin.system_analytics.activeUsers', - defaultMessage: 'Active Users With Posts' - }, - title: { - id: 'admin.system_analytics.title', - defaultMessage: 'the System' - } -}); - -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 - const {formatMessage} = this.props.intl; - 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: formatMessage(labels.totalPosts), - 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: formatMessage(labels.activeUsers), - 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}); - } - ); - - if (global.window.mm_license.IsLicensed === 'true') { - Client.getSystemAnalytics( - 'extra_counts', - (data) => { - for (var index in data) { - if (data[index].name === 'file_post_count') { - this.setState({file_post_count: data[index].value}); - } - - if (data[index].name === 'hashtag_post_count') { - this.setState({hashtag_post_count: data[index].value}); - } - - if (data[index].name === 'incoming_webhook_count') { - this.setState({incoming_webhook_count: data[index].value}); - } - - if (data[index].name === 'outgoing_webhook_count') { - this.setState({outgoing_webhook_count: data[index].value}); - } - } - }, - (err) => { - this.setState({serverError: err.message}); - } - ); - } - } - - componentWillReceiveProps() { - 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 ( - <div> - <Analytics - intl={this.props.intl} - title={this.props.intl.formatMessage(labels.title)} - channelOpenCount={this.state.channel_open_count} - channelPrivateCount={this.state.channel_private_count} - postCount={this.state.post_count} - showAdvanced={global.window.mm_license.IsLicensed === 'true'} - filePostCount={this.state.file_post_count} - hashtagPostCount={this.state.hashtag_post_count} - incomingWebhookCount={this.state.incoming_webhook_count} - outgoingWebhookCount={this.state.outgoing_webhook_count} - postCountsDay={this.state.post_counts_day} - userCountsWithPostsDay={this.state.user_counts_with_posts_day} - uniqueUserCount={this.state.unique_user_count} - serverError={this.state.serverError} - /> - </div> - ); - } -} - -SystemAnalytics.propTypes = { - intl: intlShape.isRequired, - team: React.PropTypes.object -}; - -export default injectIntl(SystemAnalytics); diff --git a/web/react/components/admin_console/team_analytics.jsx b/web/react/components/admin_console/team_analytics.jsx deleted file mode 100644 index 808d8046d..000000000 --- a/web/react/components/admin_console/team_analytics.jsx +++ /dev/null @@ -1,253 +0,0 @@ -// 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'; - -import {injectIntl, intlShape, defineMessages} from 'mm-intl'; - -const labels = defineMessages({ - totalPosts: { - id: 'admin.team_analytics.totalPosts', - defaultMessage: 'Total Posts' - }, - activeUsers: { - id: 'admin.team_analytics.activeUsers', - defaultMessage: 'Active Users With Posts' - } -}); - -class TeamAnalytics 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(this.props.team.id); - } - - getData(teamId) { // should be moved to an action creator eventually - const {formatMessage} = this.props.intl; - Client.getTeamAnalytics( - teamId, - '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.getTeamAnalytics( - teamId, - 'post_counts_day', - (data) => { - data.reverse(); - - var chartData = { - labels: [], - datasets: [{ - label: formatMessage(labels.totalPosts), - 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.getTeamAnalytics( - teamId, - 'user_counts_with_posts_day', - (data) => { - data.reverse(); - - var chartData = { - labels: [], - datasets: [{ - label: formatMessage(labels.activeUsers), - 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}); - } - ); - - Client.getProfilesForTeam( - teamId, - (users) => { - this.setState({users}); - - var usersList = []; - for (var id in users) { - if (users.hasOwnProperty(id)) { - usersList.push(users[id]); - } - } - - usersList.sort((a, b) => { - if (a.last_activity_at < b.last_activity_at) { - return 1; - } - - if (a.last_activity_at > b.last_activity_at) { - return -1; - } - - return 0; - }); - - var recentActive = []; - for (let i = 0; i < usersList.length; i++) { - if (usersList[i].last_activity_at == null) { - continue; - } - - recentActive.push(usersList[i]); - if (i > 19) { - break; - } - } - - this.setState({recent_active_users: recentActive}); - - usersList.sort((a, b) => { - if (a.create_at < b.create_at) { - return 1; - } - - if (a.create_at > b.create_at) { - return -1; - } - - return 0; - }); - - var newlyCreated = []; - for (let i = 0; i < usersList.length; i++) { - newlyCreated.push(usersList[i]); - if (i > 19) { - break; - } - } - - this.setState({newly_created_users: newlyCreated}); - }, - (err) => { - this.setState({serverError: err.message}); - } - ); - } - - componentWillReceiveProps(newProps) { - this.setState({ - 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 - }); - - this.getData(newProps.team.id); - } - - render() { - return ( - <div> - <Analytics - intl={this.props.intl} - title={this.props.team.name} - users={this.state.users} - channelOpenCount={this.state.channel_open_count} - channelPrivateCount={this.state.channel_private_count} - postCount={this.state.post_count} - postCountsDay={this.state.post_counts_day} - userCountsWithPostsDay={this.state.user_counts_with_posts_day} - recentActiveUsers={this.state.recent_active_users} - newlyCreatedUsers={this.state.newly_created_users} - uniqueUserCount={this.state.unique_user_count} - serverError={this.state.serverError} - /> - </div> - ); - } -} - -TeamAnalytics.propTypes = { - intl: intlShape.isRequired, - team: React.PropTypes.object -}; - -export default injectIntl(TeamAnalytics); diff --git a/web/react/components/admin_console/user_item.jsx b/web/react/components/admin_console/user_item.jsx index 009a9f004..4af350bcd 100644 --- a/web/react/components/admin_console/user_item.jsx +++ b/web/react/components/admin_console/user_item.jsx @@ -7,26 +7,7 @@ import UserStore from '../../stores/user_store.jsx'; import ConfirmModal from '../confirm_modal.jsx'; import TeamStore from '../../stores/team_store.jsx'; -import {injectIntl, intlShape, defineMessages, FormattedMessage} from 'mm-intl'; - -var holders = defineMessages({ - confirmDemoteRoleTitle: { - id: 'admin.user_item.confirmDemoteRoleTitle', - defaultMessage: 'Confirm demotion from System Admin role' - }, - confirmDemotion: { - id: 'admin.user_item.confirmDemotion', - defaultMessage: 'Confirm Demotion' - }, - confirmDemoteDescription: { - id: 'admin.user_item.confirmDemoteDescription', - defaultMessage: 'If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you\'ll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command.' - }, - confirmDemotionCmd: { - id: 'admin.user_item.confirmDemotionCmd', - defaultMessage: 'platform -assign_role -team_name="yourteam" -email="name@yourcompany.com" -role="system_admin"' - } -}); +import {FormattedMessage} from 'mm-intl'; export default class UserItem extends React.Component { constructor(props) { @@ -336,15 +317,44 @@ export default class UserItem extends React.Component { ); } const me = UserStore.getCurrentUser(); - const {formatMessage} = this.props.intl; let makeDemoteModal = null; if (this.props.user.id === me.id) { + const title = ( + <FormattedMessage + id='admin.user_item.confirmDemoteRoleTitle' + defaultMessage='Confirm demotion from System Admin role' + /> + ); + + const message = ( + <div> + <FormattedMessage + id='admin.user_item.confirmDemoteDescription' + defaultMessage="If you demote yourself from the System Admin role and there is not another user with System Admin privileges, you\'ll need to re-assign a System Admin by accessing the Mattermost server through a terminal and running the following command." + /> + <br/> + <br/> + <FormattedMessage + id='admin.user_item.confirmDemotionCmd' + defaultMessage='platform -assign_role -team_name="yourteam" -email="name@yourcompany.com" -role="system_admin"' + /> + {serverError} + </div> + ); + + const confirmButton = ( + <FormattedMessage + id='admin.user_item.confirmDemotion' + defaultMessage='Confirm Demotion' + /> + ); + makeDemoteModal = ( <ConfirmModal show={this.state.showDemoteModal} - title={formatMessage(holders.confirmDemoteRoleTitle)} - message={[formatMessage(holders.confirmDemoteDescription), React.createElement('br'), React.createElement('br'), formatMessage(holders.confirmDemotionCmd), serverError]} - confirm_button={formatMessage(holders.confirmDemotion)} + title={title} + message={message} + confirmButton={confirmButton} onConfirm={this.handleDemoteSubmit} onCancel={this.handleDemoteCancel} /> @@ -405,10 +415,7 @@ export default class UserItem extends React.Component { } UserItem.propTypes = { - intl: intlShape.isRequired, user: React.PropTypes.object.isRequired, refreshProfiles: React.PropTypes.func.isRequired, doPasswordReset: React.PropTypes.func.isRequired }; - -export default injectIntl(UserItem); |