bf0d16c4ff
* fixed critical a11y violation on Application page * fixed moderate a11y violation on Application page * fixed serious a11y violation on Signin page * fixed aria-label * updated label * Add Dutch translation --------- Co-authored-by: Agnieszka Gancarczyk <agancarc@redhat.com> Co-authored-by: Jon Koops <jonkoops@gmail.com>
269 lines
9.7 KiB
TypeScript
269 lines
9.7 KiB
TypeScript
import {
|
|
Button,
|
|
DataList,
|
|
DataListCell,
|
|
DataListContent,
|
|
DataListItem,
|
|
DataListItemCells,
|
|
DataListItemRow,
|
|
DataListToggle,
|
|
DescriptionList,
|
|
DescriptionListDescription,
|
|
DescriptionListGroup,
|
|
DescriptionListTerm,
|
|
Grid,
|
|
GridItem,
|
|
Spinner,
|
|
} from "@patternfly/react-core";
|
|
import {
|
|
CheckIcon,
|
|
ExternalLinkAltIcon,
|
|
InfoAltIcon,
|
|
} from "@patternfly/react-icons";
|
|
import { useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { ContinueCancelModal, useAlerts } from "ui-shared";
|
|
import { deleteConsent, getApplications } from "../api/methods";
|
|
import { ClientRepresentation } from "../api/representations";
|
|
import { Page } from "../components/page/Page";
|
|
import { TFuncKey } from "../i18n";
|
|
import { formatDate } from "../utils/formatDate";
|
|
import { usePromise } from "../utils/usePromise";
|
|
|
|
type Application = ClientRepresentation & {
|
|
open: boolean;
|
|
};
|
|
|
|
const Applications = () => {
|
|
const { t } = useTranslation();
|
|
const { addAlert, addError } = useAlerts();
|
|
|
|
const [applications, setApplications] = useState<Application[]>();
|
|
const [key, setKey] = useState(1);
|
|
const refresh = () => setKey(key + 1);
|
|
|
|
usePromise(
|
|
(signal) => getApplications({ signal }),
|
|
(clients) => setApplications(clients.map((c) => ({ ...c, open: false }))),
|
|
[key],
|
|
);
|
|
|
|
const toggleOpen = (clientId: string) => {
|
|
setApplications([
|
|
...applications!.map((a) =>
|
|
a.clientId === clientId ? { ...a, open: !a.open } : a,
|
|
),
|
|
]);
|
|
};
|
|
|
|
const removeConsent = async (id: string) => {
|
|
try {
|
|
await deleteConsent(id);
|
|
refresh();
|
|
addAlert(t("removeConsentSuccess"));
|
|
} catch (error) {
|
|
addError(t("removeConsentError", { error }).toString());
|
|
}
|
|
};
|
|
|
|
if (!applications) {
|
|
return <Spinner />;
|
|
}
|
|
|
|
return (
|
|
<Page title={t("application")} description={t("applicationsIntroMessage")}>
|
|
<DataList id="applications-list" aria-label={t("application")}>
|
|
<DataListItem
|
|
id="applications-list-header"
|
|
aria-labelledby="Columns names"
|
|
>
|
|
<DataListItemRow>
|
|
<span style={{ visibility: "hidden", height: 55 }}>
|
|
<DataListToggle
|
|
id="applications-list-header-invisible-toggle"
|
|
aria-controls="applications-list-content"
|
|
/>
|
|
</span>
|
|
<DataListItemCells
|
|
dataListCells={[
|
|
<DataListCell
|
|
key="applications-list-client-id-header"
|
|
width={2}
|
|
className="pf-u-pt-md"
|
|
>
|
|
<strong>{t("name")}</strong>
|
|
</DataListCell>,
|
|
<DataListCell
|
|
key="applications-list-app-type-header"
|
|
width={2}
|
|
className="pf-u-pt-md"
|
|
>
|
|
<strong>{t("applicationType")}</strong>
|
|
</DataListCell>,
|
|
<DataListCell
|
|
key="applications-list-status"
|
|
width={2}
|
|
className="pf-u-pt-md"
|
|
>
|
|
<strong>{t("status")}</strong>
|
|
</DataListCell>,
|
|
]}
|
|
/>
|
|
</DataListItemRow>
|
|
</DataListItem>
|
|
{applications.map((application) => (
|
|
<DataListItem
|
|
key={application.clientId}
|
|
aria-labelledby="applications-list"
|
|
isExpanded={application.open}
|
|
>
|
|
<DataListItemRow className="pf-u-align-items-center">
|
|
<DataListToggle
|
|
onClick={() => toggleOpen(application.clientId)}
|
|
isExpanded={application.open}
|
|
id={`toggle-${application.clientId}`}
|
|
aria-controls={`content-${application.clientId}`}
|
|
/>
|
|
<DataListItemCells
|
|
className="pf-u-align-items-center"
|
|
dataListCells={[
|
|
<DataListCell width={2} key={`client${application.clientId}`}>
|
|
<Button
|
|
className="pf-u-pl-0 title-case"
|
|
component="a"
|
|
variant="link"
|
|
onClick={() => window.open(application.effectiveUrl)}
|
|
>
|
|
{application.clientName || application.clientId}{" "}
|
|
<ExternalLinkAltIcon />
|
|
</Button>
|
|
</DataListCell>,
|
|
<DataListCell
|
|
width={2}
|
|
key={`internal${application.clientId}`}
|
|
>
|
|
{application.userConsentRequired
|
|
? t("thirdPartyApp")
|
|
: t("internalApp")}
|
|
{application.offlineAccess ? ", " + t("offlineAccess") : ""}
|
|
</DataListCell>,
|
|
<DataListCell width={2} key={`status${application.clientId}`}>
|
|
{application.inUse ? t("inUse") : t("notInUse")}
|
|
</DataListCell>,
|
|
]}
|
|
/>
|
|
</DataListItemRow>
|
|
|
|
<DataListContent
|
|
id={`content-${application.clientId}`}
|
|
className="pf-u-pl-4xl"
|
|
aria-label={t("applicationDetails", {
|
|
clientId: application.clientId,
|
|
})}
|
|
isHidden={!application.open}
|
|
>
|
|
<DescriptionList>
|
|
<DescriptionListGroup>
|
|
<DescriptionListTerm>{t("client")}</DescriptionListTerm>
|
|
<DescriptionListDescription>
|
|
{application.clientId}
|
|
</DescriptionListDescription>
|
|
</DescriptionListGroup>
|
|
{application.description && (
|
|
<DescriptionListGroup>
|
|
<DescriptionListTerm>
|
|
{t("description")}
|
|
</DescriptionListTerm>
|
|
<DescriptionListDescription>
|
|
{application.description}
|
|
</DescriptionListDescription>
|
|
</DescriptionListGroup>
|
|
)}
|
|
{application.effectiveUrl && (
|
|
<DescriptionListGroup>
|
|
<DescriptionListTerm>URL</DescriptionListTerm>
|
|
<DescriptionListDescription>
|
|
{application.effectiveUrl.split('"')}
|
|
</DescriptionListDescription>
|
|
</DescriptionListGroup>
|
|
)}
|
|
{application.consent && (
|
|
<>
|
|
<DescriptionListGroup>
|
|
<DescriptionListTerm>
|
|
{t("hasAccessTo")}
|
|
</DescriptionListTerm>
|
|
{application.consent.grantedScopes.map((scope) => (
|
|
<DescriptionListDescription key={`scope${scope.id}`}>
|
|
<CheckIcon /> {t(scope.name as TFuncKey)}
|
|
</DescriptionListDescription>
|
|
))}
|
|
</DescriptionListGroup>
|
|
{application.tosUri && (
|
|
<DescriptionListGroup>
|
|
<DescriptionListTerm>
|
|
{t("termsOfService")}
|
|
</DescriptionListTerm>
|
|
<DescriptionListDescription>
|
|
{application.tosUri}
|
|
</DescriptionListDescription>
|
|
</DescriptionListGroup>
|
|
)}
|
|
{application.policyUri && (
|
|
<DescriptionListGroup>
|
|
<DescriptionListTerm>
|
|
{t("privacyPolicy")}
|
|
</DescriptionListTerm>
|
|
<DescriptionListDescription>
|
|
{application.policyUri}
|
|
</DescriptionListDescription>
|
|
</DescriptionListGroup>
|
|
)}
|
|
{application.logoUri && (
|
|
<DescriptionListGroup>
|
|
<DescriptionListTerm>{t("logo")}</DescriptionListTerm>
|
|
<DescriptionListDescription>
|
|
<img src={application.logoUri} />
|
|
</DescriptionListDescription>
|
|
</DescriptionListGroup>
|
|
)}
|
|
<DescriptionListGroup>
|
|
<DescriptionListTerm>
|
|
{t("accessGrantedOn")}
|
|
</DescriptionListTerm>
|
|
<DescriptionListDescription>
|
|
{formatDate(new Date(application.consent.createdDate))}
|
|
</DescriptionListDescription>
|
|
</DescriptionListGroup>
|
|
</>
|
|
)}
|
|
</DescriptionList>
|
|
{(application.consent || application.offlineAccess) && (
|
|
<Grid hasGutter>
|
|
<hr />
|
|
<GridItem>
|
|
<ContinueCancelModal
|
|
buttonTitle={t("removeAccess")}
|
|
modalTitle={t("removeAccess")}
|
|
continueLabel={t("confirm")}
|
|
cancelLabel={t("cancel")}
|
|
buttonVariant="secondary"
|
|
onContinue={() => removeConsent(application.clientId)}
|
|
>
|
|
{t("removeModalMessage", [application.clientId])}
|
|
</ContinueCancelModal>
|
|
</GridItem>
|
|
<GridItem>
|
|
<InfoAltIcon /> {t("infoMessage")}
|
|
</GridItem>
|
|
</Grid>
|
|
)}
|
|
</DataListContent>
|
|
</DataListItem>
|
|
))}
|
|
</DataList>
|
|
</Page>
|
|
);
|
|
};
|
|
|
|
export default Applications;
|