created better error handling (#362)
by using this react-error-boundary package this works also in hooks
This commit is contained in:
parent
3a3bce0955
commit
689e01b461
17 changed files with 151 additions and 46 deletions
|
@ -4,7 +4,9 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"private": true,
|
"private": true,
|
||||||
"workspaces": ["tests"],
|
"workspaces": [
|
||||||
|
"tests"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "snowpack build",
|
"build": "snowpack build",
|
||||||
"build-storybook": "build-storybook -s public",
|
"build-storybook": "build-storybook -s public",
|
||||||
|
@ -31,6 +33,7 @@
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"react": "^16.8.5",
|
"react": "^16.8.5",
|
||||||
"react-dom": "^16.8.5",
|
"react-dom": "^16.8.5",
|
||||||
|
"react-error-boundary": "^3.1.0",
|
||||||
"react-hook-form": "^6.8.3",
|
"react-hook-form": "^6.8.3",
|
||||||
"react-i18next": "^11.7.0",
|
"react-i18next": "^11.7.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
|
|
11
src/App.tsx
11
src/App.tsx
|
@ -6,6 +6,7 @@ import {
|
||||||
Switch,
|
Switch,
|
||||||
useParams,
|
useParams,
|
||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
|
import { ErrorBoundary, useErrorHandler } from "react-error-boundary";
|
||||||
|
|
||||||
import { Header } from "./PageHeader";
|
import { Header } from "./PageHeader";
|
||||||
import { PageNav } from "./PageNav";
|
import { PageNav } from "./PageNav";
|
||||||
|
@ -21,6 +22,7 @@ import { ForbiddenSection } from "./ForbiddenSection";
|
||||||
import { SubGroups } from "./groups/GroupsSection";
|
import { SubGroups } from "./groups/GroupsSection";
|
||||||
import { useRealm } from "./context/realm-context/RealmContext";
|
import { useRealm } from "./context/realm-context/RealmContext";
|
||||||
import { useAdminClient, asyncStateFetch } from "./context/auth/AdminClient";
|
import { useAdminClient, asyncStateFetch } from "./context/auth/AdminClient";
|
||||||
|
import { ErrorRenderer } from "./components/error/ErrorRenderer";
|
||||||
|
|
||||||
// This must match the id given as scrollableSelector in scroll-form
|
// This must match the id given as scrollableSelector in scroll-form
|
||||||
const mainPageContentId = "kc-main-content-page-container";
|
const mainPageContentId = "kc-main-content-page-container";
|
||||||
|
@ -42,6 +44,7 @@ const RealmPathSelector = ({ children }: { children: ReactNode }) => {
|
||||||
const { setRealm } = useRealm();
|
const { setRealm } = useRealm();
|
||||||
const { realm } = useParams<{ realm: string }>();
|
const { realm } = useParams<{ realm: string }>();
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
const handleError = useErrorHandler();
|
||||||
useEffect(
|
useEffect(
|
||||||
() =>
|
() =>
|
||||||
asyncStateFetch(
|
asyncStateFetch(
|
||||||
|
@ -50,7 +53,8 @@ const RealmPathSelector = ({ children }: { children: ReactNode }) => {
|
||||||
if (realms.findIndex((r) => r.realm == realm) !== -1) {
|
if (realms.findIndex((r) => r.realm == realm) !== -1) {
|
||||||
setRealm(realm);
|
setRealm(realm);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
handleError
|
||||||
),
|
),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
@ -78,6 +82,10 @@ export const App = () => {
|
||||||
sidebar={<PageNav />}
|
sidebar={<PageNav />}
|
||||||
breadcrumb={<PageBreadCrumbs />}
|
breadcrumb={<PageBreadCrumbs />}
|
||||||
mainContainerId={mainPageContentId}
|
mainContainerId={mainPageContentId}
|
||||||
|
>
|
||||||
|
<ErrorBoundary
|
||||||
|
FallbackComponent={ErrorRenderer}
|
||||||
|
onReset={() => (location.href = "/")}
|
||||||
>
|
>
|
||||||
<Switch>
|
<Switch>
|
||||||
{routes(() => {}).map((route, i) => (
|
{routes(() => {}).map((route, i) => (
|
||||||
|
@ -97,6 +105,7 @@ export const App = () => {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Switch>
|
</Switch>
|
||||||
|
</ErrorBoundary>
|
||||||
</Page>
|
</Page>
|
||||||
</Router>
|
</Router>
|
||||||
</AppContexts>
|
</AppContexts>
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, { useContext, useEffect, useState } from "react";
|
||||||
import { useHistory, useParams } from "react-router-dom";
|
import { useHistory, useParams } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
import {
|
import {
|
||||||
FormGroup,
|
FormGroup,
|
||||||
PageSection,
|
PageSection,
|
||||||
|
@ -35,6 +36,7 @@ import { FormAccess } from "../../components/form-access/FormAccess";
|
||||||
export const RoleMappingForm = () => {
|
export const RoleMappingForm = () => {
|
||||||
const { realm } = useContext(RealmContext);
|
const { realm } = useContext(RealmContext);
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
const handleError = useErrorHandler();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
|
|
||||||
|
@ -74,7 +76,8 @@ export const RoleMappingForm = () => {
|
||||||
);
|
);
|
||||||
return filteredClients;
|
return filteredClients;
|
||||||
},
|
},
|
||||||
(filteredClients) => setClients(filteredClients)
|
(filteredClients) => setClients(filteredClients),
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -91,7 +94,8 @@ export const RoleMappingForm = () => {
|
||||||
return await adminClient.roles.find();
|
return await adminClient.roles.find();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(clientRoles) => setClientRoles(clientRoles)
|
(clientRoles) => setClientRoles(clientRoles),
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, [selectedClient]);
|
}, [selectedClient]);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useHistory, useParams, useRouteMatch } from "react-router-dom";
|
import { useHistory, useParams, useRouteMatch } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
import {
|
import {
|
||||||
ActionGroup,
|
ActionGroup,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
|
@ -43,6 +44,7 @@ type Params = {
|
||||||
export const MappingDetails = () => {
|
export const MappingDetails = () => {
|
||||||
const { t } = useTranslation("client-scopes");
|
const { t } = useTranslation("client-scopes");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
const handleError = useErrorHandler();
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
|
|
||||||
const { scopeId, id } = useParams<Params>();
|
const { scopeId, id } = useParams<Params>();
|
||||||
|
@ -90,7 +92,8 @@ export const MappingDetails = () => {
|
||||||
(result) => {
|
(result) => {
|
||||||
setConfigProperties(result.configProperties);
|
setConfigProperties(result.configProperties);
|
||||||
setMapping(result.mapping);
|
setMapping(result.mapping);
|
||||||
}
|
},
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useHistory, useParams } from "react-router-dom";
|
import { useHistory, useParams } from "react-router-dom";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
import {
|
import {
|
||||||
ActionGroup,
|
ActionGroup,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
|
@ -41,6 +42,7 @@ export const ClientScopeForm = () => {
|
||||||
const [clientScope, setClientScope] = useState<ClientScopeRepresentation>();
|
const [clientScope, setClientScope] = useState<ClientScopeRepresentation>();
|
||||||
|
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
const handleError = useErrorHandler();
|
||||||
const providers = useLoginProviders();
|
const providers = useLoginProviders();
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
|
|
||||||
|
@ -67,7 +69,8 @@ export const ClientScopeForm = () => {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(data) => setClientScope(data)
|
(data) => setClientScope(data),
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, [key]);
|
}, [key]);
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,10 @@ import {
|
||||||
Tab,
|
Tab,
|
||||||
TabTitleText,
|
TabTitleText,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Controller, useForm, useWatch } from "react-hook-form";
|
import { Controller, useForm, useWatch } from "react-hook-form";
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
|
import ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentation";
|
||||||
|
|
||||||
import { ClientSettings } from "./ClientSettings";
|
import { ClientSettings } from "./ClientSettings";
|
||||||
|
@ -96,6 +97,8 @@ const ClientDetailHeader = ({
|
||||||
export const ClientDetails = () => {
|
export const ClientDetails = () => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
const handleError = useErrorHandler();
|
||||||
|
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
|
|
||||||
const form = useForm();
|
const form = useForm();
|
||||||
|
@ -164,7 +167,8 @@ export const ClientDetails = () => {
|
||||||
(fetchedClient) => {
|
(fetchedClient) => {
|
||||||
setClient(fetchedClient);
|
setClient(fetchedClient);
|
||||||
setupForm(fetchedClient);
|
setupForm(fetchedClient);
|
||||||
}
|
},
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Controller, UseFormMethods, useWatch } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
import {
|
import {
|
||||||
ActionGroup,
|
ActionGroup,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
|
@ -14,18 +18,16 @@ import {
|
||||||
SplitItem,
|
SplitItem,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import CredentialRepresentation from "keycloak-admin/lib/defs/credentialRepresentation";
|
import CredentialRepresentation from "keycloak-admin/lib/defs/credentialRepresentation";
|
||||||
import React, { useEffect, useState } from "react";
|
|
||||||
import { Controller, UseFormMethods, useWatch } from "react-hook-form";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { useAlerts } from "../../components/alert/Alerts";
|
import { useAlerts } from "../../components/alert/Alerts";
|
||||||
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
||||||
import { FormAccess } from "../../components/form-access/FormAccess";
|
import { FormAccess } from "../../components/form-access/FormAccess";
|
||||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useAdminClient,
|
useAdminClient,
|
||||||
asyncStateFetch,
|
asyncStateFetch,
|
||||||
} from "../../context/auth/AdminClient";
|
} from "../../context/auth/AdminClient";
|
||||||
|
|
||||||
import { ClientSecret } from "./ClientSecret";
|
import { ClientSecret } from "./ClientSecret";
|
||||||
import { SignedJWT } from "./SignedJWT";
|
import { SignedJWT } from "./SignedJWT";
|
||||||
import { X509 } from "./X509";
|
import { X509 } from "./X509";
|
||||||
|
@ -53,6 +55,7 @@ export type CredentialsProps = {
|
||||||
export const Credentials = ({ clientId, form, save }: CredentialsProps) => {
|
export const Credentials = ({ clientId, form, save }: CredentialsProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
const handleError = useErrorHandler();
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
const clientAuthenticatorType = useWatch({
|
const clientAuthenticatorType = useWatch({
|
||||||
control: form.control,
|
control: form.control,
|
||||||
|
@ -84,7 +87,8 @@ export const Credentials = ({ clientId, form, save }: CredentialsProps) => {
|
||||||
({ providers, secret }) => {
|
({ providers, secret }) => {
|
||||||
setProviders(providers);
|
setProviders(providers);
|
||||||
setSecret(secret);
|
setSecret(secret);
|
||||||
}
|
},
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
import {
|
import {
|
||||||
IFormatter,
|
IFormatter,
|
||||||
IFormatterValueType,
|
IFormatterValueType,
|
||||||
|
@ -123,6 +124,7 @@ type TableRow = {
|
||||||
export const ClientScopes = ({ clientId, protocol }: ClientScopesProps) => {
|
export const ClientScopes = ({ clientId, protocol }: ClientScopesProps) => {
|
||||||
const { t } = useTranslation("clients");
|
const { t } = useTranslation("clients");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
const handleError = useErrorHandler();
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
|
|
||||||
const [searchToggle, setSearchToggle] = useState(false);
|
const [searchToggle, setSearchToggle] = useState(false);
|
||||||
|
@ -182,7 +184,8 @@ export const ClientScopes = ({ clientId, protocol }: ClientScopesProps) => {
|
||||||
({ rows, rest }) => {
|
({ rows, rest }) => {
|
||||||
setRows(rows);
|
setRows(rows);
|
||||||
setRest(rest);
|
setRest(rest);
|
||||||
}
|
},
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, [key]);
|
}, [key]);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
import {
|
import {
|
||||||
ClipboardCopy,
|
ClipboardCopy,
|
||||||
EmptyState,
|
EmptyState,
|
||||||
|
@ -145,10 +146,12 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
|
||||||
const tabContent2 = useRef(null);
|
const tabContent2 = useRef(null);
|
||||||
const tabContent3 = useRef(null);
|
const tabContent3 = useRef(null);
|
||||||
|
|
||||||
|
const handleError = useErrorHandler();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return asyncStateFetch(
|
return asyncStateFetch(
|
||||||
() => adminClient.clients.listOptionalClientScopes({ id: clientId }),
|
() => adminClient.clients.listOptionalClientScopes({ id: clientId }),
|
||||||
(optionalClientScopes) => setSelectableScopes(optionalClientScopes)
|
(optionalClientScopes) => setSelectableScopes(optionalClientScopes),
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -182,7 +185,8 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
|
||||||
return user;
|
return user;
|
||||||
})
|
})
|
||||||
.map((user) => <SelectOption key={user.id} value={user} />)
|
.map((user) => <SelectOption key={user.id} value={user} />)
|
||||||
)
|
),
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, [userSearch]);
|
}, [userSearch]);
|
||||||
|
|
||||||
|
@ -221,7 +225,8 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
|
||||||
|
|
||||||
setProtocolMappers(mapperList);
|
setProtocolMappers(mapperList);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
},
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, [selected]);
|
}, [selected]);
|
||||||
|
|
||||||
|
@ -241,7 +246,8 @@ export const EvaluateScopes = ({ clientId, protocol }: EvaluateScopesProps) => {
|
||||||
},
|
},
|
||||||
(accessToken) => {
|
(accessToken) => {
|
||||||
setAccessToken(JSON.stringify(accessToken, undefined, 3));
|
setAccessToken(JSON.stringify(accessToken, undefined, 3));
|
||||||
}
|
},
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, [user, selected]);
|
}, [user, selected]);
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,8 @@
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"category": "Category",
|
"category": "Category",
|
||||||
"priority": "Priority",
|
"priority": "Priority",
|
||||||
|
"unexpectedError": "An unexpected error occurred: '{{error}}'",
|
||||||
|
"retry": "Retry",
|
||||||
|
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
"manage": "Manage",
|
"manage": "Manage",
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import React, { DependencyList, useEffect, useState } from "react";
|
import React, { DependencyList, useEffect, useState } from "react";
|
||||||
import { Spinner } from "@patternfly/react-core";
|
import { Spinner } from "@patternfly/react-core";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
|
|
||||||
import { asyncStateFetch } from "../../context/auth/AdminClient";
|
import { asyncStateFetch } from "../../context/auth/AdminClient";
|
||||||
|
|
||||||
type DataLoaderProps<T> = {
|
type DataLoaderProps<T> = {
|
||||||
|
@ -10,11 +12,13 @@ type DataLoaderProps<T> = {
|
||||||
|
|
||||||
export function DataLoader<T>(props: DataLoaderProps<T>) {
|
export function DataLoader<T>(props: DataLoaderProps<T>) {
|
||||||
const [data, setData] = useState<T | undefined>();
|
const [data, setData] = useState<T | undefined>();
|
||||||
|
const handleError = useErrorHandler();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return asyncStateFetch(
|
return asyncStateFetch(
|
||||||
() => props.loader(),
|
() => props.loader(),
|
||||||
(result) => setData(result)
|
(result) => setData(result),
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, props.deps || []);
|
}, props.deps || []);
|
||||||
|
|
||||||
|
|
36
src/components/error/ErrorRenderer.tsx
Normal file
36
src/components/error/ErrorRenderer.tsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import {
|
||||||
|
Alert,
|
||||||
|
AlertActionCloseButton,
|
||||||
|
AlertActionLink,
|
||||||
|
AlertVariant,
|
||||||
|
PageSection,
|
||||||
|
} from "@patternfly/react-core";
|
||||||
|
import React from "react";
|
||||||
|
import { FallbackProps } from "react-error-boundary";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
export const ErrorRenderer = ({ error, resetErrorBoundary }: FallbackProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<PageSection>
|
||||||
|
<Alert
|
||||||
|
isInline
|
||||||
|
variant={AlertVariant.danger}
|
||||||
|
title={error.message}
|
||||||
|
actionClose={
|
||||||
|
<AlertActionCloseButton
|
||||||
|
title={error.message}
|
||||||
|
onClose={resetErrorBoundary}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
actionLinks={
|
||||||
|
<React.Fragment>
|
||||||
|
<AlertActionLink onClick={resetErrorBoundary}>
|
||||||
|
{t("retry")}
|
||||||
|
</AlertActionLink>
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
|
></Alert>
|
||||||
|
</PageSection>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { ReactNode, useEffect, useState } from "react";
|
import React, { ReactNode, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
import {
|
import {
|
||||||
IAction,
|
IAction,
|
||||||
IActions,
|
IActions,
|
||||||
|
@ -139,6 +140,7 @@ export function KeycloakDataTable<T>({
|
||||||
|
|
||||||
const [key, setKey] = useState(0);
|
const [key, setKey] = useState(0);
|
||||||
const refresh = () => setKey(new Date().getTime());
|
const refresh = () => setKey(new Date().getTime());
|
||||||
|
const handleError = useErrorHandler();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRefresher && setRefresher(refresh);
|
setRefresher && setRefresher(refresh);
|
||||||
|
@ -168,7 +170,8 @@ export function KeycloakDataTable<T>({
|
||||||
setRows(result);
|
setRows(result);
|
||||||
setFilteredData(result);
|
setFilteredData(result);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
},
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, [key, first, max]);
|
}, [key, first, max]);
|
||||||
|
|
||||||
|
|
|
@ -32,18 +32,26 @@ export const useAdminClient = () => {
|
||||||
*
|
*
|
||||||
* @param adminClientCall use this to do your adminClient call
|
* @param adminClientCall use this to do your adminClient call
|
||||||
* @param callback when the data is fetched this is where you set your state
|
* @param callback when the data is fetched this is where you set your state
|
||||||
|
* @param onError custom error handler
|
||||||
*/
|
*/
|
||||||
export function asyncStateFetch<T>(
|
export function asyncStateFetch<T>(
|
||||||
adminClientCall: () => Promise<T>,
|
adminClientCall: () => Promise<T>,
|
||||||
callback: (param: T) => void
|
callback: (param: T) => void,
|
||||||
|
onError?: (error: Error) => void
|
||||||
) {
|
) {
|
||||||
let canceled = false;
|
let canceled = false;
|
||||||
|
|
||||||
adminClientCall().then((result) => {
|
adminClientCall()
|
||||||
|
.then((result) => {
|
||||||
|
try {
|
||||||
if (!canceled) {
|
if (!canceled) {
|
||||||
callback(result);
|
callback(result);
|
||||||
}
|
}
|
||||||
});
|
} catch (error) {
|
||||||
|
if (onError) onError(error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(onError);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
canceled = true;
|
canceled = true;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useContext, useEffect, useState } from "react";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
import i18n from "../../i18n";
|
import i18n from "../../i18n";
|
||||||
|
|
||||||
import { AdminClient, asyncStateFetch } from "../auth/AdminClient";
|
import { AdminClient, asyncStateFetch } from "../auth/AdminClient";
|
||||||
|
@ -62,6 +63,7 @@ export const WhoAmIContext = React.createContext<WhoAmIProps>({
|
||||||
type WhoAmIProviderProps = { children: React.ReactNode };
|
type WhoAmIProviderProps = { children: React.ReactNode };
|
||||||
export const WhoAmIContextProvider = ({ children }: WhoAmIProviderProps) => {
|
export const WhoAmIContextProvider = ({ children }: WhoAmIProviderProps) => {
|
||||||
const adminClient = useContext(AdminClient)!;
|
const adminClient = useContext(AdminClient)!;
|
||||||
|
const handleError = useErrorHandler();
|
||||||
const { realm, setRealm } = useContext(RealmContext);
|
const { realm, setRealm } = useContext(RealmContext);
|
||||||
const [whoAmI, setWhoAmI] = useState<WhoAmI>(new WhoAmI());
|
const [whoAmI, setWhoAmI] = useState<WhoAmI>(new WhoAmI());
|
||||||
const [key, setKey] = useState(0);
|
const [key, setKey] = useState(0);
|
||||||
|
@ -78,7 +80,8 @@ export const WhoAmIContextProvider = ({ children }: WhoAmIProviderProps) => {
|
||||||
setRealm(whoAmI.getHomeRealm());
|
setRealm(whoAmI.getHomeRealm());
|
||||||
}
|
}
|
||||||
setWhoAmI(whoAmI);
|
setWhoAmI(whoAmI);
|
||||||
}
|
},
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, [key]);
|
}, [key]);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { useErrorHandler } from "react-error-boundary";
|
||||||
import {
|
import {
|
||||||
ActionGroup,
|
ActionGroup,
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
|
@ -119,6 +120,7 @@ const requireSslTypes = ["all", "external", "none"];
|
||||||
export const RealmSettingsSection = () => {
|
export const RealmSettingsSection = () => {
|
||||||
const { t } = useTranslation("realm-settings");
|
const { t } = useTranslation("realm-settings");
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
const handleError = useErrorHandler();
|
||||||
const { realm: realmName } = useRealm();
|
const { realm: realmName } = useRealm();
|
||||||
const { addAlert } = useAlerts();
|
const { addAlert } = useAlerts();
|
||||||
const { register, control, getValues, setValue, handleSubmit } = useForm();
|
const { register, control, getValues, setValue, handleSubmit } = useForm();
|
||||||
|
@ -134,7 +136,8 @@ export const RealmSettingsSection = () => {
|
||||||
(realm) => {
|
(realm) => {
|
||||||
setRealm(realm);
|
setRealm(realm);
|
||||||
setupForm(realm);
|
setupForm(realm);
|
||||||
}
|
},
|
||||||
|
handleError
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
|
@ -16880,6 +16880,13 @@ react-element-to-jsx-string@^14.0.2, react-element-to-jsx-string@^14.3.1:
|
||||||
"@base2/pretty-print-object" "1.0.0"
|
"@base2/pretty-print-object" "1.0.0"
|
||||||
is-plain-object "3.0.0"
|
is-plain-object "3.0.0"
|
||||||
|
|
||||||
|
react-error-boundary@^3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.0.tgz#9487443df2f9ba1db90d8ab52351814907ea4af3"
|
||||||
|
integrity sha512-lmPrdi5SLRJR+AeJkqdkGlW/CRkAUvZnETahK58J4xb5wpbfDngasEGu+w0T1iXEhVrYBJZeW+c4V1hILCnMWQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.5"
|
||||||
|
|
||||||
react-error-overlay@^6.0.7:
|
react-error-overlay@^6.0.7:
|
||||||
version "6.0.7"
|
version "6.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"
|
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"
|
||||||
|
|
Loading…
Reference in a new issue