keycloak-scim/src/client-scopes/ClientScopesSection.tsx

256 lines
7.8 KiB
TypeScript
Raw Normal View History

import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import {
AlertVariant,
Button,
ButtonVariant,
Dropdown,
DropdownItem,
KebabToggle,
PageSection,
ToolbarItem,
} from "@patternfly/react-core";
import { cellWidth } from "@patternfly/react-table";
import { useAdminClient } from "../context/auth/AdminClient";
import { ViewHeader } from "../components/view-header/ViewHeader";
import { useAlerts } from "../components/alert/Alerts";
2020-12-11 10:28:38 +00:00
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { useRealm } from "../context/realm-context/RealmContext";
import { upperCaseFormatter, emptyFormatter } from "../util";
import {
CellDropdown,
ClientScope,
AllClientScopes,
ClientScopeDefaultOptionalType,
changeScope,
removeScope,
} from "../components/client-scope/ClientScopeTypes";
import { ChangeTypeDialog } from "./ChangeTypeDialog";
import { toNewClientScope } from "./routes/NewClientScope";
import "./client-scope.css";
import { toClientScope } from "./routes/ClientScope";
import { useWhoAmI } from "../context/whoami/WhoAmI";
export const ClientScopesSection = () => {
const { realm } = useRealm();
const { whoAmI } = useWhoAmI();
const { t } = useTranslation("client-scopes");
const adminClient = useAdminClient();
const { addAlert, addError } = useAlerts();
const [key, setKey] = useState(0);
const refresh = () => setKey(new Date().getTime());
const [kebabOpen, setKebabOpen] = useState(false);
const [changeTypeOpen, setChangeTypeOpen] = useState(false);
const [selectedScopes, setSelectedScopes] = useState<
ClientScopeDefaultOptionalType[]
>([]);
const loader = async () => {
const defaultScopes =
await adminClient.clientScopes.listDefaultClientScopes();
const optionalScopes =
await adminClient.clientScopes.listDefaultOptionalClientScopes();
const clientScopes = await adminClient.clientScopes.find();
return clientScopes
.map((scope) => {
return {
...scope,
type: defaultScopes.find(
(defaultScope) => defaultScope.name === scope.name
)
? ClientScope.default
: optionalScopes.find(
(optionalScope) => optionalScope.name === scope.name
)
? ClientScope.optional
: AllClientScopes.none,
};
})
.sort((a, b) => a.name!.localeCompare(b.name!, whoAmI.getLocale()));
};
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
titleKey: t("deleteClientScope", {
count: selectedScopes.length,
name: selectedScopes[0]?.name,
}),
messageKey: "client-scopes:deleteConfirm",
continueButtonLabel: "common:delete",
continueButtonVariant: ButtonVariant.danger,
onConfirm: async () => {
try {
for (const scope of selectedScopes) {
2021-05-27 11:41:29 +00:00
try {
await removeScope(adminClient, scope);
} catch (error) {
console.warn(
"could not remove scope",
error.response?.data?.errorMessage || error
);
}
await adminClient.clientScopes.del({ id: scope.id! });
}
addAlert(t("deletedSuccess"), AlertVariant.success);
refresh();
} catch (error) {
addError("client-scopes:deleteError", error);
}
},
});
const TypeSelector = (scope: ClientScopeDefaultOptionalType) => (
2021-08-26 12:15:28 +00:00
<CellDropdown
clientScope={scope}
type={scope.type}
all
onSelect={async (value) => {
try {
await changeScope(adminClient, scope, value);
addAlert(t("clientScopeSuccess"), AlertVariant.success);
refresh();
} catch (error) {
addError("client-scopes:clientScopeError", error);
}
}}
/>
);
const ClientScopeDetailLink = ({
id,
type,
name,
}: ClientScopeDefaultOptionalType) => (
2021-08-26 12:15:28 +00:00
<Link
key={id}
to={toClientScope({ realm, id: id!, type, tab: "settings" })}
>
{name}
</Link>
);
return (
<>
<DeleteConfirm />
{changeTypeOpen && (
<ChangeTypeDialog
selectedClientScopes={selectedScopes.length}
onConfirm={(type) => {
selectedScopes.map(async (scope) => {
try {
await changeScope(adminClient, scope, type);
addAlert(t("clientScopeSuccess"), AlertVariant.success);
refresh();
} catch (error) {
addError("client-scopes:clientScopeError", error);
}
});
setChangeTypeOpen(false);
}}
onClose={() => setChangeTypeOpen(false)}
/>
)}
<ViewHeader
titleKey="clientScopes"
subKey="client-scopes:clientScopeExplain"
/>
<PageSection variant="light" className="pf-u-p-0">
2020-12-11 10:18:29 +00:00
<KeycloakDataTable
key={key}
loader={loader}
ariaLabelKey="client-scopes:clientScopeList"
searchPlaceholderKey="client-scopes:searchFor"
onSelect={(clientScopes) => setSelectedScopes([...clientScopes])}
canSelectAll
toolbarItem={
<>
<ToolbarItem>
{/* @ts-ignore */}
<Button component={Link} to={toNewClientScope({ realm })}>
{t("createClientScope")}
</Button>
</ToolbarItem>
<ToolbarItem>
<Dropdown
toggle={
<KebabToggle onToggle={() => setKebabOpen(!kebabOpen)} />
}
isOpen={kebabOpen}
isPlain
dropdownItems={[
<DropdownItem
key="changeType"
component="button"
isDisabled={selectedScopes.length === 0}
onClick={() => {
setChangeTypeOpen(true);
setKebabOpen(false);
}}
>
{t("changeType")}
</DropdownItem>,
<DropdownItem
key="action"
component="button"
isDisabled={selectedScopes.length === 0}
onClick={() => {
toggleDeleteDialog();
setKebabOpen(false);
}}
>
{t("common:delete")}
</DropdownItem>,
]}
/>
</ToolbarItem>
</>
}
actions={[
{
title: t("common:export"),
},
{
title: t("common:delete"),
onRowClick: (clientScope) => {
setSelectedScopes([clientScope]);
toggleDeleteDialog();
},
},
]}
columns={[
{
name: "name",
cellRenderer: ClientScopeDetailLink,
},
{
name: "type",
displayKey: "client-scopes:assignedType",
cellRenderer: TypeSelector,
},
{
name: "protocol",
displayKey: "client-scopes:protocol",
cellFormatters: [upperCaseFormatter()],
transforms: [cellWidth(15)],
},
{
name: "attributes['gui.order']",
displayKey: "client-scopes:displayOrder",
cellFormatters: [emptyFormatter()],
transforms: [cellWidth(15)],
},
{ name: "description", cellFormatters: [emptyFormatter()] },
]}
/>
</PageSection>
</>
);
};