UI Improvements
Этот коммит содержится в:
@@ -210,69 +210,89 @@ export default class TeamAnalytics extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var totalCount = (
|
var totalCount = (
|
||||||
<div className='total-count text-center'>
|
<div className='col-sm-3'>
|
||||||
<div>{'Total Users'}</div>
|
<div className='total-count'>
|
||||||
<div>{this.state.users == null ? 'Loading...' : Object.keys(this.state.users).length}</div>
|
<div className='title'>{'Total Users'}<i className='fa fa-users'/></div>
|
||||||
|
<div className='content'>{this.state.users == null ? 'Loading...' : Object.keys(this.state.users).length}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
var openChannelCount = (
|
var openChannelCount = (
|
||||||
<div className='total-count text-center'>
|
<div className='col-sm-3'>
|
||||||
<div>{'Public Groups'}</div>
|
<div className='total-count'>
|
||||||
<div>{this.state.channel_open_count == null ? 'Loading...' : this.state.channel_open_count}</div>
|
<div className='title'>{'Public Groups'}<i className='fa fa-unlock-alt'/></div>
|
||||||
|
<div className='content'>{this.state.channel_open_count == null ? 'Loading...' : this.state.channel_open_count}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
var openPrivateCount = (
|
var openPrivateCount = (
|
||||||
<div className='total-count text-center'>
|
<div className='col-sm-3'>
|
||||||
<div>{'Private Groups'}</div>
|
<div className='total-count'>
|
||||||
<div>{this.state.channel_private_count == null ? 'Loading...' : this.state.channel_private_count}</div>
|
<div className='title'>{'Private Groups'}<i className='fa fa-lock'/></div>
|
||||||
|
<div className='content'>{this.state.channel_private_count == null ? 'Loading...' : this.state.channel_private_count}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
var postCount = (
|
var postCount = (
|
||||||
<div className='total-count text-center'>
|
<div className='col-sm-3'>
|
||||||
<div>{'Total Posts'}</div>
|
<div className='total-count'>
|
||||||
<div>{this.state.post_count == null ? 'Loading...' : this.state.post_count}</div>
|
<div className='title'>{'Total Posts'}<i className='fa fa-comment'/></div>
|
||||||
|
<div className='content'>{this.state.post_count == null ? 'Loading...' : this.state.post_count}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
var postCountsByDay = (
|
var postCountsByDay = (
|
||||||
<div className='total-count-by-day'>
|
<div className='col-sm-12'>
|
||||||
<div>{'Total Posts'}</div>
|
<div className='total-count by-day'>
|
||||||
<div>{'Loading...'}</div>
|
<div className='title'>{'Total Posts'}</div>
|
||||||
|
<div className='content'>{'Loading...'}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.state.post_counts_day != null) {
|
if (this.state.post_counts_day != null) {
|
||||||
postCountsByDay = (
|
postCountsByDay = (
|
||||||
<div className='total-count-by-day'>
|
<div className='col-sm-12'>
|
||||||
<div>{'Total Posts'}</div>
|
<div className='total-count by-day'>
|
||||||
<LineChart
|
<div className='title'>{'Total Posts'}</div>
|
||||||
data={this.state.post_counts_day}
|
<div className='content'>
|
||||||
width='740'
|
<LineChart
|
||||||
height='225'
|
data={this.state.post_counts_day}
|
||||||
/>
|
width='740'
|
||||||
|
height='225'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var usersWithPostsByDay = (
|
var usersWithPostsByDay = (
|
||||||
<div className='total-count-by-day'>
|
<div className='col-sm-12'>
|
||||||
<div>{'Total Posts'}</div>
|
<div className='total-count by-day'>
|
||||||
<div>{'Loading...'}</div>
|
<div className='title'>{'Total Posts'}</div>
|
||||||
|
<div>{'Loading...'}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.state.user_counts_with_posts_day != null) {
|
if (this.state.user_counts_with_posts_day != null) {
|
||||||
usersWithPostsByDay = (
|
usersWithPostsByDay = (
|
||||||
<div className='total-count-by-day'>
|
<div className='col-sm-12'>
|
||||||
<div>{'Active Users With Posts'}</div>
|
<div className='total-count by-day'>
|
||||||
<LineChart
|
<div className='title'>{'Active Users With Posts'}</div>
|
||||||
data={this.state.user_counts_with_posts_day}
|
<div className='content'>
|
||||||
width='740'
|
<LineChart
|
||||||
height='225'
|
data={this.state.user_counts_with_posts_day}
|
||||||
/>
|
width='740'
|
||||||
|
height='225'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -286,22 +306,26 @@ export default class TeamAnalytics extends React.Component {
|
|||||||
|
|
||||||
if (this.state.recent_active_users != null) {
|
if (this.state.recent_active_users != null) {
|
||||||
recentActiveUser = (
|
recentActiveUser = (
|
||||||
<div className='recent-active-users'>
|
<div className='col-sm-6'>
|
||||||
<div>{'Recent Active Users'}</div>
|
<div className='total-count recent-active-users'>
|
||||||
<table width='90%'>
|
<div className='title'>{'Recent Active Users'}</div>
|
||||||
<tbody>
|
<div className='content'>
|
||||||
{
|
<table>
|
||||||
this.state.recent_active_users.map((user) => {
|
<tbody>
|
||||||
return (
|
{
|
||||||
<tr key={user.id}>
|
this.state.recent_active_users.map((user) => {
|
||||||
<td className='recent-active-users-td'>{user.email}</td>
|
return (
|
||||||
<td className='recent-active-users-td'>{Utils.displayDateTime(user.last_activity_at)}</td>
|
<tr key={user.id}>
|
||||||
</tr>
|
<td>{user.email}</td>
|
||||||
);
|
<td>{Utils.displayDateTime(user.last_activity_at)}</td>
|
||||||
})
|
</tr>
|
||||||
}
|
);
|
||||||
</tbody>
|
})
|
||||||
</table>
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -315,38 +339,50 @@ export default class TeamAnalytics extends React.Component {
|
|||||||
|
|
||||||
if (this.state.newly_created_users != null) {
|
if (this.state.newly_created_users != null) {
|
||||||
newUsers = (
|
newUsers = (
|
||||||
<div className='recent-active-users'>
|
<div className='col-sm-6'>
|
||||||
<div>{'Newly Created Users'}</div>
|
<div className='total-count recent-active-users'>
|
||||||
<table width='90%'>
|
<div className='title'>{'Newly Created Users'}</div>
|
||||||
<tbody>
|
<div className='content'>
|
||||||
{
|
<table>
|
||||||
this.state.newly_created_users.map((user) => {
|
<tbody>
|
||||||
return (
|
{
|
||||||
<tr key={user.id}>
|
this.state.newly_created_users.map((user) => {
|
||||||
<td className='recent-active-users-td'>{user.email}</td>
|
return (
|
||||||
<td className='recent-active-users-td'>{Utils.displayDateTime(user.create_at)}</td>
|
<tr key={user.id}>
|
||||||
</tr>
|
<td>{user.email}</td>
|
||||||
);
|
<td>{Utils.displayDateTime(user.create_at)}</td>
|
||||||
})
|
</tr>
|
||||||
}
|
);
|
||||||
</tbody>
|
})
|
||||||
</table>
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='wrapper--fixed'>
|
<div className='wrapper--fixed team_statistics'>
|
||||||
<h2>{'Statistics for ' + this.props.team.name}</h2>
|
<h3>{'Statistics for ' + this.props.team.name}</h3>
|
||||||
{serverError}
|
{serverError}
|
||||||
{totalCount}
|
<div className='row'>
|
||||||
{postCount}
|
{totalCount}
|
||||||
{openChannelCount}
|
{postCount}
|
||||||
{openPrivateCount}
|
{openChannelCount}
|
||||||
{postCountsByDay}
|
{openPrivateCount}
|
||||||
{usersWithPostsByDay}
|
</div>
|
||||||
{recentActiveUser}
|
<div className='row'>
|
||||||
{newUsers}
|
{postCountsByDay}
|
||||||
|
</div>
|
||||||
|
<div className='row'>
|
||||||
|
{usersWithPostsByDay}
|
||||||
|
</div>
|
||||||
|
<div className='row'>
|
||||||
|
{recentActiveUser}
|
||||||
|
{newUsers}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ export default class PopoverListMembers extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
$(ReactDOM.findDOMNode(this.refs.memebersPopover)).find('.popover-content').perfectScrollbar();
|
||||||
|
}
|
||||||
|
|
||||||
handleShowDirectChannel(teammate, e) {
|
handleShowDirectChannel(teammate, e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -106,27 +110,27 @@ export default class PopoverListMembers extends React.Component {
|
|||||||
let button = '';
|
let button = '';
|
||||||
if (currentUserId !== m.id && ch.type !== 'D') {
|
if (currentUserId !== m.id && ch.type !== 'D') {
|
||||||
button = (
|
button = (
|
||||||
<button
|
<a
|
||||||
type='button'
|
href='#'
|
||||||
className='btn btn-primary btn-message'
|
className='btn-message'
|
||||||
onClick={(e) => this.handleShowDirectChannel(m, e)}
|
onClick={(e) => this.handleShowDirectChannel(m, e)}
|
||||||
>
|
>
|
||||||
{'Message'}
|
{'Message'}
|
||||||
</button>
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (teamMembers[m.username] && teamMembers[m.username].delete_at <= 0) {
|
if (teamMembers[m.username] && teamMembers[m.username].delete_at <= 0) {
|
||||||
popoverHtml.push(
|
popoverHtml.push(
|
||||||
<div
|
<div
|
||||||
className='text--nowrap'
|
className='text-nowrap'
|
||||||
key={'popover-member-' + i}
|
key={'popover-member-' + i}
|
||||||
>
|
>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
className='profile-img pull-left'
|
className='profile-img rounded pull-left'
|
||||||
width='38'
|
width='26px'
|
||||||
height='38'
|
height='26px'
|
||||||
src={`/api/v1/users/${m.id}/image?time=${m.update_at}&${Utils.getSessionIndex()}`}
|
src={`/api/v1/users/${m.id}/image?time=${m.update_at}&${Utils.getSessionIndex()}`}
|
||||||
/>
|
/>
|
||||||
<div className='pull-left'>
|
<div className='pull-left'>
|
||||||
@@ -135,14 +139,9 @@ export default class PopoverListMembers extends React.Component {
|
|||||||
>
|
>
|
||||||
{m.username}
|
{m.username}
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
className='more-description'
|
|
||||||
>
|
|
||||||
{details}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className='pull-right profile-action'
|
className='pull-right'
|
||||||
>
|
>
|
||||||
{button}
|
{button}
|
||||||
</div>
|
</div>
|
||||||
@@ -182,12 +181,11 @@ export default class PopoverListMembers extends React.Component {
|
|||||||
placement='bottom'
|
placement='bottom'
|
||||||
>
|
>
|
||||||
<Popover
|
<Popover
|
||||||
|
ref='memebersPopover'
|
||||||
title='Members'
|
title='Members'
|
||||||
id='member-list-popover'
|
id='member-list-popover'
|
||||||
>
|
>
|
||||||
<div>
|
{popoverHtml}
|
||||||
{popoverHtml}
|
|
||||||
</div>
|
|
||||||
</Popover>
|
</Popover>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,65 +2,18 @@
|
|||||||
> div {
|
> div {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-weight: 600;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.total-count {
|
|
||||||
width: 175px;
|
|
||||||
height: 100px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
padding: 22px 10px 10px 10px;
|
|
||||||
margin: 10px 10px 10px 10px;
|
|
||||||
background: #fff;
|
|
||||||
float: left;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.total-count-by-day {
|
|
||||||
width: 760px;
|
|
||||||
height: 275px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
padding: 5px 10px 10px 10px;
|
|
||||||
margin: 10px 10px 10px 10px;
|
|
||||||
background: #fff;
|
|
||||||
clear: both;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.recent-active-users {
|
|
||||||
width: 365px;
|
|
||||||
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;
|
||||||
|
|||||||
@@ -215,7 +215,7 @@
|
|||||||
color: #999;
|
color: #999;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
.fa {
|
.fa {
|
||||||
margin-left: 3px;
|
margin-left: 4px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,9 @@
|
|||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
margin: 3px 0;
|
margin: 3px 0;
|
||||||
@include border-radius(2px);
|
@include border-radius(2px);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(black, 0.1);
|
background: rgba(black, 0.1);
|
||||||
@@ -94,33 +97,20 @@
|
|||||||
|
|
||||||
#member-list-popover {
|
#member-list-popover {
|
||||||
max-width: initial;
|
max-width: initial;
|
||||||
.popover-content > div {
|
.popover-content {
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
width: 260px;
|
||||||
max-height: 350px;
|
max-height: 350px;
|
||||||
overflow-y: auto;
|
.text-nowrap {
|
||||||
overflow-x: hidden;
|
padding: 6px 10px;
|
||||||
> div {
|
|
||||||
border-bottom: 1px solid rgba(51,51,51,0.1);
|
|
||||||
padding: 8px 8px 8px 15px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: content-box;
|
overflow: hidden;
|
||||||
@include clearfix;
|
line-height: 26px;
|
||||||
.profile-img {
|
font-size: 13px;
|
||||||
border-radius: 50px;
|
}
|
||||||
margin-right: 8px;
|
.more-name {
|
||||||
}
|
margin-left: 6px;
|
||||||
.more-name {
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 0.95em;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
.more-description {
|
|
||||||
@include opacity(0.7);
|
|
||||||
}
|
|
||||||
.profile-action {
|
|
||||||
margin-left: 8px;
|
|
||||||
margin-right: 18px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
84
web/sass-files/sass/partials/_statistics.scss
Обычный файл
84
web/sass-files/sass/partials/_statistics.scss
Обычный файл
@@ -0,0 +1,84 @@
|
|||||||
|
.team_statistics {
|
||||||
|
.total-count {
|
||||||
|
margin: 1em 0;
|
||||||
|
text-align: center;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
width: 100%;
|
||||||
|
@include border-radius(3px);
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 7px 10px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
float: right;
|
||||||
|
margin: 3px 0 0;
|
||||||
|
color: #555;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #555;
|
||||||
|
padding: 0.3em 0 0.35em;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-count--day {
|
||||||
|
width: 760px;
|
||||||
|
height: 275px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 5px 10px 10px 10px;
|
||||||
|
margin: 10px 10px 10px 10px;
|
||||||
|
background: #fff;
|
||||||
|
clear: both;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-active-users {
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
max-height: 400px;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
&:first-child {
|
||||||
|
td {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
font-weight: 400;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 13px;
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
padding: 5px 5px 6px;
|
||||||
|
@include clearfix;
|
||||||
|
&:first-child {
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
@import "partials/loading";
|
@import "partials/loading";
|
||||||
@import "partials/get-link";
|
@import "partials/get-link";
|
||||||
@import "partials/markdown";
|
@import "partials/markdown";
|
||||||
|
@import "partials/statistics";
|
||||||
|
|
||||||
// Responsive Css
|
// Responsive Css
|
||||||
@import "partials/responsive";
|
@import "partials/responsive";
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user