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 PropTypes from 'prop-types';
|
||||
import { Modal, ModalBody } from 'reactstrap';
|
||||
import { Modal, ModalBody, Input } from 'reactstrap';
|
||||
import { gettext, isOrgContext, username } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api.js';
|
||||
import { Utils } from '../../utils/utils';
|
||||
@@ -38,6 +38,9 @@ class DepartmentDetailDialog extends React.Component {
|
||||
membersLoading: true,
|
||||
selectedMemberMap: {},
|
||||
departmentsTree: [],
|
||||
keyword: '',
|
||||
searching: false,
|
||||
usersFound: []
|
||||
};
|
||||
}
|
||||
|
||||
@@ -127,15 +130,13 @@ class DepartmentDetailDialog extends React.Component {
|
||||
};
|
||||
|
||||
onMemberChecked = (member) => {
|
||||
if (this.state.departmentMembers.indexOf(member) !== -1) {
|
||||
let newMembersTempObj = this.state.newMembersTempObj;
|
||||
if (member.email in newMembersTempObj) {
|
||||
delete newMembersTempObj[member.email];
|
||||
} else {
|
||||
newMembersTempObj[member.email] = member;
|
||||
}
|
||||
this.setState({ newMembersTempObj: newMembersTempObj });
|
||||
let { newMembersTempObj } = this.state;
|
||||
if (member.email in newMembersTempObj) {
|
||||
delete newMembersTempObj[member.email];
|
||||
} else {
|
||||
newMembersTempObj[member.email] = member;
|
||||
}
|
||||
this.setState({ newMembersTempObj: newMembersTempObj });
|
||||
};
|
||||
|
||||
addGroupMember = () => {
|
||||
@@ -162,7 +163,10 @@ class DepartmentDetailDialog extends React.Component {
|
||||
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;
|
||||
for (let member of members) {
|
||||
if (Object.keys(selectedMemberMap).indexOf(member.email) !== -1) {
|
||||
@@ -173,13 +177,58 @@ class DepartmentDetailDialog extends React.Component {
|
||||
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 = () => {
|
||||
const title = this.props.usedFor === 'add_group_member' ? gettext('Select group members') : gettext('Select shared users');
|
||||
return <SeahubModalHeader toggle={this.toggle}>{title}</SeahubModalHeader>;
|
||||
};
|
||||
|
||||
render() {
|
||||
let { departmentsLoading, departments } = this.state;
|
||||
const { departmentsLoading, departments, keyword } = this.state;
|
||||
|
||||
if (departmentsLoading) {
|
||||
return (
|
||||
<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' }}>
|
||||
{this.renderHeader()}
|
||||
<ModalBody className="department-dialog-content">
|
||||
<DepartmentGroup
|
||||
departments={this.state.departments}
|
||||
getMembers={this.getMembers}
|
||||
setCurrent={this.setCurrent}
|
||||
currentDepartment={this.state.currentDepartment}
|
||||
loading={this.state.departmentsLoading}
|
||||
departmentsTree={this.state.departmentsTree}
|
||||
/>
|
||||
<div className="department-dialog-left-panel">
|
||||
<div className="mb-2 position-relative">
|
||||
<i className="sf3-font sf3-font-search input-icon-addon"></i>
|
||||
<Input
|
||||
bsSize="sm"
|
||||
className="px-6"
|
||||
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
|
||||
keyword={this.state.keyword}
|
||||
searching={this.state.searching}
|
||||
usersFound={this.state.usersFound}
|
||||
members={this.state.departmentMembers}
|
||||
memberSelected={this.state.newMembersTempObj}
|
||||
onUserChecked={this.onMemberChecked}
|
||||
currentDepartment={this.state.currentDepartment}
|
||||
selectAll={this.selectAll}
|
||||
unselectAll={this.unselectAll}
|
||||
loading={this.state.membersLoading}
|
||||
selectedMemberMap={this.state.selectedMemberMap}
|
||||
isLoadingMore={this.state.isLoadingMore}
|
||||
|
@@ -94,21 +94,20 @@ const DepartmentGroupMembersPropTypes = {
|
||||
currentDepartment: PropTypes.object.isRequired,
|
||||
selectedMemberMap: PropTypes.object,
|
||||
selectAll: PropTypes.func.isRequired,
|
||||
unselectAll: PropTypes.func.isRequired,
|
||||
loading: PropTypes.bool,
|
||||
usedFor: PropTypes.oneOf(['add_group_member', 'add_user_share']),
|
||||
keyword: PropTypes.string,
|
||||
searching: PropTypes.bool,
|
||||
usersFound: PropTypes.array
|
||||
};
|
||||
|
||||
class DepartmentGroupMembers extends Component {
|
||||
|
||||
selectAll = () => {
|
||||
const { members } = this.props;
|
||||
this.props.selectAll(members);
|
||||
};
|
||||
|
||||
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');
|
||||
if (loading) {
|
||||
if (loading || searching) {
|
||||
return (
|
||||
<div className="department-dialog-member pt-4">
|
||||
<div className="w-100">
|
||||
@@ -119,26 +118,35 @@ class DepartmentGroupMembers extends Component {
|
||||
</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');
|
||||
return (
|
||||
<div className="department-dialog-member pt-4">
|
||||
<div className="w-100">
|
||||
<div className='department-dialog-member-head px-4'>
|
||||
<div className='department-name'>
|
||||
{headerTitle}
|
||||
{keyword ? gettext('Search results') : headerTitle}
|
||||
</div>
|
||||
{enableSelectAll ?
|
||||
<div className='select-all' onClick={this.selectAll}>{gettext('Select All')}</div>
|
||||
:
|
||||
<div className='select-all-disable'>{gettext('Select All')}</div>
|
||||
}
|
||||
{itemsShown.length > 0 && (
|
||||
<>
|
||||
{unselectedItems.length > 0
|
||||
? <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>
|
||||
{members.length > 0 ?
|
||||
{itemsShown.length > 0 ?
|
||||
<Fragment>
|
||||
<table className="department-dialog-member-table">
|
||||
<tbody>
|
||||
{members.map((member, index) => {
|
||||
{itemsShown.map((member, index) => {
|
||||
return (
|
||||
<Item
|
||||
key={index}
|
||||
@@ -156,7 +164,7 @@ class DepartmentGroupMembers extends Component {
|
||||
</Fragment>
|
||||
:
|
||||
<EmptyTip tipSrc={`${mediaUrl}img/no-users-tip.png`}>
|
||||
<h2>{gettext('No members')}</h2>
|
||||
<h2>{keyword ? gettext('No users found') : gettext('No members')}</h2>
|
||||
</EmptyTip>
|
||||
}
|
||||
</div>
|
||||
|
@@ -115,24 +115,22 @@ class DepartmentGroup extends Component {
|
||||
}
|
||||
return (
|
||||
<div className="department-dialog-group">
|
||||
<div>
|
||||
{departments.length > 0 && departments.map((department, index) => {
|
||||
if (department.parent_group_id !== -1) return null;
|
||||
return (
|
||||
<Item
|
||||
key={department.id}
|
||||
department={department}
|
||||
departments={departments}
|
||||
getMembers={this.getMembers}
|
||||
setCurrent={this.props.setCurrent}
|
||||
toggleExpanded={this.toggleExpanded}
|
||||
currentDepartment={this.props.currentDepartment}
|
||||
allMembersClick={this.state.allMembersClick}
|
||||
padding={10}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{departments.length > 0 && departments.map((department, index) => {
|
||||
if (department.parent_group_id !== -1) return null;
|
||||
return (
|
||||
<Item
|
||||
key={department.id}
|
||||
department={department}
|
||||
departments={departments}
|
||||
getMembers={this.getMembers}
|
||||
setCurrent={this.props.setCurrent}
|
||||
toggleExpanded={this.toggleExpanded}
|
||||
currentDepartment={this.props.currentDepartment}
|
||||
allMembersClick={this.state.allMembersClick}
|
||||
padding={10}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -14,12 +14,27 @@
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.department-dialog-content .department-dialog-group {
|
||||
.department-dialog-content .department-dialog-left-panel {
|
||||
flex: 0 0 30%;
|
||||
padding: 1rem;
|
||||
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 {
|
||||
display: flex;
|
||||
flex: 0 0 35%;
|
||||
|
Reference in New Issue
Block a user