2024-01-09 19:02:31 +00:00
|
|
|
import { useMemo, useState } from "react";
|
2023-02-20 21:38:59 +00:00
|
|
|
import { useTranslation } from "react-i18next";
|
2021-01-15 01:44:16 +00:00
|
|
|
import {
|
2024-01-09 19:02:31 +00:00
|
|
|
ActionList,
|
|
|
|
ActionListItem,
|
2021-01-15 01:44:16 +00:00
|
|
|
Brand,
|
2024-01-09 19:02:31 +00:00
|
|
|
Button,
|
2021-01-15 01:44:16 +00:00
|
|
|
Card,
|
|
|
|
CardBody,
|
|
|
|
CardTitle,
|
|
|
|
DescriptionList,
|
|
|
|
DescriptionListDescription,
|
|
|
|
DescriptionListGroup,
|
|
|
|
DescriptionListTerm,
|
|
|
|
EmptyState,
|
|
|
|
EmptyStateBody,
|
|
|
|
Grid,
|
|
|
|
GridItem,
|
|
|
|
Label,
|
|
|
|
List,
|
|
|
|
ListItem,
|
|
|
|
ListVariant,
|
|
|
|
PageSection,
|
2022-04-28 13:36:43 +00:00
|
|
|
Tab,
|
|
|
|
TabTitleText,
|
2021-01-15 01:44:16 +00:00
|
|
|
Text,
|
|
|
|
TextContent,
|
2024-01-09 19:02:31 +00:00
|
|
|
TextVariants,
|
2021-01-15 01:44:16 +00:00
|
|
|
Title,
|
|
|
|
} from "@patternfly/react-core";
|
|
|
|
|
2023-09-12 14:03:13 +00:00
|
|
|
import FeatureRepresentation, {
|
|
|
|
FeatureType,
|
|
|
|
} from "@keycloak/keycloak-admin-client/lib/defs/featureRepresentation";
|
2021-01-15 01:44:16 +00:00
|
|
|
import { useRealm } from "../context/realm-context/RealmContext";
|
|
|
|
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
2024-02-26 13:04:38 +00:00
|
|
|
import { HelpItem, label } from "ui-shared";
|
2021-07-15 09:50:01 +00:00
|
|
|
import environment from "../environment";
|
2022-04-28 13:36:43 +00:00
|
|
|
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
|
2023-09-12 14:03:13 +00:00
|
|
|
import useLocaleSort, { mapByKey } from "../utils/useLocaleSort";
|
2022-04-28 13:36:43 +00:00
|
|
|
import {
|
|
|
|
RoutableTabs,
|
2023-01-04 09:57:46 +00:00
|
|
|
useRoutableTab,
|
2022-04-28 13:36:43 +00:00
|
|
|
} from "../components/routable-tabs/RoutableTabs";
|
|
|
|
import { DashboardTab, toDashboard } from "./routes/Dashboard";
|
2022-05-09 10:44:07 +00:00
|
|
|
import { ProviderInfo } from "./ProviderInfo";
|
2021-01-15 01:44:16 +00:00
|
|
|
|
2022-02-08 22:10:27 +00:00
|
|
|
import "./dashboard.css";
|
2024-01-09 19:02:31 +00:00
|
|
|
import { useFetch } from "../utils/useFetch";
|
|
|
|
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
|
|
|
|
import { adminClient } from "../admin-client";
|
|
|
|
import helpUrls from "../help-urls";
|
2022-02-08 22:10:27 +00:00
|
|
|
|
2021-01-15 01:44:16 +00:00
|
|
|
const EmptyDashboard = () => {
|
2023-09-08 13:17:17 +00:00
|
|
|
const { t } = useTranslation();
|
2021-07-14 15:35:49 +00:00
|
|
|
const { realm } = useRealm();
|
2024-01-29 18:23:08 +00:00
|
|
|
const [realmInfo, setRealmInfo] = useState<RealmRepresentation>();
|
2023-05-17 11:53:38 +00:00
|
|
|
const brandImage = environment.logo ? environment.logo : "/icon.svg";
|
2024-01-29 18:23:08 +00:00
|
|
|
useFetch(() => adminClient.realms.findOne({ realm }), setRealmInfo, []);
|
2024-02-26 13:04:38 +00:00
|
|
|
const realmDisplayInfo = label(t, realmInfo?.displayName, realm);
|
2023-05-17 11:53:38 +00:00
|
|
|
|
2021-01-15 01:44:16 +00:00
|
|
|
return (
|
|
|
|
<PageSection variant="light">
|
|
|
|
<EmptyState variant="large">
|
|
|
|
<Brand
|
2023-05-17 11:53:38 +00:00
|
|
|
src={environment.resourceUrl + brandImage}
|
2021-01-15 01:44:16 +00:00
|
|
|
alt="Keycloak icon"
|
|
|
|
className="keycloak__dashboard_icon"
|
|
|
|
/>
|
2023-03-10 09:54:35 +00:00
|
|
|
<Title headingLevel="h2" size="3xl">
|
2021-01-15 01:44:16 +00:00
|
|
|
{t("welcome")}
|
|
|
|
</Title>
|
2023-03-10 09:54:35 +00:00
|
|
|
<Title headingLevel="h1" size="4xl">
|
2024-01-29 18:23:08 +00:00
|
|
|
{realmDisplayInfo}
|
2021-01-15 01:44:16 +00:00
|
|
|
</Title>
|
|
|
|
<EmptyStateBody>{t("introduction")}</EmptyStateBody>
|
|
|
|
</EmptyState>
|
|
|
|
</PageSection>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2023-09-12 14:03:13 +00:00
|
|
|
type FeatureItemProps = {
|
|
|
|
feature: FeatureRepresentation;
|
|
|
|
};
|
|
|
|
|
|
|
|
const FeatureItem = ({ feature }: FeatureItemProps) => {
|
|
|
|
const { t } = useTranslation();
|
|
|
|
return (
|
|
|
|
<ListItem className="pf-u-mb-sm">
|
|
|
|
{feature.name}
|
|
|
|
{feature.type === FeatureType.Experimental && (
|
|
|
|
<Label color="orange">{t("experimental")}</Label>
|
|
|
|
)}
|
|
|
|
{feature.type === FeatureType.Preview && (
|
|
|
|
<Label color="blue">{t("preview")}</Label>
|
|
|
|
)}
|
|
|
|
{feature.type === FeatureType.Default && (
|
|
|
|
<Label color="green">{t("supported")}</Label>
|
|
|
|
)}
|
|
|
|
</ListItem>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2021-01-15 01:44:16 +00:00
|
|
|
const Dashboard = () => {
|
2023-09-08 13:17:17 +00:00
|
|
|
const { t } = useTranslation();
|
2021-01-15 01:44:16 +00:00
|
|
|
const { realm } = useRealm();
|
|
|
|
const serverInfo = useServerInfo();
|
2022-05-09 10:44:07 +00:00
|
|
|
const localeSort = useLocaleSort();
|
2024-01-09 19:02:31 +00:00
|
|
|
const [realmInfo, setRealmInfo] = useState<RealmRepresentation>();
|
2021-01-15 01:44:16 +00:00
|
|
|
|
2023-09-12 14:03:13 +00:00
|
|
|
const sortedFeatures = useMemo(
|
|
|
|
() => localeSort(serverInfo.features ?? [], mapByKey("name")),
|
|
|
|
[serverInfo.features],
|
|
|
|
);
|
2022-08-08 10:59:00 +00:00
|
|
|
|
|
|
|
const disabledFeatures = useMemo(
|
2023-09-12 14:03:13 +00:00
|
|
|
() => sortedFeatures.filter((f) => !f.enabled) || [],
|
|
|
|
[serverInfo.features],
|
2022-05-09 10:44:07 +00:00
|
|
|
);
|
|
|
|
|
2022-08-08 10:59:00 +00:00
|
|
|
const enabledFeatures = useMemo(
|
2023-09-12 14:03:13 +00:00
|
|
|
() => sortedFeatures.filter((f) => f.enabled) || [],
|
|
|
|
[serverInfo.features],
|
2021-01-15 01:44:16 +00:00
|
|
|
);
|
|
|
|
|
2023-01-04 09:57:46 +00:00
|
|
|
const useTab = (tab: DashboardTab) =>
|
|
|
|
useRoutableTab(
|
|
|
|
toDashboard({
|
|
|
|
realm,
|
|
|
|
tab,
|
2023-07-11 14:03:21 +00:00
|
|
|
}),
|
2023-01-04 09:57:46 +00:00
|
|
|
);
|
|
|
|
|
2024-01-09 19:02:31 +00:00
|
|
|
useFetch(() => adminClient.realms.findOne({ realm }), setRealmInfo, []);
|
|
|
|
|
2024-02-26 13:04:38 +00:00
|
|
|
const realmDisplayInfo = label(t, realmInfo?.displayName, realm);
|
2024-01-09 19:02:31 +00:00
|
|
|
|
|
|
|
const welcomeTab = useTab("welcome");
|
2023-01-04 09:57:46 +00:00
|
|
|
const infoTab = useTab("info");
|
|
|
|
const providersTab = useTab("providers");
|
|
|
|
|
2022-02-08 22:10:27 +00:00
|
|
|
if (Object.keys(serverInfo).length === 0) {
|
|
|
|
return <KeycloakSpinner />;
|
|
|
|
}
|
|
|
|
|
2021-01-15 01:44:16 +00:00
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<PageSection variant="light">
|
|
|
|
<TextContent className="pf-u-mr-sm">
|
2023-09-14 09:01:15 +00:00
|
|
|
<Text component="h1">{t("realmNameTitle", { name: realm })}</Text>
|
2021-01-15 01:44:16 +00:00
|
|
|
</TextContent>
|
|
|
|
</PageSection>
|
2022-04-28 13:36:43 +00:00
|
|
|
<PageSection variant="light" className="pf-u-p-0">
|
|
|
|
<RoutableTabs
|
|
|
|
data-testid="dashboard-tabs"
|
|
|
|
defaultLocation={toDashboard({
|
|
|
|
realm,
|
2024-01-09 19:02:31 +00:00
|
|
|
tab: "welcome",
|
2022-04-28 13:36:43 +00:00
|
|
|
})}
|
|
|
|
isBox
|
|
|
|
mountOnEnter
|
|
|
|
>
|
|
|
|
<Tab
|
2024-01-09 19:02:31 +00:00
|
|
|
id="welcome"
|
|
|
|
data-testid="welcomeTab"
|
|
|
|
title={<TabTitleText>{t("welcomeTabTitle")}</TabTitleText>}
|
|
|
|
{...welcomeTab}
|
|
|
|
>
|
|
|
|
<PageSection variant="light">
|
|
|
|
<div className="pf-l-grid pf-u-ml-lg">
|
|
|
|
<div className="pf-l-grid__item pf-m-12-col">
|
|
|
|
<Title
|
|
|
|
className="pf-u-font-weight-bold"
|
|
|
|
headingLevel="h2"
|
|
|
|
size="3xl"
|
|
|
|
>
|
|
|
|
{t("welcomeTo", { realmDisplayInfo })}
|
|
|
|
</Title>
|
|
|
|
</div>
|
|
|
|
<div className="pf-l-grid__item keycloak__dashboard_welcome_tab">
|
|
|
|
<Text component={TextVariants.h3}>{t("welcomeText")}</Text>
|
|
|
|
</div>
|
|
|
|
<div className="pf-l-grid__item pf-m-10-col pf-u-mt-md">
|
|
|
|
<Button
|
|
|
|
className="pf-u-px-lg pf-u-py-sm"
|
|
|
|
component="a"
|
|
|
|
href={helpUrls.documentation}
|
|
|
|
target="_blank"
|
|
|
|
variant="primary"
|
|
|
|
>
|
|
|
|
{t("viewDocumentation")}
|
|
|
|
</Button>
|
|
|
|
</div>
|
|
|
|
<ActionList className="pf-u-mt-sm">
|
|
|
|
<ActionListItem>
|
|
|
|
<Button
|
|
|
|
component="a"
|
|
|
|
href={helpUrls.guides}
|
|
|
|
target="_blank"
|
|
|
|
variant="tertiary"
|
|
|
|
>
|
|
|
|
{t("viewGuides")}
|
|
|
|
</Button>
|
|
|
|
</ActionListItem>
|
|
|
|
<ActionListItem>
|
|
|
|
<Button
|
|
|
|
component="a"
|
|
|
|
href={helpUrls.community}
|
|
|
|
target="_blank"
|
|
|
|
variant="tertiary"
|
|
|
|
>
|
|
|
|
{t("joinCommunity")}
|
|
|
|
</Button>
|
|
|
|
</ActionListItem>
|
|
|
|
<ActionListItem>
|
|
|
|
<Button
|
|
|
|
component="a"
|
|
|
|
href={helpUrls.blog}
|
|
|
|
target="_blank"
|
|
|
|
variant="tertiary"
|
|
|
|
>
|
|
|
|
{t("readBlog")}
|
|
|
|
</Button>
|
|
|
|
</ActionListItem>
|
|
|
|
</ActionList>
|
|
|
|
</div>
|
|
|
|
</PageSection>
|
|
|
|
</Tab>
|
|
|
|
<Tab
|
2022-04-28 13:36:43 +00:00
|
|
|
id="info"
|
|
|
|
data-testid="infoTab"
|
2023-01-03 16:21:37 +00:00
|
|
|
title={<TabTitleText>{t("serverInfo")}</TabTitleText>}
|
2023-01-04 09:57:46 +00:00
|
|
|
{...infoTab}
|
2022-04-28 13:36:43 +00:00
|
|
|
>
|
|
|
|
<PageSection variant="light">
|
|
|
|
<Grid hasGutter>
|
|
|
|
<GridItem lg={2} sm={12}>
|
|
|
|
<Card className="keycloak__dashboard_card">
|
|
|
|
<CardTitle>{t("serverInfo")}</CardTitle>
|
|
|
|
<CardBody>
|
|
|
|
<DescriptionList>
|
|
|
|
<DescriptionListGroup>
|
|
|
|
<DescriptionListTerm>
|
|
|
|
{t("version")}
|
|
|
|
</DescriptionListTerm>
|
|
|
|
<DescriptionListDescription>
|
|
|
|
{serverInfo.systemInfo?.version}
|
|
|
|
</DescriptionListDescription>
|
|
|
|
</DescriptionListGroup>
|
|
|
|
</DescriptionList>
|
|
|
|
</CardBody>
|
2023-01-03 16:21:37 +00:00
|
|
|
<CardTitle>{t("memory")}</CardTitle>
|
|
|
|
<CardBody>
|
|
|
|
<DescriptionList>
|
|
|
|
<DescriptionListGroup>
|
|
|
|
<DescriptionListTerm>
|
|
|
|
{t("totalMemory")}
|
|
|
|
</DescriptionListTerm>
|
|
|
|
<DescriptionListDescription>
|
|
|
|
{serverInfo.memoryInfo?.totalFormated}
|
|
|
|
</DescriptionListDescription>
|
|
|
|
<DescriptionListTerm>
|
|
|
|
{t("freeMemory")}
|
|
|
|
</DescriptionListTerm>
|
|
|
|
<DescriptionListDescription>
|
|
|
|
{serverInfo.memoryInfo?.freeFormated}
|
|
|
|
</DescriptionListDescription>
|
|
|
|
<DescriptionListTerm>
|
|
|
|
{t("usedMemory")}
|
|
|
|
</DescriptionListTerm>
|
|
|
|
<DescriptionListDescription>
|
|
|
|
{serverInfo.memoryInfo?.usedFormated}
|
|
|
|
</DescriptionListDescription>
|
|
|
|
</DescriptionListGroup>
|
|
|
|
</DescriptionList>
|
|
|
|
</CardBody>
|
2022-04-28 13:36:43 +00:00
|
|
|
</Card>
|
|
|
|
</GridItem>
|
|
|
|
<GridItem lg={10} sm={12}>
|
|
|
|
<Card className="keycloak__dashboard_card">
|
|
|
|
<CardTitle>{t("profile")}</CardTitle>
|
|
|
|
<CardBody>
|
|
|
|
<DescriptionList>
|
|
|
|
<DescriptionListGroup>
|
|
|
|
<DescriptionListTerm>
|
|
|
|
{t("enabledFeatures")}{" "}
|
|
|
|
<HelpItem
|
2023-09-14 09:01:15 +00:00
|
|
|
fieldLabelId="enabledFeatures"
|
|
|
|
helpText={t("infoEnabledFeatures")}
|
2022-04-28 13:36:43 +00:00
|
|
|
/>
|
|
|
|
</DescriptionListTerm>
|
|
|
|
<DescriptionListDescription>
|
|
|
|
<List variant={ListVariant.inline}>
|
|
|
|
{enabledFeatures.map((feature) => (
|
2023-09-12 14:03:13 +00:00
|
|
|
<FeatureItem
|
|
|
|
key={feature.name}
|
|
|
|
feature={feature}
|
|
|
|
/>
|
2022-04-28 13:36:43 +00:00
|
|
|
))}
|
|
|
|
</List>
|
|
|
|
</DescriptionListDescription>
|
|
|
|
</DescriptionListGroup>
|
|
|
|
<DescriptionListGroup>
|
|
|
|
<DescriptionListTerm>
|
|
|
|
{t("disabledFeatures")}{" "}
|
|
|
|
<HelpItem
|
2023-09-14 09:01:15 +00:00
|
|
|
fieldLabelId="disabledFeatures"
|
|
|
|
helpText={t("infoDisabledFeatures")}
|
2022-04-28 13:36:43 +00:00
|
|
|
/>
|
|
|
|
</DescriptionListTerm>
|
|
|
|
<DescriptionListDescription>
|
|
|
|
<List variant={ListVariant.inline}>
|
2022-05-09 10:44:07 +00:00
|
|
|
{disabledFeatures.map((feature) => (
|
2023-09-12 14:03:13 +00:00
|
|
|
<FeatureItem
|
|
|
|
key={feature.name}
|
|
|
|
feature={feature}
|
|
|
|
/>
|
2022-05-09 10:44:07 +00:00
|
|
|
))}
|
2022-04-28 13:36:43 +00:00
|
|
|
</List>
|
|
|
|
</DescriptionListDescription>
|
|
|
|
</DescriptionListGroup>
|
|
|
|
</DescriptionList>
|
|
|
|
</CardBody>
|
|
|
|
</Card>
|
|
|
|
</GridItem>
|
|
|
|
</Grid>
|
|
|
|
</PageSection>
|
|
|
|
</Tab>
|
|
|
|
<Tab
|
|
|
|
id="providers"
|
|
|
|
data-testid="providersTab"
|
|
|
|
title={<TabTitleText>{t("providerInfo")}</TabTitleText>}
|
2023-01-04 09:57:46 +00:00
|
|
|
{...providersTab}
|
2022-04-28 13:36:43 +00:00
|
|
|
>
|
2022-05-02 14:30:24 +00:00
|
|
|
<ProviderInfo />
|
2022-04-28 13:36:43 +00:00
|
|
|
</Tab>
|
|
|
|
</RoutableTabs>
|
2021-01-15 01:44:16 +00:00
|
|
|
</PageSection>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2021-10-29 16:11:06 +00:00
|
|
|
export default function DashboardSection() {
|
2021-01-15 01:44:16 +00:00
|
|
|
const { realm } = useRealm();
|
|
|
|
const isMasterRealm = realm === "master";
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
{!isMasterRealm && <EmptyDashboard />}
|
|
|
|
{isMasterRealm && <Dashboard />}
|
|
|
|
</>
|
|
|
|
);
|
2021-10-29 16:11:06 +00:00
|
|
|
}
|