diff options
Diffstat (limited to 'web/react')
-rw-r--r-- | web/react/components/admin_console/team_analytics.jsx | 184 | ||||
-rw-r--r-- | web/react/components/edit_post_modal.jsx | 21 | ||||
-rw-r--r-- | web/react/components/popover_list_members.jsx | 32 | ||||
-rw-r--r-- | web/react/components/post_list.jsx | 47 | ||||
-rw-r--r-- | web/react/utils/constants.jsx | 2 |
5 files changed, 190 insertions, 96 deletions
diff --git a/web/react/components/admin_console/team_analytics.jsx b/web/react/components/admin_console/team_analytics.jsx index a945a551c..0c9d1f61b 100644 --- a/web/react/components/admin_console/team_analytics.jsx +++ b/web/react/components/admin_console/team_analytics.jsx @@ -210,69 +210,89 @@ export default class TeamAnalytics extends React.Component { } var totalCount = ( - <div className='total-count text-center'> - <div>{'Total Users'}</div> - <div>{this.state.users == null ? 'Loading...' : Object.keys(this.state.users).length}</div> + <div className='col-sm-3'> + <div className='total-count'> + <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> ); var openChannelCount = ( - <div className='total-count text-center'> - <div>{'Public Groups'}</div> - <div>{this.state.channel_open_count == null ? 'Loading...' : this.state.channel_open_count}</div> + <div className='col-sm-3'> + <div className='total-count'> + <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> ); var openPrivateCount = ( - <div className='total-count text-center'> - <div>{'Private Groups'}</div> - <div>{this.state.channel_private_count == null ? 'Loading...' : this.state.channel_private_count}</div> + <div className='col-sm-3'> + <div className='total-count'> + <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> ); var postCount = ( - <div className='total-count text-center'> - <div>{'Total Posts'}</div> - <div>{this.state.post_count == null ? 'Loading...' : this.state.post_count}</div> + <div className='col-sm-3'> + <div className='total-count'> + <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> ); var postCountsByDay = ( - <div className='total-count-by-day'> - <div>{'Total Posts'}</div> - <div>{'Loading...'}</div> + <div className='col-sm-12'> + <div className='total-count by-day'> + <div className='title'>{'Total Posts'}</div> + <div className='content'>{'Loading...'}</div> + </div> </div> ); if (this.state.post_counts_day != null) { postCountsByDay = ( - <div className='total-count-by-day'> - <div>{'Total Posts'}</div> - <LineChart - data={this.state.post_counts_day} - width='740' - height='225' - /> + <div className='col-sm-12'> + <div className='total-count by-day'> + <div className='title'>{'Total Posts'}</div> + <div className='content'> + <LineChart + data={this.state.post_counts_day} + width='740' + height='225' + /> + </div> + </div> </div> ); } var usersWithPostsByDay = ( - <div className='total-count-by-day'> - <div>{'Total Posts'}</div> - <div>{'Loading...'}</div> + <div className='col-sm-12'> + <div className='total-count by-day'> + <div className='title'>{'Total Posts'}</div> + <div>{'Loading...'}</div> + </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 className='col-sm-12'> + <div className='total-count by-day'> + <div className='title'>{'Active Users With Posts'}</div> + <div className='content'> + <LineChart + data={this.state.user_counts_with_posts_day} + width='740' + height='225' + /> + </div> + </div> </div> ); } @@ -286,22 +306,26 @@ export default class TeamAnalytics extends React.Component { if (this.state.recent_active_users != null) { recentActiveUser = ( - <div className='recent-active-users'> - <div>{'Recent Active Users'}</div> - <table width='90%'> - <tbody> - { - this.state.recent_active_users.map((user) => { - return ( - <tr key={user.id}> - <td className='recent-active-users-td'>{user.email}</td> - <td className='recent-active-users-td'>{Utils.displayDateTime(user.last_activity_at)}</td> - </tr> - ); - }) - } - </tbody> - </table> + <div className='col-sm-6'> + <div className='total-count recent-active-users'> + <div className='title'>{'Recent Active Users'}</div> + <div className='content'> + <table> + <tbody> + { + this.state.recent_active_users.map((user) => { + return ( + <tr key={user.id}> + <td>{user.email}</td> + <td>{Utils.displayDateTime(user.last_activity_at)}</td> + </tr> + ); + }) + } + </tbody> + </table> + </div> + </div> </div> ); } @@ -315,38 +339,50 @@ export default class TeamAnalytics extends React.Component { if (this.state.newly_created_users != null) { newUsers = ( - <div className='recent-active-users'> - <div>{'Newly Created Users'}</div> - <table width='90%'> - <tbody> - { - this.state.newly_created_users.map((user) => { - return ( - <tr key={user.id}> - <td className='recent-active-users-td'>{user.email}</td> - <td className='recent-active-users-td'>{Utils.displayDateTime(user.create_at)}</td> - </tr> - ); - }) - } - </tbody> - </table> + <div className='col-sm-6'> + <div className='total-count recent-active-users'> + <div className='title'>{'Newly Created Users'}</div> + <div className='content'> + <table> + <tbody> + { + this.state.newly_created_users.map((user) => { + return ( + <tr key={user.id}> + <td>{user.email}</td> + <td>{Utils.displayDateTime(user.create_at)}</td> + </tr> + ); + }) + } + </tbody> + </table> + </div> + </div> </div> ); } return ( - <div className='wrapper--fixed'> - <h2>{'Statistics for ' + this.props.team.name}</h2> + <div className='wrapper--fixed team_statistics'> + <h3>{'Statistics for ' + this.props.team.name}</h3> {serverError} - {totalCount} - {postCount} - {openChannelCount} - {openPrivateCount} - {postCountsByDay} - {usersWithPostsByDay} - {recentActiveUser} - {newUsers} + <div className='row'> + {totalCount} + {postCount} + {openChannelCount} + {openPrivateCount} + </div> + <div className='row'> + {postCountsByDay} + </div> + <div className='row'> + {usersWithPostsByDay} + </div> + <div className='row'> + {recentActiveUser} + {newUsers} + </div> </div> ); } diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx index e5bede026..2abb3f151 100644 --- a/web/react/components/edit_post_modal.jsx +++ b/web/react/components/edit_post_modal.jsx @@ -6,6 +6,10 @@ var AsyncClient = require('../utils/async_client.jsx'); var Textbox = require('./textbox.jsx'); var BrowserStore = require('../stores/browser_store.jsx'); var PostStore = require('../stores/post_store.jsx'); +var PreferenceStore = require('../stores/preference_store.jsx'); + +var Constants = require('../utils/constants.jsx'); +var KeyCodes = Constants.KeyCodes; export default class EditPostModal extends React.Component { constructor() { @@ -16,6 +20,8 @@ export default class EditPostModal extends React.Component { this.handleEditKeyPress = this.handleEditKeyPress.bind(this); this.handleUserInput = this.handleUserInput.bind(this); this.handleEditPostEvent = this.handleEditPostEvent.bind(this); + this.handleKeyDown = this.handleKeyDown.bind(this); + this.onPreferenceChange = this.onPreferenceChange.bind(this); this.state = {editText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: ''}; } @@ -51,7 +57,7 @@ export default class EditPostModal extends React.Component { this.setState({editText: editMessage}); } handleEditKeyPress(e) { - if (e.which === 13 && !e.shiftKey && !e.altKey) { + if (this.state.ctrlSend === 'false' && e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) { e.preventDefault(); ReactDOM.findDOMNode(this.refs.editbox).blur(); this.handleEdit(e); @@ -72,6 +78,16 @@ export default class EditPostModal extends React.Component { $(ReactDOM.findDOMNode(this.refs.modal)).modal('show'); } + handleKeyDown(e) { + if (this.state.ctrlSend === 'true' && e.keyCode === KeyCodes.ENTER && e.ctrlKey === true) { + this.handleEdit(e); + } + } + onPreferenceChange() { + this.setState({ + ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value + }); + } componentDidMount() { var self = this; @@ -101,9 +117,11 @@ export default class EditPostModal extends React.Component { }); PostStore.addEditPostListener(this.handleEditPostEvent); + PreferenceStore.addChangeListener(this.onPreferenceChange); } componentWillUnmount() { PostStore.removeEditPostListener(this.handleEditPostEvent); + PreferenceStore.removeChangeListener(this.onPreferenceChange); } render() { var error = (<div className='form-group'><br /></div>); @@ -138,6 +156,7 @@ export default class EditPostModal extends React.Component { <Textbox onUserInput={this.handleEditInput} onKeyPress={this.handleEditKeyPress} + onKeyDown={this.handleKeyDown} messageText={this.state.editText} createMessage='Edit the post...' id='edit_textbox' diff --git a/web/react/components/popover_list_members.jsx b/web/react/components/popover_list_members.jsx index 9cffa2400..f3c0fa0b4 100644 --- a/web/react/components/popover_list_members.jsx +++ b/web/react/components/popover_list_members.jsx @@ -42,6 +42,10 @@ export default class PopoverListMembers extends React.Component { }; } + componentDidUpdate() { + $(ReactDOM.findDOMNode(this.refs.memebersPopover)).find('.popover-content').perfectScrollbar(); + } + handleShowDirectChannel(teammate, e) { e.preventDefault(); @@ -106,27 +110,27 @@ export default class PopoverListMembers extends React.Component { let button = ''; if (currentUserId !== m.id && ch.type !== 'D') { button = ( - <button - type='button' - className='btn btn-primary btn-message' + <a + href='#' + className='btn-message' onClick={(e) => this.handleShowDirectChannel(m, e)} > {'Message'} - </button> + </a> ); } if (teamMembers[m.username] && teamMembers[m.username].delete_at <= 0) { popoverHtml.push( <div - className='text--nowrap' + className='text-nowrap' key={'popover-member-' + i} > <img - className='profile-img pull-left' - width='38' - height='38' + className='profile-img rounded pull-left' + width='26px' + height='26px' src={`/api/v1/users/${m.id}/image?time=${m.update_at}&${Utils.getSessionIndex()}`} /> <div className='pull-left'> @@ -135,14 +139,9 @@ export default class PopoverListMembers extends React.Component { > {m.username} </div> - <div - className='more-description' - > - {details} - </div> </div> <div - className='pull-right profile-action' + className='pull-right' > {button} </div> @@ -182,12 +181,11 @@ export default class PopoverListMembers extends React.Component { placement='bottom' > <Popover + ref='memebersPopover' title='Members' id='member-list-popover' > - <div> - {popoverHtml} - </div> + {popoverHtml} </Popover> </Overlay> </div> diff --git a/web/react/components/post_list.jsx b/web/react/components/post_list.jsx index 0d69e56bf..b9741bac4 100644 --- a/web/react/components/post_list.jsx +++ b/web/react/components/post_list.jsx @@ -9,6 +9,7 @@ const LoadingScreen = require('./loading_screen.jsx'); const PostStore = require('../stores/post_store.jsx'); const ChannelStore = require('../stores/channel_store.jsx'); const UserStore = require('../stores/user_store.jsx'); +const TeamStore = require('../stores/team_store.jsx'); const SocketStore = require('../stores/socket_store.jsx'); const PreferenceStore = require('../stores/preference_store.jsx'); @@ -386,17 +387,55 @@ export default class PostList extends React.Component { } } createDefaultIntroMessage(channel) { + const team = TeamStore.getCurrent(); + let inviteModalLink; + if (team.type === Constants.INVITE_TEAM) { + inviteModalLink = ( + <a + className='intro-links' + href='#' + data-toggle='modal' + data-target='#invite_member' + > + <i className='fa fa-user-plus'></i>{'Invite others to this team'} + </a> + ); + } else { + inviteModalLink = ( + <a + className='intro-links' + href='#' + data-toggle='modal' + data-target='#get_link' + data-title='Team Invite' + data-value={Utils.getWindowLocationOrigin() + '/signup_user_complete/?id=' + team.id} + > + <i className='fa fa-user-plus'></i>{'Invite others to this team'} + </a> + ); + } + return ( <div className='channel-intro'> <h4 className='channel-intro__title'>{'Beginning of ' + channel.display_name}</h4> <p className='channel-intro__content'> - {'Welcome to ' + channel.display_name + '!'} + <strong>{'Welcome to ' + channel.display_name + '!'}</strong> <br/><br/> {'This is the first channel teammates see when they sign up - use it for posting updates everyone needs to know.'} - <br/><br/> - {'To create a new channel or join an existing one, go to the Left Sidebar under “Channels” and click “More…”.'} - <br/> </p> + {inviteModalLink} + <a + className='intro-links' + href='#' + data-toggle='modal' + data-target='#edit_channel' + data-header={channel.header} + data-title={channel.display_name} + data-channelid={channel.id} + > + <i className='fa fa-pencil'></i>{'Set a header'} + </a> + <br/> </div> ); } diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index 69e3b007d..43d81d322 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -127,6 +127,8 @@ module.exports = { MAX_DMS: 20, DM_CHANNEL: 'D', OPEN_CHANNEL: 'O', + INVITE_TEAM: 'I', + OPEN_TEAM: 'O', MAX_POST_LEN: 4000, EMOJI_SIZE: 16, ONLINE_ICON_SVG: "<svg version='1.1' id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:cc='http://creativecommons.org/ns#' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' sodipodi:docname='TRASH_1_4.svg' inkscape:version='0.48.4 r9939' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='12px' height='12px' viewBox='0 0 12 12' enable-background='new 0 0 12 12' xml:space='preserve'><sodipodi:namedview inkscape:cy='139.7898' inkscape:cx='26.358185' inkscape:zoom='1.18' showguides='true' showgrid='false' id='namedview6' guidetolerance='10' gridtolerance='10' objecttolerance='10' borderopacity='1' bordercolor='#666666' pagecolor='#ffffff' inkscape:current-layer='Layer_1' inkscape:window-maximized='1' inkscape:window-y='-8' inkscape:window-x='-8' inkscape:window-height='705' inkscape:window-width='1366' inkscape:guide-bbox='true' inkscape:pageshadow='2' inkscape:pageopacity='0'><sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide><sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide></sodipodi:namedview><g><g><path class='online--icon' d='M6,5.487c1.371,0,2.482-1.116,2.482-2.493c0-1.378-1.111-2.495-2.482-2.495S3.518,1.616,3.518,2.994C3.518,4.371,4.629,5.487,6,5.487z M10.452,8.545c-0.101-0.829-0.36-1.968-0.726-2.541C9.475,5.606,8.5,5.5,8.5,5.5S8.43,7.521,6,7.521C3.507,7.521,3.5,5.5,3.5,5.5S2.527,5.606,2.273,6.004C1.908,6.577,1.648,7.716,1.547,8.545C1.521,8.688,1.49,9.082,1.498,9.142c0.161,1.295,2.238,2.322,4.375,2.358C5.916,11.501,5.958,11.501,6,11.501c0.043,0,0.084,0,0.127-0.001c2.076-0.026,4.214-1.063,4.375-2.358C10.509,9.082,10.471,8.696,10.452,8.545z'/></g></g></svg>", |