1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-25 23:02:26 +00:00

Admin new sort (#8002)

* [system admin] Libraries - 'All' & 'Wikis': removed the sort in the table head, added a sort menu

* [org admin] libraries - 'all': removed the sort in the table head, and added a sort menu

* [org admin] users - 'all': removed the sort in the table head, and added a sort menu

* [system admin] Users - 'Database' & 'LDAP(imported)': removed the sort in the table head, added a sort menu

* [system admin] Links - Share Links: removed the sort in the table head, and added a sort menu

* [system admin] Departments - department - members: removed the sort in the table head, added a sort menu, and bugfix

* [org admin] Departments - department - members: removed the sort in the table head, added a sort menu, and bugfix

* [sort menu] fixed the default menu options
This commit is contained in:
llj
2025-07-04 11:55:33 +08:00
committed by GitHub
parent 52c2578e9b
commit 4703a2a75e
19 changed files with 232 additions and 190 deletions

View File

@@ -17,12 +17,12 @@ class SortMenu extends React.Component {
constructor(props) {
super(props);
this.sortOptions = this.props.sortOptions || [
{ value: 'name-asc', text: gettext('By name ascending') },
{ value: 'name-desc', text: gettext('By name descending') },
{ value: 'size-asc', text: gettext('By size ascending') },
{ value: 'size-desc', text: gettext('By size descending') },
{ value: 'time-asc', text: gettext('By time ascending') },
{ value: 'time-desc', text: gettext('By time descending') }
{ value: 'name-asc', text: gettext('Ascending by name') },
{ value: 'name-desc', text: gettext('Descending by name') },
{ value: 'size-asc', text: gettext('Ascending by size') },
{ value: 'size-desc', text: gettext('Descending by size') },
{ value: 'time-asc', text: gettext('Ascending by time') },
{ value: 'time-desc', text: gettext('Descending by time') }
];
this.state = {
isDropdownMenuOpen: false

View File

@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { Dropdown, DropdownToggle } from 'reactstrap';
import Loading from '../../../components/loading';
import EmptyTip from '../../../components/empty-tip';
import SortMenu from '../../../components/sort-menu';
import { gettext } from '../../../utils/constants';
import MemberItem from './member-item';
import RepoItem from './repo-item';
@@ -31,6 +32,13 @@ class Department extends React.Component {
repos: [],
dropdownOpen: false,
};
this.sortOptions = [
{ value: 'name-asc', text: gettext('Ascending by name') },
{ value: 'name-desc', text: gettext('Descending by name') },
{ value: 'role-asc', text: gettext('Ascending by role') },
{ value: 'role-desc', text: gettext('Descending by role') }
];
}
componentDidMount() {
@@ -67,19 +75,8 @@ class Department extends React.Component {
return currentDepartment;
};
sortByName = (e) => {
e.preventDefault();
const sortBy = 'name';
let { sortOrder } = this.props;
sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
this.props.sortItems(sortBy, sortOrder);
};
sortByRole = (e) => {
e.preventDefault();
const sortBy = 'role';
let { sortOrder } = this.props;
sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
onSelectSortOption = (item) => {
const [sortBy, sortOrder] = item.value.split('-');
this.props.sortItems(sortBy, sortOrder);
};
@@ -106,9 +103,7 @@ class Department extends React.Component {
render() {
const { activeNav, repos } = this.state;
const { membersList, isMembersListLoading, sortBy, sortOrder } = this.props;
const sortByName = sortBy === 'name';
const sortByRole = sortBy === 'role';
const sortIcon = <span className={`sort-dirent sf3-font sf3-font-down ${sortOrder === 'asc' ? 'rotate-180' : ''}`}></span>;
const showSortIcon = activeNav == 'members';
const currentDepartment = this.getCurrentDepartment();
return (
@@ -150,6 +145,14 @@ class Department extends React.Component {
<span className={`nav-link ${activeNav === 'repos' ? 'active' : ''}`} onClick={() => this.changeActiveNav('repos')}>{gettext('Libraries')}</span>
</li>
</ul>
{showSortIcon &&
<SortMenu
sortBy={sortBy}
sortOrder={sortOrder}
sortOptions={this.sortOptions}
onSelectSortOption={this.onSelectSortOption}
/>
}
</div>
{activeNav === 'members' &&
@@ -161,8 +164,8 @@ class Department extends React.Component {
<thead>
<tr>
<th width="60px"></th>
<th width="25%" onClick={this.sortByName}>{gettext('Name')}{' '}{sortByName && sortIcon}</th>
<th width="23%" onClick={this.sortByRole}>{gettext('Role')}{' '}{sortByRole && sortIcon}</th>
<th width="25%">{gettext('Name')}</th>
<th width="23%">{gettext('Role')}</th>
<th width="35%">{gettext('Contact email')}</th>
<th width="calc(17% - 60px)">{/* Operations */}</th>
</tr>

View File

@@ -35,8 +35,8 @@ class Departments extends React.Component {
membersList: [],
isTopDepartmentLoading: false,
isMembersListLoading: false,
sortBy: 'name', // 'name' or 'role'
sortOrder: 'asc', // 'asc' or 'desc',
sortBy: '', // 'name' or 'role'
sortOrder: '', // 'asc' or 'desc',
};
}

View File

@@ -52,8 +52,7 @@ class Content extends Component {
};
render() {
// offer 'sort' only for 'all repos'
const { loading, errorMsg, items, pageInfo, curPerPage, sortBy } = this.props;
const { loading, errorMsg, items, pageInfo, curPerPage } = this.props;
if (loading) {
return <Loading />;
} else if (errorMsg) {
@@ -62,8 +61,6 @@ class Content extends Component {
const emptyTip = (
<EmptyTip text={gettext('No libraries')}/>
);
const initialSortIcon = <span className="sf3-font sf3-font-sort3"></span>;
const sortIcon = <span className="sf3-font sf3-font-down"></span>;
const table = (
<Fragment>
<table>
@@ -71,15 +68,7 @@ class Content extends Component {
<tr>
<th width="5%">{/* icon*/}</th>
<th width="25%">{gettext('Name')}</th>
<th width="15%">
{sortBy != undefined ?
<Fragment>
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortByFileCount}>{gettext('Files')} {sortBy == 'file_count' ? sortIcon : initialSortIcon}</a>{' / '}
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortBySize}>{gettext('Size')} {sortBy == 'size' ? sortIcon : initialSortIcon}</a>
</Fragment> :
gettext('Files') / gettext('Size')
}
</th>
<th width="15%">{gettext('Files')} / {gettext('Size')}</th>
<th width="32%">ID</th>
<th width="18%">{gettext('Owner')}</th>
<th width="5%">{/* Operations*/}</th>
@@ -128,8 +117,6 @@ Content.propTypes = {
resetPerPage: PropTypes.func,
pageInfo: PropTypes.object,
curPerPage: PropTypes.number,
sortItems: PropTypes.func,
sortBy: PropTypes.string,
transferRepoItem: PropTypes.func.isRequired,
};
@@ -389,14 +376,16 @@ class OrgAllRepos extends Component {
<MainPanelTopbar />
<div className="main-panel-center flex-row">
<div className="cur-view-container">
<ReposNav currentItem="all" />
<ReposNav
currentItem="all"
sortBy={this.state.sortBy}
sortItems={this.sortItems}
/>
<div className="cur-view-content">
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={this.state.repos}
sortBy={this.state.sortBy}
sortItems={this.sortItems}
pageInfo={this.state.pageInfo}
curPerPage={this.state.perPage}
getListByPage={this.getReposByPage}

View File

@@ -2,9 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Link } from '@gatsbyjs/reach-router';
import { siteRoot, gettext } from '../../../utils/constants';
import SortMenu from '../../../components/sort-menu';
const propTypes = {
currentItem: PropTypes.string.isRequired
currentItem: PropTypes.string.isRequired,
sortBy: PropTypes.string,
sortItems: PropTypes.func
};
class Nav extends React.Component {
@@ -15,10 +18,21 @@ class Nav extends React.Component {
{ name: 'all', urlPart: 'repoadmin', text: gettext('All') },
{ name: 'trash', urlPart: 'repoadmin-trash', text: gettext('Trash') }
];
this.sortOptions = [
{ value: 'file_count-desc', text: gettext('Descending by files') },
{ value: 'size-desc', text: gettext('Descending by size') }
];
}
onSelectSortOption = (item) => {
const [sortBy,] = item.value.split('-');
this.props.sortItems(sortBy);
};
render() {
const { currentItem } = this.props;
const { currentItem, sortBy, sortOrder = 'desc' } = this.props;
const showSortIcon = currentItem == 'all';
return (
<div className="cur-view-path tab-nav-container">
<ul className="nav">
@@ -30,6 +44,14 @@ class Nav extends React.Component {
);
})}
</ul>
{showSortIcon &&
<SortMenu
sortBy={sortBy}
sortOrder={sortOrder}
sortOptions={this.sortOptions}
onSelectSortOption={this.onSelectSortOption}
/>
}
</div>
);
}

View File

@@ -9,10 +9,7 @@ const propTypes = {
changeStatus: PropTypes.func.isRequired,
orgUsers: PropTypes.array.isRequired,
page: PropTypes.number.isRequired,
pageNext: PropTypes.bool.isRequired,
sortByQuotaUsage: PropTypes.func.isRequired,
sortOrder: PropTypes.string.isRequired,
sortBy: PropTypes.string.isRequired,
pageNext: PropTypes.bool.isRequired
};
class OrgUsersList extends React.Component {
@@ -49,21 +46,8 @@ class OrgUsersList extends React.Component {
this.props.initOrgUsersData(page);
};
sortByQuotaUsage = (e) => {
e.preventDefault();
this.props.sortByQuotaUsage();
};
render() {
const { sortBy, sortOrder } = this.props;
let sortIcon;
if (sortBy == '') {
// initial sort icon
sortIcon = <span className="sf3-font sf3-font-sort3"></span>;
} else {
sortIcon = <span className={`sf3-font ${sortOrder == 'asc' ? 'sf3-font-down rotate-180 d-inline-block' : 'sf3-font-down'}`}></span>;
}
let { orgUsers, page, pageNext } = this.props;
const { orgUsers, page, pageNext } = this.props;
return (
<div className="cur-view-content">
<table>
@@ -71,9 +55,7 @@ class OrgUsersList extends React.Component {
<tr>
<th width="30%">{gettext('Name')}</th>
<th width="15%">{gettext('Status')}</th>
<th width="20%">
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortByQuotaUsage}>{gettext('Space Used')} {sortIcon}</a> / {gettext('Quota')}
</th>
<th width="20%">{gettext('Space Used')} / {gettext('Quota')}</th>
<th width="25%">{gettext('Created At')} / {gettext('Last Login')}</th>
<th width="10%">{/* Operations*/}</th>
</tr>

View File

@@ -2,9 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Link } from '@gatsbyjs/reach-router';
import { siteRoot, gettext } from '../../utils/constants';
import SortMenu from '../../components/sort-menu';
const propTypes = {
currentItem: PropTypes.string.isRequired
currentItem: PropTypes.string.isRequired,
sortBy: PropTypes.string,
sortItems: PropTypes.func
};
class Nav extends React.Component {
@@ -15,10 +18,21 @@ class Nav extends React.Component {
{ name: 'all', urlPart: 'useradmin', text: gettext('All') },
{ name: 'admins', urlPart: 'useradmin/admins', text: gettext('Admin') }
];
this.sortOptions = [
{ value: 'quota_usage-asc', text: gettext('Ascending by space used') },
{ value: 'quota_usage-desc', text: gettext('Descending by space used') }
];
}
onSelectSortOption = (item) => {
const [sortBy, sortOrder] = item.value.split('-');
this.props.sortItems(sortBy, sortOrder);
};
render() {
const { currentItem } = this.props;
const { currentItem, sortBy, sortOrder } = this.props;
const showSortIcon = currentItem == 'all';
return (
<div className="cur-view-path tab-nav-container">
<ul className="nav">
@@ -30,6 +44,14 @@ class Nav extends React.Component {
);
})}
</ul>
{showSortIcon &&
<SortMenu
sortBy={sortBy}
sortOrder={sortOrder}
sortOptions={this.sortOptions}
onSelectSortOption={this.onSelectSortOption}
/>
}
</div>
);
}

View File

@@ -102,10 +102,10 @@ class OrgUsers extends Component {
});
}
sortByQuotaUsage = () => {
sortByQuotaUsage = (sortBy, sortOrder) => {
this.setState({
sortBy: 'quota_usage',
sortOrder: this.state.sortOrder == 'asc' ? 'desc' : 'asc',
sortBy: sortBy,
sortOrder: sortOrder,
page: 1
}, () => {
let url = new URL(location.href);
@@ -309,7 +309,12 @@ class OrgUsers extends Component {
<MainPanelTopbar children={topbarChildren} search={this.getSearch()}/>
<div className="main-panel-center flex-row">
<div className="cur-view-container">
<Nav currentItem="all" />
<Nav
currentItem="all"
sortBy={this.state.sortBy}
sortOrder={this.state.sortOrder}
sortItems={this.sortByQuotaUsage}
/>
<OrgUsersList
initOrgUsersData={this.initOrgUsersData}
toggleDelete={this.toggleOrgUsersDelete}
@@ -317,9 +322,6 @@ class OrgUsers extends Component {
orgUsers={this.state.orgUsers}
page={this.state.page}
pageNext={this.state.pageNext}
sortBy={this.state.sortBy}
sortOrder={this.state.sortOrder}
sortByQuotaUsage={this.sortByQuotaUsage}
/>
</div>
</div>

View File

@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Dropdown, DropdownToggle } from 'reactstrap';
import { gettext } from '../../../utils/constants';
import SortMenu from '../../../components/sort-menu';
import Paginator from '../../../components/paginator';
import Loading from '../../../components/loading';
import EmptyTip from '../../../components/empty-tip';
@@ -41,6 +42,13 @@ class Department extends React.Component {
showDeleteRepoDialog: false,
dropdownOpen: false,
};
this.sortOptions = [
{ value: 'name-asc', text: gettext('Ascending by name') },
{ value: 'name-desc', text: gettext('Descending by name') },
{ value: 'role-asc', text: gettext('Ascending by role') },
{ value: 'role-desc', text: gettext('Descending by role') }
];
}
componentDidMount() {
@@ -95,19 +103,8 @@ class Department extends React.Component {
return currentDepartment;
};
sortByName = (e) => {
e.preventDefault();
const sortBy = 'name';
let { sortOrder } = this.props;
sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
this.props.sortItems(sortBy, sortOrder);
};
sortByRole = (e) => {
e.preventDefault();
const sortBy = 'role';
let { sortOrder } = this.props;
sortOrder = sortOrder === 'asc' ? 'desc' : 'asc';
onSelectSortOption = (item) => {
const [sortBy, sortOrder] = item.value.split('-');
this.props.sortItems(sortBy, sortOrder);
};
@@ -129,9 +126,7 @@ class Department extends React.Component {
render() {
const { activeNav, repos } = this.state;
const { membersList, isMembersListLoading, sortBy, sortOrder } = this.props;
const sortByName = sortBy === 'name';
const sortByRole = sortBy === 'role';
const sortIcon = <span className={`sort-dirent sf3-font sf3-font-down ${sortOrder === 'asc' ? 'rotate-180' : ''}`}></span>;
const showSortIcon = activeNav == 'members';
const currentDepartment = this.getCurrentDepartment();
return (
@@ -173,6 +168,15 @@ class Department extends React.Component {
<span className={`nav-link ${activeNav === 'repos' ? 'active' : ''}`} onClick={() => this.changeActiveNav('repos')}>{gettext('Libraries')}</span>
</li>
</ul>
{showSortIcon &&
<SortMenu
sortBy={sortBy}
sortOrder={sortOrder}
sortOptions={this.sortOptions}
onSelectSortOption={this.onSelectSortOption}
/>
}
</div>
{activeNav === 'members' && (
@@ -187,8 +191,8 @@ class Department extends React.Component {
<thead>
<tr>
<th width="10%"></th>
<th width="25%" onClick={this.sortByName}>{gettext('Name')}{' '}{sortByName && sortIcon}</th>
<th width="25%" onClick={this.sortByRole}>{gettext('Role')}{' '}{sortByRole && sortIcon}</th>
<th width="25%">{gettext('Name')}</th>
<th width="25%">{gettext('Role')}</th>
<th width="30%">{gettext('Contact email')}</th>
<th width="10%">{/* Operations */}</th>
</tr>

View File

@@ -35,8 +35,8 @@ class Departments extends React.Component {
membersList: [],
isTopDepartmentLoading: false,
isMembersListLoading: false,
sortBy: 'name', // 'name' or 'role'
sortOrder: 'asc', // 'asc' or 'desc',
sortBy: '', // 'name' or 'role'
sortOrder: '', // 'asc' or 'desc',
currentPageInfo: null,
currentPage: 1,
perPage: 100

View File

@@ -2,9 +2,13 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Link } from '@gatsbyjs/reach-router';
import { siteRoot, gettext } from '../../../utils/constants';
import SortMenu from '../../../components/sort-menu';
const propTypes = {
currentItem: PropTypes.string.isRequired
currentItem: PropTypes.string.isRequired,
sortBy: PropTypes.string,
sortOrder: PropTypes.string,
sortItems: PropTypes.func
};
class Nav extends React.Component {
@@ -15,10 +19,23 @@ class Nav extends React.Component {
{ name: 'shareLinks', urlPart: 'share-links', text: gettext('Share Links') },
{ name: 'uploadLinks', urlPart: 'upload-links', text: gettext('Upload Links') },
];
this.sortOptions = [
{ value: 'ctime-asc', text: gettext('Ascending by creation time') },
{ value: 'ctime-desc', text: gettext('Descending by creation time') },
{ value: 'view_cnt-asc', text: gettext('Ascending by visit count') },
{ value: 'view_cnt-desc', text: gettext('Descending by visit count') }
];
}
onSelectSortOption = (item) => {
const [sortBy, sortOrder] = item.value.split('-');
this.props.sortItems(sortBy, sortOrder);
};
render() {
const { currentItem } = this.props;
const { currentItem, sortBy, sortOrder } = this.props;
const showSortIcon = currentItem == 'shareLinks';
return (
<div className="cur-view-path tab-nav-container">
<ul className="nav">
@@ -30,6 +47,14 @@ class Nav extends React.Component {
);
})}
</ul>
{showSortIcon &&
<SortMenu
sortBy={sortBy}
sortOrder={sortOrder}
sortOptions={this.sortOptions}
onSelectSortOption={this.onSelectSortOption}
/>
}
</div>
);
}

View File

@@ -31,21 +31,10 @@ class Content extends Component {
this.props.getShareLinksByPage(this.props.currentPage + 1);
};
sortByTime = (e) => {
e.preventDefault();
this.props.sortItems('ctime');
};
sortByCount = (e) => {
e.preventDefault();
this.props.sortItems('view_cnt');
};
render() {
const {
loading, errorMsg, items,
perPage, currentPage, hasNextPage,
sortBy, sortOrder
perPage, currentPage, hasNextPage
} = this.props;
if (loading) {
return <Loading />;
@@ -57,8 +46,6 @@ class Content extends Component {
</EmptyTip>
);
const initialSortIcon = <span className="sf3-font sf3-font-sort3"></span>;
const sortIcon = <span className={`sf3-font ${sortOrder == 'asc' ? 'sf3-font-down rotate-180 d-inline-block' : 'sf3-font-down'}`}></span>;
const table = (
<Fragment>
<table className="table-hover">
@@ -67,12 +54,8 @@ class Content extends Component {
<th width="18%">{gettext('Name')}</th>
<th width="18%">{gettext('Token')}</th>
<th width="18%">{gettext('Owner')}</th>
<th width="15%">
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortByTime}>{gettext('Created At')} {sortBy == 'ctime' ? sortIcon : initialSortIcon}</a>
</th>
<th width="10%">
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortByCount}>{gettext('Visit count')} {sortBy == 'view_cnt' ? sortIcon : initialSortIcon}</a>
</th>
<th width="15%">{gettext('Created At')}</th>
<th width="10%">{gettext('Visit count')}</th>
<th width="11%">{gettext('Expiration')}</th>
<th width="10%">{/* Operations*/}</th>
</tr>
@@ -115,9 +98,6 @@ Content.propTypes = {
pageInfo: PropTypes.object,
hasNextPage: PropTypes.bool,
getShareLinksByPage: PropTypes.func.isRequired,
sortItems: PropTypes.func.isRequired,
sortBy: PropTypes.string.isRequired,
sortOrder: PropTypes.string.isRequired,
deleteShareLink: PropTypes.func.isRequired,
};
@@ -242,11 +222,11 @@ class ShareLinks extends Component {
});
};
sortItems = (sortBy) => {
sortItems = (sortBy, sortOrder) => {
this.setState({
currentPage: 1,
sortBy: sortBy,
sortOrder: this.state.sortOrder == 'asc' ? 'desc' : 'asc'
sortOrder: sortOrder
}, () => {
let url = new URL(location.href);
let searchParams = new URLSearchParams(url.search);
@@ -285,7 +265,12 @@ class ShareLinks extends Component {
<MainPanelTopbar {...this.props} />
<div className="main-panel-center flex-row">
<div className="cur-view-container">
<LinksNav currentItem="shareLinks" />
<LinksNav
currentItem="shareLinks"
sortBy={this.state.sortBy}
sortOrder={this.state.sortOrder}
sortItems={this.sortItems}
/>
<div className="cur-view-content">
<Content
loading={this.state.loading}
@@ -296,9 +281,6 @@ class ShareLinks extends Component {
hasNextPage={hasNextPage}
getShareLinksByPage={this.getShareLinksByPage}
resetPerPage={this.resetPerPage}
sortBy={this.state.sortBy}
sortOrder={this.state.sortOrder}
sortItems={this.sortItems}
deleteShareLink={this.deleteShareLink}
/>
</div>

View File

@@ -155,14 +155,16 @@ class AllRepos extends Component {
</MainPanelTopbar>
<div className="main-panel-center flex-row">
<div className="cur-view-container">
<ReposNav currentItem="all" />
<ReposNav
currentItem="all"
sortBy={this.state.sortBy}
sortItems={this.sortItems}
/>
<div className="cur-view-content">
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={this.state.repos}
sortBy={this.state.sortBy}
sortItems={this.sortItems}
pageInfo={this.state.pageInfo}
curPerPage={this.state.perPage}
getListByPage={this.getReposByPage}

View File

@@ -96,14 +96,16 @@ class AllWikis extends Component {
<MainPanelTopbar {...this.props} />
<div className="main-panel-center flex-row">
<div className="cur-view-container">
<ReposNav currentItem="wikis" />
<ReposNav
currentItem="wikis"
sortBy={this.state.sortBy}
sortItems={this.sortItems}
/>
<div className="cur-view-content">
<Content
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={this.state.wikis}
sortBy={this.state.sortBy}
sortItems={this.sortItems}
pageInfo={this.state.pageInfo}
curPerPage={this.state.perPage}
getListByPage={this.getWikisByPage}

View File

@@ -2,9 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Link } from '@gatsbyjs/reach-router';
import { siteRoot, gettext } from '../../../utils/constants';
import SortMenu from '../../../components/sort-menu';
const propTypes = {
currentItem: PropTypes.string.isRequired
currentItem: PropTypes.string.isRequired,
sortBy: PropTypes.string,
sortItems: PropTypes.func
};
class Nav extends React.Component {
@@ -17,10 +20,20 @@ class Nav extends React.Component {
{ name: 'system', urlPart: 'system-library', text: gettext('System') },
{ name: 'trash', urlPart: 'trash-libraries', text: gettext('Trash') }
];
this.sortOptions = [
{ value: 'file_count-desc', text: gettext('Descending by files') },
{ value: 'size-desc', text: gettext('Descending by size') }
];
}
onSelectSortOption = (item) => {
const [sortBy,] = item.value.split('-');
this.props.sortItems(sortBy);
};
render() {
const { currentItem } = this.props;
const { currentItem, sortBy, sortOrder = 'desc' } = this.props;
const showSortIcon = currentItem == 'all' || currentItem == 'wikis';
return (
<div className="cur-view-path tab-nav-container">
<ul className="nav">
@@ -32,6 +45,14 @@ class Nav extends React.Component {
);
})}
</ul>
{showSortIcon &&
<SortMenu
sortBy={sortBy}
sortOrder={sortOrder}
sortOptions={this.sortOptions}
onSelectSortOption={this.onSelectSortOption}
/>
}
</div>
);
}

View File

@@ -43,19 +43,8 @@ class Content extends Component {
this.props.getListByPage(this.props.pageInfo.current_page + 1);
};
sortByFileCount = (e) => {
e.preventDefault();
this.props.sortItems('file_count');
};
sortBySize = (e) => {
e.preventDefault();
this.props.sortItems('size');
};
render() {
// offer 'sort' only for 'all repos'
const { loading, errorMsg, items, pageInfo, curPerPage, sortBy } = this.props;
const { loading, errorMsg, items, pageInfo, curPerPage } = this.props;
if (loading) {
return <Loading />;
} else if (errorMsg) {
@@ -65,8 +54,6 @@ class Content extends Component {
<EmptyTip text={gettext('No libraries')}>
</EmptyTip>
);
const initialSortIcon = <span className="sf3-font sf3-font-sort3"></span>;
const sortIcon = <span className="sf3-font sf3-font-down"></span>;
const table = (
<Fragment>
<table>
@@ -74,20 +61,10 @@ class Content extends Component {
<tr>
<th width="5%">{/* icon*/}</th>
<th width="25%">{gettext('Name')}</th>
<th width="15%">
{sortBy != undefined ?
<Fragment>
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortByFileCount}>{gettext('Files')} {sortBy == 'file_count' ? sortIcon : initialSortIcon}</a>{' / '}
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortBySize}>{gettext('Size')} {sortBy == 'size' ? sortIcon : initialSortIcon}</a>
</Fragment> :
<>{gettext('Files')}{' / '}{gettext('Size')}</>
}
</th>
<Fragment>
<th width="15%">{gettext('Files')} / {gettext('Size')}</th>
<th width="32%">ID</th>
<th width="18%">{gettext('Owner')}</th>
<th width="5%">{/* Operations*/}</th>
</Fragment>
</tr>
</thead>
<tbody>
@@ -134,8 +111,6 @@ Content.propTypes = {
resetPerPage: PropTypes.func,
pageInfo: PropTypes.object,
curPerPage: PropTypes.number,
sortItems: PropTypes.func,
sortBy: PropTypes.string,
onTransferRepo: PropTypes.func.isRequired,
};

View File

@@ -57,8 +57,7 @@ class Content extends Component {
render() {
const {
isAdmin, loading, errorMsg, items, isAllUsersSelected,
curPerPage, hasNextPage, currentPage,
sortBy, sortOrder
curPerPage, hasNextPage, currentPage
} = this.props;
if (loading) {
@@ -68,20 +67,7 @@ class Content extends Component {
} else {
let columns = [];
let sortIcon;
if (sortBy == '') {
// initial sort icon
sortIcon = <span className="sf3-font sf3-font-sort3"></span>;
} else {
sortIcon = <span className={`sf3-font ${sortOrder == 'asc' ? 'sf3-font-down rotate-180 d-inline-block' : 'sf3-font-down'}`}></span>;
}
const spaceText = gettext('Space Used');
const spaceEl =
sortBy != undefined ? // only offer 'sort' for 'DB' & 'LDAPImported' users
<a className="d-inline-block table-sort-op" href="#" onClick={this.sortByQuotaUsage}>{spaceText} {sortIcon}</a> :
spaceText;
const colSpaceText = <Fragment>{spaceEl}{` / ${gettext('Quota')}`}</Fragment>;
const colSpaceText = `${gettext('Space Used')} / ${gettext('Quota')}`;
const colNameText = `${gettext('Name')} / ${gettext('Contact Email')}`;
const colCreatedText = `${gettext('Created At')} / ${gettext('Last Login')} / ${gettext('Last Access')}`;
if (isPro) {

View File

@@ -2,9 +2,13 @@ import React from 'react';
import PropTypes from 'prop-types';
import { Link } from '@gatsbyjs/reach-router';
import { siteRoot, gettext, haveLDAP, isDefaultAdmin } from '../../../utils/constants';
import SortMenu from '../../../components/sort-menu';
const propTypes = {
currentItem: PropTypes.string.isRequired
currentItem: PropTypes.string.isRequired,
sortBy: PropTypes.string,
sortOrder: PropTypes.string,
sortItems: PropTypes.func
};
class Nav extends React.Component {
@@ -25,10 +29,21 @@ class Nav extends React.Component {
{ name: 'admin', urlPart: 'users/admins', text: gettext('Admin') }
);
}
this.sortOptions = [
{ value: 'quota_usage-asc', text: gettext('Ascending by space used') },
{ value: 'quota_usage-desc', text: gettext('Descending by space used') }
];
}
onSelectSortOption = (item) => {
const [sortBy, sortOrder] = item.value.split('-');
this.props.sortItems(sortBy, sortOrder);
};
render() {
const { currentItem } = this.props;
const { currentItem, sortBy, sortOrder } = this.props;
const showSortIcon = currentItem == 'database' || currentItem == 'ldap-imported';
return (
<div className="cur-view-path tab-nav-container">
<ul className="nav">
@@ -40,6 +55,14 @@ class Nav extends React.Component {
);
})}
</ul>
{showSortIcon &&
<SortMenu
sortBy={sortBy}
sortOrder={sortOrder}
sortOptions={this.sortOptions}
onSelectSortOption={this.onSelectSortOption}
/>
}
</div>
);
}

View File

@@ -227,10 +227,10 @@ class Users extends Component {
});
};
sortByQuotaUsage = () => {
sortByQuotaUsage = (sortBy, sortOrder) => {
this.setState({
sortBy: 'quota_usage',
sortOrder: this.state.sortOrder == 'asc' ? 'desc' : 'asc',
sortBy: sortBy,
sortOrder: sortOrder,
currentPage: 1
}, () => {
const { currentPage, perPage, sortBy, sortOrder } = this.state;
@@ -504,7 +504,12 @@ class Users extends Component {
</MainPanelTopbar>
<div className="main-panel-center flex-row">
<div className="cur-view-container">
<UsersNav currentItem={curTab} />
<UsersNav
currentItem={curTab}
sortBy={this.state.sortBy}
sortOrder={this.state.sortOrder}
sortItems={this.sortByQuotaUsage}
/>
<div className="cur-view-content">
{curTab == 'database' &&
<UsersFilterBar
@@ -520,9 +525,6 @@ class Users extends Component {
loading={this.state.loading}
errorMsg={this.state.errorMsg}
items={this.state.userList}
sortBy={this.state.sortBy}
sortOrder={this.state.sortOrder}
sortByQuotaUsage={this.sortByQuotaUsage}
currentPage={this.state.currentPage}
hasNextPage={this.state.hasNextPage}
curPerPage={this.state.perPage}