From 08f8ad68257a7ecd8442055edbe264e6093a766a Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 16 Jun 2026 15:25:28 +0200 Subject: [PATCH 1/2] Make "Create User" dialog similar to other create dialogs Out of all "Create" dialogs, the one for users was working differently from the all the others. As I can see no reason why it should be like that, this patch brings the "Create New User" dialog in line with all the other create dialogs, for consistencies and usabilities sake. --- .../partials/wizard/NewUserGeneralTab.tsx | 177 ++++++++++-------- .../partials/wizard/NewUserSummaryPage.tsx | 62 ++++++ .../users/partials/wizard/NewUserWizard.tsx | 94 ++++++---- .../users/partials/wizard/UserRolesTab.tsx | 59 +++--- .../adminui/languages/lang-en_US.json | 3 +- 5 files changed, 253 insertions(+), 142 deletions(-) create mode 100644 src/components/users/partials/wizard/NewUserSummaryPage.tsx diff --git a/src/components/users/partials/wizard/NewUserGeneralTab.tsx b/src/components/users/partials/wizard/NewUserGeneralTab.tsx index 6d981a137d..a2367ed828 100644 --- a/src/components/users/partials/wizard/NewUserGeneralTab.tsx +++ b/src/components/users/partials/wizard/NewUserGeneralTab.tsx @@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next"; import cn from "classnames"; import ModalContent from "../../../shared/modals/ModalContent"; import { ParseKeys } from "i18next"; +import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; /** * This component renders the general user information tab for new users in the new users wizard. @@ -19,96 +20,108 @@ interface RequiredFormProps { const NewUserGeneralTab = ({ formik, + nextPage, }: { formik: FormikProps + nextPage: (values: T) => void, }) => { const { t } = useTranslation(); return ( - -
- - {/* Fields for user information needed */} -
- - + +
+ + {/* Fields for user information needed */} +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
-
- - -
-
- - -
-
- - -
-
- - -
- -
- + + + {/* Navigation buttons */} + + ); }; diff --git a/src/components/users/partials/wizard/NewUserSummaryPage.tsx b/src/components/users/partials/wizard/NewUserSummaryPage.tsx new file mode 100644 index 0000000000..900217c94d --- /dev/null +++ b/src/components/users/partials/wizard/NewUserSummaryPage.tsx @@ -0,0 +1,62 @@ +import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; +import { useTranslation } from "react-i18next"; +import Notifications from "../../../shared/Notifications"; +import { FormikProps } from "formik"; +import { initialFormValuesNewUser } from "../../../../configs/modalConfig"; +import ModalContentTable from "../../../shared/modals/ModalContentTable"; + +/** + * This component renders the summary page for new groups in the new group wizard. + */ +const NewUserSummaryPage = ({ + formik, + previousPage, +}: { + formik: FormikProps, + previousPage?: (values: T) => void, +}) => { + const { t } = useTranslation(); + + // get values of objects in field that should be shown + const getValues = (fields: { name: string }[]) => { + const names = []; + for (const field of fields) { + names.push(field.name); + } + return names; + }; + + return ( + <> + + + +
+
{t("USERS.USERS.DETAILS.TABS.SUMMARY")}
+
+ + + + + + + + + + + +
{t("USERS.USERS.DETAILS.TABS.USER")}{formik.values.name}
{t("USERS.USERS.DETAILS.TABS.ROLES")}{getValues(formik.values.roles).join(", ")}
+
+
+
+ {/* Button for navigation to next page */} + + + ); +}; + +export default NewUserSummaryPage; diff --git a/src/components/users/partials/wizard/NewUserWizard.tsx b/src/components/users/partials/wizard/NewUserWizard.tsx index 6e9f458c81..6fec5e8908 100644 --- a/src/components/users/partials/wizard/NewUserWizard.tsx +++ b/src/components/users/partials/wizard/NewUserWizard.tsx @@ -1,7 +1,4 @@ -import { useState } from "react"; import { Formik } from "formik"; -import { useTranslation } from "react-i18next"; -import cn from "classnames"; import NewUserGeneralTab from "./NewUserGeneralTab"; import UserRolesTab from "./UserRolesTab"; import { initialFormValuesNewUser } from "../../../../configs/modalConfig"; @@ -9,9 +6,10 @@ import { getUsernames } from "../../../../selectors/userSelectors"; import { NewUserSchema } from "../../../../utils/validate"; import { NewUser, postNewUser, UserRole } from "../../../../slices/userSlice"; import { useAppDispatch, useAppSelector } from "../../../../store"; -import ButtonLikeAnchor from "../../../shared/ButtonLikeAnchor"; -import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; import { Role } from "../../../../slices/aclSlice"; +import WizardStepper, { WizardStep } from "../../../shared/wizard/WizardStepper"; +import { usePageFunctions } from "../../../../hooks/wizardHooks"; +import NewUserSummaryPage from "./NewUserSummaryPage"; /** * This component renders the new user wizard @@ -21,16 +19,39 @@ const NewUserWizard = ({ }: { close: () => void, }) => { - const { t } = useTranslation(); const dispatch = useAppDispatch(); const usernames = useAppSelector(state => getUsernames(state)); - const [tab, setTab] = useState(0); + const { + page, + nextPage, + previousPage, + setPage, + pageCompleted, + setPageCompleted, + } = usePageFunctions(0); - const openTab = (tabNr: number) => { - setTab(tabNr); - }; + type StepName = "metadata" | "roles" | "summary"; + type Step = WizardStep & { + name: StepName, + } + + // Caption of steps used by Stepper + const steps: Step[] = [ + { + translation: "USERS.USERS.DETAILS.TABS.USER", + name: "metadata", + }, + { + translation: "USERS.USERS.DETAILS.TABS.ROLES", + name: "roles", + }, + { + translation: "USERS.USERS.DETAILS.TABS.SUMMARY", + name: "summary", + }, + ]; const handleSubmit = (values: { username: string, @@ -54,23 +75,6 @@ const NewUserWizard = ({ return ( <> - {/* Head navigation*/} - - {/* Initialize overall form */} { return ( <> - {tab === 0 && } - {tab === 1 && } - - {/* Navigation buttons and validation */} - formik.handleSubmit()} - previousPage={() => close()} - cancelTranslationString={"CANCEL"} + + {steps[page].name === "metadata" && + + } + {steps[page].name === "roles" && + + } + {steps[page].name === "summary" && + + } ); }} diff --git a/src/components/users/partials/wizard/UserRolesTab.tsx b/src/components/users/partials/wizard/UserRolesTab.tsx index f3a7701683..431f972b26 100644 --- a/src/components/users/partials/wizard/UserRolesTab.tsx +++ b/src/components/users/partials/wizard/UserRolesTab.tsx @@ -4,6 +4,7 @@ import SelectContainer from "../../../shared/wizard/SelectContainer"; import { FormikProps } from "formik"; import { NotificationComponent } from "../../../shared/Notifications"; import ModalContent from "../../../shared/modals/ModalContent"; +import WizardNavigationButtons from "../../../shared/wizard/WizardNavigationButtons"; /** * This component renders the role selection tab of the new user wizard and the user details modal @@ -14,8 +15,12 @@ interface RequiredFormProps { const UserRolesTab = ({ formik, + nextPage, + previousPage, }: { formik: FormikProps + nextPage?: (values: T) => void, // For create modal + previousPage?: (values: T) => void, // For create modal }) => { // roles that can be chosen by user const [roles, setRoles] = useState([]); @@ -35,31 +40,41 @@ const UserRolesTab = ({ }, []); return ( - - {!formik.values.manageable && ( - - )} -
- {/* Select container for roles*/} - {!loading && ( - + + {!formik.values.manageable && ( + )} -
-
+
+ {/* Select container for roles*/} + {!loading && ( + + )} +
+ + + {previousPage && nextPage && + + } + ); }; diff --git a/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json b/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json index b9183c0152..200b3c6d1a 100644 --- a/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json +++ b/src/i18n/org/opencastproject/adminui/languages/lang-en_US.json @@ -1381,7 +1381,8 @@ "USER": "User", "ROLES": "Roles", "EXTERNALROLES": "External Roles", - "EFFECTIVEROLES": "Effective Roles" + "EFFECTIVEROLES": "Effective Roles", + "SUMMARY": "Summary" }, "DESCRIPTION": { "ROLES": "Roles and groups that can be or are already assigned to the user.", From 0a0624e8e73c41330e0852cf8a8430fca4af8577 Mon Sep 17 00:00:00 2001 From: Arnei Date: Tue, 16 Jun 2026 15:26:22 +0200 Subject: [PATCH 2/2] Add password indicator to user details Because you might want to know how strong your new password is. --- src/components/users/partials/wizard/EditUserGeneralTab.tsx | 4 ++++ src/components/users/partials/wizard/NewUserGeneralTab.tsx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/users/partials/wizard/EditUserGeneralTab.tsx b/src/components/users/partials/wizard/EditUserGeneralTab.tsx index 10161aeb3e..180ae05b2f 100644 --- a/src/components/users/partials/wizard/EditUserGeneralTab.tsx +++ b/src/components/users/partials/wizard/EditUserGeneralTab.tsx @@ -4,6 +4,7 @@ import { Field } from "../../../shared/Field"; import { FormikProps } from "formik"; import { NotificationComponent } from "../../../shared/Notifications"; import ModalContent from "../../../shared/modals/ModalContent"; +import { PasswordStrengthIndicator } from "./NewUserGeneralTab"; /** * This component renders the general user information tab in the users details modal. @@ -105,6 +106,9 @@ const EditUserGeneralTab = ({ } />
+
); diff --git a/src/components/users/partials/wizard/NewUserGeneralTab.tsx b/src/components/users/partials/wizard/NewUserGeneralTab.tsx index a2367ed828..d97fc42f8e 100644 --- a/src/components/users/partials/wizard/NewUserGeneralTab.tsx +++ b/src/components/users/partials/wizard/NewUserGeneralTab.tsx @@ -125,7 +125,7 @@ const NewUserGeneralTab = ({ ); }; -const PasswordStrengthIndicator = ({ +export const PasswordStrengthIndicator = ({ password, }: { password: string