diff --git a/src/App.tsx b/src/App.tsx index a46a18ec98..afdeb3375a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -29,66 +29,52 @@ export const App = () => { } isManagedSidebar sidebar={}> - - - + + - - - + + + - - - - - - + + + + + + - - - - + + + + - - - - + + + diff --git a/src/client-scopes/ClientScopesSection.tsx b/src/client-scopes/ClientScopesSection.tsx index ef8cc2d287..e8210c6c64 100644 --- a/src/client-scopes/ClientScopesSection.tsx +++ b/src/client-scopes/ClientScopesSection.tsx @@ -1,5 +1,10 @@ +import { PageSection } from "@patternfly/react-core"; import React from "react"; export const ClientScopesSection = () => { - return <>The Client Scopes Page; + return ( + <> + The Client Scopes Page + + ); }; diff --git a/src/clients/ClientsSection.tsx b/src/clients/ClientsSection.tsx index 07c4b2c1a2..598b9a23dc 100644 --- a/src/clients/ClientsSection.tsx +++ b/src/clients/ClientsSection.tsx @@ -1,7 +1,7 @@ import React, { useState, useContext } from "react"; import { useHistory } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { Button } from "@patternfly/react-core"; +import { Button, PageSection } from "@patternfly/react-core"; import { DataLoader } from "../components/data-loader/DataLoader"; import { TableToolbar } from "../components/table-toolbar/TableToolbar"; @@ -28,35 +28,40 @@ export const ClientsSection = () => { }; return ( - - {(clients) => ( - { - setFirst(f); - setMax(m); - }} - toolbarItem={ - <> - - - - } - > - - - )} - + + + {(clients) => ( + { + setFirst(f); + setMax(m); + }} + toolbarItem={ + <> + + + + } + > + + + )} + + ); }; diff --git a/src/events/EventsSection.tsx b/src/events/EventsSection.tsx index fad54953ba..27e937679d 100644 --- a/src/events/EventsSection.tsx +++ b/src/events/EventsSection.tsx @@ -1,5 +1,10 @@ +import { PageSection } from "@patternfly/react-core"; import React from "react"; export const EventsSection = () => { - return <>The Events Page; + return ( + <> + The Events Page + + ); }; diff --git a/src/groups/GroupsSection.tsx b/src/groups/GroupsSection.tsx index 1f9f895419..c49c029de1 100644 --- a/src/groups/GroupsSection.tsx +++ b/src/groups/GroupsSection.tsx @@ -1,7 +1,7 @@ import React, { useContext, useState } from "react"; import { useTranslation } from "react-i18next"; import { useHistory } from "react-router-dom"; -import { Button } from "@patternfly/react-core"; +import { Button, PageSection } from "@patternfly/react-core"; import { HttpClientContext } from "../http-service/HttpClientContext"; import { GroupsList } from "./GroupsList"; @@ -24,30 +24,32 @@ export const GroupsSection = () => { return ( <> - - {(groups) => ( - { - setFirst(f); - setMax(m); - }} - toolbarItem={ - <> - - - } - > - - - )} - + + + {(groups) => ( + { + setFirst(f); + setMax(m); + }} + toolbarItem={ + <> + + + } + > + + + )} + + ); }; diff --git a/src/i18n.ts b/src/i18n.ts index d58c7ed28a..1453e643af 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -5,13 +5,14 @@ import { initReactI18next } from "react-i18next"; import common from "./common-messages.json"; import clients from "./clients/messages.json"; import realm from "./realm/messages.json"; +import roles from "./realm-roles/messages.json"; import help from "./help.json"; const initOptions = { - ns: ["common", "help", "clients", "realm"], + ns: ["common", "help", "clients", "realm", "roles"], defaultNS: "common", resources: { - en: { ...common, ...help, ...clients, ...realm }, + en: { ...common, ...help, ...clients, ...realm, ...roles }, }, lng: "en", fallbackLng: "en", diff --git a/src/model/role-model.ts b/src/model/role-model.ts new file mode 100644 index 0000000000..377aaa7b8e --- /dev/null +++ b/src/model/role-model.ts @@ -0,0 +1,19 @@ +// Generated using typescript-generator version 2.0.400 on 2020-09-11 12:02:07. + +export interface RoleRepresentation { + id?: string; + name?: string; + description?: string; + scopeParamRequired?: boolean; + composite?: boolean; + composites?: Composites; + clientRole?: boolean; + containerId?: string; + attributes?: { [index: string]: string[] }; +} + +export interface Composites { + realm?: string[]; + client?: { [index: string]: string[] }; + application?: { [index: string]: string[] }; +} diff --git a/src/realm-roles/NoRealmRoles.tsx b/src/realm-roles/NoRealmRoles.tsx new file mode 100644 index 0000000000..1e28359f33 --- /dev/null +++ b/src/realm-roles/NoRealmRoles.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { + Button, + PageSection, + EmptyState, + EmptyStateVariant, + EmptyStateIcon, + Title, + EmptyStateBody, +} from "@patternfly/react-core"; +import { useHistory } from "react-router-dom"; + +import { PlusCircleIcon } from "@patternfly/react-icons"; +import { useTranslation } from "react-i18next"; + +export const NoRealmRolesPage = () => { + const { t } = useTranslation("realm"); + const history = useHistory(); + return ( + <> + + + + + {t("noRealmRoles")} + + {t("emptyStateText")} + + + + + ); +}; diff --git a/src/realm-roles/RealmRolesSection.tsx b/src/realm-roles/RealmRolesSection.tsx index 62b150db95..e7e2460d92 100644 --- a/src/realm-roles/RealmRolesSection.tsx +++ b/src/realm-roles/RealmRolesSection.tsx @@ -1,5 +1,78 @@ -import React from "react"; +/* eslint-disable @typescript-eslint/no-unused-vars */ +import React, { useState, useContext, useEffect } from "react"; +import { useHistory } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { + Button, + Divider, + Page, + PageSection, + PageSectionVariants, + Text, + TextContent, +} from "@patternfly/react-core"; + +import { DataLoader } from "../components/data-loader/DataLoader"; +import { TableToolbar } from "../components/table-toolbar/TableToolbar"; +import { HttpClientContext } from "../http-service/HttpClientContext"; +import { RoleRepresentation } from "../model/role-model"; +import { RolesList } from "./RoleList"; +import { RealmContext } from "../components/realm-context/RealmContext"; export const RealmRolesSection = () => { - return <>The Realm Roles Page; + const { t } = useTranslation("roles"); + const history = useHistory(); + const [max, setMax] = useState(10); + const [, setRoles] = useState([] as RoleRepresentation[]); + const [first, setFirst] = useState(0); + const httpClient = useContext(HttpClientContext)!; + const { realm } = useContext(RealmContext); + + const loader = async () => { + return await httpClient + .doGet(`/admin/realms/${realm}/roles`) + .then((r) => r.data as RoleRepresentation[]); + }; + + useEffect(() => { + loader().then((result) => setRoles(result || [])); + }, []); + + return ( + + {(roles) => ( + <> + + + Realm roles + {t("roleExplain")} + + + + + { + setFirst(f); + setMax(m); + }} + toolbarItem={ + <> + + + } + > + + + + + )} + + ); }; diff --git a/src/realm-roles/RoleList.tsx b/src/realm-roles/RoleList.tsx new file mode 100644 index 0000000000..7e9b233c4a --- /dev/null +++ b/src/realm-roles/RoleList.tsx @@ -0,0 +1,79 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; + +import { + Table, + TableBody, + TableHeader, + TableVariant, + IFormatter, + IFormatterValueType, +} from "@patternfly/react-table"; + +import { ExternalLink } from "../components/external-link/ExternalLink"; +import { RoleRepresentation } from "../model/role-model"; + +type RolesListProps = { + roles?: RoleRepresentation[]; +}; + +const columns: (keyof RoleRepresentation)[] = [ + "name", + "composite", + "description", +]; + +export const RolesList = ({ roles }: RolesListProps) => { + const { t } = useTranslation("roles"); + + const emptyFormatter = (): IFormatter => (data?: IFormatterValueType) => { + return data ? data : "—"; + }; + + const externalLink = (): IFormatter => (data?: IFormatterValueType) => { + return (data ? ( + + ) : undefined) as object; + }; + + const boolFormatter = (): IFormatter => (data?: IFormatterValueType) => { + const boolVal = data?.toString(); + + return (boolVal + ? boolVal.charAt(0).toUpperCase() + boolVal.slice(1) + : undefined) as string; + }; + + const data = roles!.map((c) => { + return { cells: columns.map((col) => c[col]) }; + }); + return ( + + + +
+ ); +}; diff --git a/src/realm-roles/__tests__/mock-roles.json b/src/realm-roles/__tests__/mock-roles.json new file mode 100644 index 0000000000..e98cefd363 --- /dev/null +++ b/src/realm-roles/__tests__/mock-roles.json @@ -0,0 +1,77 @@ +[ + { + "name":"Admin", + "composite":true, + "description": "Lorem ipsum dolor sit amet" + }, + { + "name":"Author", + "composite":false, + "description": "Lorem ipsum dolor sit amet" + }, + { + "name":"Billing", + "composite":true, + "description": "Lorem ipsum dolor sit" + }, + { + "name":"Contributor", + "composite":true, + "description": "Lorem ipsum dolor sit, consecte" + }, + { + "name":"Editor", + "composite":true, + "description": "Lorem ipsum dolor sit amet" + }, + { + "name":"Engineer", + "composite":true, + "description": "Lorem ipsum dolor sit amet" + }, + { + "name":"Member", + "composite":false, + "description": "Lorem ipsum dolor sit amet" + }, + { + "name":"Moderator", + "composite":true, + "description": "Lorem ipsum dolor sit amet" + }, + { + "name":"Owner", + "composite":true, + "description": "Lorem ipsum dolor sit amet" + }, + { + "name":"Reader", + "composite":true, + "description": "Lorem ipsum dolor sit amet" + }, + { + "name":"Subscriber", + "composite":true, + "description": "Lorem ipsum dolor sit " + }, + { + "name":"Teenager", + "composite":true, + "description": "Lorem ipsum dolor sit amet, consecte occaecat" + }, + { + "name":"User", + "composite":true, + "description": "Lorem ipsum dolor sit amet, consecte" + }, + { + "name":"Writer", + "composite":true, + "description": "Lorem ipsum dolor" + }, + { + "name":"Zara", + "composite":true, + "description": "Lorem ipsum dolor sit amet" + } + ] \ No newline at end of file diff --git a/src/realm-roles/messages.json b/src/realm-roles/messages.json new file mode 100644 index 0000000000..8e434d5d15 --- /dev/null +++ b/src/realm-roles/messages.json @@ -0,0 +1,22 @@ +{ + "roles": { + "createRole": "Create role", + "importRole": "Import role", + "roleID": "Role ID", + "type": "Type", + "homeURL": "Home URL", + "roleExplain": "Realm-level roles are a global namespace to define your roles.", + "roleName": "Role name", + "composite": "Composite", + "description": "Description", + "roleList": "Role list", + "generalSettings": "General Settings", + "capabilityConfig": "Capability config", + "roleImportError": "Could not import role", + "roleImportSuccess": "Role imported succeful", + "roleDeletedSucess": "The role has been deleted", + "roleDeleteError": "Could not delete role:", + "roleAuthentication": "Role authentication" + } + } + \ No newline at end of file diff --git a/src/realm/messages.json b/src/realm/messages.json index a14b6d5a93..061d91f158 100644 --- a/src/realm/messages.json +++ b/src/realm/messages.json @@ -3,6 +3,9 @@ "uploadFile":"Upload JSON file", "realmName":"Realm name", "enabled":"Enabled", - "create":"Create" + "create":"Create", + "createRealm": "Create realm", + "noRealmRoles": "No realm roles", + "emptyStateText": "There aren't any realm roles in this realm. Create a realm role to get started." } } diff --git a/src/sessions/SessionsSection.tsx b/src/sessions/SessionsSection.tsx index c4cb3de3dd..5224ccc534 100644 --- a/src/sessions/SessionsSection.tsx +++ b/src/sessions/SessionsSection.tsx @@ -1,5 +1,13 @@ +import { PageSection } from "@patternfly/react-core"; import React from "react"; export const SessionsSection = () => { - return <>The Sessions Page; + return ( + <> + <> + The Sessions Page + + ; + + ); }; diff --git a/src/stories/RealmForm.stories.tsx b/src/stories/RealmForm.stories.tsx index 34960d6e2b..f8a1afd24d 100644 --- a/src/stories/RealmForm.stories.tsx +++ b/src/stories/RealmForm.stories.tsx @@ -4,7 +4,7 @@ import { Page } from "@patternfly/react-core"; import { NewRealmForm } from "../realm/add/NewRealmForm"; export default { - title: "New reaml form", + title: "New realm form", component: NewRealmForm, } as Meta; diff --git a/src/stories/RealmRoles.stories.tsx b/src/stories/RealmRoles.stories.tsx new file mode 100644 index 0000000000..6b15dd2137 --- /dev/null +++ b/src/stories/RealmRoles.stories.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import { Meta } from "@storybook/react"; +import { RolesList } from "../realm-roles/RoleList"; +import rolesMock from "../realm-roles/__tests__/mock-roles.json"; + +export default { + title: "Roles List", + component: RolesList, +} as Meta; + +export const RolesListExample = () => ; diff --git a/src/user/UsersSection.tsx b/src/user/UsersSection.tsx index e0f7176e30..7502e7fa6b 100644 --- a/src/user/UsersSection.tsx +++ b/src/user/UsersSection.tsx @@ -1,5 +1,10 @@ +import { PageSection } from "@patternfly/react-core"; import React from "react"; export const UsersSection = () => { - return <>The Users Page; + return ( + <> + The Users Page + + ); };