Factorise LoginUserProfile and LoginUpdateProfile

This commit is contained in:
Joseph Garrone 2024-05-08 16:10:03 +02:00
parent f97d33ffc1
commit 58dfd3c25c
5 changed files with 59 additions and 216 deletions

View File

@ -27,7 +27,6 @@ const LoginPageExpired = lazy(() => import("keycloakify/login/pages/LoginPageExp
const LoginIdpLinkEmail = lazy(() => import("keycloakify/login/pages/LoginIdpLinkEmail")); const LoginIdpLinkEmail = lazy(() => import("keycloakify/login/pages/LoginIdpLinkEmail"));
const LoginConfigTotp = lazy(() => import("keycloakify/login/pages/LoginConfigTotp")); const LoginConfigTotp = lazy(() => import("keycloakify/login/pages/LoginConfigTotp"));
const LogoutConfirm = lazy(() => import("keycloakify/login/pages/LogoutConfirm")); const LogoutConfirm = lazy(() => import("keycloakify/login/pages/LogoutConfirm"));
const UpdateUserProfile = lazy(() => import("keycloakify/login/pages/UpdateUserProfile"));
const IdpReviewUserProfile = lazy(() => import("keycloakify/login/pages/IdpReviewUserProfile")); const IdpReviewUserProfile = lazy(() => import("keycloakify/login/pages/IdpReviewUserProfile"));
const UpdateEmail = lazy(() => import("keycloakify/login/pages/UpdateEmail")); const UpdateEmail = lazy(() => import("keycloakify/login/pages/UpdateEmail"));
const SelectAuthenticator = lazy(() => import("keycloakify/login/pages/SelectAuthenticator")); const SelectAuthenticator = lazy(() => import("keycloakify/login/pages/SelectAuthenticator"));
@ -75,6 +74,7 @@ export default function Fallback(props: FallbackProps) {
case "login-update-password.ftl": case "login-update-password.ftl":
return <LoginUpdatePassword kcContext={kcContext} {...rest} />; return <LoginUpdatePassword kcContext={kcContext} {...rest} />;
case "login-update-profile.ftl": case "login-update-profile.ftl":
case "update-user-profile.ftl":
return <LoginUpdateProfile kcContext={kcContext} {...rest} />; return <LoginUpdateProfile kcContext={kcContext} {...rest} />;
case "login-idp-link-confirm.ftl": case "login-idp-link-confirm.ftl":
return <LoginIdpLinkConfirm kcContext={kcContext} {...rest} />; return <LoginIdpLinkConfirm kcContext={kcContext} {...rest} />;
@ -86,8 +86,6 @@ export default function Fallback(props: FallbackProps) {
return <LoginConfigTotp kcContext={kcContext} {...rest} />; return <LoginConfigTotp kcContext={kcContext} {...rest} />;
case "logout-confirm.ftl": case "logout-confirm.ftl":
return <LogoutConfirm kcContext={kcContext} {...rest} />; return <LogoutConfirm kcContext={kcContext} {...rest} />;
case "update-user-profile.ftl":
return <UpdateUserProfile kcContext={kcContext} {...rest} />;
case "idp-review-user-profile.ftl": case "idp-review-user-profile.ftl":
return <IdpReviewUserProfile kcContext={kcContext} {...rest} />; return <IdpReviewUserProfile kcContext={kcContext} {...rest} />;
case "update-email.ftl": case "update-email.ftl":

View File

@ -31,7 +31,6 @@ export type KcContext =
| KcContext.LoginPageExpired | KcContext.LoginPageExpired
| KcContext.LoginConfigTotp | KcContext.LoginConfigTotp
| KcContext.LogoutConfirm | KcContext.LogoutConfirm
| KcContext.UpdateUserProfile
| KcContext.IdpReviewUserProfile | KcContext.IdpReviewUserProfile
| KcContext.UpdateEmail | KcContext.UpdateEmail
| KcContext.SelectAuthenticator | KcContext.SelectAuthenticator
@ -362,17 +361,6 @@ export declare namespace KcContext {
username: string; username: string;
}; };
export type LoginUpdateProfile = Common & {
pageId: "login-update-profile.ftl";
user: {
editUsernameAllowed: boolean;
username?: string;
email?: string;
firstName?: string;
lastName?: string;
};
};
export type LoginIdpLinkConfirm = Common & { export type LoginIdpLinkConfirm = Common & {
pageId: "login-idp-link-confirm.ftl"; pageId: "login-idp-link-confirm.ftl";
idpAlias: string; idpAlias: string;
@ -432,14 +420,27 @@ export declare namespace KcContext {
}; };
}; };
export type UpdateUserProfile = Common & { export type LoginUpdateProfile = Common & {
pageId: "update-user-profile.ftl"; pageId: "login-update-profile.ftl" | "update-user-profile.ftl";
profile: { profile: {
attributes: Attribute[]; attributes: Attribute[];
attributesByName: Record<string, Attribute>; attributesByName: Record<string, Attribute>;
}; };
}; };
/*
export type LoginUpdateProfile = Common & {
pageId: "login-update-profile.ftl";
user: {
editUsernameAllowed: boolean;
username?: string;
email?: string;
firstName?: string;
lastName?: string;
};
};
*/
export type IdpReviewUserProfile = Common & { export type IdpReviewUserProfile = Common & {
pageId: "idp-review-user-profile.ftl"; pageId: "idp-review-user-profile.ftl";
profile: { profile: {

View File

@ -1,146 +1,72 @@
import { useState } from "react";
import { clsx } from "keycloakify/tools/clsx"; import { clsx } from "keycloakify/tools/clsx";
import type { PageProps } from "keycloakify/login/pages/PageProps"; import type { PageProps } from "keycloakify/login/pages/PageProps";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName"; import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import type { KcContext } from "../kcContext"; import type { KcContext } from "../kcContext";
import type { I18n } from "../i18n"; import type { I18n } from "../i18n";
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFields";
export default function LoginUpdateProfile(props: PageProps<Extract<KcContext, { pageId: "login-update-profile.ftl" }>, I18n>) { type LoginUpdateProfileProps = PageProps<Extract<KcContext, { pageId: "login-update-profile.ftl" | "update-user-profile.ftl" }>, I18n> & {
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props; UserProfileFormFields: LazyOrNot<(props: UserProfileFormFieldsProps) => JSX.Element>;
};
export default function LoginUpdateProfile(props: LoginUpdateProfileProps) {
const { kcContext, i18n, doUseDefaultCss, Template, classes, UserProfileFormFields } = props;
const { getClassName } = useGetClassName({ const { getClassName } = useGetClassName({
doUseDefaultCss, doUseDefaultCss,
classes classes
}); });
const { url, messagesPerField, isAppInitiatedAction } = kcContext;
const { msg, msgStr } = i18n; const { msg, msgStr } = i18n;
const { url, user, messagesPerField, isAppInitiatedAction } = kcContext; const [isFormSubmittable, setIsFormSubmittable] = useState(false);
return ( return (
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} headerNode={msg("loginProfileTitle")}> <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} headerNode={msg("loginProfileTitle")}>
<form id="kc-update-profile-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post"> <form id="kc-update-profile-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
{user.editUsernameAllowed && ( <UserProfileFormFields
<div {...{
className={clsx( kcContext,
getClassName("kcFormGroupClass"), i18n,
messagesPerField.printIfExists("username", getClassName("kcFormGroupErrorClass")) getClassName,
)} messagesPerField
> }}
<div className={getClassName("kcLabelWrapperClass")}> onIsFormSubmittableValueChange={setIsFormSubmittable}
<label htmlFor="username" className={getClassName("kcLabelClass")}> />
{msg("username")}
</label>
</div>
<div className={getClassName("kcInputWrapperClass")}>
<input
type="text"
id="username"
name="username"
defaultValue={user.username ?? ""}
className={getClassName("kcInputClass")}
/>
</div>
</div>
)}
<div
className={clsx(getClassName("kcFormGroupClass"), messagesPerField.printIfExists("email", getClassName("kcFormGroupErrorClass")))}
>
<div className={getClassName("kcLabelWrapperClass")}>
<label htmlFor="email" className={getClassName("kcLabelClass")}>
{msg("email")}
</label>
</div>
<div className={getClassName("kcInputWrapperClass")}>
<input type="text" id="email" name="email" defaultValue={user.email ?? ""} className={getClassName("kcInputClass")} />
</div>
</div>
<div
className={clsx(
getClassName("kcFormGroupClass"),
messagesPerField.printIfExists("firstName", getClassName("kcFormGroupErrorClass"))
)}
>
<div className={getClassName("kcLabelWrapperClass")}>
<label htmlFor="firstName" className={getClassName("kcLabelClass")}>
{msg("firstName")}
</label>
</div>
<div className={getClassName("kcInputWrapperClass")}>
<input
type="text"
id="firstName"
name="firstName"
defaultValue={user.firstName ?? ""}
className={getClassName("kcInputClass")}
/>
</div>
</div>
<div
className={clsx(
getClassName("kcFormGroupClass"),
messagesPerField.printIfExists("lastName", getClassName("kcFormGroupErrorClass"))
)}
>
<div className={getClassName("kcLabelWrapperClass")}>
<label htmlFor="lastName" className={getClassName("kcLabelClass")}>
{msg("lastName")}
</label>
</div>
<div className={getClassName("kcInputWrapperClass")}>
<input
type="text"
id="lastName"
name="lastName"
defaultValue={user.lastName ?? ""}
className={getClassName("kcInputClass")}
/>
</div>
</div>
<div className={getClassName("kcFormGroupClass")}> <div className={getClassName("kcFormGroupClass")}>
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}> <div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")} /> <div className={getClassName("kcFormOptionsWrapperClass")} />
</div> </div>
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}> <div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
{isAppInitiatedAction ? ( <input
<> disabled={!isFormSubmittable}
<input className={clsx(
className={clsx( getClassName("kcButtonClass"),
getClassName("kcButtonClass"), getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonPrimaryClass"), !isAppInitiatedAction && getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass") getClassName("kcButtonLargeClass")
)} )}
type="submit" type="submit"
defaultValue={msgStr("doSubmit")} value={msgStr("doSubmit")}
/> />
<button {isAppInitiatedAction && (
className={clsx( <button
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonLargeClass")
)}
type="submit"
name="cancel-aia"
value="true"
>
{msg("doCancel")}
</button>
</>
) : (
<input
className={clsx( className={clsx(
getClassName("kcButtonClass"), getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"), getClassName("kcButtonDefaultClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass") getClassName("kcButtonLargeClass")
)} )}
type="submit" type="submit"
defaultValue={msgStr("doSubmit")} name="cancel-aia"
/> value="true"
formNoValidate
>
{msg("doCancel")}
</button>
)} )}
</div> </div>
</div> </div>

View File

@ -8,12 +8,12 @@ import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFields"; import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFields";
import type { TermsAcceptanceProps } from "../TermsAcceptance"; import type { TermsAcceptanceProps } from "../TermsAcceptance";
export type PropsOfRegister = PageProps<Extract<KcContext, { pageId: "register.ftl" | "register-user-profile.ftl" }>, I18n> & { type RegisterProps = PageProps<Extract<KcContext, { pageId: "register.ftl" | "register-user-profile.ftl" }>, I18n> & {
UserProfileFormFields: LazyOrNot<(props: UserProfileFormFieldsProps) => JSX.Element>; UserProfileFormFields: LazyOrNot<(props: UserProfileFormFieldsProps) => JSX.Element>;
TermsAcceptance: LazyOrNot<(props: TermsAcceptanceProps) => JSX.Element | null>; TermsAcceptance: LazyOrNot<(props: TermsAcceptanceProps) => JSX.Element | null>;
}; };
export default function Register(props: PropsOfRegister) { export default function Register(props: RegisterProps) {
const { kcContext, i18n, doUseDefaultCss, Template, classes, UserProfileFormFields, TermsAcceptance } = props; const { kcContext, i18n, doUseDefaultCss, Template, classes, UserProfileFormFields, TermsAcceptance } = props;
const { getClassName } = useGetClassName({ const { getClassName } = useGetClassName({

View File

@ -1,82 +0,0 @@
import { useState } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { UserProfileFormFields } from "keycloakify/login/pages/shared/UserProfileFormFields";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
import type { KcContext } from "../kcContext";
import type { I18n } from "../i18n";
export default function UpdateUserProfile(props: PageProps<Extract<KcContext, { pageId: "update-user-profile.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
const { getClassName } = useGetClassName({
doUseDefaultCss,
classes
});
const { msg, msgStr } = i18n;
const { url, isAppInitiatedAction } = kcContext;
const [isFomSubmittable, setIsFomSubmittable] = useState(false);
return (
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} headerNode={msg("loginProfileTitle")}>
<form id="kc-update-profile-form" className={getClassName("kcFormClass")} action={url.loginAction} method="post">
<UserProfileFormFields
kcContext={kcContext}
onIsFormSubmittableValueChange={setIsFomSubmittable}
i18n={i18n}
getClassName={getClassName}
/>
<div className={getClassName("kcFormGroupClass")}>
<div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
<div className={getClassName("kcFormOptionsWrapperClass")}></div>
</div>
<div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
{isAppInitiatedAction ? (
<>
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonLargeClass")
)}
type="submit"
value={msgStr("doSubmit")}
/>
<button
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonLargeClass")
)}
type="submit"
name="cancel-aia"
value="true"
formNoValidate
>
{msg("doCancel")}
</button>
</>
) : (
<input
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonBlockClass"),
getClassName("kcButtonLargeClass")
)}
type="submit"
defaultValue={msgStr("doSubmit")}
disabled={!isFomSubmittable}
/>
)}
</div>
</div>
</form>
</Template>
);
}