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-core": "^4.40.4",
|
||||||
"@patternfly/react-icons": "^4.7.2",
|
"@patternfly/react-icons": "^4.7.2",
|
||||||
"@patternfly/react-table": "^4.15.5",
|
"@patternfly/react-table": "^4.15.5",
|
||||||
|
"file-saver": "^2.0.2",
|
||||||
"i18next": "^19.6.2",
|
"i18next": "^19.6.2",
|
||||||
"i18next-http-backend": "^1.0.18",
|
"i18next-http-backend": "^1.0.18",
|
||||||
"keycloak-js": "^11.0.0",
|
"keycloak-js": "^11.0.0",
|
||||||
|
@ -47,6 +48,7 @@
|
||||||
"@testing-library/react": "^10.4.6",
|
"@testing-library/react": "^10.4.6",
|
||||||
"@types/dot": "^1.1.4",
|
"@types/dot": "^1.1.4",
|
||||||
"@types/enzyme": "^3.10.5",
|
"@types/enzyme": "^3.10.5",
|
||||||
|
"@types/file-saver": "^2.0.1",
|
||||||
"@types/jest": "^26.0.4",
|
"@types/jest": "^26.0.4",
|
||||||
"@types/react": "^16.9.23",
|
"@types/react": "^16.9.23",
|
||||||
"@types/react-dom": "^16.9.5",
|
"@types/react-dom": "^16.9.5",
|
||||||
|
|
|
@ -3,10 +3,14 @@ import { render } from "@testing-library/react";
|
||||||
|
|
||||||
import clientMock from "./mock-clients.json";
|
import clientMock from "./mock-clients.json";
|
||||||
import { ClientList } from "./ClientList";
|
import { ClientList } from "./ClientList";
|
||||||
|
import { I18nextProvider } from "react-i18next";
|
||||||
|
import { i18n } from "../i18n";
|
||||||
|
|
||||||
test("renders ClientList", () => {
|
test("renders ClientList", () => {
|
||||||
const { getByText } = render(
|
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);
|
const headerElement = getByText(/Client ID/i);
|
||||||
expect(headerElement).toBeInTheDocument();
|
expect(headerElement).toBeInTheDocument();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import React, { useContext } from "react";
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
@ -7,10 +7,15 @@ import {
|
||||||
IFormatter,
|
IFormatter,
|
||||||
IFormatterValueType,
|
IFormatterValueType,
|
||||||
} from "@patternfly/react-table";
|
} 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 { ExternalLink } from "../components/external-link/ExternalLink";
|
||||||
import { ClientRepresentation } from "../model/client-model";
|
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 = {
|
type ClientListProps = {
|
||||||
clients?: ClientRepresentation[];
|
clients?: ClientRepresentation[];
|
||||||
|
@ -25,9 +30,15 @@ const columns: (keyof ClientRepresentation)[] = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
|
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 enabled = (): IFormatter => (data?: IFormatterValueType) => {
|
||||||
const field = data!.toString();
|
const field = data!.toString();
|
||||||
const value = field.substring(0, field.indexOf("#"));
|
const value = convertClientId(field);
|
||||||
return field.indexOf("true") !== -1 ? (
|
return field.indexOf("true") !== -1 ? (
|
||||||
<>{value}</>
|
<>{value}</>
|
||||||
) : (
|
) : (
|
||||||
|
@ -61,25 +72,67 @@ export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
|
||||||
return r;
|
return r;
|
||||||
})
|
})
|
||||||
.map((c) => {
|
.map((c) => {
|
||||||
return { cells: columns.map((col) => c[col]) };
|
return { cells: columns.map((col) => c[col]), client: c };
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Table
|
<>
|
||||||
variant={TableVariant.compact}
|
<AlertPanel alerts={alerts} onCloseAlert={hide} />
|
||||||
cells={[
|
<Table
|
||||||
{ title: "Client ID", cellFormatters: [enabled()] },
|
variant={TableVariant.compact}
|
||||||
"Type",
|
cells={[
|
||||||
{ title: "Description", cellFormatters: [emptyFormatter()] },
|
{ title: t("Client ID"), cellFormatters: [enabled()] },
|
||||||
{
|
t("Type"),
|
||||||
title: "Home URL",
|
{ title: t("Description"), cellFormatters: [emptyFormatter()] },
|
||||||
cellFormatters: [externalLink(), emptyFormatter()],
|
{
|
||||||
},
|
title: t("Home URL"),
|
||||||
]}
|
cellFormatters: [externalLink(), emptyFormatter()],
|
||||||
rows={data}
|
},
|
||||||
aria-label="Client list"
|
]}
|
||||||
>
|
rows={data}
|
||||||
<TableHeader />
|
actions={[
|
||||||
<TableBody />
|
{
|
||||||
</Table>
|
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)
|
await this.makeConfig(config)
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
const contentType = response.headers.get("content-type");
|
||||||
response.data = await response.json();
|
if (contentType && contentType.indexOf("application/json") !== -1) {
|
||||||
} catch (e) {
|
try {
|
||||||
console.warn(e);
|
response.data = await response.json();
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response.ok) {
|
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"
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
|
||||||
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
|
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":
|
"@types/glob-base@^0.3.0":
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/glob-base/-/glob-base-0.3.0.tgz#a581d688347e10e50dd7c17d6f2880a10354319d"
|
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"
|
loader-utils "^2.0.0"
|
||||||
schema-utils "^2.7.1"
|
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:
|
file-selector@^0.1.8:
|
||||||
version "0.1.12"
|
version "0.1.12"
|
||||||
resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.1.12.tgz#fe726547be219a787a9dcc640575a04a032b1fd0"
|
resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.1.12.tgz#fe726547be219a787a9dcc640575a04a032b1fd0"
|
||||||
|
|
Loading…
Reference in a new issue