Add Team Description to the Team Settings (#4652)
* draft * Add Team Description to the Team Settings * add tooltips for team description * made changes per PM review * add message when there is no description set in the team * squash
Этот коммит содержится в:
коммит произвёл
Joram Wilander
родитель
8c18da21f3
Коммит
c51afba71a
@@ -775,6 +775,7 @@ func updateTeam(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
oldTeam.DisplayName = team.DisplayName
|
oldTeam.DisplayName = team.DisplayName
|
||||||
|
oldTeam.Description = team.Description
|
||||||
oldTeam.InviteId = team.InviteId
|
oldTeam.InviteId = team.InviteId
|
||||||
oldTeam.AllowOpenInvite = team.AllowOpenInvite
|
oldTeam.AllowOpenInvite = team.AllowOpenInvite
|
||||||
oldTeam.CompanyName = team.CompanyName
|
oldTeam.CompanyName = team.CompanyName
|
||||||
@@ -1010,6 +1011,7 @@ func getInviteInfo(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
result := map[string]string{}
|
result := map[string]string{}
|
||||||
result["display_name"] = team.DisplayName
|
result["display_name"] = team.DisplayName
|
||||||
|
result["description"] = team.Description
|
||||||
result["name"] = team.Name
|
result["name"] = team.Name
|
||||||
result["id"] = team.Id
|
result["id"] = team.Id
|
||||||
w.Write([]byte(model.MapToJson(result)))
|
w.Write([]byte(model.MapToJson(result)))
|
||||||
|
|||||||
@@ -759,3 +759,43 @@ func TestGetTeamStats(t *testing.T) {
|
|||||||
t.Fatal("should have errored - not on team")
|
t.Fatal("should have errored - not on team")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateTeamDescription(t *testing.T) {
|
||||||
|
th := Setup().InitBasic()
|
||||||
|
th.BasicClient.Logout()
|
||||||
|
Client := th.BasicClient
|
||||||
|
|
||||||
|
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "success+" + model.NewId() + "@simulator.amazonses.com", Type: model.TEAM_OPEN}
|
||||||
|
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||||
|
|
||||||
|
user := &model.User{Email: team.Email, Nickname: "My Testing", Password: "passwd1"}
|
||||||
|
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||||
|
LinkUserToTeam(user, team)
|
||||||
|
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||||
|
|
||||||
|
user2 := &model.User{Email: "success+" + model.NewId() + "@simulator.amazonses.com", Nickname: "Jabba the Hutt", Password: "passwd1"}
|
||||||
|
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||||
|
LinkUserToTeam(user2, team)
|
||||||
|
store.Must(Srv.Store.User().VerifyEmail(user2.Id))
|
||||||
|
|
||||||
|
Client.Login(user2.Email, "passwd1")
|
||||||
|
Client.SetTeamId(team.Id)
|
||||||
|
|
||||||
|
vteam := &model.Team{DisplayName: team.DisplayName, Name: team.Name, Description: team.Description, Email: team.Email, Type: team.Type}
|
||||||
|
vteam.Description = "yommamma"
|
||||||
|
if _, err := Client.UpdateTeam(vteam); err == nil {
|
||||||
|
t.Fatal("Should have errored, not admin")
|
||||||
|
}
|
||||||
|
|
||||||
|
Client.Login(user.Email, "passwd1")
|
||||||
|
|
||||||
|
vteam.Description = ""
|
||||||
|
if _, err := Client.UpdateTeam(vteam); err != nil {
|
||||||
|
t.Fatal("Should have errored, should save blank Description")
|
||||||
|
}
|
||||||
|
|
||||||
|
vteam.Description = "yommamma"
|
||||||
|
if _, err := Client.UpdateTeam(vteam); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3771,6 +3771,10 @@
|
|||||||
"id": "model.team.is_valid.name.app_error",
|
"id": "model.team.is_valid.name.app_error",
|
||||||
"translation": "Invalid name"
|
"translation": "Invalid name"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "model.team.is_valid.description.app_error",
|
||||||
|
"translation": "Invalid description"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "model.team.is_valid.reserved.app_error",
|
"id": "model.team.is_valid.reserved.app_error",
|
||||||
"translation": "This URL is unavailable. Please try another."
|
"translation": "This URL is unavailable. Please try another."
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ type Team struct {
|
|||||||
DeleteAt int64 `json:"delete_at"`
|
DeleteAt int64 `json:"delete_at"`
|
||||||
DisplayName string `json:"display_name"`
|
DisplayName string `json:"display_name"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
CompanyName string `json:"company_name"`
|
CompanyName string `json:"company_name"`
|
||||||
@@ -130,6 +131,10 @@ func (o *Team) IsValid() *AppError {
|
|||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id)
|
return NewLocAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(o.Description) > 255 {
|
||||||
|
return NewLocAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id)
|
||||||
|
}
|
||||||
|
|
||||||
if IsReservedTeamName(o.Name) {
|
if IsReservedTeamName(o.Name) {
|
||||||
return NewLocAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id)
|
return NewLocAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore {
|
|||||||
table.ColMap("Id").SetMaxSize(26)
|
table.ColMap("Id").SetMaxSize(26)
|
||||||
table.ColMap("DisplayName").SetMaxSize(64)
|
table.ColMap("DisplayName").SetMaxSize(64)
|
||||||
table.ColMap("Name").SetMaxSize(64).SetUnique(true)
|
table.ColMap("Name").SetMaxSize(64).SetUnique(true)
|
||||||
|
table.ColMap("Description").SetMaxSize(255)
|
||||||
table.ColMap("Email").SetMaxSize(128)
|
table.ColMap("Email").SetMaxSize(128)
|
||||||
table.ColMap("CompanyName").SetMaxSize(64)
|
table.ColMap("CompanyName").SetMaxSize(64)
|
||||||
table.ColMap("AllowedDomains").SetMaxSize(500)
|
table.ColMap("AllowedDomains").SetMaxSize(500)
|
||||||
@@ -43,6 +44,7 @@ func NewSqlTeamStore(sqlStore *SqlStore) TeamStore {
|
|||||||
|
|
||||||
func (s SqlTeamStore) CreateIndexesIfNotExists() {
|
func (s SqlTeamStore) CreateIndexesIfNotExists() {
|
||||||
s.CreateIndexIfNotExists("idx_teams_name", "Teams", "Name")
|
s.CreateIndexIfNotExists("idx_teams_name", "Teams", "Name")
|
||||||
|
s.CreateIndexIfNotExists("idx_teams_description", "Teams", "Description")
|
||||||
s.CreateIndexIfNotExists("idx_teams_invite_id", "Teams", "InviteId")
|
s.CreateIndexIfNotExists("idx_teams_invite_id", "Teams", "InviteId")
|
||||||
s.CreateIndexIfNotExists("idx_teams_update_at", "Teams", "UpdateAt")
|
s.CreateIndexIfNotExists("idx_teams_update_at", "Teams", "UpdateAt")
|
||||||
s.CreateIndexIfNotExists("idx_teams_create_at", "Teams", "CreateAt")
|
s.CreateIndexIfNotExists("idx_teams_create_at", "Teams", "CreateAt")
|
||||||
|
|||||||
@@ -218,6 +218,9 @@ func UpgradeDatabaseToVersion36(sqlStore *SqlStore) {
|
|||||||
|
|
||||||
sqlStore.CreateColumnIfNotExists("Posts", "HasReactions", "tinyint", "boolean", "0")
|
sqlStore.CreateColumnIfNotExists("Posts", "HasReactions", "tinyint", "boolean", "0")
|
||||||
|
|
||||||
|
// Create Team Description column
|
||||||
|
sqlStore.CreateColumnIfNotExists("Teams", "Description", "varchar(255)", "varchar(255)", "")
|
||||||
|
|
||||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
// TODO FIXME UNCOMMENT WHEN WE DO RELEASE
|
// TODO FIXME UNCOMMENT WHEN WE DO RELEASE
|
||||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default class TeamSignupDisplayNamePage extends React.Component {
|
|||||||
this.setState({nameError: (
|
this.setState({nameError: (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='create_team.display_name.charLength'
|
id='create_team.display_name.charLength'
|
||||||
defaultMessage='Name must be {min} or more characters up to a maximum of {max}'
|
defaultMessage='Name must be {min} or more characters up to a maximum of {max}. You can add a longer team description later.'
|
||||||
values={{
|
values={{
|
||||||
min: Constants.MIN_TEAMNAME_LENGTH,
|
min: Constants.MIN_TEAMNAME_LENGTH,
|
||||||
max: Constants.MAX_TEAMNAME_LENGTH
|
max: Constants.MAX_TEAMNAME_LENGTH
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {Link} from 'react-router/es6';
|
import {Link} from 'react-router/es6';
|
||||||
|
import {Tooltip, OverlayTrigger} from 'react-bootstrap';
|
||||||
|
|
||||||
export default class SelectTeamItem extends React.Component {
|
export default class SelectTeamItem extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@@ -35,8 +36,31 @@ export default class SelectTeamItem extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var descriptionTooltip = '';
|
||||||
|
var showDescriptionTooltip = '';
|
||||||
|
if (this.props.team.description) {
|
||||||
|
descriptionTooltip = (
|
||||||
|
<Tooltip id='team-description__tooltip'>
|
||||||
|
{this.props.team.description}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
|
||||||
|
showDescriptionTooltip = (
|
||||||
|
<OverlayTrigger
|
||||||
|
trigger={['hover', 'focus', 'click']}
|
||||||
|
delayShow={1000}
|
||||||
|
placement='left'
|
||||||
|
overlay={descriptionTooltip}
|
||||||
|
ref='descriptionOverlay'
|
||||||
|
>
|
||||||
|
<span className='fa fa-info-circle signup-team__icon'/>
|
||||||
|
</OverlayTrigger>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='signup-team-dir'>
|
<div className='signup-team-dir'>
|
||||||
|
{showDescriptionTooltip}
|
||||||
<Link
|
<Link
|
||||||
to={this.props.url}
|
to={this.props.url}
|
||||||
onClick={this.handleTeamClick}
|
onClick={this.handleTeamClick}
|
||||||
|
|||||||
@@ -736,6 +736,7 @@ export default class Sidebar extends React.Component {
|
|||||||
|
|
||||||
<SidebarHeader
|
<SidebarHeader
|
||||||
teamDisplayName={this.state.currentTeam.display_name}
|
teamDisplayName={this.state.currentTeam.display_name}
|
||||||
|
teamDescription={this.state.currentTeam.description}
|
||||||
teamName={this.state.currentTeam.name}
|
teamName={this.state.currentTeam.name}
|
||||||
teamType={this.state.currentTeam.type}
|
teamType={this.state.currentTeam.type}
|
||||||
currentUser={this.state.currentUser}
|
currentUser={this.state.currentUser}
|
||||||
|
|||||||
@@ -69,6 +69,25 @@ export default class SidebarHeader extends React.Component {
|
|||||||
tutorialTip = createMenuTip(this.toggleDropdown);
|
tutorialTip = createMenuTip(this.toggleDropdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let teamNameWithToolTip = null;
|
||||||
|
if (this.props.teamDescription === '') {
|
||||||
|
teamNameWithToolTip = (
|
||||||
|
<div className='team__name'>{this.props.teamDisplayName}</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
teamNameWithToolTip = (
|
||||||
|
<OverlayTrigger
|
||||||
|
trigger={['hover', 'focus']}
|
||||||
|
delayShow={1000}
|
||||||
|
placement='bottom'
|
||||||
|
overlay={<Tooltip id='team-name__tooltip'>{this.props.teamDescription}</Tooltip>}
|
||||||
|
ref='descriptionOverlay'
|
||||||
|
>
|
||||||
|
<div className='team__name'>{this.props.teamDisplayName}</div>
|
||||||
|
</OverlayTrigger>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='team__header theme'>
|
<div className='team__header theme'>
|
||||||
{tutorialTip}
|
{tutorialTip}
|
||||||
@@ -79,15 +98,7 @@ export default class SidebarHeader extends React.Component {
|
|||||||
{profilePicture}
|
{profilePicture}
|
||||||
<div className='header__info'>
|
<div className='header__info'>
|
||||||
<div className='user__name'>{'@' + me.username}</div>
|
<div className='user__name'>{'@' + me.username}</div>
|
||||||
<OverlayTrigger
|
{teamNameWithToolTip}
|
||||||
trigger={['hover', 'focus']}
|
|
||||||
delayShow={1000}
|
|
||||||
placement='bottom'
|
|
||||||
overlay={<Tooltip id='team-name__tooltip'>{this.props.teamDisplayName}</Tooltip>}
|
|
||||||
ref='descriptionOverlay'
|
|
||||||
>
|
|
||||||
<div className='team__name'>{this.props.teamDisplayName}</div>
|
|
||||||
</OverlayTrigger>
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<SidebarHeaderDropdown
|
<SidebarHeaderDropdown
|
||||||
@@ -104,10 +115,12 @@ export default class SidebarHeader extends React.Component {
|
|||||||
|
|
||||||
SidebarHeader.defaultProps = {
|
SidebarHeader.defaultProps = {
|
||||||
teamDisplayName: '',
|
teamDisplayName: '',
|
||||||
|
teamDescription: '',
|
||||||
teamType: ''
|
teamType: ''
|
||||||
};
|
};
|
||||||
SidebarHeader.propTypes = {
|
SidebarHeader.propTypes = {
|
||||||
teamDisplayName: React.PropTypes.string,
|
teamDisplayName: React.PropTypes.string,
|
||||||
|
teamDescription: React.PropTypes.string,
|
||||||
teamName: React.PropTypes.string,
|
teamName: React.PropTypes.string,
|
||||||
teamType: React.PropTypes.string,
|
teamType: React.PropTypes.string,
|
||||||
currentUser: React.PropTypes.object
|
currentUser: React.PropTypes.object
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ const holders = defineMessages({
|
|||||||
teamNameInfo: {
|
teamNameInfo: {
|
||||||
id: 'general_tab.teamNameInfo',
|
id: 'general_tab.teamNameInfo',
|
||||||
defaultMessage: 'Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.'
|
defaultMessage: 'Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.'
|
||||||
|
},
|
||||||
|
teamDescriptionInfo: {
|
||||||
|
id: 'general_tab.teamDescriptionInfo',
|
||||||
|
defaultMessage: 'Team description provides additional information to help users select the right team. Maximum of 50 characters.'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -68,9 +72,12 @@ class GeneralTab extends React.Component {
|
|||||||
this.handleNameSubmit = this.handleNameSubmit.bind(this);
|
this.handleNameSubmit = this.handleNameSubmit.bind(this);
|
||||||
this.handleInviteIdSubmit = this.handleInviteIdSubmit.bind(this);
|
this.handleInviteIdSubmit = this.handleInviteIdSubmit.bind(this);
|
||||||
this.handleOpenInviteSubmit = this.handleOpenInviteSubmit.bind(this);
|
this.handleOpenInviteSubmit = this.handleOpenInviteSubmit.bind(this);
|
||||||
|
this.handleDescriptionSubmit = this.handleDescriptionSubmit.bind(this);
|
||||||
this.handleClose = this.handleClose.bind(this);
|
this.handleClose = this.handleClose.bind(this);
|
||||||
this.onUpdateNameSection = this.onUpdateNameSection.bind(this);
|
this.onUpdateNameSection = this.onUpdateNameSection.bind(this);
|
||||||
this.updateName = this.updateName.bind(this);
|
this.updateName = this.updateName.bind(this);
|
||||||
|
this.updateDescription = this.updateDescription.bind(this);
|
||||||
|
this.onUpdateDescriptionSection = this.onUpdateDescriptionSection.bind(this);
|
||||||
this.onUpdateInviteIdSection = this.onUpdateInviteIdSection.bind(this);
|
this.onUpdateInviteIdSection = this.onUpdateInviteIdSection.bind(this);
|
||||||
this.updateInviteId = this.updateInviteId.bind(this);
|
this.updateInviteId = this.updateInviteId.bind(this);
|
||||||
this.onUpdateOpenInviteSection = this.onUpdateOpenInviteSection.bind(this);
|
this.onUpdateOpenInviteSection = this.onUpdateOpenInviteSection.bind(this);
|
||||||
@@ -95,6 +102,7 @@ class GeneralTab extends React.Component {
|
|||||||
name: team.display_name,
|
name: team.display_name,
|
||||||
invite_id: team.invite_id,
|
invite_id: team.invite_id,
|
||||||
allow_open_invite: team.allow_open_invite,
|
allow_open_invite: team.allow_open_invite,
|
||||||
|
description: team.description,
|
||||||
serverError: '',
|
serverError: '',
|
||||||
clientError: ''
|
clientError: ''
|
||||||
};
|
};
|
||||||
@@ -103,6 +111,7 @@ class GeneralTab extends React.Component {
|
|||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
this.setState({
|
this.setState({
|
||||||
name: nextProps.team.display_name,
|
name: nextProps.team.display_name,
|
||||||
|
description: nextProps.team.description,
|
||||||
invite_id: nextProps.team.invite_id,
|
invite_id: nextProps.team.invite_id,
|
||||||
allow_open_invite: nextProps.team.allow_open_invite
|
allow_open_invite: nextProps.team.allow_open_invite
|
||||||
});
|
});
|
||||||
@@ -215,6 +224,40 @@ class GeneralTab extends React.Component {
|
|||||||
this.updateSection('');
|
this.updateSection('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleDescriptionSubmit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var state = {serverError: '', clientError: ''};
|
||||||
|
let valid = true;
|
||||||
|
|
||||||
|
const {formatMessage} = this.props.intl;
|
||||||
|
const description = this.state.description.trim();
|
||||||
|
if (description === this.props.team.description) {
|
||||||
|
state.clientError = formatMessage(holders.chooseName);
|
||||||
|
valid = false;
|
||||||
|
} else {
|
||||||
|
state.clientError = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState(state);
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = this.props.team;
|
||||||
|
data.description = this.state.description;
|
||||||
|
updateTeam(data,
|
||||||
|
() => {
|
||||||
|
this.updateSection('');
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
state.serverError = err.message;
|
||||||
|
this.setState(state);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
$('#team_settings').on('hidden.bs.modal', this.handleClose);
|
$('#team_settings').on('hidden.bs.modal', this.handleClose);
|
||||||
}
|
}
|
||||||
@@ -232,6 +275,15 @@ class GeneralTab extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onUpdateDescriptionSection(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (this.props.activeSection === 'description') {
|
||||||
|
this.updateSection('');
|
||||||
|
} else {
|
||||||
|
this.updateSection('description');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onUpdateInviteIdSection(e) {
|
onUpdateInviteIdSection(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (this.props.activeSection === 'invite_id') {
|
if (this.props.activeSection === 'invite_id') {
|
||||||
@@ -254,6 +306,10 @@ class GeneralTab extends React.Component {
|
|||||||
this.setState({name: e.target.value});
|
this.setState({name: e.target.value});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateDescription(e) {
|
||||||
|
this.setState({description: e.target.value});
|
||||||
|
}
|
||||||
|
|
||||||
updateInviteId(e) {
|
updateInviteId(e) {
|
||||||
this.setState({invite_id: e.target.value});
|
this.setState({invite_id: e.target.value});
|
||||||
}
|
}
|
||||||
@@ -457,6 +513,74 @@ class GeneralTab extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let descriptionSection;
|
||||||
|
|
||||||
|
if (this.props.activeSection === 'description') {
|
||||||
|
const inputs = [];
|
||||||
|
|
||||||
|
let teamDescriptionLabel = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='general_tab.teamDescription'
|
||||||
|
defaultMessage='Team Description'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
if (Utils.isMobile()) {
|
||||||
|
teamDescriptionLabel = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs.push(
|
||||||
|
<div
|
||||||
|
key='teamDescriptionSetting'
|
||||||
|
className='form-group'
|
||||||
|
>
|
||||||
|
<label className='col-sm-5 control-label'>{teamDescriptionLabel}</label>
|
||||||
|
<div className='col-sm-7'>
|
||||||
|
<input
|
||||||
|
className='form-control'
|
||||||
|
type='text'
|
||||||
|
maxLength={Constants.MAX_TEAMDESCRIPTION_LENGTH.toString()}
|
||||||
|
onChange={this.updateDescription}
|
||||||
|
value={this.state.description}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const descriptionExtraInfo = <span>{formatMessage(holders.teamDescriptionInfo)}</span>;
|
||||||
|
|
||||||
|
descriptionSection = (
|
||||||
|
<SettingItemMax
|
||||||
|
title={formatMessage({id: 'general_tab.teamDescription'})}
|
||||||
|
inputs={inputs}
|
||||||
|
submit={this.handleDescriptionSubmit}
|
||||||
|
server_error={serverError}
|
||||||
|
client_error={clientError}
|
||||||
|
updateSection={this.onUpdateDescriptionSection}
|
||||||
|
extraInfo={descriptionExtraInfo}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let describemsg = '';
|
||||||
|
if (this.state.description) {
|
||||||
|
describemsg = this.state.description;
|
||||||
|
} else {
|
||||||
|
describemsg = (
|
||||||
|
<FormattedMessage
|
||||||
|
id='general_tab.emptyDescription'
|
||||||
|
defaultMessage="Click 'Edit' to add a team description."
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptionSection = (
|
||||||
|
<SettingItemMin
|
||||||
|
title={formatMessage({id: 'general_tab.teamDescription'})}
|
||||||
|
describe={describemsg}
|
||||||
|
updateSection={this.onUpdateDescriptionSection}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='modal-header'>
|
<div className='modal-header'>
|
||||||
@@ -496,6 +620,8 @@ class GeneralTab extends React.Component {
|
|||||||
<div className='divider-dark first'/>
|
<div className='divider-dark first'/>
|
||||||
{nameSection}
|
{nameSection}
|
||||||
<div className='divider-light'/>
|
<div className='divider-light'/>
|
||||||
|
{descriptionSection}
|
||||||
|
<div className='divider-light'/>
|
||||||
{openInviteSection}
|
{openInviteSection}
|
||||||
<div className='divider-light'/>
|
<div className='divider-light'/>
|
||||||
{inviteSection}
|
{inviteSection}
|
||||||
|
|||||||
@@ -1145,7 +1145,7 @@
|
|||||||
"create_post.write": "Write a message...",
|
"create_post.write": "Write a message...",
|
||||||
"create_team.agreement": "By proceeding to create your account and use {siteName}, you agree to our <a href={TermsOfServiceLink}>Terms of Service</a> and <a href={PrivacyPolicyLink}>Privacy Policy</a>. If you do not agree, you cannot use {siteName}.",
|
"create_team.agreement": "By proceeding to create your account and use {siteName}, you agree to our <a href={TermsOfServiceLink}>Terms of Service</a> and <a href={PrivacyPolicyLink}>Privacy Policy</a>. If you do not agree, you cannot use {siteName}.",
|
||||||
"create_team.display_name.back": "Back to previous step",
|
"create_team.display_name.back": "Back to previous step",
|
||||||
"create_team.display_name.charLength": "Name must be 2 or more characters up to a maximum of 15",
|
"create_team.display_name.charLength": "Name must be {min} or more characters up to a maximum of {max}. You can add a longer team description later.",
|
||||||
"create_team.display_name.nameHelp": "Name your team in any language. Your team name shows in menus and headings.",
|
"create_team.display_name.nameHelp": "Name your team in any language. Your team name shows in menus and headings.",
|
||||||
"create_team.display_name.next": "Next",
|
"create_team.display_name.next": "Next",
|
||||||
"create_team.display_name.required": "This field is required",
|
"create_team.display_name.required": "This field is required",
|
||||||
@@ -1272,8 +1272,11 @@
|
|||||||
"general_tab.required": "This field is required",
|
"general_tab.required": "This field is required",
|
||||||
"general_tab.teamName": "Team Name",
|
"general_tab.teamName": "Team Name",
|
||||||
"general_tab.teamNameInfo": "Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.",
|
"general_tab.teamNameInfo": "Set the name of the team as it appears on your sign-in screen and at the top of the left-hand sidebar.",
|
||||||
|
"general_tab.teamDescription": "Team Description",
|
||||||
|
"general_tab.teamDescriptionInfo": "Team description provides additional information to help users select the right team. Maximum of 50 characters.",
|
||||||
"general_tab.title": "General Settings",
|
"general_tab.title": "General Settings",
|
||||||
"general_tab.yes": "Yes",
|
"general_tab.yes": "Yes",
|
||||||
|
"general_tab.emptyDescription": "Click 'Edit' to add a team description.",
|
||||||
"get_app.alreadyHaveIt": "Already have it?",
|
"get_app.alreadyHaveIt": "Already have it?",
|
||||||
"get_app.androidAppName": "Mattermost for Android",
|
"get_app.androidAppName": "Mattermost for Android",
|
||||||
"get_app.androidHeader": "Mattermost works best if you switch to our Android app",
|
"get_app.androidHeader": "Mattermost works best if you switch to our Android app",
|
||||||
|
|||||||
@@ -481,7 +481,8 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
width: 90%;
|
width: calc(100% - 50px);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.signup-team__icon {
|
.signup-team__icon {
|
||||||
@@ -497,6 +498,15 @@
|
|||||||
right: -2px;
|
right: -2px;
|
||||||
top: 16px;
|
top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.fa-info-circle {
|
||||||
|
float: left;
|
||||||
|
line-height: 1.5em;
|
||||||
|
margin-right: .3em;
|
||||||
|
padding-left: .5em;
|
||||||
|
font-size: 1.5em;
|
||||||
|
top: 11px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -237,6 +237,24 @@ describe('Client.Team', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('updateTeamDescription', function(done) {
|
||||||
|
TestHelper.initBasic(() => {
|
||||||
|
var team = TestHelper.basicTeam();
|
||||||
|
team.description = 'test_updated';
|
||||||
|
|
||||||
|
TestHelper.basicClient().updateTeam(
|
||||||
|
team,
|
||||||
|
function(data) {
|
||||||
|
assert.equal(data.description, 'test_updated');
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
function(err) {
|
||||||
|
done(new Error(err.message));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('addUserToTeam', function(done) {
|
it('addUserToTeam', function(done) {
|
||||||
TestHelper.initBasic(() => {
|
TestHelper.initBasic(() => {
|
||||||
TestHelper.basicClient().createUser(
|
TestHelper.basicClient().createUser(
|
||||||
|
|||||||
@@ -829,6 +829,7 @@ export const Constants = {
|
|||||||
DEFAULT_MAX_CHANNELS_PER_TEAM: 2000,
|
DEFAULT_MAX_CHANNELS_PER_TEAM: 2000,
|
||||||
DEFAULT_MAX_NOTIFICATIONS_PER_CHANNEL: 1000,
|
DEFAULT_MAX_NOTIFICATIONS_PER_CHANNEL: 1000,
|
||||||
MAX_TEAMNAME_LENGTH: 15,
|
MAX_TEAMNAME_LENGTH: 15,
|
||||||
|
MAX_TEAMDESCRIPTION_LENGTH: 50,
|
||||||
MIN_USERNAME_LENGTH: 3,
|
MIN_USERNAME_LENGTH: 3,
|
||||||
MAX_USERNAME_LENGTH: 22,
|
MAX_USERNAME_LENGTH: 22,
|
||||||
MAX_NICKNAME_LENGTH: 22,
|
MAX_NICKNAME_LENGTH: 22,
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user