Files
keycloak/js/apps/admin-ui/src/components/users/UserDataTableToolbarItems.tsx
Erik Jan de Wit a339e79d3e added exact search option to attributes
Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
2024-10-03 08:48:36 -03:00

233 lines
6.3 KiB
TypeScript

import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import type { UserProfileConfig } from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata";
import {
Button,
ButtonVariant,
Dropdown,
DropdownItem,
DropdownList,
InputGroup,
InputGroupItem,
MenuToggle,
SearchInput,
ToolbarItem,
} from "@patternfly/react-core";
import { ArrowRightIcon, EllipsisVIcon } from "@patternfly/react-icons";
import { ReactNode, useState } from "react";
import { useTranslation } from "react-i18next";
import { useAccess } from "../../context/access/Access";
import { SearchDropdown, SearchType } from "../../user/details/SearchFilter";
import DropdownPanel from "../dropdown-panel/DropdownPanel";
import { UserFilter } from "./UserDataTable";
import { UserDataTableAttributeSearchForm } from "./UserDataTableAttributeSearchForm";
type UserDataTableToolbarItemsProps = {
searchDropdownOpen: boolean;
setSearchDropdownOpen: (open: boolean) => void;
realm: RealmRepresentation;
hasSelectedRows: boolean;
toggleDeleteDialog: () => void;
toggleUnlockUsersDialog: () => void;
goToCreate: () => void;
searchType: SearchType;
setSearchType: (searchType: SearchType) => void;
searchUser: string;
setSearchUser: (searchUser: string) => void;
activeFilters: UserFilter;
setActiveFilters: (activeFilters: UserFilter) => void;
refresh: () => void;
profile: UserProfileConfig;
clearAllFilters: () => void;
createAttributeSearchChips: () => ReactNode;
searchUserWithAttributes: () => void;
};
export function UserDataTableToolbarItems({
searchDropdownOpen,
setSearchDropdownOpen,
realm,
hasSelectedRows,
toggleDeleteDialog,
toggleUnlockUsersDialog,
goToCreate,
searchType,
setSearchType,
searchUser,
setSearchUser,
activeFilters,
setActiveFilters,
refresh,
profile,
clearAllFilters,
createAttributeSearchChips,
searchUserWithAttributes,
}: UserDataTableToolbarItemsProps) {
const { t } = useTranslation();
const [kebabOpen, setKebabOpen] = useState(false);
const { hasAccess } = useAccess();
// Only needs query-users access to attempt add/delete of users.
// This is because the user could have fine-grained access to users
// of a group. There is no way to know this without searching the
// permissions of every group.
const isManager = hasAccess("query-users");
const searchItem = () => {
return (
<ToolbarItem>
<InputGroup>
<InputGroupItem>
<SearchDropdown
searchType={searchType}
onSelect={(searchType) => {
clearAllFilters();
setSearchType(searchType);
}}
/>
</InputGroupItem>
{searchType === "default" && defaultSearchInput()}
{searchType === "attribute" && attributeSearchInput()}
</InputGroup>
</ToolbarItem>
);
};
const defaultSearchInput = () => {
return (
<ToolbarItem>
<SearchInput
data-testid="table-search-input"
placeholder={t("searchForUser")}
aria-label={t("search")}
value={searchUser}
onSearch={(_, _v, attribute) => {
setSearchUser(attribute["haswords"]);
refresh();
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
const target = e.target as HTMLInputElement;
setSearchUser(target.value);
refresh();
}
}}
onClear={() => {
setSearchUser("");
refresh();
}}
/>
</ToolbarItem>
);
};
const attributeSearchInput = () => {
return (
<>
<DropdownPanel
data-testid="select-attributes-dropdown"
buttonText={t("selectAttributes")}
setSearchDropdownOpen={setSearchDropdownOpen}
searchDropdownOpen={searchDropdownOpen}
width="15vw"
>
<UserDataTableAttributeSearchForm
activeFilters={activeFilters}
setActiveFilters={setActiveFilters}
profile={profile}
createAttributeSearchChips={createAttributeSearchChips}
searchUserWithAttributes={() => {
searchUserWithAttributes();
setSearchDropdownOpen(false);
}}
/>
</DropdownPanel>
<Button
icon={<ArrowRightIcon />}
variant="control"
onClick={() => {
searchUserWithAttributes();
setSearchDropdownOpen(false);
}}
aria-label={t("searchAttributes")}
/>
</>
);
};
const bruteForceProtectionToolbarItem = !realm.bruteForceProtected ? (
<ToolbarItem>
<Button
variant={ButtonVariant.link}
onClick={toggleDeleteDialog}
data-testid="delete-user-btn"
isDisabled={hasSelectedRows}
>
{t("deleteUser")}
</Button>
</ToolbarItem>
) : (
<ToolbarItem>
<Dropdown
onOpenChange={(isOpen) => setKebabOpen(isOpen)}
toggle={(ref) => (
<MenuToggle
ref={ref}
isExpanded={kebabOpen}
variant="plain"
onClick={() => setKebabOpen(!kebabOpen)}
>
<EllipsisVIcon />
</MenuToggle>
)}
isOpen={kebabOpen}
shouldFocusToggleOnSelect
>
<DropdownList>
<DropdownItem
key="deleteUser"
component="button"
isDisabled={hasSelectedRows}
onClick={() => {
toggleDeleteDialog();
setKebabOpen(false);
}}
>
{t("deleteUser")}
</DropdownItem>
<DropdownItem
key="unlock"
component="button"
onClick={() => {
toggleUnlockUsersDialog();
setKebabOpen(false);
}}
>
{t("unlockAllUsers")}
</DropdownItem>
</DropdownList>
</Dropdown>
</ToolbarItem>
);
const actionItems = (
<>
<ToolbarItem>
<Button data-testid="add-user" onClick={goToCreate}>
{t("addUser")}
</Button>
</ToolbarItem>
{bruteForceProtectionToolbarItem}
</>
);
return (
<>
{searchItem()}
{isManager ? actionItems : null}
</>
);
}