added export and delete (#63)

* added export and delete

* added types
This commit is contained in:
Erik Jan de Wit 2020-09-09 16:34:05 +02:00 committed by GitHub
parent 8fbcf7582a
commit 7f66895631
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 98 additions and 26 deletions

View file

@ -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",

View file

@ -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();

View file

@ -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>
</>
);
};

View file

@ -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) {

View file

@ -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"