mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-27 15:54:39 +00:00
Depts members select users (#7829)
* ['departments, members' dialog] added 'search users'(search users and list, select/unselct, select all users found) * ['departments, members' dialog] added 'unselect all' * ['departments, members' dialog] updated 'select/unselect all'
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import React, { Fragment, } from 'react';
|
import React, { Fragment, } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Modal, ModalBody } from 'reactstrap';
|
import { Modal, ModalBody, Input } from 'reactstrap';
|
||||||
import { gettext, isOrgContext, username } from '../../utils/constants';
|
import { gettext, isOrgContext, username } from '../../utils/constants';
|
||||||
import { seafileAPI } from '../../utils/seafile-api.js';
|
import { seafileAPI } from '../../utils/seafile-api.js';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
@@ -38,6 +38,9 @@ class DepartmentDetailDialog extends React.Component {
|
|||||||
membersLoading: true,
|
membersLoading: true,
|
||||||
selectedMemberMap: {},
|
selectedMemberMap: {},
|
||||||
departmentsTree: [],
|
departmentsTree: [],
|
||||||
|
keyword: '',
|
||||||
|
searching: false,
|
||||||
|
usersFound: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,15 +130,13 @@ class DepartmentDetailDialog extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMemberChecked = (member) => {
|
onMemberChecked = (member) => {
|
||||||
if (this.state.departmentMembers.indexOf(member) !== -1) {
|
let { newMembersTempObj } = this.state;
|
||||||
let newMembersTempObj = this.state.newMembersTempObj;
|
if (member.email in newMembersTempObj) {
|
||||||
if (member.email in newMembersTempObj) {
|
delete newMembersTempObj[member.email];
|
||||||
delete newMembersTempObj[member.email];
|
} else {
|
||||||
} else {
|
newMembersTempObj[member.email] = member;
|
||||||
newMembersTempObj[member.email] = member;
|
|
||||||
}
|
|
||||||
this.setState({ newMembersTempObj: newMembersTempObj });
|
|
||||||
}
|
}
|
||||||
|
this.setState({ newMembersTempObj: newMembersTempObj });
|
||||||
};
|
};
|
||||||
|
|
||||||
addGroupMember = () => {
|
addGroupMember = () => {
|
||||||
@@ -162,7 +163,10 @@ class DepartmentDetailDialog extends React.Component {
|
|||||||
this.setState({ currentDepartment: department });
|
this.setState({ currentDepartment: department });
|
||||||
};
|
};
|
||||||
|
|
||||||
selectAll = (members) => {
|
selectAll = () => {
|
||||||
|
const { keyword, departmentMembers, usersFound } = this.state;
|
||||||
|
const members = keyword ? usersFound : departmentMembers; // 'members': to be compatible with the old code
|
||||||
|
|
||||||
let { newMembersTempObj, selectedMemberMap } = this.state;
|
let { newMembersTempObj, selectedMemberMap } = this.state;
|
||||||
for (let member of members) {
|
for (let member of members) {
|
||||||
if (Object.keys(selectedMemberMap).indexOf(member.email) !== -1) {
|
if (Object.keys(selectedMemberMap).indexOf(member.email) !== -1) {
|
||||||
@@ -173,13 +177,58 @@ class DepartmentDetailDialog extends React.Component {
|
|||||||
this.setState({ newMembersTempObj: newMembersTempObj });
|
this.setState({ newMembersTempObj: newMembersTempObj });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
unselectAll = () => {
|
||||||
|
const { keyword, departmentMembers, usersFound } = this.state;
|
||||||
|
const members = keyword ? usersFound : departmentMembers; // 'members': to be compatible with the old code
|
||||||
|
|
||||||
|
let { newMembersTempObj, selectedMemberMap } = this.state;
|
||||||
|
|
||||||
|
for (let member of members) {
|
||||||
|
if (Object.keys(selectedMemberMap).indexOf(member.email) !== -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (member.email in newMembersTempObj) {
|
||||||
|
delete newMembersTempObj[member.email];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setState({ newMembersTempObj: newMembersTempObj });
|
||||||
|
};
|
||||||
|
|
||||||
|
onKeywordChanged = (e) => {
|
||||||
|
this.setState({ keyword: e.target.value }, () => {
|
||||||
|
const { keyword } = this.state;
|
||||||
|
const q = keyword.trim();
|
||||||
|
if (!q) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.setState({ searching: true });
|
||||||
|
seafileAPI.searchUsers(q).then((res) => {
|
||||||
|
this.setState({
|
||||||
|
searching: false,
|
||||||
|
usersFound: res.data.users
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
|
toaster.danger(errMessage);
|
||||||
|
this.setState({
|
||||||
|
searching: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
clearKeyword = () => {
|
||||||
|
this.setState({ keyword: '' });
|
||||||
|
};
|
||||||
|
|
||||||
renderHeader = () => {
|
renderHeader = () => {
|
||||||
const title = this.props.usedFor === 'add_group_member' ? gettext('Select group members') : gettext('Select shared users');
|
const title = this.props.usedFor === 'add_group_member' ? gettext('Select group members') : gettext('Select shared users');
|
||||||
return <SeahubModalHeader toggle={this.toggle}>{title}</SeahubModalHeader>;
|
return <SeahubModalHeader toggle={this.toggle}>{title}</SeahubModalHeader>;
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { departmentsLoading, departments } = this.state;
|
const { departmentsLoading, departments, keyword } = this.state;
|
||||||
|
|
||||||
if (departmentsLoading) {
|
if (departmentsLoading) {
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={true} toggle={this.toggle}>
|
<Modal isOpen={true} toggle={this.toggle}>
|
||||||
@@ -206,20 +255,48 @@ class DepartmentDetailDialog extends React.Component {
|
|||||||
<Modal isOpen={true} toggle={this.toggle} className="department-dialog" style={{ maxWidth: '900px' }}>
|
<Modal isOpen={true} toggle={this.toggle} className="department-dialog" style={{ maxWidth: '900px' }}>
|
||||||
{this.renderHeader()}
|
{this.renderHeader()}
|
||||||
<ModalBody className="department-dialog-content">
|
<ModalBody className="department-dialog-content">
|
||||||
<DepartmentGroup
|
<div className="department-dialog-left-panel">
|
||||||
departments={this.state.departments}
|
<div className="mb-2 position-relative">
|
||||||
getMembers={this.getMembers}
|
<i className="sf3-font sf3-font-search input-icon-addon"></i>
|
||||||
setCurrent={this.setCurrent}
|
<Input
|
||||||
currentDepartment={this.state.currentDepartment}
|
bsSize="sm"
|
||||||
loading={this.state.departmentsLoading}
|
className="px-6"
|
||||||
departmentsTree={this.state.departmentsTree}
|
type="text"
|
||||||
/>
|
value={keyword}
|
||||||
|
onChange={this.onKeywordChanged}
|
||||||
|
placeholder={gettext('Search users')}
|
||||||
|
/>
|
||||||
|
{keyword &&
|
||||||
|
<span className="input-icon-addon pe-auto">
|
||||||
|
<i
|
||||||
|
className="sf3-font sf3-font-x-01 clear-keyword-icon"
|
||||||
|
onClick={this.clearKeyword}
|
||||||
|
>
|
||||||
|
</i>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
{!keyword &&
|
||||||
|
<DepartmentGroup
|
||||||
|
departments={this.state.departments}
|
||||||
|
getMembers={this.getMembers}
|
||||||
|
setCurrent={this.setCurrent}
|
||||||
|
currentDepartment={this.state.currentDepartment}
|
||||||
|
loading={this.state.departmentsLoading}
|
||||||
|
departmentsTree={this.state.departmentsTree}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<DepartmentGroupMembers
|
<DepartmentGroupMembers
|
||||||
|
keyword={this.state.keyword}
|
||||||
|
searching={this.state.searching}
|
||||||
|
usersFound={this.state.usersFound}
|
||||||
members={this.state.departmentMembers}
|
members={this.state.departmentMembers}
|
||||||
memberSelected={this.state.newMembersTempObj}
|
memberSelected={this.state.newMembersTempObj}
|
||||||
onUserChecked={this.onMemberChecked}
|
onUserChecked={this.onMemberChecked}
|
||||||
currentDepartment={this.state.currentDepartment}
|
currentDepartment={this.state.currentDepartment}
|
||||||
selectAll={this.selectAll}
|
selectAll={this.selectAll}
|
||||||
|
unselectAll={this.unselectAll}
|
||||||
loading={this.state.membersLoading}
|
loading={this.state.membersLoading}
|
||||||
selectedMemberMap={this.state.selectedMemberMap}
|
selectedMemberMap={this.state.selectedMemberMap}
|
||||||
isLoadingMore={this.state.isLoadingMore}
|
isLoadingMore={this.state.isLoadingMore}
|
||||||
|
@@ -94,21 +94,20 @@ const DepartmentGroupMembersPropTypes = {
|
|||||||
currentDepartment: PropTypes.object.isRequired,
|
currentDepartment: PropTypes.object.isRequired,
|
||||||
selectedMemberMap: PropTypes.object,
|
selectedMemberMap: PropTypes.object,
|
||||||
selectAll: PropTypes.func.isRequired,
|
selectAll: PropTypes.func.isRequired,
|
||||||
|
unselectAll: PropTypes.func.isRequired,
|
||||||
loading: PropTypes.bool,
|
loading: PropTypes.bool,
|
||||||
usedFor: PropTypes.oneOf(['add_group_member', 'add_user_share']),
|
usedFor: PropTypes.oneOf(['add_group_member', 'add_user_share']),
|
||||||
|
keyword: PropTypes.string,
|
||||||
|
searching: PropTypes.bool,
|
||||||
|
usersFound: PropTypes.array
|
||||||
};
|
};
|
||||||
|
|
||||||
class DepartmentGroupMembers extends Component {
|
class DepartmentGroupMembers extends Component {
|
||||||
|
|
||||||
selectAll = () => {
|
|
||||||
const { members } = this.props;
|
|
||||||
this.props.selectAll(members);
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { members, memberSelected, loading, selectedMemberMap, currentDepartment, usedFor } = this.props;
|
const { members, memberSelected, loading, selectedMemberMap, currentDepartment, usedFor, keyword, searching, usersFound } = this.props;
|
||||||
let headerTitle = (currentDepartment.name || '') + ' ' + gettext('members');
|
let headerTitle = (currentDepartment.name || '') + ' ' + gettext('members');
|
||||||
if (loading) {
|
if (loading || searching) {
|
||||||
return (
|
return (
|
||||||
<div className="department-dialog-member pt-4">
|
<div className="department-dialog-member pt-4">
|
||||||
<div className="w-100">
|
<div className="w-100">
|
||||||
@@ -119,26 +118,35 @@ class DepartmentGroupMembers extends Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const enableSelectAll = Object.keys(memberSelected).length < members.length;
|
|
||||||
|
const itemsShown = keyword ? usersFound : members;
|
||||||
|
const unselectedItems = itemsShown.filter(item => !(item.email in memberSelected) && !selectedMemberMap[item.email]);
|
||||||
|
const addedItems = itemsShown.filter(item => selectedMemberMap[item.email]);
|
||||||
|
|
||||||
const tip = usedFor === 'add_group_member' ? gettext('User is already in this group') : gettext('It is already shared to user');
|
const tip = usedFor === 'add_group_member' ? gettext('User is already in this group') : gettext('It is already shared to user');
|
||||||
return (
|
return (
|
||||||
<div className="department-dialog-member pt-4">
|
<div className="department-dialog-member pt-4">
|
||||||
<div className="w-100">
|
<div className="w-100">
|
||||||
<div className='department-dialog-member-head px-4'>
|
<div className='department-dialog-member-head px-4'>
|
||||||
<div className='department-name'>
|
<div className='department-name'>
|
||||||
{headerTitle}
|
{keyword ? gettext('Search results') : headerTitle}
|
||||||
</div>
|
</div>
|
||||||
{enableSelectAll ?
|
{itemsShown.length > 0 && (
|
||||||
<div className='select-all' onClick={this.selectAll}>{gettext('Select All')}</div>
|
<>
|
||||||
:
|
{unselectedItems.length > 0
|
||||||
<div className='select-all-disable'>{gettext('Select All')}</div>
|
? <div className='select-all' onClick={this.props.selectAll}>{gettext('Select all')}</div>
|
||||||
}
|
: itemsShown.length > addedItems.length
|
||||||
|
? <div className='select-all' onClick={this.props.unselectAll}>{gettext('Unselect all')}</div>
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{members.length > 0 ?
|
{itemsShown.length > 0 ?
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<table className="department-dialog-member-table">
|
<table className="department-dialog-member-table">
|
||||||
<tbody>
|
<tbody>
|
||||||
{members.map((member, index) => {
|
{itemsShown.map((member, index) => {
|
||||||
return (
|
return (
|
||||||
<Item
|
<Item
|
||||||
key={index}
|
key={index}
|
||||||
@@ -156,7 +164,7 @@ class DepartmentGroupMembers extends Component {
|
|||||||
</Fragment>
|
</Fragment>
|
||||||
:
|
:
|
||||||
<EmptyTip tipSrc={`${mediaUrl}img/no-users-tip.png`}>
|
<EmptyTip tipSrc={`${mediaUrl}img/no-users-tip.png`}>
|
||||||
<h2>{gettext('No members')}</h2>
|
<h2>{keyword ? gettext('No users found') : gettext('No members')}</h2>
|
||||||
</EmptyTip>
|
</EmptyTip>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -115,24 +115,22 @@ class DepartmentGroup extends Component {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="department-dialog-group">
|
<div className="department-dialog-group">
|
||||||
<div>
|
{departments.length > 0 && departments.map((department, index) => {
|
||||||
{departments.length > 0 && departments.map((department, index) => {
|
if (department.parent_group_id !== -1) return null;
|
||||||
if (department.parent_group_id !== -1) return null;
|
return (
|
||||||
return (
|
<Item
|
||||||
<Item
|
key={department.id}
|
||||||
key={department.id}
|
department={department}
|
||||||
department={department}
|
departments={departments}
|
||||||
departments={departments}
|
getMembers={this.getMembers}
|
||||||
getMembers={this.getMembers}
|
setCurrent={this.props.setCurrent}
|
||||||
setCurrent={this.props.setCurrent}
|
toggleExpanded={this.toggleExpanded}
|
||||||
toggleExpanded={this.toggleExpanded}
|
currentDepartment={this.props.currentDepartment}
|
||||||
currentDepartment={this.props.currentDepartment}
|
allMembersClick={this.state.allMembersClick}
|
||||||
allMembersClick={this.state.allMembersClick}
|
padding={10}
|
||||||
padding={10}
|
/>
|
||||||
/>
|
);
|
||||||
);
|
})}
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -14,12 +14,27 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.department-dialog-content .department-dialog-group {
|
.department-dialog-content .department-dialog-left-panel {
|
||||||
flex: 0 0 30%;
|
flex: 0 0 30%;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-right: 1px solid #eee;
|
border-right: 1px solid #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.department-dialog-content .department-dialog-left-panel .input-icon-addon {
|
||||||
|
min-width: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-dialog-content .department-dialog-left-panel .clear-keyword-icon {
|
||||||
|
line-height: 1;
|
||||||
|
padding: 2px;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.department-dialog-content .department-dialog-left-panel .clear-keyword-icon:hover {
|
||||||
|
background: #efefef;
|
||||||
|
}
|
||||||
|
|
||||||
.department-dialog-content .department-dialog-member {
|
.department-dialog-content .department-dialog-member {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 0 0 35%;
|
flex: 0 0 35%;
|
||||||
|
Reference in New Issue
Block a user