mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-03 07:55:36 +00:00
Share to user support select department (#7327)
* select department user when share repo to user * update * update * 01 fix code warnings * 02 change dialog style * 03 manage group members support select department user --------- Co-authored-by: Michael An <2331806369@qq.com>
This commit is contained in:
249
frontend/src/components/dialog/department-detail-dialog.js
Normal file
249
frontend/src/components/dialog/department-detail-dialog.js
Normal file
@@ -0,0 +1,249 @@
|
||||
import React, { Fragment, } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { gettext, isOrgContext, username } from '../../utils/constants';
|
||||
import { Modal, ModalBody } from 'reactstrap';
|
||||
import { seafileAPI } from '../../utils/seafile-api.js';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import toaster from '../../components/toast';
|
||||
import EmptyTip from '../../components/empty-tip';
|
||||
import Loading from '../../components/loading';
|
||||
import Department from '../../models/department';
|
||||
import SeahubModalHeader from '../common/seahub-modal-header';
|
||||
import DepartmentGroup from './department-detail-widget/department-group';
|
||||
import DepartmentGroupMembers from './department-detail-widget/department-group-members';
|
||||
import DepartmentGroupMemberSelected from './department-detail-widget/department-group-member-selected';
|
||||
import '../../css/manage-members-dialog.css';
|
||||
import '../../css/group-departments.css';
|
||||
|
||||
const propTypes = {
|
||||
groupID: PropTypes.any,
|
||||
toggleManageMembersDialog: PropTypes.func,
|
||||
toggleDepartmentDetailDialog: PropTypes.func,
|
||||
isOwner: PropTypes.bool,
|
||||
addUserShares: PropTypes.func,
|
||||
usedFor: PropTypes.oneOf(['add_group_member', 'add_user_share']),
|
||||
userList: PropTypes.array,
|
||||
};
|
||||
|
||||
class DepartmentDetailDialog extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
departments: [],
|
||||
departmentMembers: [],
|
||||
newMembersTempObj: {},
|
||||
currentDepartment: {},
|
||||
departmentsLoading: true,
|
||||
membersLoading: true,
|
||||
selectedMemberMap: {},
|
||||
departmentsTree: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getSelectedMembers();
|
||||
this.getDepartmentsList();
|
||||
}
|
||||
|
||||
getSelectedMembers = () => {
|
||||
const { usedFor, userList, groupID } = this.props;
|
||||
if (usedFor === 'add_user_share') {
|
||||
let selectedMemberMap = {};
|
||||
selectedMemberMap[username] = true;
|
||||
userList.forEach(member => {
|
||||
selectedMemberMap[member.email] = true;
|
||||
});
|
||||
this.setState({ selectedMemberMap });
|
||||
}
|
||||
else if (usedFor === 'add_group_member') {
|
||||
seafileAPI.listGroupMembers(groupID).then((res) => {
|
||||
const groupMembers = res.data;
|
||||
let selectedMemberMap = {};
|
||||
selectedMemberMap[username] = true;
|
||||
groupMembers.forEach(member => {
|
||||
selectedMemberMap[member.email] = true;
|
||||
});
|
||||
this.setState({ selectedMemberMap });
|
||||
}).catch(error => {
|
||||
this.onError(error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onError = (error) => {
|
||||
let errMsg = Utils.getErrorMsg(error, true);
|
||||
if (!error.response || error.response.status !== 403) {
|
||||
toaster.danger(errMsg);
|
||||
}
|
||||
};
|
||||
|
||||
initDepartments(departments) {
|
||||
const parentIdMap = {};
|
||||
for (let i = 0; i < departments.length; i++) {
|
||||
let item = departments[i];
|
||||
parentIdMap[item.parent_group_id] = true;
|
||||
}
|
||||
return departments.map(depart => {
|
||||
depart.hasChild = !!parentIdMap[depart.id];
|
||||
depart.isExpanded = false;
|
||||
return depart;
|
||||
});
|
||||
}
|
||||
|
||||
getDepartmentsList = () => {
|
||||
seafileAPI.listAddressBookDepartments().then((res) => {
|
||||
let departments = res.data.departments.map(item => {
|
||||
return new Department(item);
|
||||
});
|
||||
let currentDepartment = departments.length > 0 ? departments[0] : {};
|
||||
let departmentsTree = this.initDepartments(departments);
|
||||
this.setState({
|
||||
departments: departments,
|
||||
currentDepartment: currentDepartment,
|
||||
departmentsLoading: false,
|
||||
departmentsTree: departmentsTree
|
||||
});
|
||||
this.getMembers(currentDepartment.id);
|
||||
}).catch(error => {
|
||||
this.onError(error);
|
||||
});
|
||||
};
|
||||
|
||||
getMembers = (department_id) => {
|
||||
this.setState({ membersLoading: true });
|
||||
seafileAPI.listAddressBookDepartmentMembers(department_id).then((res) => {
|
||||
this.setState({
|
||||
departmentMembers: res.data.members,
|
||||
membersLoading: false,
|
||||
});
|
||||
}).catch(error => {
|
||||
this.onError(error);
|
||||
});
|
||||
};
|
||||
|
||||
toggle = () => {
|
||||
this.props.toggleDepartmentDetailDialog();
|
||||
};
|
||||
|
||||
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 });
|
||||
}
|
||||
};
|
||||
|
||||
addGroupMember = () => {
|
||||
let emails = Object.keys(this.state.newMembersTempObj);
|
||||
seafileAPI.addGroupMembers(this.props.groupID, emails).then((res) => {
|
||||
this.toggle();
|
||||
this.props.toggleManageMembersDialog();
|
||||
}).catch(error => {
|
||||
this.onError(error);
|
||||
});
|
||||
};
|
||||
|
||||
addUserShares = () => {
|
||||
this.props.addUserShares(this.state.newMembersTempObj);
|
||||
};
|
||||
|
||||
removeSelectedMember = (email) => {
|
||||
let newMembersTempObj = this.state.newMembersTempObj;
|
||||
delete newMembersTempObj[email];
|
||||
this.setState({ newMembersTempObj: newMembersTempObj });
|
||||
};
|
||||
|
||||
setCurrent = (department) => {
|
||||
this.setState({ currentDepartment: department });
|
||||
};
|
||||
|
||||
selectAll = (members) => {
|
||||
let { newMembersTempObj, selectedMemberMap } = this.state;
|
||||
for (let member of members) {
|
||||
if (Object.keys(selectedMemberMap).indexOf(member.email) !== -1) {
|
||||
continue;
|
||||
}
|
||||
newMembersTempObj[member.email] = member;
|
||||
}
|
||||
this.setState({ newMembersTempObj: newMembersTempObj });
|
||||
};
|
||||
|
||||
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;
|
||||
if (departmentsLoading) {
|
||||
return (
|
||||
<Modal isOpen={true} toggle={this.toggle}>
|
||||
{this.renderHeader()}
|
||||
<ModalBody>
|
||||
<div className="d-flex flex-fill align-items-center"><Loading /></div>
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
const emptyTips = (
|
||||
<Modal isOpen={true} toggle={this.toggle}>
|
||||
{this.renderHeader()}
|
||||
<ModalBody>
|
||||
<EmptyTip>
|
||||
<h2>{gettext('No departments')}</h2>
|
||||
</EmptyTip>
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
const details = (
|
||||
<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}
|
||||
/>
|
||||
<DepartmentGroupMembers
|
||||
members={this.state.departmentMembers}
|
||||
memberSelected={this.state.newMembersTempObj}
|
||||
onUserChecked={this.onMemberChecked}
|
||||
currentDepartment={this.state.currentDepartment}
|
||||
selectAll={this.selectAll}
|
||||
loading={this.state.membersLoading}
|
||||
selectedMemberMap={this.state.selectedMemberMap}
|
||||
isLoadingMore={this.state.isLoadingMore}
|
||||
usedFor={this.props.usedFor}
|
||||
/>
|
||||
<DepartmentGroupMemberSelected
|
||||
members={this.state.newMembersTempObj}
|
||||
removeSelectedMember={this.removeSelectedMember}
|
||||
addGroupMember={this.addGroupMember}
|
||||
toggle={this.toggle}
|
||||
addUserShares={this.addUserShares}
|
||||
usedFor={this.props.usedFor}
|
||||
/>
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
);
|
||||
return (
|
||||
<Fragment>
|
||||
{(departments.length > 0 || isOrgContext) ? details : emptyTips}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DepartmentDetailDialog.propTypes = propTypes;
|
||||
|
||||
export default DepartmentDetailDialog;
|
@@ -0,0 +1,107 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, ModalFooter } from 'reactstrap';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
|
||||
const ItemPropTypes = {
|
||||
member: PropTypes.object,
|
||||
removeSelectedMember: PropTypes.func
|
||||
};
|
||||
|
||||
class Item extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
highlight: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleMouseEnter = () => {
|
||||
this.setState({ highlight: true });
|
||||
};
|
||||
|
||||
handleMouseLeave = () => {
|
||||
this.setState({ highlight: false });
|
||||
};
|
||||
|
||||
removeSelectedMember = (email) => {
|
||||
this.props.removeSelectedMember(email);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { member } = this.props;
|
||||
return (
|
||||
<tr
|
||||
className={this.state.highlight ? 'tr-highlight group-item' : 'group-item'}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
>
|
||||
<td width="17%"><img className="avatar" src={member.avatar_url} alt=""/></td>
|
||||
<td width="78%">{member.name}</td>
|
||||
<td width="10%">
|
||||
<i
|
||||
className="sf3-font sf3-font-close cursor-pointer"
|
||||
name={member.email}
|
||||
onClick={this.removeSelectedMember.bind(this, member.email)}>
|
||||
</i>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Item.propTypes = ItemPropTypes;
|
||||
|
||||
|
||||
const DepartmentGroupMemberSelectedPropTypes = {
|
||||
members: PropTypes.object.isRequired,
|
||||
removeSelectedMember: PropTypes.func.isRequired,
|
||||
addGroupMember: PropTypes.func.isRequired,
|
||||
toggle: PropTypes.func.isRequired,
|
||||
usedFor: PropTypes.string,
|
||||
addUserShares: PropTypes.func,
|
||||
};
|
||||
|
||||
class DepartmentGroupMemberSelected extends Component {
|
||||
|
||||
render() {
|
||||
const { members, usedFor } = this.props;
|
||||
return (
|
||||
<div className="department-dialog-member-selected pt-4">
|
||||
<div style={{ height: 'calc(100% - 70px)' }}>
|
||||
<div className='department-dialog-member-head px-4'>
|
||||
<div className='department-name'>{gettext('Selected')}</div>
|
||||
</div>
|
||||
{Object.keys(members).length > 0 &&
|
||||
<table className="department-dialog-member-table">
|
||||
<tbody>
|
||||
{Object.keys(members).map((email, index) => {
|
||||
return (
|
||||
<Item
|
||||
key={index}
|
||||
member={members[email]}
|
||||
removeSelectedMember={this.props.removeSelectedMember}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
</div>
|
||||
<ModalFooter>
|
||||
<Button color="secondary" onClick={this.props.toggle}>{gettext('Cancel')}</Button>
|
||||
{usedFor === 'add_group_member' &&
|
||||
<Button color="primary" onClick={this.props.addGroupMember}>{gettext('Add')}</Button>
|
||||
}
|
||||
{usedFor === 'add_user_share' &&
|
||||
<Button color="primary" onClick={this.props.addUserShares}>{gettext('Add')}</Button>
|
||||
}
|
||||
</ModalFooter>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DepartmentGroupMemberSelected.propTypes = DepartmentGroupMemberSelectedPropTypes;
|
||||
|
||||
export default DepartmentGroupMemberSelected;
|
@@ -0,0 +1,175 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Tooltip } from 'reactstrap';
|
||||
import { gettext, mediaUrl } from '../../../utils/constants';
|
||||
import EmptyTip from '../../../components/empty-tip';
|
||||
import Loading from '../../../components/loading';
|
||||
|
||||
const ItemPropTypes = {
|
||||
member: PropTypes.object,
|
||||
index: PropTypes.number,
|
||||
tip: PropTypes.string,
|
||||
memberSelected: PropTypes.object,
|
||||
isMemberSelected: PropTypes.bool,
|
||||
onUserChecked: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class Item extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
highlight: false,
|
||||
tooltipOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleMouseEnter = () => {
|
||||
this.setState({ highlight: true });
|
||||
};
|
||||
|
||||
handleMouseLeave = () => {
|
||||
this.setState({ highlight: false });
|
||||
};
|
||||
|
||||
onChange = (e) => {
|
||||
const { member } = this.props;
|
||||
this.props.onUserChecked(member);
|
||||
};
|
||||
|
||||
toggleTooltip = () => {
|
||||
this.setState({ tooltipOpen: !this.state.tooltipOpen });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { member, memberSelected, isMemberSelected, index, tip } = this.props;
|
||||
if (isMemberSelected) {
|
||||
return (
|
||||
<tr
|
||||
className={this.state.highlight ? 'tr-highlight group-item' : 'group-item'}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
>
|
||||
<td width="13%">
|
||||
<input type="checkbox" className="vam" checked='checked' disabled/>
|
||||
</td>
|
||||
<td width="11%"><img className="avatar" src={member.avatar_url} alt=""/></td>
|
||||
<td width="60%">{member.name}</td>
|
||||
<td width="16%" className={this.state.highlight ? 'visible' : 'invisible' }>
|
||||
<i className="sf3-font-help sf3-font" id={`no-select-${index}`}></i>
|
||||
<Tooltip placement='bottom' isOpen={this.state.tooltipOpen} toggle={this.toggleTooltip} target={`no-select-${index}`} delay={{ show: 0, hide: 0 }} fade={false}>
|
||||
{tip}
|
||||
</Tooltip>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<tr
|
||||
className={this.state.highlight ? 'tr-highlight group-item' : 'group-item'}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
>
|
||||
<td width="13%">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="vam"
|
||||
onChange={this.onChange}
|
||||
checked={(member.email in memberSelected) ? 'checked' : ''}
|
||||
/>
|
||||
</td>
|
||||
<td width="11%"><img className="avatar" src={member.avatar_url} alt=""/></td>
|
||||
<td width="76%">{member.name}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Item.propTypes = ItemPropTypes;
|
||||
|
||||
|
||||
const DepartmentGroupMembersPropTypes = {
|
||||
members: PropTypes.array.isRequired,
|
||||
memberSelected: PropTypes.object.isRequired,
|
||||
onUserChecked: PropTypes.func.isRequired,
|
||||
currentDepartment: PropTypes.object.isRequired,
|
||||
selectedMemberMap: PropTypes.object,
|
||||
selectAll: PropTypes.func.isRequired,
|
||||
loading: PropTypes.bool,
|
||||
usedFor: PropTypes.oneOf(['add_group_member', 'add_user_share']),
|
||||
};
|
||||
|
||||
class DepartmentGroupMembers extends Component {
|
||||
|
||||
selectAll = () => {
|
||||
const { members } = this.props;
|
||||
this.props.selectAll(members);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { members, memberSelected, loading, selectedMemberMap, currentDepartment, usedFor } = this.props;
|
||||
let headerTitle;
|
||||
if (currentDepartment.id === -1) {
|
||||
headerTitle = gettext('All users');
|
||||
} else {
|
||||
headerTitle = currentDepartment.name + ' ' + gettext('members');
|
||||
}
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="department-dialog-member pt-4">
|
||||
<div className="w-100">
|
||||
<div className='department-dialog-member-head px-4 mt-4'>
|
||||
<Loading />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const enableSelectAll = Object.keys(memberSelected).length < members.length;
|
||||
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}
|
||||
</div>
|
||||
{enableSelectAll ?
|
||||
<div className='select-all' onClick={this.selectAll}>{gettext('Select All')}</div>
|
||||
:
|
||||
<div className='select-all-disable'>{gettext('Select All')}</div>
|
||||
}
|
||||
</div>
|
||||
{members.length > 0 ?
|
||||
<Fragment>
|
||||
<table className="department-dialog-member-table">
|
||||
<tbody>
|
||||
{members.map((member, index) => {
|
||||
return (
|
||||
<Item
|
||||
key={index}
|
||||
index={index}
|
||||
member={member}
|
||||
tip={tip}
|
||||
memberSelected={memberSelected}
|
||||
onUserChecked={this.props.onUserChecked}
|
||||
isMemberSelected={selectedMemberMap[member.email]}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</Fragment>
|
||||
:
|
||||
<EmptyTip tipSrc={`${mediaUrl}img/no-users-tip.png`}>
|
||||
<h2>{gettext('No members')}</h2>
|
||||
</EmptyTip>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DepartmentGroupMembers.propTypes = DepartmentGroupMembersPropTypes;
|
||||
|
||||
export default DepartmentGroupMembers;
|
@@ -0,0 +1,151 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Loading from '../../../components/loading';
|
||||
import { gettext, isOrgContext } from '../../../utils/constants';
|
||||
|
||||
const ItemPropTypes = {
|
||||
department: PropTypes.object,
|
||||
departments: PropTypes.array,
|
||||
getMembers: PropTypes.func.isRequired,
|
||||
setCurrent: PropTypes.func.isRequired,
|
||||
toggleExpanded: PropTypes.func.isRequired,
|
||||
currentDepartment: PropTypes.object,
|
||||
allMembersClick: PropTypes.bool,
|
||||
};
|
||||
|
||||
class Item extends Component {
|
||||
|
||||
getMembers = (e) => {
|
||||
e.stopPropagation();
|
||||
const { department } = this.props;
|
||||
this.props.getMembers(department.id);
|
||||
this.props.setCurrent(department);
|
||||
};
|
||||
|
||||
toggleExpanded = (e) => {
|
||||
e.stopPropagation();
|
||||
this.props.toggleExpanded(this.props.department.id, !this.props.department.isExpanded);
|
||||
};
|
||||
|
||||
renderSubDepartments = () => {
|
||||
const { departments } = this.props;
|
||||
return (
|
||||
<div style={{ paddingLeft: '10px' }}>
|
||||
{departments.map((department, index) => {
|
||||
if (department.parent_group_id !== this.props.department.id) return null;
|
||||
return (
|
||||
<Item
|
||||
key={department.id}
|
||||
department={department}
|
||||
departments={departments}
|
||||
getMembers={this.props.getMembers}
|
||||
setCurrent={this.props.setCurrent}
|
||||
toggleExpanded={this.props.toggleExpanded}
|
||||
currentDepartment={this.props.currentDepartment}
|
||||
allMembersClick={this.props.allMembersClick}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { department, currentDepartment, allMembersClick } = this.props;
|
||||
const isCurrent = !allMembersClick && currentDepartment.id === department.id;
|
||||
const { hasChild, isExpanded } = department;
|
||||
return (
|
||||
<>
|
||||
<div className={isCurrent ? 'tr-highlight group-item' : 'group-item'} onClick={this.getMembers}>
|
||||
{hasChild &&
|
||||
<span
|
||||
className={`sf3-font sf3-font-down ${isExpanded ? '' : 'rotate-270'} d-inline-block`}
|
||||
onClick={this.toggleExpanded}
|
||||
style={{ color: isCurrent ? '#fff' : '#999', fontSize: '12px' }}
|
||||
>
|
||||
</span>
|
||||
}
|
||||
<span style={hasChild ? { paddingLeft: '8px' } : { paddingLeft: '20px' }}>{department.name}</span>
|
||||
</div>
|
||||
{(isExpanded && hasChild) && this.renderSubDepartments()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Item.propTypes = ItemPropTypes;
|
||||
|
||||
|
||||
const DepartmentGroupPropTypes = {
|
||||
departments: PropTypes.array.isRequired,
|
||||
getMembers: PropTypes.func.isRequired,
|
||||
setCurrent: PropTypes.func.isRequired,
|
||||
currentDepartment: PropTypes.object.isRequired,
|
||||
loading: PropTypes.bool,
|
||||
departmentsTree: PropTypes.array,
|
||||
};
|
||||
|
||||
class DepartmentGroup extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
allMembersClick: !!isOrgContext
|
||||
};
|
||||
}
|
||||
|
||||
toggleExpanded = (id, state) => {
|
||||
let departments = this.props.departmentsTree.slice(0);
|
||||
let index = departments.findIndex(item => item.id === id);
|
||||
departments[index].isExpanded = state;
|
||||
this.setState({ departments });
|
||||
};
|
||||
|
||||
getMembers = (department_id) => {
|
||||
this.props.getMembers(department_id);
|
||||
this.setState({ allMembersClick: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { loading } = this.props;
|
||||
let departments = this.props.departmentsTree;
|
||||
if (loading) {
|
||||
return (<Loading/>);
|
||||
}
|
||||
const { allMembersClick } = this.state;
|
||||
return (
|
||||
<div className="department-dialog-group">
|
||||
<div>
|
||||
{isOrgContext &&
|
||||
<div className={allMembersClick ? 'tr-highlight group-item' : 'group-item'}>
|
||||
<span
|
||||
className={'pr-2'}
|
||||
style={{ color: allMembersClick ? '#fff' : '#999', fontSize: '12px' }}
|
||||
/>
|
||||
<span>{gettext('All users')}</span>
|
||||
</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}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DepartmentGroup.propTypes = DepartmentGroupPropTypes;
|
||||
|
||||
export default DepartmentGroup;
|
@@ -10,20 +10,23 @@ import '../../css/manage-members-dialog.css';
|
||||
const propTypes = {
|
||||
groupID: PropTypes.string,
|
||||
isOwner: PropTypes.bool.isRequired,
|
||||
toggleManageMembersDialog: PropTypes.func.isRequired
|
||||
toggleManageMembersDialog: PropTypes.func,
|
||||
toggleDepartmentDetailDialog: PropTypes.func,
|
||||
};
|
||||
|
||||
class ManageMembersDialog extends React.Component {
|
||||
|
||||
render() {
|
||||
const { groupID, isOwner, toggleManageMembersDialog: toggle } = this.props;
|
||||
const { groupID, isOwner } = this.props;
|
||||
return (
|
||||
<Modal isOpen={true} toggle={toggle} className="group-manage-members-dialog">
|
||||
<SeahubModalHeader toggle={toggle}>{gettext('Manage group members')}</SeahubModalHeader>
|
||||
<Modal isOpen={true} toggle={this.props.toggleManageMembersDialog} className="group-manage-members-dialog">
|
||||
<SeahubModalHeader toggle={this.props.toggleManageMembersDialog}>{gettext('Manage group members')}</SeahubModalHeader>
|
||||
<ModalBody className="pb-0">
|
||||
<ListAndAddGroupMembers
|
||||
groupID={groupID}
|
||||
isOwner={isOwner}
|
||||
toggleManageMembersDialog={this.props.toggleManageMembersDialog}
|
||||
toggleDepartmentDetailDialog={this.props.toggleDepartmentDetailDialog}
|
||||
/>
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
|
@@ -1,12 +1,14 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import { gettext, isPro, cloudMode, isOrgContext } from '../../utils/constants';
|
||||
import { Button } from 'reactstrap';
|
||||
import { gettext, isPro } from '../../utils/constants';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import toaster from '../toast';
|
||||
import UserSelect from '../user-select';
|
||||
import SharePermissionEditor from '../select-editor/share-permission-editor';
|
||||
import DepartmentDetailDialog from './department-detail-dialog';
|
||||
|
||||
import '../../css/invitations.css';
|
||||
import '../../css/share-to-user.css';
|
||||
@@ -167,7 +169,9 @@ class ShareToUser extends React.Component {
|
||||
errorMsg: [],
|
||||
permission: 'rw',
|
||||
sharedItems: [],
|
||||
isWiki: this.props.repoType === 'wiki'
|
||||
isWiki: this.props.repoType === 'wiki',
|
||||
tmpUserList: [],
|
||||
isShowDepartmentDetailDialog: false
|
||||
};
|
||||
this.options = [];
|
||||
this.permissions = [];
|
||||
@@ -198,7 +202,16 @@ class ShareToUser extends React.Component {
|
||||
let repoID = this.props.repoID;
|
||||
seafileAPI.listSharedItems(repoID, path, 'user').then((res) => {
|
||||
if (res.data.length !== 0) {
|
||||
this.setState({ sharedItems: res.data });
|
||||
let tmpUserList = res.data.map(item => {
|
||||
return {
|
||||
'email': item.user_info.name,
|
||||
'name': item.user_info.nickname,
|
||||
'avatar_url': item.user_info.avatar_url,
|
||||
'contact_email': item.user_info.contact_email,
|
||||
'permission': item.permission
|
||||
};
|
||||
});
|
||||
this.setState({ sharedItems: res.data, tmpUserList: tmpUserList });
|
||||
}
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
@@ -341,7 +354,88 @@ class ShareToUser extends React.Component {
|
||||
this.setState({ sharedItems: sharedItems });
|
||||
};
|
||||
|
||||
toggleDepartmentDetailDialog = () => {
|
||||
this.setState({ isShowDepartmentDetailDialog: !this.state.isShowDepartmentDetailDialog });
|
||||
};
|
||||
|
||||
addUserShares = (membersSelectedObj) => {
|
||||
let path = this.props.itemPath;
|
||||
let repoID = this.props.repoID;
|
||||
let users = Object.keys(membersSelectedObj);
|
||||
|
||||
if (this.props.isGroupOwnedRepo) {
|
||||
seafileAPI.shareGroupOwnedRepoToUser(repoID, this.state.permission, users, path).then(res => {
|
||||
let errorMsg = [];
|
||||
if (res.data.failed.length > 0) {
|
||||
for (let i = 0 ; i < res.data.failed.length ; i++) {
|
||||
errorMsg[i] = res.data.failed[i];
|
||||
}
|
||||
}
|
||||
// todo modify api
|
||||
let items = res.data.success.map(item => {
|
||||
let sharedItem = {
|
||||
'user_info': { 'nickname': item.user_name, 'name': item.user_email },
|
||||
'permission': item.permission,
|
||||
'share_type': 'user',
|
||||
};
|
||||
return sharedItem;
|
||||
});
|
||||
this.setState({
|
||||
errorMsg: errorMsg,
|
||||
sharedItems: this.state.sharedItems.concat(items),
|
||||
selectedOption: null,
|
||||
permission: 'rw',
|
||||
});
|
||||
this.refs.userSelect.clearSelect();
|
||||
}).catch(error => {
|
||||
if (error.response) {
|
||||
let message = gettext('Library can not be shared to owner.');
|
||||
let errMessage = [];
|
||||
errMessage.push(message);
|
||||
this.setState({
|
||||
errorMsg: errMessage,
|
||||
selectedOption: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
seafileAPI.shareFolder(repoID, path, 'user', this.state.permission, users).then(res => {
|
||||
let errorMsg = [];
|
||||
if (res.data.failed.length > 0) {
|
||||
for (let i = 0 ; i < res.data.failed.length ; i++) {
|
||||
errorMsg[i] = res.data.failed[i];
|
||||
}
|
||||
}
|
||||
this.setState({
|
||||
errorMsg: errorMsg,
|
||||
sharedItems: this.state.sharedItems.concat(res.data.success),
|
||||
selectedOption: null,
|
||||
permission: 'rw',
|
||||
});
|
||||
this.refs.userSelect.clearSelect();
|
||||
}).catch(error => {
|
||||
if (error.response) {
|
||||
let message = gettext('Library can not be shared to owner.');
|
||||
let errMessage = [];
|
||||
errMessage.push(message);
|
||||
this.setState({
|
||||
errorMsg: errMessage,
|
||||
selectedOption: null,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
this.toggleDepartmentDetailDialog();
|
||||
};
|
||||
|
||||
render() {
|
||||
let showDeptBtn = true;
|
||||
if (window.app.config.lang !== 'zh-cn') {
|
||||
showDeptBtn = false;
|
||||
}
|
||||
if (cloudMode && !isOrgContext) {
|
||||
showDeptBtn = false;
|
||||
}
|
||||
let { sharedItems } = this.state;
|
||||
const thead = (
|
||||
<thead>
|
||||
@@ -353,18 +447,28 @@ class ShareToUser extends React.Component {
|
||||
</thead>
|
||||
);
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="share-link-container">
|
||||
<table className="w-xs-200">
|
||||
{thead}
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<UserSelect
|
||||
ref="userSelect"
|
||||
isMulti={true}
|
||||
placeholder={gettext('Search users')}
|
||||
onSelectChange={this.handleSelectChange}
|
||||
/>
|
||||
<div className='add-members'>
|
||||
<UserSelect
|
||||
ref="userSelect"
|
||||
isMulti={true}
|
||||
className={classnames('reviewer-select', { 'user-select-right-btn': showDeptBtn })}
|
||||
placeholder={gettext('Search users...')}
|
||||
onSelectChange={this.handleSelectChange}
|
||||
excludeCurrentUser={false}
|
||||
/>
|
||||
{showDeptBtn &&
|
||||
<span
|
||||
onClick={this.toggleDepartmentDetailDialog}
|
||||
className="sf3-font sf3-font-invite-visitors toggle-detail-btn">
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<SharePermissionEditor
|
||||
@@ -411,8 +515,16 @@ class ShareToUser extends React.Component {
|
||||
onChangeUserPermission={this.onChangeUserPermission}
|
||||
/>
|
||||
</table>
|
||||
{this.state.isShowDepartmentDetailDialog &&
|
||||
<DepartmentDetailDialog
|
||||
toggleDepartmentDetailDialog={this.toggleDepartmentDetailDialog}
|
||||
addUserShares={this.addUserShares}
|
||||
userList={this.state.tmpUserList}
|
||||
usedFor='add_user_share'
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</Fragment>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@ import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, InputGroup, InputGroupText, Input } from 'reactstrap';
|
||||
import { Utils } from '../utils/utils';
|
||||
import { gettext } from '../utils/constants';
|
||||
import { gettext, cloudMode, isOrgContext } from '../utils/constants';
|
||||
import { seafileAPI } from '../utils/seafile-api';
|
||||
import UserSelect from './user-select';
|
||||
import toaster from './toast';
|
||||
@@ -10,6 +10,8 @@ import Loading from './loading';
|
||||
import GroupMembers from './group-members';
|
||||
|
||||
const propTypes = {
|
||||
toggleManageMembersDialog: PropTypes.func,
|
||||
toggleDepartmentDetailDialog: PropTypes.func,
|
||||
groupID: PropTypes.string,
|
||||
isOwner: PropTypes.bool.isRequired
|
||||
};
|
||||
@@ -159,12 +161,24 @@ class ManageMembersDialog extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
onClickDeptBtn = () => {
|
||||
this.props.toggleManageMembersDialog();
|
||||
this.props.toggleDepartmentDetailDialog();
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
isLoading, hasNextPage, groupMembers,
|
||||
keyword, membersFound,
|
||||
searchActive
|
||||
} = this.state;
|
||||
let showDeptBtn = true;
|
||||
if (window.app.config.lang !== 'zh-cn') {
|
||||
showDeptBtn = false;
|
||||
}
|
||||
if (cloudMode && !isOrgContext) {
|
||||
showDeptBtn = false;
|
||||
}
|
||||
return (
|
||||
<Fragment>
|
||||
<p className="mb-2">{gettext('Add group member')}</p>
|
||||
@@ -176,6 +190,9 @@ class ManageMembersDialog extends React.Component {
|
||||
isMulti={true}
|
||||
className="add-members-select"
|
||||
/>
|
||||
{showDeptBtn &&
|
||||
<span onClick={this.onClickDeptBtn} className="sf3-font sf3-font-invite-visitors toggle-detail-btn"></span>
|
||||
}
|
||||
{this.state.selectedOption ?
|
||||
<Button color="primary" onClick={this.addGroupMember}>{gettext('Submit')}</Button> :
|
||||
<Button color="primary" disabled>{gettext('Submit')}</Button>
|
||||
|
Reference in New Issue
Block a user