Fixed feedback discussions (#31409)

see: #24805

Signed-off-by: Erik Jan de Wit <erikjan.dewit@gmail.com>
This commit is contained in:
Erik Jan de Wit 2024-07-25 07:16:23 +02:00 committed by GitHub
parent 6ce89670b5
commit 83f8622d15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 62 additions and 19 deletions

View file

@ -25,7 +25,7 @@ export const PageBreadCrumbs = () => {
const crumbs = uniqBy( const crumbs = uniqBy(
useBreadcrumbs(routesWithCrumbs, { useBreadcrumbs(routesWithCrumbs, {
disableDefaults: true, disableDefaults: true,
excludePaths: ["/", `/${realm}`], excludePaths: ["/", `/${realm}`, `/${realm}/page-section`],
}), }),
elementText, elementText,
); );

View file

@ -1,8 +1,9 @@
import { useAlerts } from "@keycloak/keycloak-ui-shared";
import { ButtonVariant, DropdownItem } from "@patternfly/react-core"; import { ButtonVariant, DropdownItem } from "@patternfly/react-core";
import { get } from "lodash-es";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { useAdminClient } from "../admin-client"; import { useAdminClient } from "../admin-client";
import { useAlerts } from "@keycloak/keycloak-ui-shared";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { ViewHeader } from "../components/view-header/ViewHeader"; import { ViewHeader } from "../components/view-header/ViewHeader";
import { useRealm } from "../context/realm-context/RealmContext"; import { useRealm } from "../context/realm-context/RealmContext";
@ -10,6 +11,9 @@ import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { PageHandler } from "./PageHandler"; import { PageHandler } from "./PageHandler";
import { PAGE_PROVIDER } from "./PageList"; import { PAGE_PROVIDER } from "./PageList";
import { PageParams, toPage } from "./routes"; import { PageParams, toPage } from "./routes";
import { useFetch } from "../utils/useFetch";
import ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import { useState } from "react";
export default function Page() { export default function Page() {
const { adminClient } = useAdminClient(); const { adminClient } = useAdminClient();
@ -21,12 +25,19 @@ export default function Page() {
const navigate = useNavigate(); const navigate = useNavigate();
const { id, providerId } = useParams<PageParams>(); const { id, providerId } = useParams<PageParams>();
const { addAlert, addError } = useAlerts(); const { addAlert, addError } = useAlerts();
const [pageData, setPageData] = useState<ComponentRepresentation>();
const page = pages?.find((p) => p.id === providerId); const page = pages?.find((p) => p.id === providerId);
if (!page) { if (!page) {
throw new Error(t("notFound")); throw new Error(t("notFound"));
} }
useFetch(
async () => adminClient.components.findOne({ id: id! }),
setPageData,
[id],
);
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
titleKey: "itemDeleteConfirmTitle", titleKey: "itemDeleteConfirmTitle",
messageKey: "itemDeleteConfirm", messageKey: "itemDeleteConfirm",
@ -48,7 +59,10 @@ export default function Page() {
<> <>
<DeleteConfirm /> <DeleteConfirm />
<ViewHeader <ViewHeader
titleKey={id || t("createItem")} titleKey={
get(pageData, `config.${page.metadata.displayFields[0]}`)?.[0] ||
t("createItem")
}
dropdownItems={ dropdownItems={
id id
? [ ? [

View file

@ -70,7 +70,7 @@ export const PageHandler = ({
} else { } else {
await adminClient.components.create(updatedComponent); await adminClient.components.create(updatedComponent);
} }
addAlert("itemSaveSuccessful"); addAlert(t("itemSaveSuccessful"));
} catch (error) { } catch (error) {
addError("itemSaveError", error); addError("itemSaveError", error);
} }

View file

@ -1,5 +1,6 @@
import ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation"; import ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation";
import type { ComponentQuery } from "@keycloak/keycloak-admin-client/lib/resources/components"; import type { ComponentQuery } from "@keycloak/keycloak-admin-client/lib/resources/components";
import { useAlerts } from "@keycloak/keycloak-ui-shared";
import { import {
Button, Button,
ButtonVariant, ButtonVariant,
@ -7,30 +8,36 @@ import {
ToolbarItem, ToolbarItem,
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { IRowData } from "@patternfly/react-table"; import { IRowData } from "@patternfly/react-table";
import { get } from "lodash-es";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link, useNavigate, useParams } from "react-router-dom"; import { Link, useNavigate, useParams } from "react-router-dom";
import { useAdminClient } from "../admin-client"; import { useAdminClient } from "../admin-client";
import { useAlerts } from "@keycloak/keycloak-ui-shared";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
import { ViewHeader } from "../components/view-header/ViewHeader"; import { ViewHeader } from "../components/view-header/ViewHeader";
import { useRealm } from "../context/realm-context/RealmContext"; import { useRealm } from "../context/realm-context/RealmContext";
import { useServerInfo } from "../context/server-info/ServerInfoProvider"; import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { PageListParams, toDetailPage } from "./routes"; import { addDetailPage, PageListParams, toDetailPage } from "./routes";
export const PAGE_PROVIDER = "org.keycloak.services.ui.extend.UiPageProvider"; export const PAGE_PROVIDER = "org.keycloak.services.ui.extend.UiPageProvider";
export const TAB_PROVIDER = "org.keycloak.services.ui.extend.UiTabProvider"; export const TAB_PROVIDER = "org.keycloak.services.ui.extend.UiTabProvider";
const DetailLink = (obj: ComponentRepresentation) => { type DetailLinkProps = {
obj: ComponentRepresentation;
field: string;
};
const DetailLink = ({ obj, field }: DetailLinkProps) => {
const { realm } = useRealm(); const { realm } = useRealm();
const value = get(obj, field);
return ( return (
<Link <Link
key={obj.id} key={value}
to={toDetailPage({ realm, providerId: obj.providerId!, id: obj.id! })} to={toDetailPage({ realm, providerId: obj.providerId!, id: obj.id! })}
> >
{obj.id} {value}
</Link> </Link>
); );
}; };
@ -89,7 +96,7 @@ export default function PageList() {
component={(props) => ( component={(props) => (
<Link <Link
{...props} {...props}
to={toDetailPage({ realm: realmName, providerId: page.id })} to={addDetailPage({ realm: realmName, providerId: page.id })}
/> />
)} )}
> >
@ -109,10 +116,15 @@ export default function PageList() {
searchPlaceholderKey="searchItem" searchPlaceholderKey="searchItem"
loader={loader} loader={loader}
columns={[ columns={[
{ name: "id", cellRenderer: DetailLink }, ...page.metadata.displayFields.map((name: string, index: number) => ({
...page.properties.slice(0, 3).map((p) => ({ name: `config.${name}[0]`,
name: `config.${p.name}[0]`, displayKey: page.properties.find((p) => p.name === name)!.label,
displayKey: p.label, cellRenderer:
index === 0
? (obj: ComponentRepresentation) => (
<DetailLink obj={obj} field={`config.${name}`} />
)
: undefined,
})), })),
]} ]}
ariaLabelKey="list" ariaLabelKey="list"
@ -123,7 +135,7 @@ export default function PageList() {
instructions={t("noItemsInstructions")} instructions={t("noItemsInstructions")}
primaryActionText={t("createItem")} primaryActionText={t("createItem")}
onPrimaryAction={() => onPrimaryAction={() =>
navigate(toDetailPage({ realm: realmName, providerId: page.id })) navigate(addDetailPage({ realm: realmName, providerId: page.id }))
} }
/> />
} }

View file

@ -3,7 +3,7 @@ import type { AppRouteObject } from "../routes";
import { lazy } from "react"; import { lazy } from "react";
export type PageListParams = { realm?: string; providerId: string }; export type PageListParams = { realm?: string; providerId: string };
export type PageParams = { realm: string; providerId: string; id?: string }; export type PageParams = { realm: string; providerId: string; id: string };
const PageList = lazy(() => import("./PageList")); const PageList = lazy(() => import("./PageList"));
const Page = lazy(() => import("./Page")); const Page = lazy(() => import("./Page"));
@ -18,15 +18,28 @@ const PageListRoute: AppRouteObject = {
}; };
const PageDetailRoute: AppRouteObject = { const PageDetailRoute: AppRouteObject = {
path: "/:realm/page/:providerId/:id?", path: "/:realm/page-section/:providerId/:id",
element: <Page />, element: <Page />,
breadcrumb: (t) => t("page"), breadcrumb: (t) => t("details"),
handle: { handle: {
access: "view-realm", access: "view-realm",
}, },
}; };
const routes: AppRouteObject[] = [PageListRoute, PageDetailRoute]; const AddPageDetailRoute: AppRouteObject = {
path: "/:realm/page-section/:providerId/add",
element: <Page />,
breadcrumb: (t) => t("add"),
handle: {
access: "view-realm",
},
};
const routes: AppRouteObject[] = [
PageDetailRoute,
AddPageDetailRoute,
PageListRoute,
];
export const toPage = (params: PageListParams): Partial<Path> => ({ export const toPage = (params: PageListParams): Partial<Path> => ({
pathname: generatePath(PageListRoute.path, params), pathname: generatePath(PageListRoute.path, params),
@ -36,4 +49,8 @@ export const toDetailPage = (params: PageParams): Partial<Path> => ({
pathname: generatePath(PageDetailRoute.path, params), pathname: generatePath(PageDetailRoute.path, params),
}); });
export const addDetailPage = (params: Partial<PageParams>): Partial<Path> => ({
pathname: generatePath(AddPageDetailRoute.path, params),
});
export default routes; export default routes;