parent
8fbcf7582a
commit
7f66895631
5 changed files with 98 additions and 26 deletions
|
@ -20,6 +20,7 @@
|
|||
"@patternfly/react-core": "^4.40.4",
|
||||
"@patternfly/react-icons": "^4.7.2",
|
||||
"@patternfly/react-table": "^4.15.5",
|
||||
"file-saver": "^2.0.2",
|
||||
"i18next": "^19.6.2",
|
||||
"i18next-http-backend": "^1.0.18",
|
||||
"keycloak-js": "^11.0.0",
|
||||
|
@ -47,6 +48,7 @@
|
|||
"@testing-library/react": "^10.4.6",
|
||||
"@types/dot": "^1.1.4",
|
||||
"@types/enzyme": "^3.10.5",
|
||||
"@types/file-saver": "^2.0.1",
|
||||
"@types/jest": "^26.0.4",
|
||||
"@types/react": "^16.9.23",
|
||||
"@types/react-dom": "^16.9.5",
|
||||
|
|
|
@ -3,10 +3,14 @@ import { render } from "@testing-library/react";
|
|||
|
||||
import clientMock from "./mock-clients.json";
|
||||
import { ClientList } from "./ClientList";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
import { i18n } from "../i18n";
|
||||
|
||||
test("renders ClientList", () => {
|
||||
const { getByText } = render(
|
||||
<ClientList clients={clientMock} baseUrl="http://blog.nerdin.ch" />
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<ClientList clients={clientMock} baseUrl="http://blog.nerdin.ch" />
|
||||
</I18nextProvider>
|
||||
);
|
||||
const headerElement = getByText(/Client ID/i);
|
||||
expect(headerElement).toBeInTheDocument();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from "react";
|
||||
import React, { useContext } from "react";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
|
@ -7,10 +7,15 @@ import {
|
|||
IFormatter,
|
||||
IFormatterValueType,
|
||||
} from "@patternfly/react-table";
|
||||
import { Badge } from "@patternfly/react-core";
|
||||
import { Badge, AlertVariant } from "@patternfly/react-core";
|
||||
import { saveAs } from "file-saver";
|
||||
|
||||
import { ExternalLink } from "../components/external-link/ExternalLink";
|
||||
import { ClientRepresentation } from "../model/client-model";
|
||||
import { HttpClientContext } from "../http-service/HttpClientContext";
|
||||
import { useAlerts } from "../components/alert/Alerts";
|
||||
import { AlertPanel } from "../components/alert/AlertPanel";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type ClientListProps = {
|
||||
clients?: ClientRepresentation[];
|
||||
|
@ -25,9 +30,15 @@ const columns: (keyof ClientRepresentation)[] = [
|
|||
];
|
||||
|
||||
export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
|
||||
const httpClient = useContext(HttpClientContext)!;
|
||||
const { t } = useTranslation();
|
||||
const [add, alerts, hide] = useAlerts();
|
||||
|
||||
const convertClientId = (clientId: string) =>
|
||||
clientId.substring(0, clientId.indexOf("#"));
|
||||
const enabled = (): IFormatter => (data?: IFormatterValueType) => {
|
||||
const field = data!.toString();
|
||||
const value = field.substring(0, field.indexOf("#"));
|
||||
const value = convertClientId(field);
|
||||
return field.indexOf("true") !== -1 ? (
|
||||
<>{value}</>
|
||||
) : (
|
||||
|
@ -61,25 +72,67 @@ export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
|
|||
return r;
|
||||
})
|
||||
.map((c) => {
|
||||
return { cells: columns.map((col) => c[col]) };
|
||||
return { cells: columns.map((col) => c[col]), client: c };
|
||||
});
|
||||
return (
|
||||
<Table
|
||||
variant={TableVariant.compact}
|
||||
cells={[
|
||||
{ title: "Client ID", cellFormatters: [enabled()] },
|
||||
"Type",
|
||||
{ title: "Description", cellFormatters: [emptyFormatter()] },
|
||||
{
|
||||
title: "Home URL",
|
||||
cellFormatters: [externalLink(), emptyFormatter()],
|
||||
},
|
||||
]}
|
||||
rows={data}
|
||||
aria-label="Client list"
|
||||
>
|
||||
<TableHeader />
|
||||
<TableBody />
|
||||
</Table>
|
||||
<>
|
||||
<AlertPanel alerts={alerts} onCloseAlert={hide} />
|
||||
<Table
|
||||
variant={TableVariant.compact}
|
||||
cells={[
|
||||
{ title: t("Client ID"), cellFormatters: [enabled()] },
|
||||
t("Type"),
|
||||
{ title: t("Description"), cellFormatters: [emptyFormatter()] },
|
||||
{
|
||||
title: t("Home URL"),
|
||||
cellFormatters: [externalLink(), emptyFormatter()],
|
||||
},
|
||||
]}
|
||||
rows={data}
|
||||
actions={[
|
||||
{
|
||||
title: t("Export"),
|
||||
onClick: (_, rowId) => {
|
||||
const clientCopy = JSON.parse(JSON.stringify(data[rowId].client));
|
||||
clientCopy.clientId = convertClientId(clientCopy.clientId);
|
||||
delete clientCopy.id;
|
||||
|
||||
if (clientCopy.protocolMappers) {
|
||||
for (let i = 0; i < clientCopy.protocolMappers.length; i++) {
|
||||
delete clientCopy.protocolMappers[i].id;
|
||||
}
|
||||
}
|
||||
|
||||
saveAs(
|
||||
new Blob([JSON.stringify(clientCopy, null, 2)], {
|
||||
type: "application/json",
|
||||
}),
|
||||
clientCopy.clientId + ".json"
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("Delete"),
|
||||
onClick: (_, rowId) => {
|
||||
try {
|
||||
httpClient.doDelete(
|
||||
`/admin/realms/master/clients/${data[rowId].client.id}`
|
||||
);
|
||||
add(t("The client has been deleted"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
add(
|
||||
`${t("Could not delete client:")} ${error}`,
|
||||
AlertVariant.danger
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
aria-label="Client list"
|
||||
>
|
||||
<TableHeader />
|
||||
<TableBody />
|
||||
</Table>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -74,10 +74,13 @@ export class HttpClient {
|
|||
await this.makeConfig(config)
|
||||
);
|
||||
|
||||
try {
|
||||
response.data = await response.json();
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (contentType && contentType.indexOf("application/json") !== -1) {
|
||||
try {
|
||||
response.data = await response.json();
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -4124,6 +4124,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
||||
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
|
||||
|
||||
"@types/file-saver@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.1.tgz#e18eb8b069e442f7b956d313f4fadd3ef887354e"
|
||||
integrity sha512-g1QUuhYVVAamfCifK7oB7G3aIl4BbOyzDOqVyUfEr4tfBKrXfeH+M+Tg7HKCXSrbzxYdhyCP7z9WbKo0R2hBCw==
|
||||
|
||||
"@types/glob-base@^0.3.0":
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/glob-base/-/glob-base-0.3.0.tgz#a581d688347e10e50dd7c17d6f2880a10354319d"
|
||||
|
@ -9165,6 +9170,11 @@ file-loader@^6.0.0:
|
|||
loader-utils "^2.0.0"
|
||||
schema-utils "^2.7.1"
|
||||
|
||||
file-saver@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.2.tgz#06d6e728a9ea2df2cce2f8d9e84dfcdc338ec17a"
|
||||
integrity sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw==
|
||||
|
||||
file-selector@^0.1.8:
|
||||
version "0.1.12"
|
||||
resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.1.12.tgz#fe726547be219a787a9dcc640575a04a032b1fd0"
|
||||
|
|
Loading…
Reference in a new issue