mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-12 13:24:52 +00:00
[org admin] added sort for 'libraries/users' (#4548)
* users: split 'all' & 'admin' * bugfix & improvement for pages
This commit is contained in:
@@ -4,6 +4,8 @@ class OrgAdminRepo {
|
||||
this.repoName = object.repo_name;
|
||||
this.ownerName = object.owner_name;
|
||||
this.ownerEmail = object.owner_email;
|
||||
this.size = object.size;
|
||||
this.file_count = object.file_count;
|
||||
this.encrypted = object.encrypted;
|
||||
this.isDepartmentRepo = object.is_department_repo;
|
||||
this.groupID = object.group_id;
|
||||
|
@@ -1,10 +1,10 @@
|
||||
// Import React!
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Router } from '@reach/router';
|
||||
import { siteRoot } from '../../utils/constants';
|
||||
import SidePanel from './side-panel';
|
||||
import OrgUsers from './org-users';
|
||||
import OrgUsers from './org-users-users';
|
||||
import OrgAdmins from './org-users-admins';
|
||||
import OrgUserProfile from './org-user-profile';
|
||||
import OrgUserRepos from './org-user-repos';
|
||||
import OrgUserSharedRepos from './org-user-shared-repos';
|
||||
@@ -34,7 +34,7 @@ class Org extends React.Component {
|
||||
super(props);
|
||||
this.state = {
|
||||
isSidePanelClosed: false,
|
||||
currentTab: 'users',
|
||||
currentTab: 'users'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,7 +70,8 @@ class Org extends React.Component {
|
||||
<div className="main-panel o-hidden">
|
||||
<Router className="reach-router">
|
||||
<OrgInfo path={siteRoot + 'org/orgmanage'}/>
|
||||
<OrgUsers path={siteRoot + 'org/useradmin'} currentTab={currentTab} tabItemClick={this.tabItemClick}/>
|
||||
<OrgUsers path={siteRoot + 'org/useradmin'} />
|
||||
<OrgAdmins path={siteRoot + 'org/useradmin/admins/'} />
|
||||
<OrgUserProfile path={siteRoot + 'org/useradmin/info/:email/'} />
|
||||
<OrgUserRepos path={siteRoot + 'org/useradmin/info/:email/repos/'} />
|
||||
<OrgUserSharedRepos path={siteRoot + 'org/useradmin/info/:email/shared-repos/'} />
|
||||
|
@@ -10,7 +10,7 @@ class MainPanelTopbar extends Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="main-panel-north border-left-show">
|
||||
<div className={`main-panel-north ${this.props.children ? 'border-left-show' : ''}`}>
|
||||
<div className="cur-view-toolbar">
|
||||
<span className="sf2-icon-menu side-nav-toggle hidden-md-up d-md-none" title="Side Nav Menu"></span>
|
||||
<div className="operation">
|
||||
|
@@ -10,7 +10,7 @@ const propTypes = {
|
||||
toggleDelete: PropTypes.func.isRequired,
|
||||
toggleRevokeAdmin: PropTypes.func.isRequired,
|
||||
orgAdminUsers: PropTypes.array.isRequired,
|
||||
initOrgAdmin: PropTypes.func.isRequired,
|
||||
initOrgAdmin: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
class OrgAdminList extends React.Component {
|
||||
@@ -18,7 +18,7 @@ class OrgAdminList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isItemFreezed: false,
|
||||
isItemFreezed: false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,9 +44,9 @@ class OrgAdminList extends React.Component {
|
||||
<tr>
|
||||
<th width="30%">{gettext('Name')}</th>
|
||||
<th width="15%">{gettext('Status')}</th>
|
||||
<th width="15%">{gettext('Space Used')}</th>
|
||||
<th width="20%">{gettext('Create At / Last Login')}</th>
|
||||
<th width="20%" className="text-center">{gettext('Operations')}</th>
|
||||
<th width="20%">{gettext('Space Used')} / {gettext('Quota')}</th>
|
||||
<th width="25%">{gettext('Created At')} / {gettext('Last Login')}</th>
|
||||
<th width="10%">{/*Operations*/}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -55,7 +55,7 @@ class OrgAdminList extends React.Component {
|
||||
<UserItem
|
||||
key={item.id}
|
||||
user={item}
|
||||
currentTab={this.props.currentTab}
|
||||
currentTab="admins"
|
||||
isItemFreezed={this.state.isItemFreezed}
|
||||
toggleDelete={this.props.toggleDelete}
|
||||
toggleRevokeAdmin={this.props.toggleRevokeAdmin}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import React, { Fragment, Component } from 'react';
|
||||
import { navigate } from '@reach/router';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
||||
import MainPanelTopbar from './main-panel-topbar';
|
||||
@@ -18,17 +19,26 @@ class OrgLibraries extends Component {
|
||||
page: 1,
|
||||
pageNext: false,
|
||||
orgRepos: [],
|
||||
sortBy: '',
|
||||
isItemFreezed: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let page = this.state.page;
|
||||
this.initData(page);
|
||||
let urlParams = (new URL(window.location)).searchParams;
|
||||
const { page, /*currentPage = 1, perPage, */sortBy } = this.state;
|
||||
this.setState({
|
||||
sortBy: urlParams.get('order_by') || sortBy,
|
||||
//perPage: parseInt(urlParams.get('per_page') || perPage),
|
||||
//currentPage: parseInt(urlParams.get('page') || currentPage)
|
||||
page: parseInt(urlParams.get('page') || page)
|
||||
}, () => {
|
||||
this.listRepos(this.state.page);
|
||||
});
|
||||
}
|
||||
|
||||
initData = (page) => {
|
||||
seafileAPI.orgAdminListOrgRepos(orgID, page).then(res => {
|
||||
listRepos = (page) => {
|
||||
seafileAPI.orgAdminListOrgRepos(orgID, page, this.state.sortBy).then(res => {
|
||||
let orgRepos = res.data.repo_list.map(item => {
|
||||
return new OrgAdminRepo(item);
|
||||
});
|
||||
@@ -54,7 +64,7 @@ class OrgLibraries extends Component {
|
||||
} else {
|
||||
page = page - 1;
|
||||
}
|
||||
this.initData(page);
|
||||
this.listRepos(page);
|
||||
}
|
||||
|
||||
onFreezedItem = () => {
|
||||
@@ -91,11 +101,39 @@ class OrgLibraries extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
sortItems = (sortBy) => {
|
||||
this.setState({
|
||||
page: 1,
|
||||
sortBy: sortBy
|
||||
}, () => {
|
||||
let url = new URL(location.href);
|
||||
let searchParams = new URLSearchParams(url.search);
|
||||
const { page, sortBy } = this.state;
|
||||
searchParams.set('page', page);
|
||||
searchParams.set('order_by', sortBy);
|
||||
url.search = searchParams.toString();
|
||||
navigate(url.toString());
|
||||
this.listRepos(page);
|
||||
});
|
||||
}
|
||||
|
||||
sortByFileCount = (e) => {
|
||||
e.preventDefault();
|
||||
this.sortItems('file_count');
|
||||
}
|
||||
|
||||
sortBySize = (e) => {
|
||||
e.preventDefault();
|
||||
this.sortItems('size');
|
||||
}
|
||||
|
||||
render() {
|
||||
let repos = this.state.orgRepos;
|
||||
const { orgRepos, sortBy } = this.state;
|
||||
const initialSortIcon = <span className="fas fa-sort"></span>;
|
||||
const sortIcon = <span className="fas fa-caret-down"></span>;
|
||||
return (
|
||||
<Fragment>
|
||||
<MainPanelTopbar/>
|
||||
<MainPanelTopbar />
|
||||
<div className="main-panel-center flex-row">
|
||||
<div className="cur-view-container">
|
||||
<div className="cur-view-path">
|
||||
@@ -105,15 +143,19 @@ class OrgLibraries extends Component {
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="4%"></th>
|
||||
<th width="27%">{gettext('Name')}</th>
|
||||
<th width="30%">ID</th>
|
||||
<th width="24%">{gettext('Owner')}</th>
|
||||
<th width="15%" className="text-center">{gettext('Operations')}</th>
|
||||
<th width="5%">{/*icon*/}</th>
|
||||
<th width="25%">{gettext('Name')}</th>
|
||||
<th width="15%">
|
||||
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortByFileCount}>{gettext('Files')} {sortBy == 'file_count' ? sortIcon : initialSortIcon}</a>{' / '}
|
||||
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortBySize}>{gettext('Size')} {sortBy == 'size' ? sortIcon : initialSortIcon}</a>
|
||||
</th>
|
||||
<th width="32%">ID</th>
|
||||
<th width="18%">{gettext('Owner')}</th>
|
||||
<th width="5%">{/*Operations*/}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{repos.map(item => {
|
||||
{orgRepos.map(item => {
|
||||
return (
|
||||
<RepoItem
|
||||
key={item.repoID}
|
||||
@@ -259,7 +301,8 @@ class RepoItem extends React.Component {
|
||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
<td>{this.renderLibIcon(repo)}</td>
|
||||
<td>{repo.repoName}</td>
|
||||
<td style={{'fontSize': '11px'}}>{repo.repoID}</td>
|
||||
<td>{`${repo.file_count} / ${Utils.bytesToSize(repo.size)}`}</td>
|
||||
<td>{repo.repoID}</td>
|
||||
<td><a href={this.renderRepoOwnerHref(repo)}>{repo.ownerName}</a></td>
|
||||
<td className="text-center cursor-pointer">
|
||||
{isOperationMenuShow &&
|
||||
|
@@ -130,7 +130,7 @@ class UserItem extends React.Component {
|
||||
return (
|
||||
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
<td>
|
||||
<a href={href} className="font-weight-normal">{user.name}</a>
|
||||
<a href={href}>{user.name}</a>
|
||||
</td>
|
||||
<td>
|
||||
<UserStatusEditor
|
||||
@@ -141,8 +141,12 @@ class UserItem extends React.Component {
|
||||
onStatusChanged={this.changeStatus}
|
||||
/>
|
||||
</td>
|
||||
<td>{user.quota ? user.self_usage + ' / ' + user.quota : user.self_usage}</td>
|
||||
<td style={{'fontSize': '11px'}}>{user.ctime} / {user.last_login ? user.last_login : '--'}</td>
|
||||
<td>{`${user.self_usage} / ${user.quota || '--'}`}</td>
|
||||
<td>
|
||||
{user.ctime} /
|
||||
<br />
|
||||
{user.last_login ? user.last_login : '--'}
|
||||
</td>
|
||||
<td className="text-center cursor-pointer">
|
||||
{isOperationMenuShow && (
|
||||
<Dropdown isOpen={this.state.isItemMenuShow} toggle={this.toggleOperationMenu}>
|
||||
|
114
frontend/src/pages/org-admin/org-users-admins.js
Normal file
114
frontend/src/pages/org-admin/org-users-admins.js
Normal file
@@ -0,0 +1,114 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import Nav from './org-users-nav';
|
||||
import OrgAdminList from './org-admin-list';
|
||||
import MainPanelTopbar from './main-panel-topbar';
|
||||
import AddOrgAdminDialog from '../../components/dialog/org-add-admin-dialog';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import toaster from '../../components/toast';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import OrgUserInfo from '../../models/org-user';
|
||||
import { gettext, orgID } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
|
||||
class OrgUsers extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
orgAdminUsers: [],
|
||||
isShowAddOrgAdminDialog: false
|
||||
};
|
||||
}
|
||||
|
||||
toggleAddOrgAdmin = () => {
|
||||
this.setState({isShowAddOrgAdminDialog: !this.state.isShowAddOrgAdminDialog});
|
||||
}
|
||||
|
||||
initOrgAdmin = () => {
|
||||
seafileAPI.orgAdminListOrgUsers(orgID, true).then(res => {
|
||||
let userList = res.data.user_list.map(item => {
|
||||
return new OrgUserInfo(item);
|
||||
});
|
||||
this.setState({orgAdminUsers: userList});
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
toggleOrgAdminDelete = (email) => {
|
||||
seafileAPI.orgAdminDeleteOrgUser(orgID, email).then(res => {
|
||||
this.setState({
|
||||
orgAdminUsers: this.state.orgAdminUsers.filter(item => item.email != email)
|
||||
});
|
||||
let msg = gettext('Successfully deleted %s');
|
||||
msg = msg.replace('%s', email);
|
||||
toaster.success(msg);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
toggleRevokeAdmin = (email) => {
|
||||
seafileAPI.orgAdminSetOrgAdmin(orgID, email, false).then(res => {
|
||||
this.setState({
|
||||
orgAdminUsers: this.state.orgAdminUsers.filter(item => item.email != email)
|
||||
});
|
||||
let msg = gettext('Successfully revoke the admin permission of %s');
|
||||
msg = msg.replace('%s', res.data.name);
|
||||
toaster.success(msg);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
onAddedOrgAdmin = (userInfo) => {
|
||||
this.state.orgAdminUsers.unshift(userInfo);
|
||||
this.setState({
|
||||
orgAdminUsers: this.state.orgAdminUsers
|
||||
});
|
||||
let msg = gettext('Successfully set %s as admin.');
|
||||
msg = msg.replace('%s', userInfo.email);
|
||||
toaster.success(msg);
|
||||
this.toggleAddOrgAdmin();
|
||||
}
|
||||
|
||||
render() {
|
||||
const topBtn = 'btn btn-secondary operation-item';
|
||||
let topbarChildren;
|
||||
topbarChildren = (
|
||||
<Fragment>
|
||||
<button className={topBtn} title={gettext('Add admin')} onClick={this.toggleAddOrgAdmin}>
|
||||
<i className="fas fa-plus-square text-secondary mr-1"></i>{gettext('Add admin')}
|
||||
</button>
|
||||
{this.state.isShowAddOrgAdminDialog &&
|
||||
<ModalPortal>
|
||||
<AddOrgAdminDialog toggle={this.toggleAddOrgAdmin} onAddedOrgAdmin={this.onAddedOrgAdmin}/>
|
||||
</ModalPortal>
|
||||
}
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<MainPanelTopbar children={topbarChildren}/>
|
||||
<div className="main-panel-center flex-row">
|
||||
<div className="cur-view-container">
|
||||
<Nav currentItem="admins" />
|
||||
<OrgAdminList
|
||||
currentTab="admins"
|
||||
toggleDelete={this.toggleOrgAdminDelete}
|
||||
toggleRevokeAdmin={this.toggleRevokeAdmin}
|
||||
orgAdminUsers={this.state.orgAdminUsers}
|
||||
initOrgAdmin={this.initOrgAdmin}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default OrgUsers;
|
@@ -4,7 +4,6 @@ import { gettext } from '../../utils/constants';
|
||||
import UserItem from './org-user-item';
|
||||
|
||||
const propTypes = {
|
||||
currentTab: PropTypes.string.isRequired,
|
||||
initOrgUsersData: PropTypes.func.isRequired,
|
||||
toggleDelete: PropTypes.func.isRequired,
|
||||
orgUsers: PropTypes.array.isRequired,
|
||||
@@ -17,14 +16,10 @@ class OrgUsersList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isItemFreezed: false,
|
||||
isItemFreezed: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.initOrgUsersData(this.props.page);
|
||||
}
|
||||
|
||||
onFreezedItem = () => {
|
||||
this.setState({isItemFreezed: true});
|
||||
}
|
||||
@@ -46,7 +41,20 @@ class OrgUsersList extends React.Component {
|
||||
this.props.initOrgUsersData(page);
|
||||
}
|
||||
|
||||
sortByQuotaUsage = (e) => {
|
||||
e.preventDefault();
|
||||
this.props.sortByQuotaUsage();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { sortBy, sortOrder } = this.props;
|
||||
let sortIcon;
|
||||
if (sortBy == '') {
|
||||
// initial sort icon
|
||||
sortIcon = <span className="fas fa-sort"></span>;
|
||||
} else {
|
||||
sortIcon = <span className={`fas ${sortOrder == 'asc' ? 'fa-caret-up' : 'fa-caret-down'}`}></span>;
|
||||
}
|
||||
let { orgUsers, page, pageNext } = this.props;
|
||||
return (
|
||||
<div className="cur-view-content">
|
||||
@@ -55,9 +63,11 @@ class OrgUsersList extends React.Component {
|
||||
<tr>
|
||||
<th width="30%">{gettext('Name')}</th>
|
||||
<th width="15%">{gettext('Status')}</th>
|
||||
<th width="15%">{gettext('Space Used')}</th>
|
||||
<th width="20%">{gettext('Create At / Last Login')}</th>
|
||||
<th width="20%" className="text-center">{gettext('Operations')}</th>
|
||||
<th width="20%">
|
||||
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortByQuotaUsage}>{gettext('Space Used')} {sortIcon}</a> / {gettext('Quota')}
|
||||
</th>
|
||||
<th width="25%">{gettext('Created At')} / {gettext('Last Login')}</th>
|
||||
<th width="10%">{/*Operations*/}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -66,7 +76,7 @@ class OrgUsersList extends React.Component {
|
||||
<UserItem
|
||||
key={item.id}
|
||||
user={item}
|
||||
currentTab={this.props.currentTab}
|
||||
currentTab="users"
|
||||
isItemFreezed={this.state.isItemFreezed}
|
||||
toggleDelete={this.props.toggleDelete}
|
||||
onFreezedItem={this.onFreezedItem}
|
||||
|
40
frontend/src/pages/org-admin/org-users-nav.js
Normal file
40
frontend/src/pages/org-admin/org-users-nav.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from '@reach/router';
|
||||
import { siteRoot, gettext } from '../../utils/constants';
|
||||
|
||||
const propTypes = {
|
||||
currentItem: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
class Nav extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.navItems = [
|
||||
{name: 'all', urlPart: 'useradmin', text: gettext('All')},
|
||||
{name: 'admins', urlPart: 'useradmin/admins', text: gettext('Admin')}
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
const { currentItem } = this.props;
|
||||
return (
|
||||
<div className="cur-view-path tab-nav-container">
|
||||
<ul className="nav">
|
||||
{this.navItems.map((item, index) => {
|
||||
return (
|
||||
<li className="nav-item" key={index}>
|
||||
<Link to={`${siteRoot}org/${item.urlPart}/`} className={`nav-link${currentItem == item.name ? ' active' : ''}`}>{item.text}</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Nav.propTypes = propTypes;
|
||||
|
||||
export default Nav;
|
167
frontend/src/pages/org-admin/org-users-users.js
Normal file
167
frontend/src/pages/org-admin/org-users-users.js
Normal file
@@ -0,0 +1,167 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import { navigate } from '@reach/router';
|
||||
import Nav from './org-users-nav';
|
||||
import OrgUsersList from './org-users-list';
|
||||
import MainPanelTopbar from './main-panel-topbar';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import AddOrgUserDialog from '../../components/dialog/org-add-user-dialog';
|
||||
import InviteUserDialog from '../../components/dialog/org-admin-invite-user-dialog';
|
||||
import toaster from '../../components/toast';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import OrgUserInfo from '../../models/org-user';
|
||||
import { gettext, invitationLink, orgID } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
|
||||
class OrgUsers extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
orgUsers: [],
|
||||
page: 1,
|
||||
pageNext: false,
|
||||
sortBy: '',
|
||||
sortOrder: 'asc',
|
||||
isShowAddOrgUserDialog: false,
|
||||
isInviteUserDialogOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let urlParams = (new URL(window.location)).searchParams;
|
||||
const { page, sortBy, sortOrder } = this.state;
|
||||
this.setState({
|
||||
/*
|
||||
perPage: parseInt(urlParams.get('per_page') || perPage),
|
||||
currentPage: parseInt(urlParams.get('page') || currentPage),
|
||||
*/
|
||||
page: parseInt(urlParams.get('page') || page),
|
||||
sortBy: urlParams.get('order_by') || sortBy,
|
||||
sortOrder: urlParams.get('direction') || sortOrder
|
||||
}, () => {
|
||||
this.initOrgUsersData(this.state.page);
|
||||
});
|
||||
}
|
||||
|
||||
sortByQuotaUsage = () => {
|
||||
this.setState({
|
||||
sortBy: 'quota_usage',
|
||||
sortOrder: this.state.sortOrder == 'asc' ? 'desc' : 'asc',
|
||||
page: 1
|
||||
}, () => {
|
||||
let url = new URL(location.href);
|
||||
let searchParams = new URLSearchParams(url.search);
|
||||
const { page, sortBy, sortOrder } = this.state;
|
||||
searchParams.set('page', page);
|
||||
searchParams.set('order_by', sortBy);
|
||||
searchParams.set('direction', sortOrder);
|
||||
url.search = searchParams.toString();
|
||||
navigate(url.toString());
|
||||
this.initOrgUsersData(page);
|
||||
});
|
||||
}
|
||||
|
||||
toggleAddOrgUser = () => {
|
||||
this.setState({isShowAddOrgUserDialog: !this.state.isShowAddOrgUserDialog});
|
||||
}
|
||||
|
||||
toggleInviteUserDialog = () => {
|
||||
this.setState({isInviteUserDialogOpen: !this.state.isInviteUserDialogOpen});
|
||||
}
|
||||
|
||||
initOrgUsersData = (page) => {
|
||||
const { sortBy, sortOrder } = this.state;
|
||||
seafileAPI.orgAdminListOrgUsers(orgID, '', page, sortBy, sortOrder).then(res => {
|
||||
let userList = res.data.user_list.map(item => {
|
||||
return new OrgUserInfo(item);
|
||||
});
|
||||
this.setState({
|
||||
orgUsers: userList,
|
||||
pageNext: res.data.page_next,
|
||||
page: res.data.page,
|
||||
});
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
addOrgUser = (email, name, password) => {
|
||||
seafileAPI.orgAdminAddOrgUser(orgID, email, name, password).then(res => {
|
||||
let userInfo = new OrgUserInfo(res.data);
|
||||
this.state.orgUsers.unshift(userInfo);
|
||||
this.setState({
|
||||
orgUsers: this.state.orgUsers
|
||||
});
|
||||
this.toggleAddOrgUser();
|
||||
let msg = gettext('successfully added user %s.');
|
||||
msg = msg.replace('%s', email);
|
||||
toaster.success(msg);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
this.toggleAddOrgUser();
|
||||
});
|
||||
}
|
||||
|
||||
toggleOrgUsersDelete = (email) => {
|
||||
seafileAPI.orgAdminDeleteOrgUser(orgID, email).then(res => {
|
||||
let users = this.state.orgUsers.filter(item => item.email != email);
|
||||
this.setState({orgUsers: users});
|
||||
let msg = gettext('Successfully deleted %s');
|
||||
msg = msg.replace('%s', email);
|
||||
toaster.success(msg);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const topBtn = 'btn btn-secondary operation-item';
|
||||
let topbarChildren;
|
||||
topbarChildren = (
|
||||
<Fragment>
|
||||
<button className={topBtn} title={gettext('Add user')} onClick={this.toggleAddOrgUser}>
|
||||
<i className="fas fa-plus-square text-secondary mr-1"></i>{gettext('Add user')}</button>
|
||||
{invitationLink &&
|
||||
<button className={topBtn} title={gettext('Invite user')} onClick={this.toggleInviteUserDialog}>
|
||||
<i className="fas fa-plus-square text-secondary mr-1"></i>{gettext('Invite user')}</button>
|
||||
}
|
||||
{this.state.isShowAddOrgUserDialog &&
|
||||
<ModalPortal>
|
||||
<AddOrgUserDialog handleSubmit={this.addOrgUser} toggle={this.toggleAddOrgUser}/>
|
||||
</ModalPortal>
|
||||
}
|
||||
{this.state.isInviteUserDialogOpen &&
|
||||
<ModalPortal>
|
||||
<InviteUserDialog invitationLink={invitationLink} toggle={this.toggleInviteUserDialog}/>
|
||||
</ModalPortal>
|
||||
}
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<MainPanelTopbar children={topbarChildren}/>
|
||||
<div className="main-panel-center flex-row">
|
||||
<div className="cur-view-container">
|
||||
<Nav currentItem="all" />
|
||||
<OrgUsersList
|
||||
initOrgUsersData={this.initOrgUsersData}
|
||||
toggleDelete={this.toggleOrgUsersDelete}
|
||||
orgUsers={this.state.orgUsers}
|
||||
page={this.state.page}
|
||||
pageNext={this.state.pageNext}
|
||||
sortBy={this.state.sortBy}
|
||||
sortOrder={this.state.sortOrder}
|
||||
sortByQuotaUsage={this.sortByQuotaUsage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default OrgUsers;
|
@@ -1,232 +0,0 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import OrgUsersList from './org-users-list';
|
||||
import OrgAdminList from './org-admin-list';
|
||||
import MainPanelTopbar from './main-panel-topbar';
|
||||
import AddOrgAdminDialog from '../../components/dialog/org-add-admin-dialog';
|
||||
import ModalPortal from '../../components/modal-portal';
|
||||
import AddOrgUserDialog from '../../components/dialog/org-add-user-dialog';
|
||||
import InviteUserDialog from '../../components/dialog/org-admin-invite-user-dialog';
|
||||
import toaster from '../../components/toast';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import OrgUserInfo from '../../models/org-user';
|
||||
import { gettext, invitationLink, orgID } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
|
||||
class OrgUsers extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
orgAdminUsers: [],
|
||||
isShowAddOrgAdminDialog: false,
|
||||
orgUsers: [],
|
||||
page: 1,
|
||||
pageNext: false,
|
||||
isShowAddOrgUserDialog: false,
|
||||
isInviteUserDialogOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
tabItemClick = (param) => {
|
||||
this.props.tabItemClick(param);
|
||||
}
|
||||
|
||||
toggleAddOrgAdmin = () => {
|
||||
this.setState({isShowAddOrgAdminDialog: !this.state.isShowAddOrgAdminDialog});
|
||||
}
|
||||
|
||||
toggleAddOrgUser = () => {
|
||||
this.setState({isShowAddOrgUserDialog: !this.state.isShowAddOrgUserDialog});
|
||||
}
|
||||
|
||||
toggleInviteUserDialog = () => {
|
||||
this.setState({isInviteUserDialogOpen: !this.state.isInviteUserDialogOpen});
|
||||
}
|
||||
|
||||
initOrgUsersData = (page) => {
|
||||
seafileAPI.orgAdminListOrgUsers(orgID, '', page).then(res => {
|
||||
let userList = res.data.user_list.map(item => {
|
||||
return new OrgUserInfo(item);
|
||||
});
|
||||
this.setState({
|
||||
orgUsers: userList,
|
||||
pageNext: res.data.page_next,
|
||||
page: res.data.page,
|
||||
});
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
addOrgUser = (email, name, password) => {
|
||||
seafileAPI.orgAdminAddOrgUser(orgID, email, name, password).then(res => {
|
||||
let userInfo = new OrgUserInfo(res.data);
|
||||
this.state.orgUsers.unshift(userInfo);
|
||||
this.setState({
|
||||
orgUsers: this.state.orgUsers
|
||||
});
|
||||
this.toggleAddOrgUser();
|
||||
let msg = gettext('successfully added user %s.');
|
||||
msg = msg.replace('%s', email);
|
||||
toaster.success(msg);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
this.toggleAddOrgUser();
|
||||
});
|
||||
}
|
||||
|
||||
toggleOrgUsersDelete = (email) => {
|
||||
seafileAPI.orgAdminDeleteOrgUser(orgID, email).then(res => {
|
||||
let users = this.state.orgUsers.filter(item => item.email != email);
|
||||
this.setState({orgUsers: users});
|
||||
let msg = gettext('Successfully deleted %s');
|
||||
msg = msg.replace('%s', email);
|
||||
toaster.success(msg);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
initOrgAdmin = () => {
|
||||
seafileAPI.orgAdminListOrgUsers(orgID, true).then(res => {
|
||||
let userList = res.data.user_list.map(item => {
|
||||
return new OrgUserInfo(item);
|
||||
});
|
||||
this.setState({orgAdminUsers: userList});
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
toggleOrgAdminDelete = (email) => {
|
||||
seafileAPI.orgAdminDeleteOrgUser(orgID, email).then(res => {
|
||||
this.setState({
|
||||
orgAdminUsers: this.state.orgAdminUsers.filter(item => item.email != email)
|
||||
});
|
||||
let msg = gettext('Successfully deleted %s');
|
||||
msg = msg.replace('%s', email);
|
||||
toaster.success(msg);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
toggleRevokeAdmin = (email) => {
|
||||
seafileAPI.orgAdminSetOrgAdmin(orgID, email, false).then(res => {
|
||||
this.setState({
|
||||
orgAdminUsers: this.state.orgAdminUsers.filter(item => item.email != email)
|
||||
});
|
||||
let msg = gettext('Successfully revoke the admin permission of %s');
|
||||
msg = msg.replace('%s', res.data.name);
|
||||
toaster.success(msg);
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
onAddedOrgAdmin = (userInfo) => {
|
||||
this.state.orgAdminUsers.unshift(userInfo);
|
||||
this.setState({
|
||||
orgAdminUsers: this.state.orgAdminUsers
|
||||
});
|
||||
let msg = gettext('Successfully set %s as admin.');
|
||||
msg = msg.replace('%s', userInfo.email);
|
||||
toaster.success(msg);
|
||||
this.toggleAddOrgAdmin();
|
||||
}
|
||||
|
||||
render() {
|
||||
const topBtn = 'btn btn-secondary operation-item';
|
||||
let topbarChildren;
|
||||
if (this.props.currentTab === 'admins') {
|
||||
topbarChildren = (
|
||||
<Fragment>
|
||||
<button className={topBtn} title={gettext('Add admin')} onClick={this.toggleAddOrgAdmin}>
|
||||
<i className="fas fa-plus-square text-secondary mr-1"></i>{gettext('Add admin')}
|
||||
</button>
|
||||
{this.state.isShowAddOrgAdminDialog &&
|
||||
<ModalPortal>
|
||||
<AddOrgAdminDialog toggle={this.toggleAddOrgAdmin} onAddedOrgAdmin={this.onAddedOrgAdmin}/>
|
||||
</ModalPortal>
|
||||
}
|
||||
</Fragment>
|
||||
);
|
||||
} else if (this.props.currentTab === 'users') {
|
||||
topbarChildren = (
|
||||
<Fragment>
|
||||
<button className={topBtn} title={gettext('Add user')} onClick={this.toggleAddOrgUser}>
|
||||
<i className="fas fa-plus-square text-secondary mr-1"></i>{gettext('Add user')}</button>
|
||||
{invitationLink &&
|
||||
<button className={topBtn} title={gettext('Invite user')} onClick={this.toggleInviteUserDialog}>
|
||||
<i className="fas fa-plus-square text-secondary mr-1"></i>{gettext('Invite user')}</button>
|
||||
}
|
||||
{this.state.isShowAddOrgUserDialog &&
|
||||
<ModalPortal>
|
||||
<AddOrgUserDialog handleSubmit={this.addOrgUser} toggle={this.toggleAddOrgUser}/>
|
||||
</ModalPortal>
|
||||
}
|
||||
{this.state.isInviteUserDialogOpen &&
|
||||
<ModalPortal>
|
||||
<InviteUserDialog invitationLink={invitationLink} toggle={this.toggleInviteUserDialog}/>
|
||||
</ModalPortal>
|
||||
}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<MainPanelTopbar children={topbarChildren}/>
|
||||
<div className="main-panel-center flex-row">
|
||||
<div className="cur-view-container">
|
||||
<div className="cur-view-path org-user-nav">
|
||||
<ul className="nav">
|
||||
<li className="nav-item" onClick={() => this.tabItemClick('users')}>
|
||||
<span className={`nav-link ${this.props.currentTab === 'users' ? 'active': ''}`}>{gettext('All')}</span>
|
||||
</li>
|
||||
<li className="nav-item" onClick={() => this.tabItemClick('admins')}>
|
||||
<span className={`nav-link ${this.props.currentTab === 'admins' ? 'active': ''}`} >{gettext('Admin')}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{this.props.currentTab === 'users' &&
|
||||
<OrgUsersList
|
||||
currentTab={this.props.currentTab}
|
||||
initOrgUsersData={this.initOrgUsersData}
|
||||
toggleDelete={this.toggleOrgUsersDelete}
|
||||
orgUsers={this.state.orgUsers}
|
||||
page={this.state.page}
|
||||
pageNext={this.state.pageNext}
|
||||
/>
|
||||
}
|
||||
{this.props.currentTab === 'admins' &&
|
||||
<OrgAdminList
|
||||
currentTab={this.props.currentTab}
|
||||
toggleDelete={this.toggleOrgAdminDelete}
|
||||
toggleRevokeAdmin={this.toggleRevokeAdmin}
|
||||
orgAdminUsers={this.state.orgAdminUsers}
|
||||
initOrgAdmin={this.initOrgAdmin}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const propTypes = {
|
||||
currentTab: PropTypes.string.isRequired,
|
||||
tabItemClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
OrgUsers.propTypes = propTypes;
|
||||
|
||||
export default OrgUsers;
|
Reference in New Issue
Block a user