diff --git a/frontend/src/models/org-group.js b/frontend/src/models/org-group.js
new file mode 100644
index 0000000000..ab55144f69
--- /dev/null
+++ b/frontend/src/models/org-group.js
@@ -0,0 +1,18 @@
+import { Utils } from '../utils/utils';
+import { lang } from '../utils/constants';
+import moment from 'moment';
+
+moment.locale(lang);
+
+class OrgGroupInfo {
+ constructor(object) {
+ this.id = object.id;
+ this.groupName = object.group_name;
+ this.creatorName = object.creator_name;
+ this.creatorEmail = object.creator_email;
+ this.creatorContactEmail = object.creator_contact_email;
+ this.ctime = moment(object.ctime).format('YYYY-MM-DD HH:mm:ss');
+ }
+}
+
+export default OrgGroupInfo;
diff --git a/frontend/src/pages/org-admin/index.js b/frontend/src/pages/org-admin/index.js
index 490196c204..ef33df45af 100644
--- a/frontend/src/pages/org-admin/index.js
+++ b/frontend/src/pages/org-admin/index.js
@@ -8,6 +8,7 @@ import MainPanel from './main-panel';
import OrgUsers from './org-users';
import OrgUsersList from './org-users-list';
import OrgAdminList from './org-admin-list';
+import OrgGroups from './org-groups';
import '../../assets/css/fa-solid.css';
import '../../assets/css/fa-regular.css';
@@ -56,7 +57,7 @@ class Org extends React.Component {
let { isSidePanelClosed, currentTab, isShowAddOrgUserDialog, isShowAddOrgAdminDialog } = this.state;
return (
-
+
+
+
diff --git a/frontend/src/pages/org-admin/org-groups.js b/frontend/src/pages/org-admin/org-groups.js
new file mode 100644
index 0000000000..cabb6e8ded
--- /dev/null
+++ b/frontend/src/pages/org-admin/org-groups.js
@@ -0,0 +1,231 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
+
+import OrgGroupInfo from '../../models/org-group';
+import Toast from '../../components/toast';
+
+import { seafileAPI } from '../../utils/seafile-api';
+import { siteRoot, gettext, orgID } from '../../utils/constants';
+
+class OrgGroups extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ page: 1,
+ pageNext: 2,
+ orgGroups: [],
+ isItemFreezed: false
+ }
+ }
+
+ componentDidMount() {
+ let page = this.state.page;
+ this.initData(page);
+ }
+
+ initData = (page) => {
+ seafileAPI.listOrgGroups(orgID, page).then(res => {
+ let orgGroups = res.data.groups.map(item => {
+ return new OrgGroupInfo(item);
+ });
+
+ this.setState({
+ orgGroups: orgGroups,
+ pageNext: res.data.page_next,
+ page: res.data.page,
+ });
+ });
+ }
+
+
+ onChangePageNum = (e, num) => {
+ e.preventDefault();
+ let page = this.state.page;
+
+ if (num == 1) {
+ page = page + 1;
+ } else {
+ page = page - 1;
+ }
+ this.initData(page);
+ }
+
+ onFreezedItem = () => {
+ this.setState({isItemFreezed: true});
+ }
+
+ onUnfreezedItem = () => {
+ this.setState({isItemFreezed: false});
+ }
+
+ deleteGroupItem = (group) => {
+ seafileAPI.deleteOrgGroup(orgID, group.id).then(res => {
+ this.setState({
+ orgGroups: this.state.orgGroups.filter(item => item.id != group.id)
+ });
+ let msg = gettext('Successfully deleted {name}');
+ msg = msg.replace('{name}', group.groupName);
+ Toast.success(msg);
+ })
+ }
+
+ render() {
+ let groups = this.state.orgGroups;
+ return (
+
+
+
+
{gettext('All Groups')}
+
+
+
+
+
+ {gettext('Name')} |
+ {gettext('Creator')} |
+ {gettext('Created At')} |
+ {gettext('Operations')} |
+
+
+
+ {groups.map(item => {
+ return (
+
+ )})}
+
+
+
+
+
+
+ );
+ }
+}
+
+class GroupItem extends React.Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ highlight: false,
+ showMenu: false,
+ isItemMenuShow: false
+ };
+ }
+
+ onMouseEnter = () => {
+ if (!this.props.isItemFreezed) {
+ this.setState({
+ showMenu: true,
+ highlight: true,
+ });
+ }
+ }
+
+ onMouseLeave = () => {
+ if (!this.props.isItemFreezed) {
+ this.setState({
+ showMenu: false,
+ highlight: false
+ });
+ }
+ }
+
+ onDropdownToggleClick = (e) => {
+ e.preventDefault();
+ this.toggleOperationMenu(e);
+ }
+
+ toggleOperationMenu = (e) => {
+ e.stopPropagation();
+ this.setState(
+ {isItemMenuShow: !this.state.isItemMenuShow }, () => {
+ if (this.state.isItemMenuShow) {
+ this.props.onFreezedItem();
+ } else {
+ this.setState({
+ highlight: false,
+ showMenu: false,
+ });
+ this.props.onUnfreezedItem();
+ }
+ }
+ );
+ }
+
+ toggleDelete = () => {
+ this.props.deleteGroupItem(this.props.group);
+ }
+
+ renderGroupHref = (group) => {
+ let groupInfoHref;
+ if (group.creatorName == 'system admin') {
+ groupInfoHref = siteRoot + 'org/admin/#address-book/groups/' + group.id + '/'
+ } else {
+ groupInfoHref = siteRoot + 'org/groupadmin/' + group.id + '/'
+ }
+
+ return groupInfoHref;
+ }
+
+ renderGroupCreator = (group) => {
+ let userInfoHref = siteRoot + 'org/useradmin/info/' + group.creatorEmail + '/';
+ if (group.creatorName == 'system admin') {
+ return (
+ -- |
+ )
+ } else {
+ return(
+
+ {group.creatorName}
+ |
+ )
+ }
+ }
+
+ render() {
+ let { group } = this.props;
+ let isOperationMenuShow = (group.creatorName != 'system admin') && this.state.showMenu;
+ return (
+
+
+ {group.groupName}
+ |
+ {this.renderGroupCreator(group)}
+ {group.ctime} |
+
+ {isOperationMenuShow &&
+
+
+
+ {gettext('Delete')}
+
+
+ }
+ |
+
+ );
+ }
+
+}
+
+export default OrgGroups;
diff --git a/frontend/src/pages/org-admin/side-panel.js b/frontend/src/pages/org-admin/side-panel.js
index 1a989d985d..41a9a9f12e 100644
--- a/frontend/src/pages/org-admin/side-panel.js
+++ b/frontend/src/pages/org-admin/side-panel.js
@@ -7,10 +7,20 @@ import { gettext, siteRoot } from '../../utils/constants';
const propTypes = {
isSidePanelClosed: PropTypes.bool.isRequired,
onCloseSidePanel: PropTypes.func.isRequired,
+ currentTab: PropTypes.string.isRequired,
+ tabItemClick: PropTypes.func.isRequired
};
class SidePanel extends React.Component {
+ getActiveClass = (tab) => {
+ return this.props.currentTab == tab ? 'active' : '';
+ }
+
+ tabItemClick = (tab) => {
+ this.props.tabItemClick(tab);
+ }
+
render() {
return (