grafana/public/app/features/users/UsersListPage.tsx
Alexander Zobnin f1b5014efd
Preferences: Add pagination to org configuration page (#60896)
* Add auth labels and access control metadata to org users search results

* Fix search result JSON model

* Org users: Use API for pagination

* Fix default page size

* Refactor: UsersListPage to functional component

* Refactor: update UsersTable component code style

* Add pagination to the /orgs/{org_id}/users endpoint

* Use pagination on the AdminEditOrgPage

* Add /orgs/{org_id}/users/search endpoint to prevent breaking API

* Use existing search store method

* Remove unnecessary error

* Remove unused

* Add query param to search endpoint

* Fix endpoint docs

* Minor refactor

* Fix number of pages calculation

* Use SearchOrgUsers for all org users methods

* Refactor: GetOrgUsers as a service method

* Minor refactor: rename orgId => orgID

* Fix integration tests

* Fix tests
2023-01-09 11:54:33 +03:00

123 lines
3.4 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { renderMarkdown } from '@grafana/data';
import { HorizontalGroup, Pagination, VerticalGroup } from '@grafana/ui';
import { Page } from 'app/core/components/Page/Page';
import { contextSrv } from 'app/core/core';
import { OrgUser, OrgRole, StoreState } from 'app/types';
import InviteesTable from '../invites/InviteesTable';
import { fetchInvitees } from '../invites/state/actions';
import { selectInvitesMatchingQuery } from '../invites/state/selectors';
import { UsersActionBar } from './UsersActionBar';
import { UsersTable } from './UsersTable';
import { loadUsers, removeUser, updateUser, changePage } from './state/actions';
import { getUsers, getUsersSearchQuery } from './state/selectors';
function mapStateToProps(state: StoreState) {
const searchQuery = getUsersSearchQuery(state.users);
return {
users: getUsers(state.users),
searchQuery: getUsersSearchQuery(state.users),
page: state.users.page,
totalPages: state.users.totalPages,
perPage: state.users.perPage,
invitees: selectInvitesMatchingQuery(state.invites, searchQuery),
externalUserMngInfo: state.users.externalUserMngInfo,
isLoading: state.users.isLoading,
};
}
const mapDispatchToProps = {
loadUsers,
fetchInvitees,
changePage,
updateUser,
removeUser,
};
const connector = connect(mapStateToProps, mapDispatchToProps);
export type Props = ConnectedProps<typeof connector>;
export interface State {
showInvites: boolean;
}
export const UsersListPageUnconnected = ({
users,
page,
totalPages,
invitees,
externalUserMngInfo,
isLoading,
loadUsers,
fetchInvitees,
changePage,
updateUser,
removeUser,
}: Props): JSX.Element => {
const [showInvites, setShowInvites] = useState(false);
const externalUserMngInfoHtml = externalUserMngInfo ? renderMarkdown(externalUserMngInfo) : '';
useEffect(() => {
loadUsers();
fetchInvitees();
}, [fetchInvitees, loadUsers]);
const onRoleChange = (role: OrgRole, user: OrgUser) => {
updateUser({ ...user, role: role });
};
const onShowInvites = () => {
setShowInvites(!showInvites);
};
const renderTable = () => {
if (showInvites) {
return <InviteesTable invitees={invitees} />;
} else {
return (
<VerticalGroup spacing="md">
<UsersTable
users={users}
orgId={contextSrv.user.orgId}
onRoleChange={(role, user) => onRoleChange(role, user)}
onRemoveUser={(user) => removeUser(user.userId)}
/>
<HorizontalGroup justify="flex-end">
<Pagination
onNavigate={changePage}
currentPage={page}
numberOfPages={totalPages}
hideWhenSinglePage={true}
/>
</HorizontalGroup>
</VerticalGroup>
);
}
};
return (
<Page.Contents isLoading={!isLoading}>
<UsersActionBar onShowInvites={onShowInvites} showInvites={showInvites} />
{externalUserMngInfoHtml && (
<div className="grafana-info-box" dangerouslySetInnerHTML={{ __html: externalUserMngInfoHtml }} />
)}
{isLoading && renderTable()}
</Page.Contents>
);
};
export const UsersListPageContent = connector(UsersListPageUnconnected);
export default function UsersListPage() {
return (
<Page navId="users">
<UsersListPageContent />
</Page>
);
}