diff --git a/cypress/support/pages/admin_console/manage/authentication/DuplicateFlowModal.ts b/cypress/support/pages/admin_console/manage/authentication/DuplicateFlowModal.ts index bd71e4f0dd..72ec8ba875 100644 --- a/cypress/support/pages/admin_console/manage/authentication/DuplicateFlowModal.ts +++ b/cypress/support/pages/admin_console/manage/authentication/DuplicateFlowModal.ts @@ -5,7 +5,7 @@ export default class DuplicateFlowModal { fill(name?: string, description?: string) { if (name) { - cy.findByTestId(this.aliasInput).type(name); + cy.findByTestId(this.aliasInput).clear().type(name); if (description) cy.get(this.descriptionInput).type(description); } diff --git a/src/authentication/AuthenticationSection.tsx b/src/authentication/AuthenticationSection.tsx index 913277c232..b753acd42d 100644 --- a/src/authentication/AuthenticationSection.tsx +++ b/src/authentication/AuthenticationSection.tsx @@ -219,7 +219,7 @@ export const AuthenticationSection = () => { key={key} loader={loader} ariaLabelKey="authentication:title" - searchPlaceholderKey="authentication:searchForEvent" + searchPlaceholderKey="authentication:searchForFlow" toolbarItem={ , + , + ]} + > + +
+ + +
+ + ); +}; diff --git a/src/authentication/FlowDetails.tsx b/src/authentication/FlowDetails.tsx index f46baf0413..43803121fc 100644 --- a/src/authentication/FlowDetails.tsx +++ b/src/authentication/FlowDetails.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { useParams } from "react-router-dom"; +import { useHistory, useParams } from "react-router-dom"; import { Trans, useTranslation } from "react-i18next"; import { DataList, @@ -13,13 +13,14 @@ import { ActionGroup, Button, ButtonVariant, + DropdownItem, } from "@patternfly/react-core"; import { CheckCircleIcon, PlusIcon, TableIcon } from "@patternfly/react-icons"; import type AuthenticationExecutionInfoRepresentation from "@keycloak/keycloak-admin-client/lib/defs/authenticationExecutionInfoRepresentation"; import type { AuthenticationProviderRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/authenticatorConfigRepresentation"; import type AuthenticationFlowRepresentation from "@keycloak/keycloak-admin-client/lib/defs/authenticationFlowRepresentation"; -import type { FlowParams } from "./routes/Flow"; +import { FlowParams, toFlow } from "./routes/Flow"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { useAdminClient, useFetch } from "../context/auth/AdminClient"; import { EmptyExecutionState } from "./EmptyExecutionState"; @@ -37,6 +38,10 @@ import { useAlerts } from "../components/alert/Alerts"; import { AddStepModal } from "./components/modals/AddStepModal"; import { AddSubFlowModal, Flow } from "./components/modals/AddSubFlowModal"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; +import { DuplicateFlowModal } from "./DuplicateFlowModal"; +import { useRealm } from "../context/realm-context/RealmContext"; +import { toAuthentication } from "./routes/Authentication"; +import { EditFlowModal } from "./EditFlowModal"; export const providerConditionFilter = ( value: AuthenticationProviderRepresentation @@ -45,8 +50,10 @@ export const providerConditionFilter = ( export const FlowDetails = () => { const { t } = useTranslation("authentication"); const adminClient = useAdminClient(); + const { realm } = useRealm(); const { addAlert, addError } = useAlerts(); const { id, usedBy, builtIn } = useParams(); + const history = useHistory(); const [key, setKey] = useState(0); const refresh = () => setKey(new Date().getTime()); @@ -62,6 +69,8 @@ export const FlowDetails = () => { const [showAddSubFlowDialog, setShowSubFlowDialog] = useState(); const [selectedExecution, setSelectedExecution] = useState(); + const [open, setOpen] = useState(false); + const [edit, setEdit] = useState(false); useFetch( async () => { @@ -168,6 +177,20 @@ export const FlowDetails = () => { } }; + const setAsDefault = async () => { + try { + const r = await adminClient.realms.findOne({ realm }); + await adminClient.realms.update( + { realm }, + { ...r, browserFlow: flow?.alias } + ); + addAlert(t("updateFlowSuccess"), AlertVariant.success); + history.push(toFlow({ id, realm, usedBy: "default", builtIn })); + } catch (error) { + addError("authentication:updateFlowError", error); + } + }; + const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: "authentication:deleteConfirmExecution", children: ( @@ -191,10 +214,90 @@ export const FlowDetails = () => { }, }); + const [toggleDeleteFlow, DeleteFlowConfirm] = useConfirmDialog({ + titleKey: "authentication:deleteConfirmFlow", + children: ( + + {" "} + {{ flow: flow?.alias || "" }}. + + ), + continueButtonLabel: "common:delete", + continueButtonVariant: ButtonVariant.danger, + onConfirm: async () => { + try { + await adminClient.authenticationManagement.deleteFlow({ + flowId: flow!.id!, + }); + history.push(toAuthentication({ realm })); + addAlert(t("deleteFlowSuccess"), AlertVariant.success); + } catch (error) { + addError("authentication:deleteFlowError", error); + } + }, + }); + const hasExecutions = executionList?.expandableList.length !== 0; + const dropdownItems = [ + ...(usedBy !== "default" + ? [ + setAsDefault()} + > + {t("setAsDefault")} + , + ] + : []), + setOpen(true)}> + {t("duplicate")} + , + ...(!builtIn + ? [ + setEdit(true)} + > + {t("editInfo")} + , + toggleDeleteFlow()} + > + {t("common:delete")} + , + ] + : []), + ]; + return ( <> + {open && ( + setOpen(!open)} + onComplete={() => { + refresh(); + setOpen(false); + }} + /> + )} + {edit && ( + { + setEdit(!edit); + refresh(); + }} + /> + )} + + { } : {}, ]} + dropdownItems={dropdownItems} /> {hasExecutions && ( @@ -279,6 +383,7 @@ export const FlowDetails = () => { <> {executionList.expandableList.map((execution) => ( { diff --git a/src/authentication/components/FlowRow.tsx b/src/authentication/components/FlowRow.tsx index 9ff7d06536..61d2bd04db 100644 --- a/src/authentication/components/FlowRow.tsx +++ b/src/authentication/components/FlowRow.tsx @@ -26,6 +26,7 @@ import { EditFlowDropdown } from "./EditFlowDropdown"; import "./flow-row.css"; type FlowRowProps = { + builtIn: boolean; execution: ExpandableExecution; onRowClick: (execution: ExpandableExecution) => void; onRowChange: (execution: AuthenticationExecutionInfoRepresentation) => void; @@ -38,6 +39,7 @@ type FlowRowProps = { }; export const FlowRow = ({ + builtIn, execution, onRowClick, onRowChange, @@ -101,20 +103,22 @@ export const FlowRow = ({ {execution.configurable && ( )} - {execution.authenticationFlow && ( + {execution.authenticationFlow && !builtIn && ( )} - + {!builtIn && ( + + )} , ]} /> @@ -124,6 +128,7 @@ export const FlowRow = ({ hasSubList && execution.executionList?.map((execution) => ( {{flow}}".',