Merge pull request #394 from mattermost/mm-1410
MM-1410 Initial implementaiton of import from Slack
Этот коммит содержится в:
@@ -3,7 +3,7 @@
|
||||
|
||||
module.exports = React.createClass({
|
||||
render: function() {
|
||||
var client_error = this.props.client_error ? <div className='form-group'><label className='col-sm-12 has-error'>{ this.props.client_error }</label></div> : null;
|
||||
var clientError = this.props.clientError ? <div className='form-group'><label className='col-sm-12 has-error'>{ this.props.clientError }</label></div> : null;
|
||||
var server_error = this.props.server_error ? <div className='form-group'><label className='col-sm-12 has-error'>{ this.props.server_error }</label></div> : null;
|
||||
|
||||
var inputs = this.props.inputs;
|
||||
@@ -19,7 +19,7 @@ module.exports = React.createClass({
|
||||
<li className="setting-list-item">
|
||||
<hr />
|
||||
{ server_error }
|
||||
{ client_error }
|
||||
{ clientError }
|
||||
{ this.props.submit ? <a className="btn btn-sm btn-primary" onClick={this.props.submit}>Submit</a> : "" }
|
||||
<a className="btn btn-sm theme" href="#" onClick={this.props.updateSection}>Cancel</a>
|
||||
</li>
|
||||
|
||||
79
web/react/components/setting_upload.jsx
Обычный файл
79
web/react/components/setting_upload.jsx
Обычный файл
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'Setting Upload',
|
||||
propTypes: {
|
||||
title: React.PropTypes.string.isRequired,
|
||||
submit: React.PropTypes.func.isRequired,
|
||||
fileTypesAccepted: React.PropTypes.string.isRequired,
|
||||
clientError: React.PropTypes.string,
|
||||
serverError: React.PropTypes.string
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {
|
||||
clientError: this.props.clientError,
|
||||
serverError: this.props.serverError
|
||||
};
|
||||
},
|
||||
componentWillReceiveProps: function() {
|
||||
this.setState({
|
||||
clientError: this.props.clientError,
|
||||
serverError: this.props.serverError
|
||||
});
|
||||
},
|
||||
doFileSelect: function(e) {
|
||||
e.preventDefault();
|
||||
this.setState({
|
||||
clientError: '',
|
||||
serverError: ''
|
||||
});
|
||||
},
|
||||
doSubmit: function(e) {
|
||||
e.preventDefault();
|
||||
var inputnode = this.refs.uploadinput.getDOMNode();
|
||||
if (inputnode.files && inputnode.files[0]) {
|
||||
this.props.submit(inputnode.files[0]);
|
||||
} else {
|
||||
this.setState({clientError: 'No file selected.'});
|
||||
}
|
||||
},
|
||||
doCancel: function(e) {
|
||||
e.preventDefault();
|
||||
this.refs.uploadinput.getDOMNode().value = '';
|
||||
this.setState({
|
||||
clientError: '',
|
||||
serverError: ''
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
var clientError = null;
|
||||
if (this.state.clientError) {
|
||||
clientError = (
|
||||
<div className='form-group has-error'><label className='control-label'>{this.state.clientError}</label></div>
|
||||
);
|
||||
}
|
||||
var serverError = null;
|
||||
if (this.state.serverError) {
|
||||
serverError = (
|
||||
<div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<ul className='section-max'>
|
||||
<li className='col-xs-12 section-title'>{this.props.title}</li>
|
||||
<li className='col-xs-offset-3 col-xs-8'>
|
||||
<ul className='setting-list'>
|
||||
<li className='setting-list-item'>
|
||||
{serverError}
|
||||
{clientError}
|
||||
<span className='btn btn-sm btn-primary btn-file sel-btn'>SelectFile<input ref='uploadinput' accept={this.props.fileTypesAccepted} type='file' onChange={this.onFileSelect}/></span>
|
||||
<a className={'btn btn-sm btn-primary'} onClick={this.doSubmit}>Import</a>
|
||||
<a className='btn btn-sm theme' href='#' onClick={this.doCancel}>Cancel</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -15,7 +15,7 @@ module.exports = React.createClass({
|
||||
<div className="">
|
||||
<ul className="nav nav-pills nav-stacked">
|
||||
{this.props.tabs.map(function(tab) {
|
||||
return <li key={tab.name+'_li'} className={self.props.activeTab == tab.name ? 'active' : ''}><a key={tab.name + '_a'} href="#" onClick={function(){self.updateTab(tab.name);}}><i key={tab.name+'_i'} className={tab.icon}></i>{tab.ui_name}</a></li>
|
||||
return <li key={tab.name+'_li'} className={self.props.activeTab == tab.name ? 'active' : ''}><a key={tab.name + '_a'} href="#" onClick={function(){self.updateTab(tab.name);}}><i key={tab.name+'_i'} className={tab.icon}></i>{tab.uiName}</a></li>
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
147
web/react/components/team_feature_tab.jsx
Обычный файл
147
web/react/components/team_feature_tab.jsx
Обычный файл
@@ -0,0 +1,147 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var SettingItemMin = require('./setting_item_min.jsx');
|
||||
var SettingItemMax = require('./setting_item_max.jsx');
|
||||
|
||||
var client = require('../utils/client.jsx');
|
||||
var AsyncClient = require('../utils/async_client.jsx');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'Feature Tab',
|
||||
propTypes: {
|
||||
updateSection: React.PropTypes.func.isRequired,
|
||||
team: React.PropTypes.object.isRequired,
|
||||
activeSection: React.PropTypes.string.isRequired
|
||||
},
|
||||
submitValetFeature: function() {
|
||||
var data = {};
|
||||
data.allowValet = this.state.allowValet;
|
||||
|
||||
client.updateValetFeature(data,
|
||||
function() {
|
||||
this.props.updateSection('');
|
||||
AsyncClient.getMyTeam();
|
||||
}.bind(this),
|
||||
function(err) {
|
||||
var state = this.getInitialState();
|
||||
state.serverError = err;
|
||||
this.setState(state);
|
||||
}.bind(this)
|
||||
);
|
||||
},
|
||||
handleValetRadio: function(val) {
|
||||
this.setState({allowValet: val});
|
||||
this.refs.wrapper.getDOMNode().focus();
|
||||
},
|
||||
componentWillReceiveProps: function(newProps) {
|
||||
var team = newProps.team;
|
||||
|
||||
var allowValet = 'false';
|
||||
if (team && team.allowValet) {
|
||||
allowValet = 'true';
|
||||
}
|
||||
|
||||
this.setState({allowValet: allowValet});
|
||||
},
|
||||
getInitialState: function() {
|
||||
var team = this.props.team;
|
||||
|
||||
var allowValet = 'false';
|
||||
if (team && team.allowValet) {
|
||||
allowValet = 'true';
|
||||
}
|
||||
|
||||
return {allowValet: allowValet};
|
||||
},
|
||||
onUpdateSection: function() {
|
||||
if (this.props.activeSection === 'valet') {
|
||||
self.props.updateSection('valet');
|
||||
} else {
|
||||
self.props.updateSection('');
|
||||
}
|
||||
},
|
||||
render: function() {
|
||||
var clientError = null;
|
||||
var serverError = null;
|
||||
if (this.state.clientError) {
|
||||
clientError = this.state.clientError;
|
||||
}
|
||||
if (this.state.serverError) {
|
||||
serverError = this.state.serverError;
|
||||
}
|
||||
|
||||
var valetSection;
|
||||
var self = this;
|
||||
|
||||
if (this.props.activeSection === 'valet') {
|
||||
var valetActive = ['', ''];
|
||||
if (this.state.allowValet === 'false') {
|
||||
valetActive[1] = 'active';
|
||||
} else {
|
||||
valetActive[0] = 'active';
|
||||
}
|
||||
|
||||
var inputs = [];
|
||||
|
||||
function valetActivate() {
|
||||
self.handleValetRadio('true');
|
||||
}
|
||||
|
||||
function valetDeactivate() {
|
||||
self.handleValetRadio('false');
|
||||
}
|
||||
|
||||
inputs.push(
|
||||
<div>
|
||||
<div className='btn-group' data-toggle='buttons-radio'>
|
||||
<button className={'btn btn-default ' + valetActive[0]} onClick={valetActivate}>On</button>
|
||||
<button className={'btn btn-default ' + valetActive[1]} onClick={valetDeactivate}>Off</button>
|
||||
</div>
|
||||
<div><br/>Valet is a preview feature for enabling a non-user account limited to basic member permissions that can be manipulated by 3rd parties.<br/><br/>IMPORTANT: The preview version of Valet should not be used without a secure connection and a trusted 3rd party, since user credentials are used to connect. OAuth2 will be used in the final release.</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
valetSection = (
|
||||
<SettingItemMax
|
||||
title='Valet (Preview - EXPERTS ONLY)'
|
||||
inputs={inputs}
|
||||
submit={this.submitValetFeature}
|
||||
serverError={serverError}
|
||||
clientError={clientError}
|
||||
updateSection={this.onUpdateSection}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
var describe = '';
|
||||
if (this.state.allowValet === 'false') {
|
||||
describe = 'Off';
|
||||
} else {
|
||||
describe = 'On';
|
||||
}
|
||||
|
||||
valetSection = (
|
||||
<SettingItemMin
|
||||
title='Valet (Preview - EXPERTS ONLY)'
|
||||
describe={describe}
|
||||
updateSection={this.onUpdateSection}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='modal-header'>
|
||||
<button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>×</span></button>
|
||||
<h4 className='modal-title' ref='title'><i className='modal-back'></i>Feature Settings</h4>
|
||||
</div>
|
||||
<div ref='wrapper' className='user-settings'>
|
||||
<h3 className='tab-header'>Feature Settings</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{valetSection}
|
||||
<div className='divider-dark'/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
68
web/react/components/team_import_tab.jsx
Обычный файл
68
web/react/components/team_import_tab.jsx
Обычный файл
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var utils = require('../utils/utils.jsx');
|
||||
var SettingUpload = require('./setting_upload.jsx');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'Import Tab',
|
||||
getInitialState: function() {
|
||||
return {status: 'ready', link: ''};
|
||||
},
|
||||
onImportFailure: function() {
|
||||
this.setState({status: 'fail', link: ''});
|
||||
},
|
||||
onImportSuccess: function(data) {
|
||||
this.setState({status: 'done', link: 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(data)});
|
||||
},
|
||||
doImportSlack: function(file) {
|
||||
this.setState({status: 'in-progress', link: ''});
|
||||
utils.importSlack(file, this.onImportSuccess, this.onImportFailure);
|
||||
},
|
||||
render: function() {
|
||||
var uploadSection = (
|
||||
<SettingUpload
|
||||
title='Import from Slack'
|
||||
submit={this.doImportSlack}
|
||||
fileTypesAccepted='.zip'/>
|
||||
);
|
||||
|
||||
var messageSection;
|
||||
switch (this.state.status) {
|
||||
case 'ready':
|
||||
messageSection = '';
|
||||
break;
|
||||
case 'in-progress':
|
||||
messageSection = (
|
||||
<p>Importing...</p>
|
||||
);
|
||||
break;
|
||||
case 'done':
|
||||
messageSection = (
|
||||
<p>Import sucessfull: <a href={this.state.link} download='MattermostImportSummery.txt'>View Summery</a></p>
|
||||
);
|
||||
break;
|
||||
case 'fail':
|
||||
messageSection = (
|
||||
<p>Import failure: <a href={this.state.link} download='MattermostImportSummery.txt'>View Summery</a></p>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='modal-header'>
|
||||
<button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>×</span></button>
|
||||
<h4 className='modal-title' ref='title'><i className='modal-back'></i>Import</h4>
|
||||
</div>
|
||||
<div ref='wrapper' className='user-settings'>
|
||||
<h3 className='tab-header'>Import</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{uploadSection}
|
||||
{messageSection}
|
||||
<div className='divider-dark'/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,161 +1,62 @@
|
||||
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
var UserStore = require('../stores/user_store.jsx');
|
||||
var TeamStore = require('../stores/team_store.jsx');
|
||||
var SettingItemMin = require('./setting_item_min.jsx');
|
||||
var SettingItemMax = require('./setting_item_max.jsx');
|
||||
var SettingPicture = require('./setting_picture.jsx');
|
||||
var ImportTab = require('./team_import_tab.jsx');
|
||||
var FeatureTab = require('./team_feature_tab.jsx');
|
||||
var utils = require('../utils/utils.jsx');
|
||||
|
||||
var client = require('../utils/client.jsx');
|
||||
var AsyncClient = require('../utils/async_client.jsx');
|
||||
var Constants = require('../utils/constants.jsx');
|
||||
|
||||
var FeatureTab = React.createClass({
|
||||
submitValetFeature: function() {
|
||||
data = {};
|
||||
data['allow_valet'] = this.state.allow_valet;
|
||||
|
||||
client.updateValetFeature(data,
|
||||
function(data) {
|
||||
this.props.updateSection("");
|
||||
AsyncClient.getMyTeam();
|
||||
}.bind(this),
|
||||
function(err) {
|
||||
state = this.getInitialState();
|
||||
state.server_error = err;
|
||||
this.setState(state);
|
||||
}.bind(this)
|
||||
);
|
||||
},
|
||||
handleValetRadio: function(val) {
|
||||
this.setState({ allow_valet: val });
|
||||
this.refs.wrapper.getDOMNode().focus();
|
||||
},
|
||||
componentWillReceiveProps: function(newProps) {
|
||||
var team = newProps.team;
|
||||
|
||||
var allow_valet = "false";
|
||||
if (team && team.allow_valet) {
|
||||
allow_valet = "true";
|
||||
}
|
||||
|
||||
this.setState({ allow_valet: allow_valet });
|
||||
},
|
||||
getInitialState: function() {
|
||||
var team = this.props.team;
|
||||
|
||||
var allow_valet = "false";
|
||||
if (team && team.allow_valet) {
|
||||
allow_valet = "true";
|
||||
}
|
||||
|
||||
return { allow_valet: allow_valet };
|
||||
},
|
||||
render: function() {
|
||||
var team = this.props.team;
|
||||
|
||||
var client_error = this.state.client_error ? this.state.client_error : null;
|
||||
var server_error = this.state.server_error ? this.state.server_error : null;
|
||||
|
||||
var valetSection;
|
||||
var self = this;
|
||||
|
||||
if (this.props.activeSection === 'valet') {
|
||||
var valetActive = ["",""];
|
||||
if (this.state.allow_valet === "false") {
|
||||
valetActive[1] = "active";
|
||||
} else {
|
||||
valetActive[0] = "active";
|
||||
}
|
||||
|
||||
var inputs = [];
|
||||
|
||||
inputs.push(
|
||||
<div>
|
||||
<div className="btn-group" data-toggle="buttons-radio">
|
||||
<button className={"btn btn-default "+valetActive[0]} onClick={function(){self.handleValetRadio("true")}}>On</button>
|
||||
<button className={"btn btn-default "+valetActive[1]} onClick={function(){self.handleValetRadio("false")}}>Off</button>
|
||||
</div>
|
||||
<div><br/>Valet is a preview feature for enabling a non-user account limited to basic member permissions that can be manipulated by 3rd parties.<br/><br/>IMPORTANT: The preview version of Valet should not be used without a secure connection and a trusted 3rd party, since user credentials are used to connect. OAuth2 will be used in the final release.</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
valetSection = (
|
||||
<SettingItemMax
|
||||
title="Valet (Preview - EXPERTS ONLY)"
|
||||
inputs={inputs}
|
||||
submit={this.submitValetFeature}
|
||||
server_error={server_error}
|
||||
client_error={client_error}
|
||||
updateSection={function(e){self.props.updateSection("");e.preventDefault();}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
var describe = "";
|
||||
if (this.state.allow_valet === "false") {
|
||||
describe = "Off";
|
||||
} else {
|
||||
describe = "On";
|
||||
}
|
||||
|
||||
valetSection = (
|
||||
<SettingItemMin
|
||||
title="Valet (Preview - EXPERTS ONLY)"
|
||||
describe={describe}
|
||||
updateSection={function(){self.props.updateSection("valet");}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="modal-header">
|
||||
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 className="modal-title" ref="title"><i className="modal-back"></i>Feature Settings</h4>
|
||||
</div>
|
||||
<div ref="wrapper" className="user-settings">
|
||||
<h3 className="tab-header">Feature Settings</h3>
|
||||
<div className="divider-dark first"/>
|
||||
{valetSection}
|
||||
<div className="divider-dark"/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'Team Settings',
|
||||
propTypes: {
|
||||
activeTab: React.PropTypes.string.isRequired,
|
||||
activeSection: React.PropTypes.string.isRequired,
|
||||
updateSection: React.PropTypes.func.isRequired
|
||||
},
|
||||
componentDidMount: function() {
|
||||
TeamStore.addChangeListener(this._onChange);
|
||||
TeamStore.addChangeListener(this.onChange);
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
TeamStore.removeChangeListener(this._onChange);
|
||||
TeamStore.removeChangeListener(this.onChange);
|
||||
},
|
||||
_onChange: function () {
|
||||
onChange: function() {
|
||||
var team = TeamStore.getCurrent();
|
||||
if (!utils.areStatesEqual(this.state.team, team)) {
|
||||
this.setState({ team: team });
|
||||
this.setState({team: team});
|
||||
}
|
||||
},
|
||||
getInitialState: function() {
|
||||
return { team: TeamStore.getCurrent() };
|
||||
return {team: TeamStore.getCurrent()};
|
||||
},
|
||||
render: function() {
|
||||
if (this.props.activeTab === 'general') {
|
||||
return (
|
||||
<div>
|
||||
</div>
|
||||
);
|
||||
} else if (this.props.activeTab === 'feature') {
|
||||
return (
|
||||
<div>
|
||||
<FeatureTab team={this.state.team} activeSection={this.props.activeSection} updateSection={this.props.updateSection} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <div/>;
|
||||
var result;
|
||||
switch (this.props.activeTab) {
|
||||
case 'general':
|
||||
result = (
|
||||
<div>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'feature':
|
||||
result = (
|
||||
<div>
|
||||
<FeatureTab team={this.state.team} activeSection={this.props.activeSection} updateSection={this.props.updateSection} />
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'import':
|
||||
result = (
|
||||
<div>
|
||||
<ImportTab team={this.state.team} activeSection={this.props.activeSection} updateSection={this.props.updateSection} />
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
result = (
|
||||
<div/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,50 +5,52 @@ var SettingsSidebar = require('./settings_sidebar.jsx');
|
||||
var TeamSettings = require('./team_settings.jsx');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'Team Settings Modal',
|
||||
componentDidMount: function() {
|
||||
$('body').on('click', '.modal-back', function(){
|
||||
$('body').on('click', '.modal-back', function onClick() {
|
||||
$(this).closest('.modal-dialog').removeClass('display--content');
|
||||
});
|
||||
$('body').on('click', '.modal-header .close', function(){
|
||||
setTimeout(function() {
|
||||
$('body').on('click', '.modal-header .close', function onClick() {
|
||||
setTimeout(function removeContent() {
|
||||
$('.modal-dialog.display--content').removeClass('display--content');
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
updateTab: function(tab) {
|
||||
this.setState({ active_tab: tab });
|
||||
this.setState({activeTab: tab});
|
||||
},
|
||||
updateSection: function(section) {
|
||||
this.setState({ active_section: section });
|
||||
this.setState({activeSection: section});
|
||||
},
|
||||
getInitialState: function() {
|
||||
return { active_tab: "feature", active_section: "" };
|
||||
return {activeTab: 'feature', activeSection: ''};
|
||||
},
|
||||
render: function() {
|
||||
var tabs = [];
|
||||
tabs.push({name: "feature", ui_name: "Features", icon: "glyphicon glyphicon-wrench"});
|
||||
tabs.push({name: 'feature', uiName: 'Features', icon: 'glyphicon glyphicon-wrench'});
|
||||
tabs.push({name: 'import', uiName: 'Import', icon: 'glyphicon glyphicon-upload'});
|
||||
|
||||
return (
|
||||
<div className="modal fade" ref="modal" id="team_settings" role="dialog" tabIndex="-1" aria-hidden="true">
|
||||
<div className="modal-dialog settings-modal">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 className="modal-title" ref="title">Team Settings</h4>
|
||||
<div className='modal fade' ref='modal' id='team_settings' role='dialog' tabIndex='-1' aria-hidden='true'>
|
||||
<div className='modal-dialog settings-modal'>
|
||||
<div className='modal-content'>
|
||||
<div className='modal-header'>
|
||||
<button type='button' className='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>×</span></button>
|
||||
<h4 className='modal-title' ref='title'>Team Settings</h4>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="settings-table">
|
||||
<div className="settings-links">
|
||||
<div className='modal-body'>
|
||||
<div className='settings-table'>
|
||||
<div className='settings-links'>
|
||||
<SettingsSidebar
|
||||
tabs={tabs}
|
||||
activeTab={this.state.active_tab}
|
||||
activeTab={this.state.activeTab}
|
||||
updateTab={this.updateTab}
|
||||
/>
|
||||
</div>
|
||||
<div className="settings-content minimize-settings">
|
||||
<div className='settings-content minimize-settings'>
|
||||
<TeamSettings
|
||||
activeTab={this.state.active_tab}
|
||||
activeSection={this.state.active_section}
|
||||
activeTab={this.state.activeTab}
|
||||
activeSection={this.state.activeSection}
|
||||
updateSection={this.updateSection}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Ссылка в новой задаче
Block a user