mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-27 11:10:10 +00:00
Org department (#3194)
* add org department * delete hash * update * update * render repo icon * update dialogs
This commit is contained in:
parent
8b6ed0eede
commit
597d22c403
@ -98,7 +98,7 @@ class AddReviewerDialog extends React.Component {
|
||||
<p>{gettext('Add new reviewer')}</p>
|
||||
<div className='add-reviewer'>
|
||||
<UserSelect
|
||||
placeholder={gettext('Please enter 1 or more character')}
|
||||
placeholder={gettext('Select users...')}
|
||||
onSelectChange={this.handleSelectChange}
|
||||
ref="reviewSelect"
|
||||
isMulti={true}
|
||||
|
92
frontend/src/components/dialog/org-add-department-dialog.js
Normal file
92
frontend/src/components/dialog/org-add-department-dialog.js
Normal file
@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input, Form, FormGroup, Label } from 'reactstrap';
|
||||
import { gettext, orgID } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
|
||||
const propTypes = {
|
||||
toggle: PropTypes.func.isRequired,
|
||||
onDepartChanged: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class AddDepartDialog extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
departName: '',
|
||||
errMessage: '',
|
||||
};
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
let isValid = this.validateName();
|
||||
if (isValid) {
|
||||
let parentGroup = -1;
|
||||
if (this.props.parentGroupID) {
|
||||
parentGroup = this.props.parentGroupID;
|
||||
}
|
||||
seafileAPI.orgAdminAddDepartGroup(orgID, parentGroup, this.state.departName.trim()).then((res) => {
|
||||
this.props.toggle();
|
||||
this.props.onDepartChanged();
|
||||
}).catch(error => {
|
||||
let errorMsg = gettext(error.response.data.error_msg);
|
||||
this.setState({ errMessage: errorMsg });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
validateName = () => {
|
||||
let errMessage = '';
|
||||
const name = this.state.departName.trim();
|
||||
if (!name.length) {
|
||||
errMessage = gettext('Name is required');
|
||||
this.setState({ errMessage: errMessage });
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
handleChange = (e) => {
|
||||
this.setState({
|
||||
departName: e.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.handleSubmit();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let header = this.props.parentGroupID ? gettext('New Sub-department') : gettext('New Department');
|
||||
return (
|
||||
<Modal isOpen={true} toggle={this.props.toggle}>
|
||||
<ModalHeader toggle={this.props.toggle}>{header}</ModalHeader>
|
||||
<ModalBody>
|
||||
<Form>
|
||||
<FormGroup>
|
||||
<Label for="departName">{gettext('Name')}</Label>
|
||||
<Input
|
||||
id="departName"
|
||||
onKeyPress={this.handleKeyPress}
|
||||
value={this.state.departName}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
{ this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AddDepartDialog.propTypes = propTypes;
|
||||
|
||||
export default AddDepartDialog;
|
73
frontend/src/components/dialog/org-add-member-dialog.js
Normal file
73
frontend/src/components/dialog/org-add-member-dialog.js
Normal file
@ -0,0 +1,73 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||
import { gettext, orgID } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import UserSelect from '../user-select.js';
|
||||
|
||||
const propTypes = {
|
||||
toggle: PropTypes.func.isRequired,
|
||||
onMemberChanged: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class AddMemberDialog extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selectedOption: null,
|
||||
errMessage: '',
|
||||
};
|
||||
this.Options = [];
|
||||
}
|
||||
|
||||
handleSelectChange = (option) => {
|
||||
this.setState({ selectedOption: option });
|
||||
this.Options = [];
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
if (!this.state.selectedOption) return;
|
||||
const email = this.state.selectedOption.email;
|
||||
this.refs.orgSelect.clearSelect();
|
||||
this.setState({ errorMsg: [] });
|
||||
seafileAPI.orgAdminAddDepartGroupUser(orgID, this.props.groupID, email).then((res) => {
|
||||
if (res.data.failed) {
|
||||
this.setState({ errorMsg: res.data.failed[0] });
|
||||
}
|
||||
this.setState({
|
||||
selectedOption: null,
|
||||
});
|
||||
if (res.data.success) {
|
||||
this.props.onMemberChanged();
|
||||
this.props.toggle();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Modal isOpen={true} toggle={this.props.toggle}>
|
||||
<ModalHeader toggle={this.props.toggle}>{gettext('Add Member')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<UserSelect
|
||||
placeholder={gettext('Select users...')}
|
||||
onSelectChange={this.handleSelectChange}
|
||||
ref="orgSelect"
|
||||
isMulti={false}
|
||||
className='org-add-member-select'
|
||||
/>
|
||||
{ this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
||||
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AddMemberDialog.propTypes = propTypes;
|
||||
|
||||
export default AddMemberDialog;
|
87
frontend/src/components/dialog/org-add-repo-dialog.js
Normal file
87
frontend/src/components/dialog/org-add-repo-dialog.js
Normal file
@ -0,0 +1,87 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input, Form, FormGroup, Label } from 'reactstrap';
|
||||
import { gettext, orgID } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
|
||||
const propTypes = {
|
||||
toggle: PropTypes.func.isRequired,
|
||||
onRepoChanged: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class AddRepoDialog extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
repoName: '',
|
||||
errMessage: '',
|
||||
};
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
let isValid = this.validateName();
|
||||
if (isValid) {
|
||||
seafileAPI.orgAdminAddDepartGroupRepo(orgID, this.props.groupID, this.state.repoName.trim()).then((res) => {
|
||||
this.props.toggle();
|
||||
this.props.onRepoChanged();
|
||||
}).catch(error => {
|
||||
let errorMsg = gettext(error.response.data.error_msg);
|
||||
this.setState({ errMessage: errorMsg });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
validateName = () => {
|
||||
let errMessage = '';
|
||||
const name = this.state.repoName.trim();
|
||||
if (!name.length) {
|
||||
errMessage = gettext('Name is required');
|
||||
this.setState({ errMessage: errMessage });
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
handleChange = (e) => {
|
||||
this.setState({
|
||||
repoName: e.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.handleSubmit();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Modal isOpen={true} toggle={this.props.toggle}>
|
||||
<ModalHeader toggle={this.props.toggle}>{gettext('New Library')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<Form>
|
||||
<FormGroup>
|
||||
<Label for="repoName">{gettext('Name')}</Label>
|
||||
<Input
|
||||
id="repoName"
|
||||
onKeyPress={this.handleKeyPress}
|
||||
value={this.state.repoName}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
{ this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AddRepoDialog.propTypes = propTypes;
|
||||
|
||||
export default AddRepoDialog;
|
@ -145,7 +145,7 @@ class AddOrgUserDialog extends React.Component {
|
||||
<FormGroup>
|
||||
<Label for="userPwd">{gettext('Password')}</Label>
|
||||
<InputGroup className="passwd">
|
||||
<Input id="userPwd" innerRef={input => {this.passwdInput = input}} value={this.state.password || ''} onChange={this.inputPassword} />
|
||||
<Input id="userPwd" innerRef={input => {this.passwdInput = input;}} value={this.state.password || ''} onChange={this.inputPassword} />
|
||||
<InputGroupAddon addonType="append">
|
||||
<Button onClick={this.togglePasswordVisible}><i className={`link-operation-icon fas ${this.state.isPasswordVisible ? 'fa-eye-slash' : 'fa-eye'}`}></i></Button>
|
||||
<Button onClick={this.generatePassword}><i className="link-operation-icon fas fa-magic"></i></Button>
|
||||
@ -154,7 +154,7 @@ class AddOrgUserDialog extends React.Component {
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<Label for="userPwdNew">{gettext('Confirm Password')}</Label>
|
||||
<Input id="userPwdNew" innerRef={input => {this.passwdNewInput = input}} className="passwd" value={this.state.passwdnew || ''} onChange={this.inputPasswordNew} />
|
||||
<Input id="userPwdNew" innerRef={input => {this.passwdNewInput = input;}} className="passwd" value={this.state.passwdnew || ''} onChange={this.inputPasswordNew} />
|
||||
</FormGroup>
|
||||
</Form>
|
||||
<Label className="err-message">{gettext(this.state.errMessage)}</Label>
|
||||
|
@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||
import { gettext, orgID } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { Utils } from '../../utils/utils';
|
||||
|
||||
const propTypes = {
|
||||
groupName: PropTypes.string,
|
||||
toggle: PropTypes.func.isRequired,
|
||||
onDepartChanged: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class DeleteDepartDialog extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
errMessage: null
|
||||
};
|
||||
}
|
||||
|
||||
deleteDepart = () => {
|
||||
seafileAPI.orgAdminDeleteDepartGroup(orgID, this.props.groupID).then((res) => {
|
||||
if (res.data.success) {
|
||||
this.props.onDepartChanged();
|
||||
this.props.toggle();
|
||||
}
|
||||
}).catch(err => {
|
||||
this.setState({ errMessage: 'There are sub-departments in this department.' });
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let subtitle = gettext('Are you sure you want to delete {placeholder} ?');
|
||||
subtitle = subtitle.replace('{placeholder}', '<span class="op-target">' + Utils.HTMLescape(this.props.groupName) + '</span>');
|
||||
return (
|
||||
<Modal isOpen={true} toggle={this.props.toggle}>
|
||||
<ModalHeader toggle={this.props.toggle}>{gettext('Delete Department')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<div dangerouslySetInnerHTML={{__html: subtitle}}></div>
|
||||
{ this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
{!this.state.errMessage && <Button color="primary" onClick={this.deleteDepart}>{gettext('Delete')}</Button>}
|
||||
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DeleteDepartDialog.propTypes = propTypes;
|
||||
|
||||
export default DeleteDepartDialog;
|
50
frontend/src/components/dialog/org-delete-member-dialog.js
Normal file
50
frontend/src/components/dialog/org-delete-member-dialog.js
Normal file
@ -0,0 +1,50 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||
import { gettext, orgID } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { Utils } from '../../utils/utils';
|
||||
|
||||
const propTypes = {
|
||||
member: PropTypes.object.isRequired,
|
||||
toggle: PropTypes.func.isRequired,
|
||||
onMemberChanged: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class DeleteMemberDialog extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
deleteMember = () => {
|
||||
const userEmail = this.props.member.email;
|
||||
seafileAPI.orgAdminDeleteDepartGroupUser(orgID, this.props.groupID, userEmail).then((res) => {
|
||||
if (res.data.success) {
|
||||
this.props.onMemberChanged();
|
||||
this.props.toggle();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let subtitle = gettext('Are you sure you want to delete {placeholder} ?');
|
||||
subtitle = subtitle.replace('{placeholder}', '<span class="op-target">' + Utils.HTMLescape(this.props.member.name) + '</span>');
|
||||
return (
|
||||
<Modal isOpen={true} toggle={this.props.toggle}>
|
||||
<ModalHeader toggle={this.props.toggle}>{gettext('Delete Member')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<div dangerouslySetInnerHTML={{__html: subtitle}}></div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="primary" onClick={this.deleteMember}>{gettext('Delete')}</Button>
|
||||
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DeleteMemberDialog.propTypes = propTypes;
|
||||
|
||||
export default DeleteMemberDialog;
|
49
frontend/src/components/dialog/org-delete-repo-dialog.js
Normal file
49
frontend/src/components/dialog/org-delete-repo-dialog.js
Normal file
@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||
import { gettext, orgID } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { Utils } from '../../utils/utils';
|
||||
|
||||
class DeleteRepoDialog extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
deleteRepo = () => {
|
||||
seafileAPI.orgAdminDeleteDepartGroupRepo(orgID, this.props.groupID, this.props.repo.repo_id).then((res) => {
|
||||
if (res.data.success) {
|
||||
this.props.onRepoChanged();
|
||||
this.props.toggle();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let subtitle = gettext('Are you sure you want to delete {placeholder} ?');
|
||||
subtitle = subtitle.replace('{placeholder}', '<span class="op-target">' + Utils.HTMLescape(this.props.repo.name) + '</span>');
|
||||
return (
|
||||
<Modal isOpen={true} toggle={this.props.toggle}>
|
||||
<ModalHeader toggle={this.props.toggle}>{gettext('Delete Library')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<div dangerouslySetInnerHTML={{__html: subtitle}}></div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="primary" onClick={this.deleteRepo}>{gettext('Delete')}</Button>
|
||||
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const propTypes = {
|
||||
repo: PropTypes.object.isRequired,
|
||||
toggle: PropTypes.func.isRequired,
|
||||
onRepoChanged: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
DeleteRepoDialog.propTypes = propTypes;
|
||||
|
||||
export default DeleteRepoDialog;
|
80
frontend/src/components/dialog/org-set-group-quota-dialog.js
Normal file
80
frontend/src/components/dialog/org-set-group-quota-dialog.js
Normal file
@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Input, InputGroupAddon, InputGroup } from 'reactstrap';
|
||||
import { gettext, orgID } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
|
||||
const propTypes = {
|
||||
toggle: PropTypes.func.isRequired,
|
||||
groupID: PropTypes.number.isRequired,
|
||||
onDepartChanged: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class SetGroupQuotaDialog extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
quota: '',
|
||||
errMessage: '',
|
||||
};
|
||||
}
|
||||
|
||||
setGroupQuota = () => {
|
||||
const myReg = /^[1-9]\d*$/im;
|
||||
let quota = this.state.quota;
|
||||
if ((quota.length && myReg.test(quota)) || quota == -2) {
|
||||
this.setState({ errMessage: '' });
|
||||
let newQuota = this.state.quota == -2 ? this.state.quota : this.state.quota * 1000000;
|
||||
seafileAPI.orgAdminSetGroupQuota(orgID, this.props.groupID, newQuota).then((res) => {
|
||||
this.props.toggle();
|
||||
this.props.onDepartChanged();
|
||||
});
|
||||
} else {
|
||||
const err = gettext('Quota is invalid.');
|
||||
this.setState({ errMessage: err });
|
||||
}
|
||||
}
|
||||
|
||||
handleChange = (e) => {
|
||||
const quota = e.target.value.trim();
|
||||
this.setState({ quota: quota });
|
||||
}
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
this.setGroupQuota();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Modal isOpen={true} toggle={this.props.toggle}>
|
||||
<ModalHeader toggle={this.props.toggle}>{gettext('Set Quota')}</ModalHeader>
|
||||
<ModalBody>
|
||||
<InputGroup>
|
||||
<Input
|
||||
onKeyPress={this.handleKeyPress}
|
||||
value={this.state.quota}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<InputGroupAddon addonType="append">{'MB'}</InputGroupAddon>
|
||||
</InputGroup>
|
||||
<p className="tip">
|
||||
<br/><span>{gettext('An integer that is greater than 0 or equal to -2.')}</span><br/>
|
||||
<span>{gettext('Tip: -2 means no limit.')}</span>
|
||||
</p>
|
||||
{ this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="primary" onClick={this.setGroupQuota}>{gettext('Submit')}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SetGroupQuotaDialog.propTypes = propTypes;
|
||||
|
||||
export default SetGroupQuotaDialog;
|
28
frontend/src/css/org-department-item.css
Normal file
28
frontend/src/css/org-department-item.css
Normal file
@ -0,0 +1,28 @@
|
||||
.cur-view-path .operation-item {
|
||||
font-size: 12px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
}
|
||||
.cur-view-container .no-libraty, .cur-view-container .no-member, .cur-view-container .no-group {
|
||||
color: #a4a4a4;
|
||||
text-align: center;
|
||||
margin: 30px 0;
|
||||
}
|
||||
.cur-view-path .sf-heading a {
|
||||
color: #eb8205;
|
||||
}
|
||||
.cur-view-subcontainer {
|
||||
margin: 10px;
|
||||
}
|
||||
.cur-view-subcontainer table {
|
||||
margin: 8px 0 40px;
|
||||
}
|
||||
.org-departments>div{
|
||||
height: 100%;
|
||||
}
|
||||
.org-members .cur-view-content {
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
.cur-view-path button:hover {
|
||||
cursor: pointer;
|
||||
}
|
@ -12,6 +12,9 @@ import OrgGroups from './org-groups';
|
||||
import OrgLibraries from './org-libraries';
|
||||
import OrgInfo from './org-info';
|
||||
import OrgLinks from './org-links';
|
||||
import OrgDepartments from './org-departments';
|
||||
import OrgDepartmentsList from './org-departments-list';
|
||||
import OrgDepartmentItem from './org-department-item';
|
||||
|
||||
import '../../assets/css/fa-solid.css';
|
||||
import '../../assets/css/fa-regular.css';
|
||||
@ -37,6 +40,9 @@ class Org extends React.Component {
|
||||
if (currentTab == 'useradmin') {
|
||||
currentTab = 'users';
|
||||
}
|
||||
if (currentTab > 0) {
|
||||
currentTab = 'departmentadmin';
|
||||
}
|
||||
this.setState({currentTab: currentTab});
|
||||
}
|
||||
|
||||
@ -63,6 +69,11 @@ class Org extends React.Component {
|
||||
render() {
|
||||
|
||||
let { isSidePanelClosed, currentTab, isShowAddOrgUserDialog, isShowAddOrgAdminDialog, isInviteUserDialogOpen } = this.state;
|
||||
let href = window.location.href;
|
||||
let newPath = 'groups/';
|
||||
if (href.indexOf('org/departmentadmin/groups/') > 0) {
|
||||
newPath = href.slice(href.indexOf('groups/'));
|
||||
}
|
||||
return (
|
||||
<div id="main">
|
||||
<SidePanel isSidePanelClosed={isSidePanelClosed} onCloseSidePanel={this.onCloseSidePanel} currentTab={currentTab} tabItemClick={this.tabItemClick} />
|
||||
@ -82,6 +93,10 @@ class Org extends React.Component {
|
||||
<OrgGroups path={siteRoot + 'org/groupadmin'} />
|
||||
<OrgLibraries path={siteRoot + 'org/repoadmin'} />
|
||||
<OrgLinks path={siteRoot + 'org/publinkadmin'} />
|
||||
<OrgDepartments path={siteRoot + 'org/departmentadmin'}>
|
||||
<OrgDepartmentsList path='/' />
|
||||
<OrgDepartmentItem path={newPath} />
|
||||
</OrgDepartments>
|
||||
</Router>
|
||||
</MainPanel>
|
||||
</div>
|
||||
|
372
frontend/src/pages/org-admin/org-department-item.js
Normal file
372
frontend/src/pages/org-admin/org-department-item.js
Normal file
@ -0,0 +1,372 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { Utils } from '../../utils/utils.js';
|
||||
import { serviceURL, mediaUrl, gettext, orgID } from '../../utils/constants';
|
||||
import OrgDepartmentsList from './org-departments-list';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import AddMemberDialog from '../../components/dialog/org-add-member-dialog';
|
||||
import DeleteMemberDialog from '../../components/dialog/org-delete-member-dialog';
|
||||
import AddRepoDialog from '../../components/dialog/org-add-repo-dialog';
|
||||
import DeleteRepoDialog from '../../components/dialog/org-delete-repo-dialog';
|
||||
import RoleEditor from '../../components/select-editor/role-editor';
|
||||
import '../../css/org-department-item.css';
|
||||
|
||||
class OrgDepartmentItem extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
members: [],
|
||||
repos: [],
|
||||
groups: [],
|
||||
ancestorGroups: [],
|
||||
deletedMember: {},
|
||||
deletedRepo: {},
|
||||
showAddMemberDialog: false,
|
||||
showDeleteMemberDialog: false,
|
||||
showAddRepoDialog: false,
|
||||
showDeleteRepoDialog: false,
|
||||
isItemFreezed: false,
|
||||
groupID: null,
|
||||
groupName: '',
|
||||
}
|
||||
}
|
||||
|
||||
listOrgGroupRepo = (groupID) => {
|
||||
seafileAPI.orgAdminListDepartGroupRepos(orgID, groupID).then(res => {
|
||||
this.setState({
|
||||
repos: res.data.libraries
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
listOrgMembers = (groupID) => {
|
||||
seafileAPI.orgAdminListGroupInfo(orgID, groupID, true).then(res => {
|
||||
this.setState({
|
||||
members: res.data.members,
|
||||
groups: res.data.groups,
|
||||
ancestorGroups: res.data.ancestor_groups,
|
||||
groupName: res.data.name,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
showAddMemberDialog = () => {
|
||||
this.setState({ showAddMemberDialog: true });
|
||||
}
|
||||
|
||||
showDeleteMemberDialog = (member) => {
|
||||
this.setState({ showDeleteMemberDialog: true, deletedMember: member });
|
||||
}
|
||||
|
||||
showAddRepoDialog = () => {
|
||||
this.setState({ showAddRepoDialog: true });
|
||||
}
|
||||
|
||||
showDeleteRepoDialog = (repo) => {
|
||||
this.setState({ showDeleteRepoDialog: true, deletedRepo: repo });
|
||||
}
|
||||
|
||||
toggleCancel = () => {
|
||||
this.setState({
|
||||
showAddMemberDialog: false,
|
||||
showDeleteMemberDialog: false,
|
||||
showAddRepoDialog: false,
|
||||
showDeleteRepoDialog: false,
|
||||
});
|
||||
}
|
||||
|
||||
onRepoChanged = () => {
|
||||
this.listOrgGroupRepo(this.state.groupID);
|
||||
}
|
||||
|
||||
onMemberChanged = () => {
|
||||
this.listOrgMembers(this.state.groupID);
|
||||
}
|
||||
|
||||
toggleItemFreezed = (isFreezed) => {
|
||||
this.setState({
|
||||
isItemFreezed: isFreezed
|
||||
});
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const href = window.location.href;
|
||||
let path = href.slice(href.indexOf('groups/'));
|
||||
let groupID = path.slice(7, path.length - 1);
|
||||
this.setState({
|
||||
groupID: groupID
|
||||
});
|
||||
this.listOrgGroupRepo(groupID);
|
||||
this.listOrgMembers(groupID);
|
||||
}
|
||||
|
||||
render() {
|
||||
const members = this.state.members;
|
||||
const repos = this.state.repos;
|
||||
return (
|
||||
<div className="main-panel-center flex-row h-100">
|
||||
<div className="cur-view-container o-auto">
|
||||
<div className="cur-view-path">
|
||||
<h3 className="sf-heading">
|
||||
{ this.state.groupID ?
|
||||
<a href={serviceURL + '/org/departmentadmin/'}>{gettext('Departments')}</a>
|
||||
: <span>{gettext('Departments')}</span>
|
||||
}
|
||||
{
|
||||
this.state.ancestorGroups.map(ancestor => {
|
||||
let newHref = serviceURL + '/org/departmentadmin/groups/' + ancestor.id + '/';
|
||||
return (
|
||||
<span key={ancestor.id}>{' / '}<a href={newHref}>{ancestor.name}</a></span>
|
||||
);
|
||||
})
|
||||
}
|
||||
{ this.state.groupID && <span>{' / '}{this.state.groupName}</span> }
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="cur-view-subcontainer org-groups">
|
||||
<OrgDepartmentsList groupID={this.state.groupID} />
|
||||
</div>
|
||||
|
||||
<div className="cur-view-subcontainer org-members">
|
||||
<div className="cur-view-path">
|
||||
<div className="fleft">
|
||||
<h3 className="sf-heading">{gettext('Members')}</h3>
|
||||
</div>
|
||||
<div className="fright">
|
||||
<button className="btn-white operation-item" onClick={this.showAddMemberDialog}>{gettext('Add Member')}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="cur-view-content">
|
||||
{(members && members.length === 1 && members[0].role === "Owner") ?
|
||||
<p className="no-member">{gettext('No Members')}</p> :
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="5%"></th>
|
||||
<th width="50%">{gettext('Name')}</th>
|
||||
<th width="15%">{gettext('Role')}</th>
|
||||
<th width="30%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{members.map((member, index) => {
|
||||
return (
|
||||
<React.Fragment key={index}>
|
||||
<MemberItem
|
||||
member={member}
|
||||
showAddMemberDialog={this.showAddMemberDialog}
|
||||
showDeleteMemberDialog={this.showDeleteMemberDialog}
|
||||
isItemFreezed={this.state.isItemFreezed}
|
||||
onMemberChanged={this.onMemberChanged}
|
||||
toggleItemFreezed={this.toggleItemFreezed}
|
||||
groupID={this.state.groupID}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="cur-view-subcontainer org-libriries">
|
||||
<div className="cur-view-path">
|
||||
<div className="fleft"><h3 className="sf-heading">{gettext('Libraries')}</h3></div>
|
||||
<div className="fright">
|
||||
<button className="btn-white operation-item" onClick={this.showAddRepoDialog}>{gettext('New Library')}</button>
|
||||
</div>
|
||||
</div>
|
||||
{ repos.length > 0 ?
|
||||
<div className="cur-view-content">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="5%"></th>
|
||||
<th width="50%">{gettext('Name')}</th>
|
||||
<th width="30%">{gettext('Size')}</th>
|
||||
<th width="15%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{repos.map((repo, index) => {
|
||||
return(
|
||||
<React.Fragment key={index}>
|
||||
<RepoItem repo={repo} showDeleteRepoDialog={this.showDeleteRepoDialog}/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
: <p className="no-libraty">{gettext('No libraries')}</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<React.Fragment>
|
||||
{this.state.showDeleteMemberDialog && (
|
||||
<ModalPortal>
|
||||
<DeleteMemberDialog
|
||||
toggle={this.toggleCancel}
|
||||
onMemberChanged={this.onMemberChanged}
|
||||
member={this.state.deletedMember}
|
||||
groupID={this.state.groupID}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
{this.state.showDeleteRepoDialog && (
|
||||
<ModalPortal>
|
||||
<DeleteRepoDialog
|
||||
toggle={this.toggleCancel}
|
||||
onRepoChanged={this.onRepoChanged}
|
||||
repo={this.state.deletedRepo}
|
||||
groupID={this.state.groupID}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
{this.state.showAddMemberDialog && (
|
||||
<ModalPortal>
|
||||
<AddMemberDialog
|
||||
toggle={this.toggleCancel}
|
||||
onMemberChanged={this.onMemberChanged}
|
||||
groupID={this.state.groupID}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
{this.state.showAddRepoDialog && (
|
||||
<ModalPortal>
|
||||
<AddRepoDialog
|
||||
toggle={this.toggleCancel}
|
||||
onRepoChanged={this.onRepoChanged}
|
||||
groupID={this.state.groupID}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
</React.Fragment>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MemberItem extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
highlight: false,
|
||||
showRoleMenu: false,
|
||||
};
|
||||
this.roles = ['Admin', 'Member'];
|
||||
}
|
||||
|
||||
onMouseEnter = () => {
|
||||
if (this.props.isItemFreezed) return;
|
||||
this.setState({ highlight: true });
|
||||
}
|
||||
|
||||
onMouseLeave = () => {
|
||||
if (this.props.isItemFreezed) return;
|
||||
this.setState({ highlight: false });
|
||||
}
|
||||
|
||||
toggleMemberRoleMenu = () => {
|
||||
this.setState({ showRoleMenu: !this.state.showRoleMenu });
|
||||
}
|
||||
|
||||
onChangeUserRole = (role) => {
|
||||
let isAdmin = role === 'Admin' ? true : false;
|
||||
seafileAPI.orgAdminSetDepartGroupUserRole(orgID, this.props.groupID, this.props.member.email, isAdmin).then((res) => {
|
||||
this.props.onMemberChanged();
|
||||
});
|
||||
this.setState({
|
||||
highlight: false,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const member = this.props.member;
|
||||
const highlight = this.state.highlight;
|
||||
let memberLink = serviceURL + '/org/useradmin/info/' + member.email + '/';
|
||||
if (member.role === 'Owner') return null;
|
||||
return (
|
||||
<tr className={highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
<td><img src={member.avatar_url} alt="member-header" width="24"/></td>
|
||||
<td><a href={memberLink}>{member.name}</a></td>
|
||||
<td>
|
||||
<RoleEditor
|
||||
isTextMode={true}
|
||||
isEditIconShow={highlight}
|
||||
currentRole={member.role}
|
||||
roles={this.roles}
|
||||
onRoleChanged={this.onChangeUserRole}
|
||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||
/>
|
||||
</td>
|
||||
{
|
||||
!this.props.isItemFreezed ?
|
||||
<td className="cursor-pointer text-center" onClick={this.props.showDeleteMemberDialog.bind(this, member)}>
|
||||
<span className={`sf2-icon-delete action-icon ${highlight ? '' : 'vh'}`} title="Delete"></span>
|
||||
</td> : <td></td>
|
||||
}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const MemberItemPropTypes = {
|
||||
isItemFreezed: PropTypes.bool.isRequired,
|
||||
onMemberChanged: PropTypes.func.isRequired,
|
||||
showDeleteMemberDialog: PropTypes.func.isRequired,
|
||||
toggleItemFreezed: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
MemberItem.propTypes = MemberItemPropTypes;
|
||||
|
||||
|
||||
class RepoItem extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
highlight: false,
|
||||
};
|
||||
}
|
||||
|
||||
onMouseEnter = () => {
|
||||
this.setState({ highlight: true });
|
||||
}
|
||||
|
||||
onMouseLeave = () => {
|
||||
this.setState({ highlight: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const repo = this.props.repo;
|
||||
const highlight = this.state.highlight;
|
||||
let iconUrl = Utils.getLibIconUrl(repo);
|
||||
return (
|
||||
<tr className={highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
<td><img src={iconUrl} width="24" alt={gettext('icon')}/></td>
|
||||
<td>{repo.name}</td>
|
||||
<td>{Utils.bytesToSize(repo.size)}{' '}</td>
|
||||
<td className="cursor-pointer text-center" onClick={this.props.showDeleteRepoDialog.bind(this, repo)}>
|
||||
<span className={`sf2-icon-delete action-icon ${highlight ? '' : 'vh'}`} title="Delete"></span>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const RepoItemPropTypes = {
|
||||
repo: PropTypes.object.isRequired,
|
||||
showDeleteRepoDialog: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
RepoItem.propTypes = RepoItemPropTypes;
|
||||
|
||||
export default OrgDepartmentItem;
|
199
frontend/src/pages/org-admin/org-departments-list.js
Normal file
199
frontend/src/pages/org-admin/org-departments-list.js
Normal file
@ -0,0 +1,199 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import moment from 'moment';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { siteRoot, serviceURL, gettext, orgID, lang } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils.js';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import AddDepartDialog from '../../components/dialog/org-add-department-dialog';
|
||||
import DeleteDepartDialog from '../../components/dialog/org-delete-department-dialog';
|
||||
import SetGroupQuotaDialog from '../../components/dialog/org-set-group-quota-dialog';
|
||||
import '../../css/org-department-item.css';
|
||||
|
||||
moment.locale(lang);
|
||||
|
||||
class OrgDepartmentsList extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
groups: null,
|
||||
groupID: -1,
|
||||
groupName: '',
|
||||
showAddDepartDialog: false,
|
||||
showDeleteDepartDialog: false,
|
||||
showSetGroupQuotaDialog: false,
|
||||
};
|
||||
}
|
||||
|
||||
listDepartGroups = () => {
|
||||
if (this.props.groupID) {
|
||||
seafileAPI.orgAdminListGroupInfo(orgID, this.props.groupID, true).then(res => {
|
||||
this.setState({
|
||||
groups: res.data.groups
|
||||
});
|
||||
});
|
||||
} else {
|
||||
seafileAPI.orgAdminListDepartGroups(orgID).then(res => {
|
||||
this.setState({
|
||||
groups: res.data.data
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
showAddDepartDialog = () => {
|
||||
this.setState({ showAddDepartDialog: true });
|
||||
}
|
||||
|
||||
showDeleteDepartDialog = (group) => {
|
||||
this.setState({ showDeleteDepartDialog: true, groupID: group.id, groupName: group.name });
|
||||
}
|
||||
|
||||
showSetGroupQuotaDialog = (groupID) => {
|
||||
this.setState({ showSetGroupQuotaDialog: true, groupID: groupID });
|
||||
}
|
||||
|
||||
toggleCancel = () => {
|
||||
this.setState({
|
||||
showAddDepartDialog: false,
|
||||
showDeleteDepartDialog: false,
|
||||
showSetGroupQuotaDialog: false,
|
||||
});
|
||||
}
|
||||
|
||||
onDepartChanged = () => {
|
||||
this.listDepartGroups();
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.listDepartGroups();
|
||||
}
|
||||
|
||||
render() {
|
||||
const groups = this.state.groups;
|
||||
let isSub = this.props.groupID ? true : false;
|
||||
let header = isSub ? gettext('Sub-departments') : gettext('Departments');
|
||||
let headerButton = isSub ? gettext('New Sub-departments') : gettext('New Departments');
|
||||
let noGroup = isSub ? gettext('No sub-departments') : gettext('No departments');
|
||||
return (
|
||||
<div className="main-panel-center flex-row h-100">
|
||||
<div className="cur-view-container o-auto">
|
||||
<div className="cur-view-path">
|
||||
<div className="fleft"><h3 className="sf-heading">{header}</h3></div>
|
||||
<div className="fright">
|
||||
<button className="btn-white operation-item" onClick={this.showAddDepartDialog}>{headerButton}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="cur-view-content">
|
||||
{groups && groups.length > 0 ?
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="40%">{gettext('Name')}</th>
|
||||
<th width="25%">{gettext('Created At')}</th>
|
||||
<th width="20%">{gettext('Quota')}</th>
|
||||
<th width="15%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{groups.map((group, index) => {
|
||||
return(
|
||||
<React.Fragment key={group.id}>
|
||||
<GroupItem
|
||||
group={group}
|
||||
showDeleteDepartDialog={this.showDeleteDepartDialog}
|
||||
showSetGroupQuotaDialog={this.showSetGroupQuotaDialog}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
:
|
||||
<p className="no-group">{noGroup}</p>
|
||||
}
|
||||
</div>
|
||||
<React.Fragment>
|
||||
{this.state.showAddDepartDialog && (
|
||||
<ModalPortal>
|
||||
<AddDepartDialog
|
||||
toggle={this.toggleCancel}
|
||||
onDepartChanged={this.onDepartChanged}
|
||||
parentGroupID={this.props.groupID}
|
||||
groupID={this.state.groupID}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
{this.state.showDeleteDepartDialog && (
|
||||
<ModalPortal>
|
||||
<DeleteDepartDialog
|
||||
toggle={this.toggleCancel}
|
||||
groupID={this.state.groupID}
|
||||
groupName={this.state.groupName}
|
||||
onDepartChanged={this.onDepartChanged}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
{this.state.showSetGroupQuotaDialog && (
|
||||
<ModalPortal>
|
||||
<SetGroupQuotaDialog
|
||||
toggle={this.toggleCancel}
|
||||
groupID={this.state.groupID}
|
||||
onDepartChanged={this.onDepartChanged}
|
||||
/>
|
||||
</ModalPortal>
|
||||
)}
|
||||
</React.Fragment>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GroupItem extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
highlight: false,
|
||||
};
|
||||
}
|
||||
|
||||
onMouseEnter = () => {
|
||||
this.setState({ highlight: true });
|
||||
}
|
||||
|
||||
onMouseLeave = () => {
|
||||
this.setState({ highlight: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const group = this.props.group;
|
||||
const highlight = this.state.highlight;
|
||||
const newHref = serviceURL + '/org/departmentadmin/groups/' + group.id + '/';
|
||||
return (
|
||||
<tr className={highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
<td><a href={newHref} onClick={this.changeOrgGroup}>{group.name}</a></td>
|
||||
<td>{moment(group.created_at).fromNow()}</td>
|
||||
<td onClick={this.props.showSetGroupQuotaDialog.bind(this, group.id)}>
|
||||
{Utils.bytesToSize(group.quota)}{' '}
|
||||
<span title="Edit Quota" className={`fa fa-pencil-alt attr-action-icon ${highlight ? '' : 'vh'}`}></span>
|
||||
</td>
|
||||
<td className="cursor-pointer text-center" onClick={this.props.showDeleteDepartDialog.bind(this, group)}>
|
||||
<span className={`sf2-icon-delete action-icon ${highlight ? '' : 'vh'}`} title="Delete"></span>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const GroupItemPropTypes = {
|
||||
group: PropTypes.object.isRequired,
|
||||
showSetGroupQuotaDialog: PropTypes.func.isRequired,
|
||||
showDeleteDepartDialog: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
GroupItem.propTypes = GroupItemPropTypes;
|
||||
|
||||
export default OrgDepartmentsList;
|
20
frontend/src/pages/org-admin/org-departments.js
Normal file
20
frontend/src/pages/org-admin/org-departments.js
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import '../../css/org-department-item.css';
|
||||
|
||||
class OrgDepartments extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="h-100 org-departments">
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default OrgDepartments;
|
@ -172,7 +172,7 @@ class GroupItem extends React.Component {
|
||||
renderGroupHref = (group) => {
|
||||
let groupInfoHref;
|
||||
if (group.creatorName == 'system admin') {
|
||||
groupInfoHref = siteRoot + 'org/admin/#address-book/groups/' + group.id + '/';
|
||||
groupInfoHref = siteRoot + 'org/departmentadmin/groups/' + group.id + '/';
|
||||
} else {
|
||||
groupInfoHref = siteRoot + 'org/groupadmin/' + group.id + '/';
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ class OrgLinks extends React.Component {
|
||||
<tr>
|
||||
<th width="50%">{gettext('Name')}</th>
|
||||
<th width="15%">{gettext('Owner')}</th>
|
||||
<th width="15%">{gettext('Create At')}</th>
|
||||
<th width="15%">{gettext('Created At')}</th>
|
||||
<th width="10%">{gettext('Count')}</th>
|
||||
<th width="10%"></th>
|
||||
</tr>
|
||||
@ -160,8 +160,8 @@ class RepoItem extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { index, link, deleteOrgLink } = this.props;
|
||||
const href = siteRoot + 'org/useradmin/info/' + link.owner_email + '/';
|
||||
const { link, deleteOrgLink } = this.props;
|
||||
const href = siteRoot + 'org/useradmin/info/' + encodeURIComponent(link.owner_email) + '/';
|
||||
return (
|
||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave} >
|
||||
<td>{link.name}</td>
|
||||
|
@ -57,10 +57,10 @@ class SidePanel extends React.Component {
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a href="/org/admin/#address-book/" className="nav-link ellipsis">
|
||||
<Link className={`nav-link ellipsis ${this.getActiveClass('departmentadmin')}`} to={siteRoot + 'org/departmentadmin/'} onClick={() => this.tabItemClick('departmentadmin')} >
|
||||
<span className="sf2-icon-organization"></span>
|
||||
<span className="nav-text">{gettext('Departments')}</span>
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<Link className={`nav-link ellipsis ${this.getActiveClass('publinkadmin')}`} to={siteRoot + 'org/publinkadmin/'} onClick={() => this.tabItemClick('publinkadmin')} >
|
||||
|
Loading…
Reference in New Issue
Block a user