diff --git a/frontend/src/components/dialog/import-members-dialog.js b/frontend/src/components/dialog/import-members-dialog.js new file mode 100644 index 0000000000..9700611ef4 --- /dev/null +++ b/frontend/src/components/dialog/import-members-dialog.js @@ -0,0 +1,68 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Alert, Modal, ModalHeader, ModalBody, ModalFooter, Button } from 'reactstrap'; +import { gettext, siteRoot } from '../../utils/constants'; + +const propTypes = { + toggleImportMembersDialog: PropTypes.func.isRequired, + importMembersInBatch: PropTypes.func.isRequired, +}; + +class ImportMembersDialog extends React.Component { + constructor(props) { + super(props); + this.fileInputRef = React.createRef(); + this.state = { + errorMsg: '' + }; + } + + toggle = () => { + this.props.toggleImportMembersDialog(); + } + + openFileInput = () => { + this.fileInputRef.current.click(); + } + + uploadFile = (e) => { + // no file selected + if (!this.fileInputRef.current.files.length) { + return; + } + // check file extension + let fileName = this.fileInputRef.current.files[0].name; + if(fileName.substr(fileName.lastIndexOf('.') + 1) != 'xlsx') { + this.setState({ + errorMsg: gettext('Please choose a .xlsx file.') + }); + return; + } + const file = this.fileInputRef.current.files[0]; + this.props.importMembersInBatch(file); + this.toggle(); + } + + render() { + let { errorMsg } = this.state; + return ( + + {gettext('Import members from a .xlsx file')} + + +

{gettext('Download an example file')}

+ + + {errorMsg && {errorMsg}} +
+ + + +
+ ); + } +} + +ImportMembersDialog.propTypes = propTypes; + +export default ImportMembersDialog; diff --git a/frontend/src/pages/groups/group-view.js b/frontend/src/pages/groups/group-view.js index c16c351c8d..d0bd60e1e8 100644 --- a/frontend/src/pages/groups/group-view.js +++ b/frontend/src/pages/groups/group-view.js @@ -18,7 +18,7 @@ import CreateDepartmentRepoDialog from '../../components/dialog/create-departmen import DismissGroupDialog from '../../components/dialog/dismiss-group-dialog'; import RenameGroupDialog from '../../components/dialog/rename-group-dialog'; import TransferGroupDialog from '../../components/dialog/transfer-group-dialog'; -// import ImportMembersDialog from '../../components/dialog/import-members-dialog'; +import ImportMembersDialog from '../../components/dialog/import-members-dialog'; import ManageMembersDialog from '../../components/dialog/manage-members-dialog'; import LeaveGroupDialog from '../../components/dialog/leave-group-dialog'; import SharedRepoListView from '../../components/shared-repo-list-view/shared-repo-list-view'; @@ -63,7 +63,7 @@ class GroupView extends React.Component { showRenameGroupDialog: false, showDismissGroupDialog: false, showTransferGroupDialog: false, - // showImportMembersDialog: false, + showImportMembersDialog: false, showManageMembersDialog: false, groupMembers: [], isShowDetails: false, @@ -283,11 +283,24 @@ class GroupView extends React.Component { }); } - // toggleImportMembersDialog= () => { - // this.setState({ - // showImportMembersDialog: !this.state.showImportMembersDialog - // }); - // } + toggleImportMembersDialog= () => { + this.setState({ + showImportMembersDialog: !this.state.showImportMembersDialog + }); + } + + importMembersInBatch= (file) => { + toaster.notify(gettext('It may take some time, please wait.')); + seafileAPI.importGroupMembersViaFile(this.state.currentGroup.id, file).then((res) => { + res.data.failed.map(item => { + const msg = `${item.email}: ${item.error_msg}`; + toaster.danger(msg); + }); + }).catch((error) => { + let errMsg = Utils.getErrorMsg(error); + toaster.danger(errMsg); + }); + } toggleManageMembersDialog = () => { this.setState({ @@ -464,7 +477,7 @@ class GroupView extends React.Component { } {(this.state.isStaff || this.state.isOwner) && } @@ -596,13 +609,12 @@ class GroupView extends React.Component { onGroupChanged={this.props.onGroupChanged} /> } - {/* this.state.showImportMembersDialog && + { this.state.showImportMembersDialog && - */} + } {this.state.showManageMembersDialog && \d+)/group-owned-libraries/(?P[-0-9a-f]{36})/$', GroupOwnedLibrary.as_view(), name='api-v2.1-owned-group-library'), url(r'^api/v2.1/groups/(?P\d+)/members/$', GroupMembers.as_view(), name='api-v2.1-group-members'), url(r'^api/v2.1/groups/(?P\d+)/members/bulk/$', GroupMembersBulk.as_view(), name='api-v2.1-group-members-bulk'), + url(r'^api/v2.1/groups/(?P\d+)/members/import/$', GroupMembersImport.as_view(), name='api-v2.1-group-members-import'), + url(r'^api/v2.1/group-members-import-example/$', GroupMembersImportExample.as_view(), name='api-v2.1-group-members-import-example'), url(r'^api/v2.1/groups/(?P\d+)/members/(?P[^/]+)/$', GroupMember.as_view(), name='api-v2.1-group-member'), url(r'^api/v2.1/search-group/$', SearchGroup.as_view(), name='api-v2.1-search-group'),