1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-09 19:01:42 +00:00

[org admin] added sort for 'libraries/users' (#4548)

* users: split 'all' & 'admin'
* bugfix & improvement for pages
This commit is contained in:
llj
2020-04-27 14:46:41 +08:00
committed by GitHub
parent 82370c25ca
commit 744eca3a7c
11 changed files with 419 additions and 270 deletions

View File

@@ -4,6 +4,8 @@ class OrgAdminRepo {
this.repoName = object.repo_name; this.repoName = object.repo_name;
this.ownerName = object.owner_name; this.ownerName = object.owner_name;
this.ownerEmail = object.owner_email; this.ownerEmail = object.owner_email;
this.size = object.size;
this.file_count = object.file_count;
this.encrypted = object.encrypted; this.encrypted = object.encrypted;
this.isDepartmentRepo = object.is_department_repo; this.isDepartmentRepo = object.is_department_repo;
this.groupID = object.group_id; this.groupID = object.group_id;

View File

@@ -1,10 +1,10 @@
// Import React!
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { Router } from '@reach/router'; import { Router } from '@reach/router';
import { siteRoot } from '../../utils/constants'; import { siteRoot } from '../../utils/constants';
import SidePanel from './side-panel'; 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 OrgUserProfile from './org-user-profile';
import OrgUserRepos from './org-user-repos'; import OrgUserRepos from './org-user-repos';
import OrgUserSharedRepos from './org-user-shared-repos'; import OrgUserSharedRepos from './org-user-shared-repos';
@@ -34,7 +34,7 @@ class Org extends React.Component {
super(props); super(props);
this.state = { this.state = {
isSidePanelClosed: false, isSidePanelClosed: false,
currentTab: 'users', currentTab: 'users'
}; };
} }
@@ -70,7 +70,8 @@ class Org extends React.Component {
<div className="main-panel o-hidden"> <div className="main-panel o-hidden">
<Router className="reach-router"> <Router className="reach-router">
<OrgInfo path={siteRoot + 'org/orgmanage'}/> <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/'} /> <OrgUserProfile path={siteRoot + 'org/useradmin/info/:email/'} />
<OrgUserRepos path={siteRoot + 'org/useradmin/info/:email/repos/'} /> <OrgUserRepos path={siteRoot + 'org/useradmin/info/:email/repos/'} />
<OrgUserSharedRepos path={siteRoot + 'org/useradmin/info/:email/shared-repos/'} /> <OrgUserSharedRepos path={siteRoot + 'org/useradmin/info/:email/shared-repos/'} />

View File

@@ -10,7 +10,7 @@ class MainPanelTopbar extends Component {
render() { render() {
return ( 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"> <div className="cur-view-toolbar">
<span className="sf2-icon-menu side-nav-toggle hidden-md-up d-md-none" title="Side Nav Menu"></span> <span className="sf2-icon-menu side-nav-toggle hidden-md-up d-md-none" title="Side Nav Menu"></span>
<div className="operation"> <div className="operation">

View File

@@ -10,7 +10,7 @@ const propTypes = {
toggleDelete: PropTypes.func.isRequired, toggleDelete: PropTypes.func.isRequired,
toggleRevokeAdmin: PropTypes.func.isRequired, toggleRevokeAdmin: PropTypes.func.isRequired,
orgAdminUsers: PropTypes.array.isRequired, orgAdminUsers: PropTypes.array.isRequired,
initOrgAdmin: PropTypes.func.isRequired, initOrgAdmin: PropTypes.func.isRequired
}; };
class OrgAdminList extends React.Component { class OrgAdminList extends React.Component {
@@ -18,7 +18,7 @@ class OrgAdminList extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
isItemFreezed: false, isItemFreezed: false
}; };
} }
@@ -44,9 +44,9 @@ class OrgAdminList extends React.Component {
<tr> <tr>
<th width="30%">{gettext('Name')}</th> <th width="30%">{gettext('Name')}</th>
<th width="15%">{gettext('Status')}</th> <th width="15%">{gettext('Status')}</th>
<th width="15%">{gettext('Space Used')}</th> <th width="20%">{gettext('Space Used')} / {gettext('Quota')}</th>
<th width="20%">{gettext('Create At / Last Login')}</th> <th width="25%">{gettext('Created At')} / {gettext('Last Login')}</th>
<th width="20%" className="text-center">{gettext('Operations')}</th> <th width="10%">{/*Operations*/}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -55,7 +55,7 @@ class OrgAdminList extends React.Component {
<UserItem <UserItem
key={item.id} key={item.id}
user={item} user={item}
currentTab={this.props.currentTab} currentTab="admins"
isItemFreezed={this.state.isItemFreezed} isItemFreezed={this.state.isItemFreezed}
toggleDelete={this.props.toggleDelete} toggleDelete={this.props.toggleDelete}
toggleRevokeAdmin={this.props.toggleRevokeAdmin} toggleRevokeAdmin={this.props.toggleRevokeAdmin}

View File

@@ -1,4 +1,5 @@
import React, { Fragment, Component } from 'react'; import React, { Fragment, Component } from 'react';
import { navigate } from '@reach/router';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
import MainPanelTopbar from './main-panel-topbar'; import MainPanelTopbar from './main-panel-topbar';
@@ -18,17 +19,26 @@ class OrgLibraries extends Component {
page: 1, page: 1,
pageNext: false, pageNext: false,
orgRepos: [], orgRepos: [],
sortBy: '',
isItemFreezed: false isItemFreezed: false
}; };
} }
componentDidMount() { componentDidMount() {
let page = this.state.page; let urlParams = (new URL(window.location)).searchParams;
this.initData(page); 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) => { listRepos = (page) => {
seafileAPI.orgAdminListOrgRepos(orgID, page).then(res => { seafileAPI.orgAdminListOrgRepos(orgID, page, this.state.sortBy).then(res => {
let orgRepos = res.data.repo_list.map(item => { let orgRepos = res.data.repo_list.map(item => {
return new OrgAdminRepo(item); return new OrgAdminRepo(item);
}); });
@@ -54,7 +64,7 @@ class OrgLibraries extends Component {
} else { } else {
page = page - 1; page = page - 1;
} }
this.initData(page); this.listRepos(page);
} }
onFreezedItem = () => { 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() { 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 ( return (
<Fragment> <Fragment>
<MainPanelTopbar/> <MainPanelTopbar />
<div className="main-panel-center flex-row"> <div className="main-panel-center flex-row">
<div className="cur-view-container"> <div className="cur-view-container">
<div className="cur-view-path"> <div className="cur-view-path">
@@ -105,15 +143,19 @@ class OrgLibraries extends Component {
<table> <table>
<thead> <thead>
<tr> <tr>
<th width="4%"></th> <th width="5%">{/*icon*/}</th>
<th width="27%">{gettext('Name')}</th> <th width="25%">{gettext('Name')}</th>
<th width="30%">ID</th> <th width="15%">
<th width="24%">{gettext('Owner')}</th> <a className="d-inline-block table-sort-op" href="#" onClick={this.sortByFileCount}>{gettext('Files')} {sortBy == 'file_count' ? sortIcon : initialSortIcon}</a>{' / '}
<th width="15%" className="text-center">{gettext('Operations')}</th> <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> </tr>
</thead> </thead>
<tbody> <tbody>
{repos.map(item => { {orgRepos.map(item => {
return ( return (
<RepoItem <RepoItem
key={item.repoID} 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}> <tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<td>{this.renderLibIcon(repo)}</td> <td>{this.renderLibIcon(repo)}</td>
<td>{repo.repoName}</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><a href={this.renderRepoOwnerHref(repo)}>{repo.ownerName}</a></td>
<td className="text-center cursor-pointer"> <td className="text-center cursor-pointer">
{isOperationMenuShow && {isOperationMenuShow &&

View File

@@ -130,7 +130,7 @@ class UserItem extends React.Component {
return ( return (
<tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}> <tr className={this.state.highlight ? 'tr-highlight' : ''} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<td> <td>
<a href={href} className="font-weight-normal">{user.name}</a> <a href={href}>{user.name}</a>
</td> </td>
<td> <td>
<UserStatusEditor <UserStatusEditor
@@ -141,8 +141,12 @@ class UserItem extends React.Component {
onStatusChanged={this.changeStatus} onStatusChanged={this.changeStatus}
/> />
</td> </td>
<td>{user.quota ? user.self_usage + ' / ' + user.quota : user.self_usage}</td> <td>{`${user.self_usage} / ${user.quota || '--'}`}</td>
<td style={{'fontSize': '11px'}}>{user.ctime} / {user.last_login ? user.last_login : '--'}</td> <td>
{user.ctime} /
<br />
{user.last_login ? user.last_login : '--'}
</td>
<td className="text-center cursor-pointer"> <td className="text-center cursor-pointer">
{isOperationMenuShow && ( {isOperationMenuShow && (
<Dropdown isOpen={this.state.isItemMenuShow} toggle={this.toggleOperationMenu}> <Dropdown isOpen={this.state.isItemMenuShow} toggle={this.toggleOperationMenu}>

View 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;

View File

@@ -4,7 +4,6 @@ import { gettext } from '../../utils/constants';
import UserItem from './org-user-item'; import UserItem from './org-user-item';
const propTypes = { const propTypes = {
currentTab: PropTypes.string.isRequired,
initOrgUsersData: PropTypes.func.isRequired, initOrgUsersData: PropTypes.func.isRequired,
toggleDelete: PropTypes.func.isRequired, toggleDelete: PropTypes.func.isRequired,
orgUsers: PropTypes.array.isRequired, orgUsers: PropTypes.array.isRequired,
@@ -17,14 +16,10 @@ class OrgUsersList extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
isItemFreezed: false, isItemFreezed: false
}; };
} }
componentDidMount() {
this.props.initOrgUsersData(this.props.page);
}
onFreezedItem = () => { onFreezedItem = () => {
this.setState({isItemFreezed: true}); this.setState({isItemFreezed: true});
} }
@@ -46,7 +41,20 @@ class OrgUsersList extends React.Component {
this.props.initOrgUsersData(page); this.props.initOrgUsersData(page);
} }
sortByQuotaUsage = (e) => {
e.preventDefault();
this.props.sortByQuotaUsage();
}
render() { 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; let { orgUsers, page, pageNext } = this.props;
return ( return (
<div className="cur-view-content"> <div className="cur-view-content">
@@ -55,9 +63,11 @@ class OrgUsersList extends React.Component {
<tr> <tr>
<th width="30%">{gettext('Name')}</th> <th width="30%">{gettext('Name')}</th>
<th width="15%">{gettext('Status')}</th> <th width="15%">{gettext('Status')}</th>
<th width="15%">{gettext('Space Used')}</th> <th width="20%">
<th width="20%">{gettext('Create At / Last Login')}</th> <a className="d-inline-block table-sort-op" href="#" onClick={this.sortByQuotaUsage}>{gettext('Space Used')} {sortIcon}</a> / {gettext('Quota')}
<th width="20%" className="text-center">{gettext('Operations')}</th> </th>
<th width="25%">{gettext('Created At')} / {gettext('Last Login')}</th>
<th width="10%">{/*Operations*/}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -66,7 +76,7 @@ class OrgUsersList extends React.Component {
<UserItem <UserItem
key={item.id} key={item.id}
user={item} user={item}
currentTab={this.props.currentTab} currentTab="users"
isItemFreezed={this.state.isItemFreezed} isItemFreezed={this.state.isItemFreezed}
toggleDelete={this.props.toggleDelete} toggleDelete={this.props.toggleDelete}
onFreezedItem={this.onFreezedItem} onFreezedItem={this.onFreezedItem}

View 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;

View 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;

View File

@@ -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;