mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-21 11:27:18 +00:00
Sys department (#5471)
* [system admin - department] redesigned it (split it into 3 tabs/pages) * [system admin - department] fixup & improvements; code cleanup
This commit is contained in:
@@ -8,7 +8,7 @@ const propTypes = {
|
|||||||
groupID: PropTypes.string,
|
groupID: PropTypes.string,
|
||||||
parentGroupID: PropTypes.string,
|
parentGroupID: PropTypes.string,
|
||||||
toggle: PropTypes.func.isRequired,
|
toggle: PropTypes.func.isRequired,
|
||||||
onDepartChanged: PropTypes.func.isRequired,
|
onAddNewDepartment: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddDepartDialog extends React.Component {
|
class AddDepartDialog extends React.Component {
|
||||||
@@ -30,7 +30,7 @@ class AddDepartDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
seafileAPI.sysAdminAddNewDepartment(parentGroup, this.state.departName.trim()).then((res) => {
|
seafileAPI.sysAdminAddNewDepartment(parentGroup, this.state.departName.trim()).then((res) => {
|
||||||
this.props.toggle();
|
this.props.toggle();
|
||||||
this.props.onDepartChanged();
|
this.props.onAddNewDepartment(res.data);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
let errorMsg = gettext(error.response.data.error_msg);
|
let errorMsg = gettext(error.response.data.error_msg);
|
||||||
this.setState({ errMessage: errorMsg });
|
this.setState({ errMessage: errorMsg });
|
||||||
@@ -80,9 +80,10 @@ class AddDepartDialog extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Form>
|
</Form>
|
||||||
{ this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
{this.state.errMessage && <p className="error">{this.state.errMessage}</p>}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
||||||
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@@ -10,7 +10,7 @@ import UserSelect from '../../user-select.js';
|
|||||||
const propTypes = {
|
const propTypes = {
|
||||||
toggle: PropTypes.func.isRequired,
|
toggle: PropTypes.func.isRequired,
|
||||||
groupID: PropTypes.string.isRequired,
|
groupID: PropTypes.string.isRequired,
|
||||||
onMemberChanged: PropTypes.func.isRequired
|
onAddNewMembers: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddMemberDialog extends React.Component {
|
class AddMemberDialog extends React.Component {
|
||||||
@@ -38,7 +38,7 @@ class AddMemberDialog extends React.Component {
|
|||||||
this.setState({ errMessage: res.data.failed[0].error_msg });
|
this.setState({ errMessage: res.data.failed[0].error_msg });
|
||||||
}
|
}
|
||||||
if (res.data.success.length > 0) {
|
if (res.data.success.length > 0) {
|
||||||
this.props.onMemberChanged();
|
this.props.onAddNewMembers(res.data.success);
|
||||||
this.props.toggle();
|
this.props.toggle();
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
@@ -62,8 +62,8 @@ class AddMemberDialog extends React.Component {
|
|||||||
{ this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
{ this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
|
||||||
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
||||||
|
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
@@ -8,7 +8,7 @@ import { Utils } from '../../../utils/utils';
|
|||||||
const propTypes = {
|
const propTypes = {
|
||||||
toggle: PropTypes.func.isRequired,
|
toggle: PropTypes.func.isRequired,
|
||||||
groupID: PropTypes.string.isRequired,
|
groupID: PropTypes.string.isRequired,
|
||||||
onRepoChanged: PropTypes.func.isRequired,
|
onAddNewRepo: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
class AddRepoDialog extends React.Component {
|
class AddRepoDialog extends React.Component {
|
||||||
@@ -26,7 +26,7 @@ class AddRepoDialog extends React.Component {
|
|||||||
if (isValid) {
|
if (isValid) {
|
||||||
seafileAPI.sysAdminAddRepoInDepartment(this.props.groupID, this.state.repoName.trim()).then((res) => {
|
seafileAPI.sysAdminAddRepoInDepartment(this.props.groupID, this.state.repoName.trim()).then((res) => {
|
||||||
this.props.toggle();
|
this.props.toggle();
|
||||||
this.props.onRepoChanged();
|
this.props.onAddNewRepo(res.data);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
let errorMsg = Utils.getErrorMsg(error);
|
let errorMsg = Utils.getErrorMsg(error);
|
||||||
this.setState({ errMessage: errorMsg });
|
this.setState({ errMessage: errorMsg });
|
||||||
@@ -78,6 +78,7 @@ class AddRepoDialog extends React.Component {
|
|||||||
{this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
{this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
||||||
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@@ -7,13 +7,12 @@ import { Utils } from '../../../utils/utils';
|
|||||||
import toaster from '../../../components/toast';
|
import toaster from '../../../components/toast';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
groupName: PropTypes.string,
|
group: PropTypes.object.isRequired,
|
||||||
groupID: PropTypes.number.isRequired,
|
|
||||||
toggle: PropTypes.func.isRequired,
|
toggle: PropTypes.func.isRequired,
|
||||||
onDepartChanged: PropTypes.func.isRequired
|
onDeleteDepartment: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeleteDepartDialog extends React.Component {
|
class DeleteDepartmentDialog extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -21,8 +20,9 @@ class DeleteDepartDialog extends React.Component {
|
|||||||
|
|
||||||
deleteDepart = () => {
|
deleteDepart = () => {
|
||||||
this.props.toggle();
|
this.props.toggle();
|
||||||
seafileAPI.sysAdminDeleteDepartment(this.props.groupID).then((res) => {
|
const { group } = this.props;
|
||||||
this.props.onDepartChanged();
|
seafileAPI.sysAdminDeleteDepartment(group.id).then((res) => {
|
||||||
|
this.props.onDeleteDepartment(group.id);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
toaster.danger(errMessage);
|
toaster.danger(errMessage);
|
||||||
@@ -30,8 +30,10 @@ class DeleteDepartDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { group } = this.props;
|
||||||
|
|
||||||
let tipMessage = gettext('Are you sure you want to delete {placeholder} ?');
|
let tipMessage = gettext('Are you sure you want to delete {placeholder} ?');
|
||||||
tipMessage = tipMessage.replace('{placeholder}', '<span class="op-target">' + Utils.HTMLescape(this.props.groupName) + '</span>');
|
tipMessage = tipMessage.replace('{placeholder}', '<span class="op-target">' + Utils.HTMLescape(group.name) + '</span>');
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={true} toggle={this.props.toggle}>
|
<Modal isOpen={true} toggle={this.props.toggle}>
|
||||||
<ModalHeader toggle={this.props.toggle}>{gettext('Delete Department')}</ModalHeader>
|
<ModalHeader toggle={this.props.toggle}>{gettext('Delete Department')}</ModalHeader>
|
||||||
@@ -47,6 +49,6 @@ class DeleteDepartDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DeleteDepartDialog.propTypes = propTypes;
|
DeleteDepartmentDialog.propTypes = propTypes;
|
||||||
|
|
||||||
export default DeleteDepartDialog;
|
export default DeleteDepartmentDialog;
|
||||||
|
@@ -42,8 +42,8 @@ class DeleteMemberDialog extends React.Component {
|
|||||||
<div dangerouslySetInnerHTML={{__html: tipMessage}}></div>
|
<div dangerouslySetInnerHTML={{__html: tipMessage}}></div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="primary" onClick={this.deleteMember}>{gettext('Delete')}</Button>
|
|
||||||
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
||||||
|
<Button color="primary" onClick={this.deleteMember}>{gettext('Delete')}</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
@@ -25,8 +25,9 @@ class DeleteRepoDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { repo } = this.props;
|
||||||
let tipMessage = gettext('Are you sure you want to delete {placeholder} ?');
|
let tipMessage = gettext('Are you sure you want to delete {placeholder} ?');
|
||||||
tipMessage = tipMessage.replace('{placeholder}', '<span class="op-target">' + Utils.HTMLescape(this.props.repo.name) + '</span>');
|
tipMessage = tipMessage.replace('{placeholder}', '<span class="op-target">' + Utils.HTMLescape(repo.name || repo.repo_name) + '</span>');
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={true} toggle={this.props.toggle}>
|
<Modal isOpen={true} toggle={this.props.toggle}>
|
||||||
<ModalHeader toggle={this.props.toggle}>{gettext('Delete Library')}</ModalHeader>
|
<ModalHeader toggle={this.props.toggle}>{gettext('Delete Library')}</ModalHeader>
|
||||||
@@ -34,8 +35,8 @@ class DeleteRepoDialog extends React.Component {
|
|||||||
<div dangerouslySetInnerHTML={{__html: tipMessage}}></div>
|
<div dangerouslySetInnerHTML={{__html: tipMessage}}></div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button color="primary" onClick={this.deleteRepo}>{gettext('Delete')}</Button>
|
|
||||||
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
||||||
|
<Button color="primary" onClick={this.deleteRepo}>{gettext('Delete')}</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
@@ -92,6 +92,7 @@ class RenameDepartmentDialog extends React.Component {
|
|||||||
{this.state.errMessage && <p className="error">{this.state.errMessage}</p>}
|
{this.state.errMessage && <p className="error">{this.state.errMessage}</p>}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
||||||
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
<Button color="primary" onClick={this.handleSubmit}>{gettext('Submit')}</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@@ -9,7 +9,7 @@ import toaster from '../../toast';
|
|||||||
const propTypes = {
|
const propTypes = {
|
||||||
toggle: PropTypes.func.isRequired,
|
toggle: PropTypes.func.isRequired,
|
||||||
groupID: PropTypes.number.isRequired,
|
groupID: PropTypes.number.isRequired,
|
||||||
onDepartChanged: PropTypes.func.isRequired,
|
onSetQuota: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
class SetGroupQuotaDialog extends React.Component {
|
class SetGroupQuotaDialog extends React.Component {
|
||||||
@@ -30,7 +30,7 @@ class SetGroupQuotaDialog extends React.Component {
|
|||||||
let newQuota = this.state.quota == -2 ? this.state.quota : this.state.quota * 1000000;
|
let newQuota = this.state.quota == -2 ? this.state.quota : this.state.quota * 1000000;
|
||||||
seafileAPI.sysAdminUpdateDepartmentQuota(this.props.groupID, newQuota).then((res) => {
|
seafileAPI.sysAdminUpdateDepartmentQuota(this.props.groupID, newQuota).then((res) => {
|
||||||
this.props.toggle();
|
this.props.toggle();
|
||||||
this.props.onDepartChanged();
|
this.props.onSetQuota(res.data);
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
toaster.danger(errMessage);
|
toaster.danger(errMessage);
|
||||||
@@ -71,9 +71,10 @@ class SetGroupQuotaDialog extends React.Component {
|
|||||||
<br/><span>{gettext('An integer that is greater than 0 or equal to -2.')}</span><br/>
|
<br/><span>{gettext('An integer that is greater than 0 or equal to -2.')}</span><br/>
|
||||||
<span>{gettext('Tip: -2 means no limit.')}</span>
|
<span>{gettext('Tip: -2 means no limit.')}</span>
|
||||||
</p>
|
</p>
|
||||||
{ this.state.errMessage && <p className="error">{this.state.errMessage}</p> }
|
{this.state.errMessage && <p className="error">{this.state.errMessage}</p>}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
||||||
<Button color="primary" onClick={this.setGroupQuota}>{gettext('Submit')}</Button>
|
<Button color="primary" onClick={this.setGroupQuota}>{gettext('Submit')}</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@@ -1,472 +0,0 @@
|
|||||||
import React, { Fragment } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import moment from 'moment';
|
|
||||||
import { Link } from '@gatsbyjs/reach-router';
|
|
||||||
import Paginator from '../../../components/paginator';
|
|
||||||
import { seafileAPI } from '../../../utils/seafile-api';
|
|
||||||
import { Utils } from '../../../utils/utils.js';
|
|
||||||
import toaster from '../../../components/toast';
|
|
||||||
import MainPanelTopbar from '../main-panel-topbar';
|
|
||||||
import ModalPortal from '../../../components/modal-portal';
|
|
||||||
import AddDepartDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-department-dialog';
|
|
||||||
import RenameDepartmentDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-rename-department-dialog';
|
|
||||||
import AddMemberDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-member-dialog';
|
|
||||||
import DeleteMemberDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-delete-member-dialog';
|
|
||||||
import AddRepoDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-repo-dialog';
|
|
||||||
import DeleteRepoDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-delete-repo-dialog';
|
|
||||||
import DeleteDepartDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-delete-department-dialog';
|
|
||||||
import SetGroupQuotaDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-set-group-quota-dialog';
|
|
||||||
import { siteRoot, gettext, lang } from '../../../utils/constants';
|
|
||||||
import GroupItem from './group-item';
|
|
||||||
import MemberItem from './member-item';
|
|
||||||
import RepoItem from './repo-item';
|
|
||||||
import '../../../css/org-department-item.css';
|
|
||||||
|
|
||||||
moment.locale(lang);
|
|
||||||
|
|
||||||
const DepartmentDetailPropTypes = {
|
|
||||||
groupID: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
class DepartmentDetail extends React.Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
orgID: '',
|
|
||||||
groupName: '',
|
|
||||||
isItemFreezed: false,
|
|
||||||
ancestorGroups: [],
|
|
||||||
members: [],
|
|
||||||
membersErrorMsg: '',
|
|
||||||
membersPageInfo: {
|
|
||||||
},
|
|
||||||
membersPage: 1,
|
|
||||||
membersPerPage: 25,
|
|
||||||
deletedMember: {},
|
|
||||||
isShowAddMemberDialog: false,
|
|
||||||
showDeleteMemberDialog: false,
|
|
||||||
repos: [],
|
|
||||||
deletedRepo: {},
|
|
||||||
isShowRenameDepartmentDialog: false,
|
|
||||||
isShowAddRepoDialog: false,
|
|
||||||
showDeleteRepoDialog: false,
|
|
||||||
groups: [],
|
|
||||||
subGroupID: '',
|
|
||||||
subGroupName: '',
|
|
||||||
isShowAddDepartDialog: false,
|
|
||||||
showDeleteDepartDialog: false,
|
|
||||||
showSetGroupQuotaDialog: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const groupID = this.props.groupID;
|
|
||||||
this.listGroupRepo(groupID);
|
|
||||||
this.getDepartmentInfo(groupID);
|
|
||||||
this.listMembers(groupID, this.state.membersPage, this.state.membersPerPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
|
||||||
if (this.props.groupID !== nextProps.groupID) {
|
|
||||||
this.listGroupRepo(nextProps.groupID);
|
|
||||||
this.getDepartmentInfo(nextProps.groupID);
|
|
||||||
this.listMembers(nextProps.groupID, this.state.membersPage, this.state.membersPerPage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
listGroupRepo = (groupID) => {
|
|
||||||
seafileAPI.sysAdminListGroupRepos(groupID).then(res => {
|
|
||||||
this.setState({ repos: res.data.libraries });
|
|
||||||
}).catch(error => {
|
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
|
||||||
toaster.danger(errMessage);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getDepartmentInfo = (groupID) => {
|
|
||||||
seafileAPI.sysAdminGetDepartmentInfo(groupID, true).then(res => {
|
|
||||||
this.setState({
|
|
||||||
groups: res.data.groups,
|
|
||||||
ancestorGroups: res.data.ancestor_groups,
|
|
||||||
groupName: res.data.name,
|
|
||||||
orgID: res.data.org_id,
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
|
||||||
toaster.danger(errMessage);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
listMembers = (groupID, page, perPage) => {
|
|
||||||
seafileAPI.sysAdminListGroupMembers(groupID, page, perPage).then((res) => {
|
|
||||||
this.setState({
|
|
||||||
members: res.data.members,
|
|
||||||
membersPageInfo: res.data.page_info
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
|
||||||
this.setState({membersErrorMsg: errMessage});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getPreviousPageList = () => {
|
|
||||||
this.listMembers(this.props.groupID, this.state.membersPageInfo.current_page - 1, this.state.membersPerPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
getNextPageList = () => {
|
|
||||||
this.listMembers(this.props.groupID, this.state.membersPageInfo.current_page + 1, this.state.membersPerPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
resetPerPage = (perPage) => {
|
|
||||||
this.setState({
|
|
||||||
membersPerPage: perPage
|
|
||||||
}, () => {
|
|
||||||
this.listMembers(this.props.groupID, 1, perPage);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
listSubDepartGroups = (groupID) => {
|
|
||||||
seafileAPI.sysAdminGetDepartmentInfo(groupID, true).then(res => {
|
|
||||||
this.setState({ groups: res.data.groups });
|
|
||||||
}).catch(error => {
|
|
||||||
let errMessage = Utils.getErrorMsg(error);
|
|
||||||
toaster.danger(errMessage);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleCancel = () => {
|
|
||||||
this.setState({
|
|
||||||
showDeleteMemberDialog: false,
|
|
||||||
showDeleteRepoDialog: false,
|
|
||||||
showDeleteDepartDialog: false,
|
|
||||||
showSetGroupQuotaDialog: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onSubDepartChanged = () => {
|
|
||||||
this.listSubDepartGroups(this.props.groupID);
|
|
||||||
}
|
|
||||||
|
|
||||||
onDepartmentNameChanged = (dept) => {
|
|
||||||
this.setState({
|
|
||||||
groupName: dept.name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onSubDepartmentNameChanged = (dept) => {
|
|
||||||
this.setState({
|
|
||||||
groups: this.state.groups.map(item => {
|
|
||||||
if (item.id == dept.id) {
|
|
||||||
item.name = dept.name;
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onRepoChanged = () => {
|
|
||||||
this.listGroupRepo(this.props.groupID);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMemberChanged = () => {
|
|
||||||
this.listMembers(this.props.groupID, this.state.membersPageInfo.current_page, this.state.membersPerPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleItemFreezed = (isFreezed) => {
|
|
||||||
this.setState({ isItemFreezed: isFreezed });
|
|
||||||
}
|
|
||||||
|
|
||||||
onFreezedItem = () => {
|
|
||||||
this.setState({isItemFreezed: true});
|
|
||||||
}
|
|
||||||
|
|
||||||
onUnfreezedItem = () => {
|
|
||||||
this.setState({isItemFreezed: false});
|
|
||||||
}
|
|
||||||
|
|
||||||
showDeleteMemberDialog = (member) => {
|
|
||||||
this.setState({ showDeleteMemberDialog: true, deletedMember: member });
|
|
||||||
}
|
|
||||||
|
|
||||||
showDeleteRepoDialog = (repo) => {
|
|
||||||
this.setState({ showDeleteRepoDialog: true, deletedRepo: repo });
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleRenameDepartmentDialog = () => {
|
|
||||||
this.setState({ isShowRenameDepartmentDialog: !this.state.isShowRenameDepartmentDialog });
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleAddRepoDialog = () => {
|
|
||||||
this.setState({ isShowAddRepoDialog: !this.state.isShowAddRepoDialog });
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleAddMemberDialog = () => {
|
|
||||||
this.setState({ isShowAddMemberDialog: !this.state.isShowAddMemberDialog });
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleAddDepartDialog = () => {
|
|
||||||
this.setState({ isShowAddDepartDialog: !this.state.isShowAddDepartDialog});
|
|
||||||
}
|
|
||||||
|
|
||||||
showDeleteDepartDialog = (subGroup) => {
|
|
||||||
this.setState({
|
|
||||||
showDeleteDepartDialog: true,
|
|
||||||
subGroupID: subGroup.id,
|
|
||||||
subGroupName: subGroup.name
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
showSetGroupQuotaDialog = (subGroupID) => {
|
|
||||||
this.setState({
|
|
||||||
showSetGroupQuotaDialog: true,
|
|
||||||
subGroupID: subGroupID
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { members, membersErrorMsg, repos, groups, groupName } = this.state;
|
|
||||||
const groupID = this.props.groupID;
|
|
||||||
const topBtn = 'btn btn-secondary operation-item';
|
|
||||||
const topbarChildren = (
|
|
||||||
<Fragment>
|
|
||||||
{groupID &&
|
|
||||||
<Fragment>
|
|
||||||
<button className={topBtn} title={gettext('Rename Department')} onClick={this.toggleRenameDepartmentDialog}>{gettext('Rename Department')}</button>
|
|
||||||
<button className={topBtn} title={gettext('New Sub-department')} onClick={this.toggleAddDepartDialog}>{gettext('New Sub-department')}</button>
|
|
||||||
<button className={topBtn} title={gettext('Add Member')} onClick={this.toggleAddMemberDialog}>{gettext('Add Member')}</button>
|
|
||||||
<button className={topBtn} onClick={this.toggleAddRepoDialog} title={gettext('New Library')}>{gettext('New Library')}</button>
|
|
||||||
</Fragment>
|
|
||||||
}
|
|
||||||
{this.state.isShowRenameDepartmentDialog && (
|
|
||||||
<ModalPortal>
|
|
||||||
<RenameDepartmentDialog
|
|
||||||
groupID={groupID}
|
|
||||||
name={groupName}
|
|
||||||
toggle={this.toggleRenameDepartmentDialog}
|
|
||||||
onDepartmentNameChanged={this.onDepartmentNameChanged}
|
|
||||||
/>
|
|
||||||
</ModalPortal>
|
|
||||||
)}
|
|
||||||
{this.state.isShowAddMemberDialog && (
|
|
||||||
<ModalPortal>
|
|
||||||
<AddMemberDialog
|
|
||||||
toggle={this.toggleAddMemberDialog}
|
|
||||||
onMemberChanged={this.onMemberChanged}
|
|
||||||
groupID={groupID}
|
|
||||||
/>
|
|
||||||
</ModalPortal>
|
|
||||||
)}
|
|
||||||
{this.state.isShowAddRepoDialog && (
|
|
||||||
<ModalPortal>
|
|
||||||
<AddRepoDialog
|
|
||||||
toggle={this.toggleAddRepoDialog}
|
|
||||||
onRepoChanged={this.onRepoChanged}
|
|
||||||
groupID={groupID}
|
|
||||||
/>
|
|
||||||
</ModalPortal>
|
|
||||||
)}
|
|
||||||
{this.state.isShowAddDepartDialog && (
|
|
||||||
<ModalPortal>
|
|
||||||
<AddDepartDialog
|
|
||||||
onDepartChanged={this.onSubDepartChanged}
|
|
||||||
parentGroupID={groupID}
|
|
||||||
toggle={this.toggleAddDepartDialog}
|
|
||||||
/>
|
|
||||||
</ModalPortal>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
<MainPanelTopbar {...this.props}>
|
|
||||||
{topbarChildren}
|
|
||||||
</MainPanelTopbar>
|
|
||||||
<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">
|
|
||||||
{groupID ?
|
|
||||||
<Link to={siteRoot + 'sys/departments/'}>{gettext('Departments')}</Link>
|
|
||||||
: <span>{gettext('Departments')}</span>
|
|
||||||
}
|
|
||||||
{this.state.ancestorGroups.map(ancestor => {
|
|
||||||
let newHref = siteRoot + 'sys/departments/' + ancestor.id + '/';
|
|
||||||
return <span key={ancestor.id}>{' / '}<Link to={newHref}>{ancestor.name}</Link></span>;
|
|
||||||
})}
|
|
||||||
{groupID && <span>{' / '}{this.state.groupName}</span>}
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="cur-view-subcontainer org-groups">
|
|
||||||
<div className="cur-view-path">
|
|
||||||
<div className="fleft"><h3 className="sf-heading">{gettext('Sub-departments')}</h3></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(
|
|
||||||
<Fragment key={group.id}>
|
|
||||||
<GroupItem
|
|
||||||
orgID={this.state.orgID}
|
|
||||||
isItemFreezed={this.state.isItemFreezed}
|
|
||||||
onFreezedItem={this.onFreezedItem}
|
|
||||||
onUnfreezedItem={this.onUnfreezedItem}
|
|
||||||
onDepartmentNameChanged={this.onSubDepartmentNameChanged}
|
|
||||||
group={group}
|
|
||||||
showDeleteDepartDialog={this.showDeleteDepartDialog}
|
|
||||||
showSetGroupQuotaDialog={this.showSetGroupQuotaDialog}
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
: <p className="no-group">{gettext('No sub-departments')}</p>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</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>
|
|
||||||
<div className="cur-view-content">
|
|
||||||
{membersErrorMsg ? <p className="error text-center">{membersErrorMsg}</p> :
|
|
||||||
members.length == 0 ?
|
|
||||||
<p className="no-member">{gettext('No members')}</p> :
|
|
||||||
<Fragment>
|
|
||||||
<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 (
|
|
||||||
<Fragment key={index}>
|
|
||||||
<MemberItem
|
|
||||||
orgID={this.state.orgID}
|
|
||||||
member={member}
|
|
||||||
showDeleteMemberDialog={this.showDeleteMemberDialog}
|
|
||||||
isItemFreezed={this.state.isItemFreezed}
|
|
||||||
onMemberChanged={this.onMemberChanged}
|
|
||||||
toggleItemFreezed={this.toggleItemFreezed}
|
|
||||||
groupID={groupID}
|
|
||||||
/>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{this.state.membersPageInfo &&
|
|
||||||
<Paginator
|
|
||||||
gotoPreviousPage={this.getPreviousPageList}
|
|
||||||
gotoNextPage={this.getNextPageList}
|
|
||||||
currentPage={this.state.membersPageInfo.current_page}
|
|
||||||
hasNextPage={this.state.membersPageInfo.has_next_page}
|
|
||||||
curPerPage={this.state.membersPerPage}
|
|
||||||
resetPerPage={this.resetPerPage}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</Fragment>
|
|
||||||
}
|
|
||||||
</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>
|
|
||||||
{ 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(
|
|
||||||
<Fragment key={index}>
|
|
||||||
<RepoItem repo={repo} orgID={this.state.orgID} showDeleteRepoDialog={this.showDeleteRepoDialog}/>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
: <p className="no-libraty">{gettext('No libraries')}</p>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{this.state.showDeleteMemberDialog && (
|
|
||||||
<ModalPortal>
|
|
||||||
<DeleteMemberDialog
|
|
||||||
toggle={this.toggleCancel}
|
|
||||||
onMemberChanged={this.onMemberChanged}
|
|
||||||
member={this.state.deletedMember}
|
|
||||||
groupID={groupID}
|
|
||||||
/>
|
|
||||||
</ModalPortal>
|
|
||||||
)}
|
|
||||||
{this.state.showDeleteRepoDialog && (
|
|
||||||
<ModalPortal>
|
|
||||||
<DeleteRepoDialog
|
|
||||||
toggle={this.toggleCancel}
|
|
||||||
onRepoChanged={this.onRepoChanged}
|
|
||||||
repo={this.state.deletedRepo}
|
|
||||||
groupID={groupID}
|
|
||||||
/>
|
|
||||||
</ModalPortal>
|
|
||||||
)}
|
|
||||||
{this.state.showDeleteDepartDialog && (
|
|
||||||
<ModalPortal>
|
|
||||||
<DeleteDepartDialog
|
|
||||||
toggle={this.toggleCancel}
|
|
||||||
groupID={this.state.subGroupID}
|
|
||||||
groupName={this.state.subGroupName}
|
|
||||||
onDepartChanged={this.onSubDepartChanged}
|
|
||||||
/>
|
|
||||||
</ModalPortal>
|
|
||||||
)}
|
|
||||||
{this.state.showSetGroupQuotaDialog && (
|
|
||||||
<ModalPortal>
|
|
||||||
<SetGroupQuotaDialog
|
|
||||||
toggle={this.toggleCancel}
|
|
||||||
groupID={this.state.subGroupID}
|
|
||||||
onDepartChanged={this.onSubDepartChanged}
|
|
||||||
/>
|
|
||||||
</ModalPortal>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DepartmentDetail.propTypes = DepartmentDetailPropTypes;
|
|
||||||
|
|
||||||
export default DepartmentDetail;
|
|
118
frontend/src/pages/sys-admin/departments/department-libraries.js
Normal file
118
frontend/src/pages/sys-admin/departments/department-libraries.js
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { seafileAPI } from '../../../utils/seafile-api';
|
||||||
|
import { Utils } from '../../../utils/utils.js';
|
||||||
|
import toaster from '../../../components/toast';
|
||||||
|
import ModalPortal from '../../../components/modal-portal';
|
||||||
|
import DeleteRepoDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-delete-repo-dialog';
|
||||||
|
import { gettext, lang } from '../../../utils/constants';
|
||||||
|
import RepoItem from './repo-item';
|
||||||
|
import Department from './department';
|
||||||
|
import '../../../css/org-department-item.css';
|
||||||
|
|
||||||
|
moment.locale(lang);
|
||||||
|
|
||||||
|
const DepartmentDetailPropTypes = {
|
||||||
|
groupID: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
class DepartmentDetail extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
repos: [],
|
||||||
|
deletedRepo: {},
|
||||||
|
showDeleteRepoDialog: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { groupID } = this.props;
|
||||||
|
this.listGroupRepo(groupID);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (this.props.groupID !== nextProps.groupID) {
|
||||||
|
this.listGroupRepo(nextProps.groupID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listGroupRepo = (groupID) => {
|
||||||
|
seafileAPI.sysAdminListGroupRepos(groupID).then(res => {
|
||||||
|
this.setState({ repos: res.data.libraries });
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCancel = () => {
|
||||||
|
this.setState({
|
||||||
|
showDeleteRepoDialog: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onRepoChanged = () => {
|
||||||
|
this.listGroupRepo(this.props.groupID);
|
||||||
|
}
|
||||||
|
|
||||||
|
showDeleteRepoDialog = (repo) => {
|
||||||
|
this.setState({ showDeleteRepoDialog: true, deletedRepo: repo });
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddNewRepo = (newRepo) => {
|
||||||
|
const { repos } = this.state;
|
||||||
|
repos.unshift(newRepo);
|
||||||
|
this.setState({ repos });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { repos } = this.state;
|
||||||
|
const { groupID } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Department groupID={groupID} currentItem="repos" onAddNewRepo={this.onAddNewRepo}>
|
||||||
|
{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 (
|
||||||
|
<RepoItem key={index} repo={repo} showDeleteRepoDialog={this.showDeleteRepoDialog} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
: <p className="no-libraty">{gettext('No libraries')}</p>
|
||||||
|
}
|
||||||
|
</Department>
|
||||||
|
{this.state.showDeleteRepoDialog && (
|
||||||
|
<ModalPortal>
|
||||||
|
<DeleteRepoDialog
|
||||||
|
toggle={this.toggleCancel}
|
||||||
|
onRepoChanged={this.onRepoChanged}
|
||||||
|
repo={this.state.deletedRepo}
|
||||||
|
groupID={groupID}
|
||||||
|
/>
|
||||||
|
</ModalPortal>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DepartmentDetail.propTypes = DepartmentDetailPropTypes;
|
||||||
|
|
||||||
|
export default DepartmentDetail;
|
@@ -4,31 +4,26 @@ import { seafileAPI } from '../../../utils/seafile-api';
|
|||||||
import MainPanelTopbar from '../main-panel-topbar';
|
import MainPanelTopbar from '../main-panel-topbar';
|
||||||
import ModalPortal from '../../../components/modal-portal';
|
import ModalPortal from '../../../components/modal-portal';
|
||||||
import AddDepartDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-department-dialog';
|
import AddDepartDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-department-dialog';
|
||||||
import DeleteDepartDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-delete-department-dialog';
|
|
||||||
import SetGroupQuotaDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-set-group-quota-dialog';
|
|
||||||
import { gettext, lang } from '../../../utils/constants';
|
import { gettext, lang } from '../../../utils/constants';
|
||||||
import GroupItem from './group-item';
|
import GroupItem from './group-item';
|
||||||
import '../../../css/org-department-item.css';
|
import '../../../css/org-department-item.css';
|
||||||
|
|
||||||
moment.locale(lang);
|
moment.locale(lang);
|
||||||
|
|
||||||
class DepartmentsList extends React.Component {
|
class DepartmentList extends React.Component {
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
groups: null,
|
groups: null,
|
||||||
groupID: '',
|
groupID: '',
|
||||||
groupName: '',
|
|
||||||
showDeleteDepartDialog: false,
|
|
||||||
showSetGroupQuotaDialog: false,
|
|
||||||
isShowAddDepartDialog: false,
|
isShowAddDepartDialog: false,
|
||||||
isItemFreezed: false
|
isItemFreezed: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.listDepartGroups();
|
this.listDepartments();
|
||||||
}
|
}
|
||||||
|
|
||||||
onFreezedItem = () => {
|
onFreezedItem = () => {
|
||||||
@@ -39,35 +34,16 @@ class DepartmentsList extends React.Component {
|
|||||||
this.setState({isItemFreezed: false});
|
this.setState({isItemFreezed: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
listDepartGroups = () => {
|
listDepartments = () => {
|
||||||
seafileAPI.sysAdminListAllDepartments().then(res => {
|
seafileAPI.sysAdminListAllDepartments().then(res => {
|
||||||
this.setState({ groups: res.data.data });
|
this.setState({ groups: res.data.data });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
showDeleteDepartDialog = (group) => {
|
|
||||||
this.setState({ showDeleteDepartDialog: true, groupID: group.id, groupName: group.name });
|
|
||||||
}
|
|
||||||
|
|
||||||
showSetGroupQuotaDialog = (groupID) => {
|
|
||||||
this.setState({ showSetGroupQuotaDialog: true, groupID: groupID });
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleAddDepartDialog = () => {
|
toggleAddDepartDialog = () => {
|
||||||
this.setState({ isShowAddDepartDialog: !this.state.isShowAddDepartDialog });
|
this.setState({ isShowAddDepartDialog: !this.state.isShowAddDepartDialog });
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCancel = () => {
|
|
||||||
this.setState({
|
|
||||||
showDeleteDepartDialog: false,
|
|
||||||
showSetGroupQuotaDialog: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onDepartChanged = () => {
|
|
||||||
this.listDepartGroups();
|
|
||||||
}
|
|
||||||
|
|
||||||
onDepartmentNameChanged = (dept) => {
|
onDepartmentNameChanged = (dept) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
groups: this.state.groups.map(item => {
|
groups: this.state.groups.map(item => {
|
||||||
@@ -79,6 +55,33 @@ class DepartmentsList extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAddNewDepartment = (newDepartment) => {
|
||||||
|
const { groups } = this.state;
|
||||||
|
groups.unshift(newDepartment);
|
||||||
|
this.setState({
|
||||||
|
groups: groups
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteDepartment = (id) => {
|
||||||
|
const { groups } = this.state;
|
||||||
|
this.setState({
|
||||||
|
groups: groups.filter((item) => item.id != id)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSetDepartmentQuota = (target) => {
|
||||||
|
const { groups } = this.state;
|
||||||
|
this.setState({
|
||||||
|
groups: groups.map((item) => {
|
||||||
|
if (item.id == target.id) {
|
||||||
|
item.quota = target.quota;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const groups = this.state.groups;
|
const groups = this.state.groups;
|
||||||
const topbarChildren = (
|
const topbarChildren = (
|
||||||
@@ -88,7 +91,7 @@ class DepartmentsList extends React.Component {
|
|||||||
{this.state.isShowAddDepartDialog && (
|
{this.state.isShowAddDepartDialog && (
|
||||||
<ModalPortal>
|
<ModalPortal>
|
||||||
<AddDepartDialog
|
<AddDepartDialog
|
||||||
onDepartChanged={this.onDepartChanged}
|
onAddNewDepartment={this.onAddNewDepartment}
|
||||||
groupID={this.state.groupID}
|
groupID={this.state.groupID}
|
||||||
toggle={this.toggleAddDepartDialog}
|
toggle={this.toggleAddDepartDialog}
|
||||||
/>
|
/>
|
||||||
@@ -129,8 +132,8 @@ class DepartmentsList extends React.Component {
|
|||||||
onFreezedItem={this.onFreezedItem}
|
onFreezedItem={this.onFreezedItem}
|
||||||
onUnfreezedItem={this.onUnfreezedItem}
|
onUnfreezedItem={this.onUnfreezedItem}
|
||||||
onDepartmentNameChanged={this.onDepartmentNameChanged}
|
onDepartmentNameChanged={this.onDepartmentNameChanged}
|
||||||
showDeleteDepartDialog={this.showDeleteDepartDialog}
|
onDeleteDepartment={this.onDeleteDepartment}
|
||||||
showSetGroupQuotaDialog={this.showSetGroupQuotaDialog}
|
onSetDepartmentQuota={this.onSetDepartmentQuota}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
@@ -141,25 +144,6 @@ class DepartmentsList extends React.Component {
|
|||||||
<p className="no-group">{gettext('No departments')}</p>
|
<p className="no-group">{gettext('No departments')}</p>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
{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>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
@@ -167,4 +151,4 @@ class DepartmentsList extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DepartmentsList;
|
export default DepartmentList;
|
180
frontend/src/pages/sys-admin/departments/department-members.js
Normal file
180
frontend/src/pages/sys-admin/departments/department-members.js
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import moment from 'moment';
|
||||||
|
import Paginator from '../../../components/paginator';
|
||||||
|
import { seafileAPI } from '../../../utils/seafile-api';
|
||||||
|
import { Utils } from '../../../utils/utils.js';
|
||||||
|
import ModalPortal from '../../../components/modal-portal';
|
||||||
|
import DeleteMemberDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-delete-member-dialog';
|
||||||
|
import { gettext, lang } from '../../../utils/constants';
|
||||||
|
import MemberItem from './member-item';
|
||||||
|
import Department from './department';
|
||||||
|
import '../../../css/org-department-item.css';
|
||||||
|
|
||||||
|
moment.locale(lang);
|
||||||
|
|
||||||
|
const DepartmentMembersPropTypes = {
|
||||||
|
groupID: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
class DepartmentMembers extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
isItemFreezed: false,
|
||||||
|
members: [],
|
||||||
|
membersErrorMsg: '',
|
||||||
|
currentPageInfo: {
|
||||||
|
},
|
||||||
|
currentPage: 1,
|
||||||
|
perPage: 25,
|
||||||
|
deletedMember: {},
|
||||||
|
showDeleteMemberDialog: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
let urlParams = (new URL(window.location)).searchParams;
|
||||||
|
const { currentPage, perPage } = this.state;
|
||||||
|
this.setState({
|
||||||
|
perPage: parseInt(urlParams.get('per_page') || perPage),
|
||||||
|
currentPage: parseInt(urlParams.get('page') || currentPage)
|
||||||
|
}, () => {
|
||||||
|
const { groupID } = this.props;
|
||||||
|
this.listMembers(groupID, this.state.currentPage, this.state.perPage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (this.props.groupID !== nextProps.groupID) {
|
||||||
|
this.listMembers(nextProps.groupID, this.state.currentPage, this.state.perPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listMembers = (groupID, page, perPage) => {
|
||||||
|
seafileAPI.sysAdminListGroupMembers(groupID, page, perPage).then((res) => {
|
||||||
|
this.setState({
|
||||||
|
members: res.data.members,
|
||||||
|
currentPageInfo: res.data.page_info
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
this.setState({membersErrorMsg: errMessage});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getPreviousPageList = () => {
|
||||||
|
this.listMembers(this.props.groupID, this.state.currentPageInfo.current_page - 1, this.state.perPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNextPageList = () => {
|
||||||
|
this.listMembers(this.props.groupID, this.state.currentPageInfo.current_page + 1, this.state.perPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetPerPage = (perPage) => {
|
||||||
|
this.setState({
|
||||||
|
perPage: perPage
|
||||||
|
}, () => {
|
||||||
|
this.listMembers(this.props.groupID, 1, perPage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCancel = () => {
|
||||||
|
this.setState({
|
||||||
|
showDeleteMemberDialog: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMemberChanged = () => {
|
||||||
|
this.listMembers(this.props.groupID, this.state.currentPageInfo.current_page, this.state.perPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleItemFreezed = (isFreezed) => {
|
||||||
|
this.setState({ isItemFreezed: isFreezed });
|
||||||
|
}
|
||||||
|
|
||||||
|
showDeleteMemberDialog = (member) => {
|
||||||
|
this.setState({ showDeleteMemberDialog: true, deletedMember: member });
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddNewMembers = (newMembers) => {
|
||||||
|
const { members } = this.state;
|
||||||
|
members.unshift(...newMembers);
|
||||||
|
this.setState({ members });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { members, membersErrorMsg } = this.state;
|
||||||
|
const { groupID } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Department
|
||||||
|
groupID={groupID}
|
||||||
|
currentItem="members"
|
||||||
|
onAddNewMembers={this.onAddNewMembers}
|
||||||
|
>
|
||||||
|
<div className="cur-view-content">
|
||||||
|
{membersErrorMsg ? <p className="error text-center">{membersErrorMsg}</p> :
|
||||||
|
members.length == 0 ?
|
||||||
|
<p className="no-member">{gettext('No members')}</p> :
|
||||||
|
<Fragment>
|
||||||
|
<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 (
|
||||||
|
<Fragment key={index}>
|
||||||
|
<MemberItem
|
||||||
|
member={member}
|
||||||
|
showDeleteMemberDialog={this.showDeleteMemberDialog}
|
||||||
|
isItemFreezed={this.state.isItemFreezed}
|
||||||
|
onMemberChanged={this.onMemberChanged}
|
||||||
|
toggleItemFreezed={this.toggleItemFreezed}
|
||||||
|
groupID={groupID}
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{this.state.currentPageInfo &&
|
||||||
|
<Paginator
|
||||||
|
gotoPreviousPage={this.getPreviousPageList}
|
||||||
|
gotoNextPage={this.getNextPageList}
|
||||||
|
currentPage={this.state.currentPageInfo.current_page}
|
||||||
|
hasNextPage={this.state.currentPageInfo.has_next_page}
|
||||||
|
curPerPage={this.state.perPage}
|
||||||
|
resetPerPage={this.resetPerPage}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Department>
|
||||||
|
{this.state.showDeleteMemberDialog && (
|
||||||
|
<ModalPortal>
|
||||||
|
<DeleteMemberDialog
|
||||||
|
toggle={this.toggleCancel}
|
||||||
|
onMemberChanged={this.onMemberChanged}
|
||||||
|
member={this.state.deletedMember}
|
||||||
|
groupID={groupID}
|
||||||
|
/>
|
||||||
|
</ModalPortal>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DepartmentMembers.propTypes = DepartmentMembersPropTypes;
|
||||||
|
|
||||||
|
export default DepartmentMembers;
|
191
frontend/src/pages/sys-admin/departments/department.js
Normal file
191
frontend/src/pages/sys-admin/departments/department.js
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
import React, { Fragment } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { Link } from '@gatsbyjs/reach-router';
|
||||||
|
import { seafileAPI } from '../../../utils/seafile-api';
|
||||||
|
import { Utils } from '../../../utils/utils.js';
|
||||||
|
import toaster from '../../../components/toast';
|
||||||
|
import MainPanelTopbar from '../main-panel-topbar';
|
||||||
|
import ModalPortal from '../../../components/modal-portal';
|
||||||
|
import AddDepartmentDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-department-dialog';
|
||||||
|
import RenameDepartmentDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-rename-department-dialog';
|
||||||
|
import AddMemberDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-member-dialog';
|
||||||
|
import AddRepoDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-add-repo-dialog';
|
||||||
|
import { siteRoot, gettext, lang } from '../../../utils/constants';
|
||||||
|
|
||||||
|
import '../../../css/org-department-item.css';
|
||||||
|
|
||||||
|
moment.locale(lang);
|
||||||
|
|
||||||
|
const DepartmentDetailPropTypes = {
|
||||||
|
groupID: PropTypes.string,
|
||||||
|
currentItem: PropTypes.string.isRequired,
|
||||||
|
onAddNewDepartment: PropTypes.func,
|
||||||
|
onAddNewMembers: PropTypes.func,
|
||||||
|
onAddNewRepo: PropTypes.func,
|
||||||
|
children: PropTypes.object
|
||||||
|
};
|
||||||
|
|
||||||
|
class Department extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
groupName: '',
|
||||||
|
ancestorGroups: [],
|
||||||
|
isShowAddDepartmentDialog: false,
|
||||||
|
isShowAddMemberDialog: false,
|
||||||
|
isShowRenameDepartmentDialog: false,
|
||||||
|
isShowAddRepoDialog: false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.navItems = [
|
||||||
|
{name: 'subDepartments', urlPart: '/', text: gettext('Sub-departments')},
|
||||||
|
{name: 'members', urlPart: '/members/', text: gettext('Members')},
|
||||||
|
{name: 'repos', urlPart: '/libraries/', text: gettext('Libraries')}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const groupID = this.props.groupID;
|
||||||
|
this.getDepartmentInfo(groupID);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (this.props.groupID !== nextProps.groupID) {
|
||||||
|
this.getDepartmentInfo(nextProps.groupID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDepartmentInfo = (groupID) => {
|
||||||
|
seafileAPI.sysAdminGetDepartmentInfo(groupID, true).then(res => {
|
||||||
|
this.setState({
|
||||||
|
ancestorGroups: res.data.ancestor_groups,
|
||||||
|
groupName: res.data.name,
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDepartmentNameChanged = (dept) => {
|
||||||
|
this.setState({
|
||||||
|
groupName: dept.name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleRenameDepartmentDialog = () => {
|
||||||
|
this.setState({ isShowRenameDepartmentDialog: !this.state.isShowRenameDepartmentDialog });
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAddRepoDialog = () => {
|
||||||
|
this.setState({ isShowAddRepoDialog: !this.state.isShowAddRepoDialog });
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAddMemberDialog = () => {
|
||||||
|
this.setState({ isShowAddMemberDialog: !this.state.isShowAddMemberDialog });
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleAddDepartmentDialog = () => {
|
||||||
|
this.setState({ isShowAddDepartmentDialog: !this.state.isShowAddDepartmentDialog});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { groupID, currentItem } = this.props;
|
||||||
|
const { groupName } = this.state;
|
||||||
|
|
||||||
|
const topBtn = 'btn btn-secondary operation-item';
|
||||||
|
const topbarChildren = (
|
||||||
|
<Fragment>
|
||||||
|
{groupID &&
|
||||||
|
<Fragment>
|
||||||
|
<button className={topBtn} title={gettext('Rename Department')} onClick={this.toggleRenameDepartmentDialog}>{gettext('Rename Department')}</button>
|
||||||
|
{currentItem == 'subDepartments' && <button className={topBtn} title={gettext('New Sub-department')} onClick={this.toggleAddDepartmentDialog}>{gettext('New Sub-department')}</button>}
|
||||||
|
{currentItem == 'members' && <button className={topBtn} title={gettext('Add Member')} onClick={this.toggleAddMemberDialog}>{gettext('Add Member')}</button>}
|
||||||
|
{currentItem == 'repos' && <button className={topBtn} onClick={this.toggleAddRepoDialog} title={gettext('New Library')}>{gettext('New Library')}</button>}
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
{this.state.isShowRenameDepartmentDialog && (
|
||||||
|
<ModalPortal>
|
||||||
|
<RenameDepartmentDialog
|
||||||
|
groupID={groupID}
|
||||||
|
name={groupName}
|
||||||
|
toggle={this.toggleRenameDepartmentDialog}
|
||||||
|
onDepartmentNameChanged={this.onDepartmentNameChanged}
|
||||||
|
/>
|
||||||
|
</ModalPortal>
|
||||||
|
)}
|
||||||
|
{this.state.isShowAddMemberDialog && (
|
||||||
|
<ModalPortal>
|
||||||
|
<AddMemberDialog
|
||||||
|
toggle={this.toggleAddMemberDialog}
|
||||||
|
onAddNewMembers={this.props.onAddNewMembers}
|
||||||
|
groupID={groupID}
|
||||||
|
/>
|
||||||
|
</ModalPortal>
|
||||||
|
)}
|
||||||
|
{this.state.isShowAddRepoDialog && (
|
||||||
|
<ModalPortal>
|
||||||
|
<AddRepoDialog
|
||||||
|
toggle={this.toggleAddRepoDialog}
|
||||||
|
onAddNewRepo={this.props.onAddNewRepo}
|
||||||
|
groupID={groupID}
|
||||||
|
/>
|
||||||
|
</ModalPortal>
|
||||||
|
)}
|
||||||
|
{this.state.isShowAddDepartmentDialog && (
|
||||||
|
<ModalPortal>
|
||||||
|
<AddDepartmentDialog
|
||||||
|
onAddNewDepartment={this.props.onAddNewDepartment}
|
||||||
|
parentGroupID={groupID}
|
||||||
|
toggle={this.toggleAddDepartmentDialog}
|
||||||
|
/>
|
||||||
|
</ModalPortal>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<MainPanelTopbar {...this.props}>
|
||||||
|
{topbarChildren}
|
||||||
|
</MainPanelTopbar>
|
||||||
|
<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">
|
||||||
|
{groupID ?
|
||||||
|
<Link to={siteRoot + 'sys/departments/'}>{gettext('Departments')}</Link>
|
||||||
|
: <span>{gettext('Departments')}</span>
|
||||||
|
}
|
||||||
|
{this.state.ancestorGroups.map(ancestor => {
|
||||||
|
let newHref = siteRoot + 'sys/departments/' + ancestor.id + '/';
|
||||||
|
return <span key={ancestor.id}>{' / '}<Link to={newHref}>{ancestor.name}</Link></span>;
|
||||||
|
})}
|
||||||
|
{groupID && <span>{' / '}{groupName}</span>}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul className="nav border-bottom mx-4">
|
||||||
|
{this.navItems.map((item, index) => {
|
||||||
|
return (
|
||||||
|
<li className="nav-item mr-2" key={index}>
|
||||||
|
<Link to={`${siteRoot}sys/departments/${groupID}${item.urlPart}`} className={`nav-link ${currentItem == item.name ? ' active' : ''}`}>{item.text}</Link>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Department.propTypes = DepartmentDetailPropTypes;
|
||||||
|
|
||||||
|
export default Department;
|
@@ -5,7 +5,10 @@ import { Link } from '@gatsbyjs/reach-router';
|
|||||||
import { Utils } from '../../../utils/utils.js';
|
import { Utils } from '../../../utils/utils.js';
|
||||||
import { siteRoot, gettext } from '../../../utils/constants';
|
import { siteRoot, gettext } from '../../../utils/constants';
|
||||||
import OpMenu from '../../../components/dialog/op-menu';
|
import OpMenu from '../../../components/dialog/op-menu';
|
||||||
|
import ModalPortal from '../../../components/modal-portal';
|
||||||
import RenameDepartmentDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-rename-department-dialog';
|
import RenameDepartmentDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-rename-department-dialog';
|
||||||
|
import DeleteDepartmentDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-delete-department-dialog';
|
||||||
|
import SetGroupQuotaDialog from '../../../components/dialog/sysadmin-dialog/sysadmin-set-group-quota-dialog';
|
||||||
|
|
||||||
const GroupItemPropTypes = {
|
const GroupItemPropTypes = {
|
||||||
isItemFreezed: PropTypes.bool.isRequired,
|
isItemFreezed: PropTypes.bool.isRequired,
|
||||||
@@ -13,8 +16,8 @@ const GroupItemPropTypes = {
|
|||||||
onUnfreezedItem: PropTypes.func.isRequired,
|
onUnfreezedItem: PropTypes.func.isRequired,
|
||||||
group: PropTypes.object.isRequired,
|
group: PropTypes.object.isRequired,
|
||||||
onDepartmentNameChanged: PropTypes.func.isRequired,
|
onDepartmentNameChanged: PropTypes.func.isRequired,
|
||||||
showSetGroupQuotaDialog: PropTypes.func.isRequired,
|
onDeleteDepartment: PropTypes.func.isRequired,
|
||||||
showDeleteDepartDialog: PropTypes.func.isRequired,
|
onSetDepartmentQuota: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
class GroupItem extends React.Component {
|
class GroupItem extends React.Component {
|
||||||
@@ -24,6 +27,8 @@ class GroupItem extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
isOpIconShown: false,
|
isOpIconShown: false,
|
||||||
highlight: false,
|
highlight: false,
|
||||||
|
isSetQuotaDialogOpen: false,
|
||||||
|
isDeleteDialogOpen: false,
|
||||||
isRenameDialogOpen: false
|
isRenameDialogOpen: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -71,13 +76,12 @@ class GroupItem extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMenuItemClick = (operation) => {
|
onMenuItemClick = (operation) => {
|
||||||
const { group } = this.props;
|
|
||||||
switch(operation) {
|
switch(operation) {
|
||||||
case 'Rename':
|
case 'Rename':
|
||||||
this.toggleRenameDialog();
|
this.toggleRenameDialog();
|
||||||
break;
|
break;
|
||||||
case 'Delete':
|
case 'Delete':
|
||||||
this.props.showDeleteDepartDialog(group);
|
this.toggleDeleteDialog();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -90,24 +94,31 @@ class GroupItem extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleDeleteDialog = () => {
|
||||||
|
this.setState({
|
||||||
|
isDeleteDialogOpen: !this.state.isDeleteDialogOpen
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleSetQuotaDialog = () => {
|
||||||
|
this.setState({
|
||||||
|
isSetQuotaDialogOpen: !this.state.isSetQuotaDialogOpen
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { group } = this.props;
|
const { group } = this.props;
|
||||||
const { highlight, isOpIconShown, isRenameDialogOpen } = this.state;
|
const { highlight, isOpIconShown, isRenameDialogOpen, isDeleteDialogOpen, isSetQuotaDialogOpen } = this.state;
|
||||||
const newHref = siteRoot+ 'sys/departments/' + group.id + '/';
|
const newHref = siteRoot+ 'sys/departments/' + group.id + '/';
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.handleMouseOver} onMouseLeave={this.handleMouseOut}>
|
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.handleMouseOver} onMouseLeave={this.handleMouseOut}>
|
||||||
<td><Link to={newHref}>{group.name}</Link></td>
|
<td><Link to={newHref}>{group.name}</Link></td>
|
||||||
<td>{moment(group.created_at).fromNow()}</td>
|
<td>{moment(group.created_at).fromNow()}</td>
|
||||||
{group.org_id === -1 ?
|
<td>
|
||||||
<td onClick={this.props.showSetGroupQuotaDialog.bind(this, group.id)}>
|
|
||||||
{Utils.bytesToSize(group.quota)}{' '}
|
{Utils.bytesToSize(group.quota)}{' '}
|
||||||
<span title="Edit Quota" className={`fa fa-pencil-alt attr-action-icon ${highlight ? '' : 'vh'}`}></span>
|
<span onClick={this.toggleSetQuotaDialog} title={gettext('Edit')} className={`fa fa-pencil-alt attr-action-icon ${highlight ? '' : 'vh'}`}></span>
|
||||||
</td>
|
</td>
|
||||||
:
|
|
||||||
<td>{Utils.bytesToSize(group.quota)}</td>
|
|
||||||
}
|
|
||||||
{group.org_id === -1 ?
|
|
||||||
<td>
|
<td>
|
||||||
{isOpIconShown &&
|
{isOpIconShown &&
|
||||||
<OpMenu
|
<OpMenu
|
||||||
@@ -119,10 +130,25 @@ class GroupItem extends React.Component {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
:
|
|
||||||
<td></td>
|
|
||||||
}
|
|
||||||
</tr>
|
</tr>
|
||||||
|
{isDeleteDialogOpen && (
|
||||||
|
<ModalPortal>
|
||||||
|
<DeleteDepartmentDialog
|
||||||
|
group={group}
|
||||||
|
onDeleteDepartment={this.props.onDeleteDepartment}
|
||||||
|
toggle={this.toggleDeleteDialog}
|
||||||
|
/>
|
||||||
|
</ModalPortal>
|
||||||
|
)}
|
||||||
|
{isSetQuotaDialogOpen && (
|
||||||
|
<ModalPortal>
|
||||||
|
<SetGroupQuotaDialog
|
||||||
|
groupID={group.id}
|
||||||
|
onSetQuota={this.props.onSetDepartmentQuota}
|
||||||
|
toggle={this.toggleSetQuotaDialog}
|
||||||
|
/>
|
||||||
|
</ModalPortal>
|
||||||
|
)}
|
||||||
{isRenameDialogOpen && (
|
{isRenameDialogOpen && (
|
||||||
<RenameDepartmentDialog
|
<RenameDepartmentDialog
|
||||||
groupID={group.id}
|
groupID={group.id}
|
||||||
|
@@ -61,7 +61,6 @@ class MemberItem extends React.Component {
|
|||||||
<tr className={highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
<tr className={highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||||
<td><img src={member.avatar_url} alt="member-header" width="24" className="avatar"/></td>
|
<td><img src={member.avatar_url} alt="member-header" width="24" className="avatar"/></td>
|
||||||
<td><UserLink email={member.email} name={member.name} /></td>
|
<td><UserLink email={member.email} name={member.name} /></td>
|
||||||
{ this.props.orgID == -1 ?
|
|
||||||
<td>
|
<td>
|
||||||
<RoleEditor
|
<RoleEditor
|
||||||
isTextMode={true}
|
isTextMode={true}
|
||||||
@@ -72,10 +71,7 @@ class MemberItem extends React.Component {
|
|||||||
toggleItemFreezed={this.props.toggleItemFreezed}
|
toggleItemFreezed={this.props.toggleItemFreezed}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
:
|
{!this.props.isItemFreezed ?
|
||||||
<td>{member.role}</td>
|
|
||||||
}
|
|
||||||
{this.props.orgID == -1 && !this.props.isItemFreezed ?
|
|
||||||
<td className="cursor-pointer text-center" onClick={this.props.showDeleteMemberDialog.bind(this, member)}>
|
<td className="cursor-pointer text-center" onClick={this.props.showDeleteMemberDialog.bind(this, member)}>
|
||||||
<span className={`sf2-icon-x3 action-icon ${highlight ? '' : 'vh'}`} title="Delete"></span>
|
<span className={`sf2-icon-x3 action-icon ${highlight ? '' : 'vh'}`} title="Delete"></span>
|
||||||
</td> : <td></td>
|
</td> : <td></td>
|
||||||
|
@@ -7,7 +7,7 @@ const { enableSysAdminViewRepo } = window.sysadmin.pageOptions;
|
|||||||
|
|
||||||
const RepoItemPropTypes = {
|
const RepoItemPropTypes = {
|
||||||
repo: PropTypes.object.isRequired,
|
repo: PropTypes.object.isRequired,
|
||||||
showDeleteRepoDialog: PropTypes.func.isRequired,
|
showDeleteRepoDialog: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
class RepoItem extends React.Component {
|
class RepoItem extends React.Component {
|
||||||
@@ -15,7 +15,7 @@ class RepoItem extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
highlight: false,
|
highlight: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,25 +28,22 @@ class RepoItem extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const repo = this.props.repo;
|
const { repo } = this.props;
|
||||||
|
const repoName = repo.name || repo.repo_name;
|
||||||
const highlight = this.state.highlight;
|
const highlight = this.state.highlight;
|
||||||
let iconUrl = Utils.getLibIconUrl(repo);
|
let iconUrl = Utils.getLibIconUrl(repo);
|
||||||
return (
|
return (
|
||||||
<tr className={highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
<tr className={highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||||
<td><img src={iconUrl} width="24" alt={gettext('icon')}/></td>
|
<td><img src={iconUrl} width="24" alt={gettext('icon')}/></td>
|
||||||
{ enableSysAdminViewRepo ?
|
{ enableSysAdminViewRepo ?
|
||||||
<td><a href={siteRoot + 'sys/libraries/' + repo.repo_id + '/' + repo.name + '/'}>{repo.name}</a></td>
|
<td><a href={`${siteRoot}sys/libraries/${repo.repo_id}/${encodeURIComponent(repoName)}/`}>{repoName}</a></td>
|
||||||
:
|
:
|
||||||
<td>{repo.name}</td>
|
<td>{repoName}</td>
|
||||||
}
|
}
|
||||||
<td>{Utils.bytesToSize(repo.size)}{' '}</td>
|
<td>{Utils.bytesToSize(repo.size)}</td>
|
||||||
{ this.props.orgID == -1 ?
|
|
||||||
<td className="cursor-pointer text-center" onClick={this.props.showDeleteRepoDialog.bind(this, repo)}>
|
<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>
|
<span className={`sf2-icon-delete action-icon ${highlight ? '' : 'vh'}`} title="Delete"></span>
|
||||||
</td>
|
</td>
|
||||||
:
|
|
||||||
<td></td>
|
|
||||||
}
|
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
142
frontend/src/pages/sys-admin/departments/sub-departments.js
Normal file
142
frontend/src/pages/sys-admin/departments/sub-departments.js
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { seafileAPI } from '../../../utils/seafile-api';
|
||||||
|
import { Utils } from '../../../utils/utils.js';
|
||||||
|
import toaster from '../../../components/toast';
|
||||||
|
import { gettext, lang } from '../../../utils/constants';
|
||||||
|
import GroupItem from './group-item';
|
||||||
|
import Department from './department';
|
||||||
|
|
||||||
|
import '../../../css/org-department-item.css';
|
||||||
|
|
||||||
|
moment.locale(lang);
|
||||||
|
|
||||||
|
const SubDepartmentsPropTypes = {
|
||||||
|
groupID: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
class SubDepartments extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
isItemFreezed: false,
|
||||||
|
groups: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.listSubDepartments(this.props.groupID);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (this.props.groupID !== nextProps.groupID) {
|
||||||
|
this.listSubDepartments(nextProps.groupID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listSubDepartments = (groupID) => {
|
||||||
|
seafileAPI.sysAdminGetDepartmentInfo(groupID, true).then(res => {
|
||||||
|
this.setState({groups: res.data.groups});
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubDepartmentNameChanged = (dept) => {
|
||||||
|
this.setState({
|
||||||
|
groups: this.state.groups.map(item => {
|
||||||
|
if (item.id == dept.id) {
|
||||||
|
item.name = dept.name;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onFreezedItem = () => {
|
||||||
|
this.setState({isItemFreezed: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnfreezedItem = () => {
|
||||||
|
this.setState({isItemFreezed: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddNewDepartment = (newDepartment) => {
|
||||||
|
const { groups } = this.state;
|
||||||
|
groups.unshift(newDepartment);
|
||||||
|
this.setState({
|
||||||
|
groups: groups
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteDepartment = (id) => {
|
||||||
|
const { groups } = this.state;
|
||||||
|
this.setState({
|
||||||
|
groups: groups.filter(item => item.id != id)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSetDepartmentQuota = (target) => {
|
||||||
|
const { groups } = this.state;
|
||||||
|
this.setState({
|
||||||
|
groups: groups.map((item) => {
|
||||||
|
if (item.id == target.id) {
|
||||||
|
item.quota = target.quota;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { groups } = this.state;
|
||||||
|
const { groupID } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Department
|
||||||
|
groupID={groupID}
|
||||||
|
currentItem="subDepartments"
|
||||||
|
onAddNewDepartment={this.onAddNewDepartment}
|
||||||
|
>
|
||||||
|
<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(
|
||||||
|
<GroupItem
|
||||||
|
key={group.id}
|
||||||
|
isItemFreezed={this.state.isItemFreezed}
|
||||||
|
onFreezedItem={this.onFreezedItem}
|
||||||
|
onUnfreezedItem={this.onUnfreezedItem}
|
||||||
|
onDepartmentNameChanged={this.onSubDepartmentNameChanged}
|
||||||
|
group={group}
|
||||||
|
onDeleteDepartment={this.onDeleteDepartment}
|
||||||
|
onSetDepartmentQuota={this.onSetDepartmentQuota}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
: <p className="no-group">{gettext('No sub-departments')}</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Department>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SubDepartments.propTypes = SubDepartmentsPropTypes;
|
||||||
|
|
||||||
|
export default SubDepartments;
|
@@ -44,8 +44,10 @@ import GroupRepos from './groups/group-repos';
|
|||||||
import GroupMembers from './groups/group-members';
|
import GroupMembers from './groups/group-members';
|
||||||
|
|
||||||
import Departments from './departments/departments';
|
import Departments from './departments/departments';
|
||||||
import DepartmentsList from './departments/departments-list';
|
import DepartmentList from './departments/department-list';
|
||||||
import DepartmentDetail from './departments/department-detail';
|
import SubDepartments from './departments/sub-departments';
|
||||||
|
import DepartmentMembers from './departments/department-members';
|
||||||
|
import DepartmentLibraries from './departments/department-libraries';
|
||||||
|
|
||||||
import ShareLinks from './links/share-links';
|
import ShareLinks from './links/share-links';
|
||||||
import UploadLinks from './links/upload-links';
|
import UploadLinks from './links/upload-links';
|
||||||
@@ -235,8 +237,10 @@ class SysAdmin extends React.Component {
|
|||||||
<GroupRepos path={siteRoot + 'sys/groups/:groupID/libraries'} {...commonProps} />
|
<GroupRepos path={siteRoot + 'sys/groups/:groupID/libraries'} {...commonProps} />
|
||||||
<GroupMembers path={siteRoot + 'sys/groups/:groupID/members'} {...commonProps} />
|
<GroupMembers path={siteRoot + 'sys/groups/:groupID/members'} {...commonProps} />
|
||||||
<Departments path={siteRoot + 'sys/departments'}>
|
<Departments path={siteRoot + 'sys/departments'}>
|
||||||
<DepartmentsList path='/' {...commonProps} />
|
<DepartmentList path='/' {...commonProps} />
|
||||||
<DepartmentDetail path='/:groupID' {...commonProps} />
|
<SubDepartments path='/:groupID' {...commonProps} />
|
||||||
|
<DepartmentMembers path='/:groupID/members' {...commonProps} />
|
||||||
|
<DepartmentLibraries path='/:groupID/libraries' {...commonProps} />
|
||||||
</Departments>
|
</Departments>
|
||||||
<ShareLinks path={siteRoot + 'sys/share-links'} {...commonProps} />
|
<ShareLinks path={siteRoot + 'sys/share-links'} {...commonProps} />
|
||||||
<UploadLinks path={siteRoot + 'sys/upload-links'} {...commonProps} />
|
<UploadLinks path={siteRoot + 'sys/upload-links'} {...commonProps} />
|
||||||
|
@@ -760,6 +760,8 @@ urlpatterns = [
|
|||||||
url(r'^sys/groups/(?P<group_id>\d+)/members/$', sysadmin_react_fake_view, name="sys_group_members"),
|
url(r'^sys/groups/(?P<group_id>\d+)/members/$', sysadmin_react_fake_view, name="sys_group_members"),
|
||||||
url(r'^sys/departments/$', sysadmin_react_fake_view, name="sys_departments"),
|
url(r'^sys/departments/$', sysadmin_react_fake_view, name="sys_departments"),
|
||||||
url(r'^sys/departments/(?P<group_id>\d+)/$', sysadmin_react_fake_view, name="sys_department"),
|
url(r'^sys/departments/(?P<group_id>\d+)/$', sysadmin_react_fake_view, name="sys_department"),
|
||||||
|
url(r'^sys/departments/(?P<group_id>\d+)/members/$', sysadmin_react_fake_view, name="sys_department_members"),
|
||||||
|
url(r'^sys/departments/(?P<group_id>\d+)/libraries/$', sysadmin_react_fake_view, name="sys_department_libraries"),
|
||||||
url(r'^sys/users/$', sysadmin_react_fake_view, name="sys_users"),
|
url(r'^sys/users/$', sysadmin_react_fake_view, name="sys_users"),
|
||||||
url(r'^sys/search-users/$', sysadmin_react_fake_view, name="sys_search_users"),
|
url(r'^sys/search-users/$', sysadmin_react_fake_view, name="sys_search_users"),
|
||||||
url(r'^sys/users/admins/$', sysadmin_react_fake_view, name="sys_users_admin"),
|
url(r'^sys/users/admins/$', sysadmin_react_fake_view, name="sys_users_admin"),
|
||||||
|
Reference in New Issue
Block a user