aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar garronej <[email protected]>2023-03-21 02:55:32 +0100
committerGravatar garronej <[email protected]>2023-03-21 02:55:32 +0100
commitdf766401002b6350bb06b0b6769d22b8b7240e6f (patch)
tree960cebcb599ca5ca6d93794e5d6606ba66c7374d /src
parentupdate setup (diff)
Working with v7 (without account theme)
Diffstat (limited to 'src')
-rw-r--r--src/App/App.tsx3
-rw-r--r--src/App/oidc.tsx33
-rw-r--r--src/index.tsx17
-rw-r--r--src/keycloak-theme/login/KcApp.css (renamed from src/keycloak-theme/KcApp.css)0
-rw-r--r--src/keycloak-theme/login/KcApp.tsx (renamed from src/keycloak-theme/KcApp.tsx)22
-rw-r--r--src/keycloak-theme/login/Template.tsx (renamed from src/keycloak-theme/Template.tsx)11
-rw-r--r--src/keycloak-theme/login/assets/background.svg (renamed from src/keycloak-theme/assets/background.svg)0
-rw-r--r--src/keycloak-theme/login/assets/tos_en.md (renamed from src/keycloak-theme/assets/tos_en.md)0
-rw-r--r--src/keycloak-theme/login/assets/tos_fr.md (renamed from src/keycloak-theme/assets/tos_fr.md)0
-rw-r--r--src/keycloak-theme/login/i18n.ts (renamed from src/keycloak-theme/i18n.ts)2
-rw-r--r--src/keycloak-theme/login/kcContext.ts (renamed from src/keycloak-theme/kcContext.ts)2
-rw-r--r--src/keycloak-theme/login/pages/Login.tsx201
-rw-r--r--src/keycloak-theme/login/pages/MyExtraPage1.tsx (renamed from src/keycloak-theme/pages/MyExtraPage1.tsx)13
-rw-r--r--src/keycloak-theme/login/pages/MyExtraPage2.tsx (renamed from src/keycloak-theme/pages/MyExtraPage2.tsx)16
-rw-r--r--src/keycloak-theme/login/pages/Register.tsx182
-rw-r--r--src/keycloak-theme/login/pages/RegisterUserProfile.tsx70
-rw-r--r--src/keycloak-theme/login/pages/Terms.tsx56
-rw-r--r--src/keycloak-theme/login/pages/shared/UserProfileCommons.tsx (renamed from src/keycloak-theme/pages/shared/UserProfileCommons.tsx)58
-rw-r--r--src/keycloak-theme/login/valuesTransferredOverUrl.ts (renamed from src/keycloak-theme/valuesTransferredOverUrl.ts)0
-rw-r--r--src/keycloak-theme/pages/Login.tsx204
-rw-r--r--src/keycloak-theme/pages/Register.tsx191
-rw-r--r--src/keycloak-theme/pages/RegisterUserProfile.tsx73
-rw-r--r--src/keycloak-theme/pages/Terms.tsx70
23 files changed, 600 insertions, 624 deletions
diff --git a/src/App/App.tsx b/src/App/App.tsx
index c0f9222..68f1393 100644
--- a/src/App/App.tsx
+++ b/src/App/App.tsx
@@ -2,7 +2,7 @@ import "./App.css";
import logo from "./logo.svg";
import myimg from "./myimg.png";
import { createOidcClientProvider, useOidcClient } from "./oidc";
-import { addFooToQueryParams, addBarToQueryParams } from "../keycloak-theme/valuesTransferredOverUrl";
+import { addFooToQueryParams, addBarToQueryParams } from "../keycloak-theme/login/valuesTransferredOverUrl";
import jwt_decode from "jwt-decode";
const { OidcClientProvider } = createOidcClientProvider({
@@ -29,7 +29,6 @@ export default function App() {
<ContextualizedApp />
</OidcClientProvider>
);
-
}
function ContextualizedApp() {
diff --git a/src/App/oidc.tsx b/src/App/oidc.tsx
index ce4a854..44eb9e1 100644
--- a/src/App/oidc.tsx
+++ b/src/App/oidc.tsx
@@ -23,7 +23,7 @@ export declare namespace OidcClient {
export type LoggedIn = {
isUserLoggedIn: true;
- getAccessToken: ()=> string;
+ getAccessToken: () => string;
logout: (params: { redirectTo: "home" | "current page" }) => Promise<never>;
//If we have sent a API request to change user's email for example
//and we want that jwt_decode(oidcClient.getAccessToken()).email be the new email
@@ -36,8 +36,8 @@ type Params = {
url: string;
realm: string;
clientId: string;
- transformUrlBeforeRedirect: (url: string) => string;
- getUiLocales: () => string;
+ transformUrlBeforeRedirect?: (url: string) => string;
+ getUiLocales?: () => string;
log?: typeof console.log;
};
@@ -65,14 +65,19 @@ async function createKeycloakOidcClient(params: Params): Promise<OidcClient> {
checkLoginIframe: false,
adapter: createKeycloakAdapter({
transformUrlBeforeRedirect: url =>
- [url].map(transformUrlBeforeRedirect).map(
- url =>
- addParamToUrl({
- url,
- "name": "ui_locales",
- "value": getUiLocales()
- }).newUrl
- )[0],
+ [url]
+ .map(transformUrlBeforeRedirect ?? (url => url))
+ .map(
+ getUiLocales === undefined ?
+ (url => url) :
+ url =>
+ addParamToUrl({
+ url,
+ "name": "ui_locales",
+ "value": getUiLocales()
+ }).newUrl
+ )
+ [0],
keycloakInstance,
getRedirectMethod: () => redirectMethod
})
@@ -103,11 +108,11 @@ async function createKeycloakOidcClient(params: Params): Promise<OidcClient> {
});
}
- let currentAccessToken= keycloakInstance.token!;
+ let currentAccessToken = keycloakInstance.token!;
const oidcClient = id<OidcClient.LoggedIn>({
"isUserLoggedIn": true,
- "getAccessToken": ()=> currentAccessToken,
+ "getAccessToken": () => currentAccessToken,
"logout": async ({ redirectTo }) => {
await keycloakInstance.logout({
"redirectUri": (() => {
@@ -157,7 +162,7 @@ async function createKeycloakOidcClient(params: Params): Promise<OidcClient> {
currentAccessToken = keycloakInstance.token!;
callee();
-
+
}, msBeforeExpiration - minValiditySecond * 1000);
})();
diff --git a/src/index.tsx b/src/index.tsx
index 37d6a5e..ba55bfe 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,17 +1,22 @@
import { createRoot } from "react-dom/client";
import { StrictMode, lazy, Suspense } from "react";
-import { kcContext } from "./keycloak-theme/kcContext";
+import { kcContext as kcLoginThemeContext } from "./keycloak-theme/login/kcContext";
const App = lazy(() => import("./App"));
-const KcApp = lazy(() => import("./keycloak-theme/KcApp"));
+const KcLoginThemeApp = lazy(() => import("./keycloak-theme/login/KcApp"));
createRoot(document.getElementById("root")!).render(
<StrictMode>
<Suspense>
- {kcContext === undefined ?
- <App /> :
- <KcApp kcContext={kcContext} />
- }
+ {(()=>{
+
+ if( kcLoginThemeContext !== undefined ){
+ return <KcLoginThemeApp kcContext={kcLoginThemeContext} />;
+ }
+
+ return <App />;
+
+ })()}
</Suspense>
</StrictMode>
);
diff --git a/src/keycloak-theme/KcApp.css b/src/keycloak-theme/login/KcApp.css
index aeaebbe..aeaebbe 100644
--- a/src/keycloak-theme/KcApp.css
+++ b/src/keycloak-theme/login/KcApp.css
diff --git a/src/keycloak-theme/KcApp.tsx b/src/keycloak-theme/login/KcApp.tsx
index 81d83ca..f41f00d 100644
--- a/src/keycloak-theme/KcApp.tsx
+++ b/src/keycloak-theme/login/KcApp.tsx
@@ -1,11 +1,11 @@
import "./KcApp.css";
import { lazy, Suspense } from "react";
+import Fallback, { type PageProps } from "keycloakify/login";
import type { KcContext } from "./kcContext";
import { useI18n } from "./i18n";
-import Fallback, { type PageProps } from "keycloakify";
const Template = lazy(() => import("./Template"));
-const DefaultTemplate = lazy(() => import("keycloakify/Template"));
+const DefaultTemplate = lazy(() => import("keycloakify/login/Template"));
// You can uncomment this to see the values passed by the main app before redirecting.
//import { foo, bar } from "./valuesTransferredOverUrl";
@@ -18,7 +18,7 @@ const RegisterUserProfile = lazy(() => import("./pages/RegisterUserProfile"));
const Terms = lazy(() => import("./pages/Terms"));
const MyExtraPage1 = lazy(() => import("./pages/MyExtraPage1"));
const MyExtraPage2 = lazy(() => import("./pages/MyExtraPage2"));
-const Info = lazy(() => import("keycloakify/pages/Info"));
+const Info = lazy(() => import("keycloakify/login/pages/Info"));
// This is like adding classes to theme.properties
// https://github.com/keycloak/keycloak/blob/11.0.3/themes/src/main/resources/theme/keycloak/login/theme.properties
@@ -49,15 +49,15 @@ export default function App(props: { kcContext: KcContext; }) {
<Suspense>
{(() => {
switch (kcContext.pageId) {
- case "login.ftl": return <Login {...{ kcContext, i18n, Template, classes, "doUseDefaultCss": true }} />;
- case "register.ftl": return <Register {...{ kcContext, i18n, Template, classes, "doUseDefaultCss": true }} />;
- case "register-user-profile.ftl": return <RegisterUserProfile {...{ kcContext, i18n, Template, classes, "doUseDefaultCss": true }} />
- case "terms.ftl": return <Terms {...{ kcContext, i18n, Template, classes, "doUseDefaultCss": true }} />;
- case "my-extra-page-1.ftl": return <MyExtraPage1 {...{ kcContext, i18n, Template, classes, "doUseDefaultCss": true }} />;
- case "my-extra-page-2.ftl": return <MyExtraPage2 {...{ kcContext, i18n, Template, classes, "doUseDefaultCss": true }} />;
+ case "login.ftl": return <Login {...{ kcContext, i18n, Template, classes }} doUseDefaultCss={true} />;
+ case "register.ftl": return <Register {...{ kcContext, i18n, Template, classes }} doUseDefaultCss={true} />;
+ case "register-user-profile.ftl": return <RegisterUserProfile {...{ kcContext, i18n, Template, classes }} doUseDefaultCss={true} />
+ case "terms.ftl": return <Terms {...{ kcContext, i18n, Template, classes }} doUseDefaultCss={true} />;
+ case "my-extra-page-1.ftl": return <MyExtraPage1 {...{ kcContext, i18n, Template, classes }} doUseDefaultCss={true} />;
+ case "my-extra-page-2.ftl": return <MyExtraPage2 {...{ kcContext, i18n, Template, classes }} doUseDefaultCss={true} />;
// We choose to use the default Template for the Info page and to download the theme resources.
- case "info.ftl": return <Info {...{ kcContext, i18n, "Template": DefaultTemplate, classes, "doUseDefaultCss": true }} />;
- default: return <Fallback {...{ kcContext, i18n, "Template": DefaultTemplate, classes, "doUseDefaultCss": true }} />;
+ case "info.ftl": return <Info {...{ kcContext, i18n, classes }} Template={DefaultTemplate} doUseDefaultCss={true} />;
+ default: return <Fallback {...{ kcContext, i18n, classes }} Template={DefaultTemplate} doUseDefaultCss={true} />;
}
})()}
</Suspense>
diff --git a/src/keycloak-theme/Template.tsx b/src/keycloak-theme/login/Template.tsx
index 57a1566..1fd077a 100644
--- a/src/keycloak-theme/Template.tsx
+++ b/src/keycloak-theme/login/Template.tsx
@@ -2,7 +2,7 @@
import { assert } from "keycloakify/tools/assert";
import { clsx } from "keycloakify/tools/clsx";
import { usePrepareTemplate } from "keycloakify/lib/usePrepareTemplate";
-import { type TemplateProps, defaultTemplateClasses } from "keycloakify/TemplateProps";
+import { type TemplateProps, defaultTemplateClasses } from "keycloakify/login/TemplateProps";
import { useGetClassName } from "keycloakify/lib/useGetClassName";
import type { KcContext } from "./kcContext";
import type { I18n } from "./i18n";
@@ -16,12 +16,12 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
showAnotherWayIfPresent = true,
headerNode,
showUsernameNode = null,
- formNode,
infoNode = null,
kcContext,
i18n,
doUseDefaultCss,
- classes
+ classes,
+ children
} = props;
const { getClassName } = useGetClassName({
@@ -42,7 +42,8 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
"lib/zocial/zocial.css"
],
"styles": ["css/login.css"],
- "htmlClassName": getClassName("kcHtmlClass")
+ "htmlClassName": getClassName("kcHtmlClass"),
+ "bodyClassName": undefined
});
if (!isReady) {
@@ -153,7 +154,7 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
/>
</div>
)}
- {formNode}
+ {children}
{auth !== undefined && auth.showTryAnotherWayLink && showAnotherWayIfPresent && (
<form
id="kc-select-try-another-way-form"
diff --git a/src/keycloak-theme/assets/background.svg b/src/keycloak-theme/login/assets/background.svg
index 0e1cada..0e1cada 100644
--- a/src/keycloak-theme/assets/background.svg
+++ b/src/keycloak-theme/login/assets/background.svg
diff --git a/src/keycloak-theme/assets/tos_en.md b/src/keycloak-theme/login/assets/tos_en.md
index 9436328..9436328 100644
--- a/src/keycloak-theme/assets/tos_en.md
+++ b/src/keycloak-theme/login/assets/tos_en.md
diff --git a/src/keycloak-theme/assets/tos_fr.md b/src/keycloak-theme/login/assets/tos_fr.md
index 0621cd3..0621cd3 100644
--- a/src/keycloak-theme/assets/tos_fr.md
+++ b/src/keycloak-theme/login/assets/tos_fr.md
diff --git a/src/keycloak-theme/i18n.ts b/src/keycloak-theme/login/i18n.ts
index bbeab0b..fadaca5 100644
--- a/src/keycloak-theme/i18n.ts
+++ b/src/keycloak-theme/login/i18n.ts
@@ -1,4 +1,4 @@
-import { createUseI18n } from "keycloakify";
+import { createUseI18n } from "keycloakify/login";
export const { useI18n } = createUseI18n({
// NOTE: Here you can override the default i18n messages
diff --git a/src/keycloak-theme/kcContext.ts b/src/keycloak-theme/login/kcContext.ts
index 44b8069..3e86c20 100644
--- a/src/keycloak-theme/kcContext.ts
+++ b/src/keycloak-theme/login/kcContext.ts
@@ -1,4 +1,4 @@
-import { getKcContext } from "keycloakify";
+import { getKcContext } from "keycloakify/login";
export type KcContextExtension =
// NOTE: A 'keycloakify' field must be added
diff --git a/src/keycloak-theme/login/pages/Login.tsx b/src/keycloak-theme/login/pages/Login.tsx
new file mode 100644
index 0000000..f89c259
--- /dev/null
+++ b/src/keycloak-theme/login/pages/Login.tsx
@@ -0,0 +1,201 @@
+import { useState, type FormEventHandler } from "react";
+import { clsx } from "keycloakify/tools/clsx";
+import { useConstCallback } from "keycloakify/tools/useConstCallback";
+import { type PageProps, defaultClasses } from "keycloakify/login/pages/PageProps";
+import { useGetClassName } from "keycloakify/lib/useGetClassName";
+import type { KcContext } from "../kcContext";
+import type { I18n } from "../i18n";
+
+export default function Login(props: PageProps<Extract<KcContext, { pageId: "login.ftl" }>, I18n>) {
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
+
+ const { getClassName } = useGetClassName({
+ "defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
+ classes
+ });
+
+ const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
+
+ const { msg, msgStr } = i18n;
+
+ const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
+
+ const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
+ e.preventDefault();
+
+ setIsLoginButtonDisabled(true);
+
+ const formElement = e.target as HTMLFormElement;
+
+ //NOTE: Even if we login with email Keycloak expect username and password in
+ //the POST request.
+ formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
+
+ formElement.submit();
+ });
+
+ return (
+ <Template
+ {...{ kcContext, i18n, doUseDefaultCss, classes }}
+ displayInfo={social.displayInfo}
+ displayWide={realm.password && social.providers !== undefined}
+ headerNode={msg("doLogIn")}
+ infoNode={
+ realm.password &&
+ realm.registrationAllowed &&
+ !registrationDisabled && (
+ <div id="kc-registration">
+ <span>
+ {msg("noAccount")}
+ <a tabIndex={6} href={url.registrationUrl}>
+ {msg("doRegister")}
+ </a>
+ </span>
+ </div>
+ )
+ }
+ >
+ <div id="kc-form" className={clsx(realm.password && social.providers !== undefined && getClassName("kcContentWrapperClass"))}>
+ <div
+ id="kc-form-wrapper"
+ className={clsx(
+ realm.password &&
+ social.providers && [getClassName("kcFormSocialAccountContentClass"), getClassName("kcFormSocialAccountClass")]
+ )}
+ >
+ {realm.password && (
+ <form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
+ <div className={getClassName("kcFormGroupClass")}>
+ {(() => {
+ const label = !realm.loginWithEmailAllowed
+ ? "username"
+ : realm.registrationEmailAsUsername
+ ? "email"
+ : "usernameOrEmail";
+
+ const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
+
+ return (
+ <>
+ <label htmlFor={autoCompleteHelper} className={getClassName("kcLabelClass")}>
+ {msg(label)}
+ </label>
+ <input
+ tabIndex={1}
+ id={autoCompleteHelper}
+ className={getClassName("kcInputClass")}
+ //NOTE: This is used by Google Chrome auto fill so we use it to tell
+ //the browser how to pre fill the form but before submit we put it back
+ //to username because it is what keycloak expects.
+ name={autoCompleteHelper}
+ defaultValue={login.username ?? ""}
+ type="text"
+ {...(usernameEditDisabled
+ ? { "disabled": true }
+ : {
+ "autoFocus": true,
+ "autoComplete": "off"
+ })}
+ />
+ </>
+ );
+ })()}
+ </div>
+ <div className={getClassName("kcFormGroupClass")}>
+ <label htmlFor="password" className={getClassName("kcLabelClass")}>
+ {msg("password")}
+ </label>
+ <input
+ tabIndex={2}
+ id="password"
+ className={getClassName("kcInputClass")}
+ name="password"
+ type="password"
+ autoComplete="off"
+ />
+ </div>
+ <div className={clsx(getClassName("kcFormGroupClass"), getClassName("kcFormSettingClass"))}>
+ <div id="kc-form-options">
+ {realm.rememberMe && !usernameEditDisabled && (
+ <div className="checkbox">
+ <label>
+ <input
+ tabIndex={3}
+ id="rememberMe"
+ name="rememberMe"
+ type="checkbox"
+ {...(login.rememberMe
+ ? {
+ "checked": true
+ }
+ : {})}
+ />
+ {msg("rememberMe")}
+ </label>
+ </div>
+ )}
+ </div>
+ <div className={getClassName("kcFormOptionsWrapperClass")}>
+ {realm.resetPasswordAllowed && (
+ <span>
+ <a tabIndex={5} href={url.loginResetCredentialsUrl}>
+ {msg("doForgotPassword")}
+ </a>
+ </span>
+ )}
+ </div>
+ </div>
+ <div id="kc-form-buttons" className={getClassName("kcFormGroupClass")}>
+ <input
+ type="hidden"
+ id="id-hidden-input"
+ name="credentialId"
+ {...(auth?.selectedCredential !== undefined
+ ? {
+ "value": auth.selectedCredential
+ }
+ : {})}
+ />
+ <input
+ tabIndex={4}
+ className={clsx(
+ getClassName("kcButtonClass"),
+ getClassName("kcButtonPrimaryClass"),
+ getClassName("kcButtonBlockClass"),
+ getClassName("kcButtonLargeClass")
+ )}
+ name="login"
+ id="kc-login"
+ type="submit"
+ value={msgStr("doLogIn")}
+ disabled={isLoginButtonDisabled}
+ />
+ </div>
+ </form>
+ )}
+ </div>
+ {realm.password && social.providers !== undefined && (
+ <div
+ id="kc-social-providers"
+ className={clsx(getClassName("kcFormSocialAccountContentClass"), getClassName("kcFormSocialAccountClass"))}
+ >
+ <ul
+ className={clsx(
+ getClassName("kcFormSocialAccountListClass"),
+ social.providers.length > 4 && getClassName("kcFormSocialAccountDoubleListClass")
+ )}
+ >
+ {social.providers.map(p => (
+ <li key={p.providerId} className={getClassName("kcFormSocialAccountListLinkClass")}>
+ <a href={p.loginUrl} id={`zocial-${p.alias}`} className={clsx("zocial", p.providerId)}>
+ <span>{p.displayName}</span>
+ </a>
+ </li>
+ ))}
+ </ul>
+ </div>
+ )}
+ </div>
+ </Template>
+ );
+}
diff --git a/src/keycloak-theme/pages/MyExtraPage1.tsx b/src/keycloak-theme/login/pages/MyExtraPage1.tsx
index ed82c60..b7c73bd 100644
--- a/src/keycloak-theme/pages/MyExtraPage1.tsx
+++ b/src/keycloak-theme/login/pages/MyExtraPage1.tsx
@@ -1,4 +1,4 @@
-import type { PageProps } from "keycloakify/pages/PageProps";
+import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../kcContext";
import type { I18n } from "../i18n";
@@ -10,13 +10,12 @@ export default function MyExtraPage1(props: PageProps<Extract<KcContext, { pageI
<Template
{...{ kcContext, i18n, doUseDefaultCss, classes }}
headerNode={<>Header <i>text</i></>}
- formNode={
- <form>
- {/*...*/}
- </form>
- }
infoNode={<span>footer</span>}
- />
+ >
+ <form>
+ {/*...*/}
+ </form>
+ </Template>
);
}
diff --git a/src/keycloak-theme/pages/MyExtraPage2.tsx b/src/keycloak-theme/login/pages/MyExtraPage2.tsx
index e086f99..e0ce046 100644
--- a/src/keycloak-theme/pages/MyExtraPage2.tsx
+++ b/src/keycloak-theme/login/pages/MyExtraPage2.tsx
@@ -1,4 +1,4 @@
-import type { PageProps } from "keycloakify/pages/PageProps";
+import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../kcContext";
import type { I18n } from "../i18n";
@@ -13,13 +13,13 @@ export default function MyExtraPage1(props: PageProps<Extract<KcContext, { pageI
<Template
{...{ kcContext, i18n, doUseDefaultCss, classes }}
headerNode={<>Header <i>text</i></>}
- formNode={
- <form>
- {/*...*/}
- </form>
- }
- infoNode={<span>footer</span> }
- />
+ infoNode={<span>footer</span>}
+ >
+
+ <form>
+ {/*...*/}
+ </form>
+ </Template>
);
}
diff --git a/src/keycloak-theme/login/pages/Register.tsx b/src/keycloak-theme/login/pages/Register.tsx
new file mode 100644
index 0000000..20fbed0
--- /dev/null
+++ b/src/keycloak-theme/login/pages/Register.tsx
@@ -0,0 +1,182 @@
+import { clsx } from "keycloakify/tools/clsx";
+import { type PageProps, defaultClasses } from "keycloakify/login/pages/PageProps";
+import { useGetClassName } from "keycloakify/lib/useGetClassName";
+import type { KcContext } from "../kcContext";
+import type { I18n } from "../i18n";
+
+export default function Register(props: PageProps<Extract<KcContext, { pageId: "register.ftl" }>, I18n>) {
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
+
+ const { getClassName } = useGetClassName({
+ "defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
+ classes
+ });
+
+ const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
+
+ const { msg, msgStr } = i18n;
+
+ return (
+ <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} headerNode={msg("registerTitle")}>
+ <form id="kc-register-form" className={getClassName("kcFormClass")} action={url.registrationAction} method="post">
+ <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"
+ className={getClassName("kcInputClass")}
+ name="firstName"
+ defaultValue={register.formData.firstName ?? ""}
+ />
+ </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"
+ className={getClassName("kcInputClass")}
+ name="lastName"
+ defaultValue={register.formData.lastName ?? ""}
+ />
+ </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"
+ className={getClassName("kcInputClass")}
+ name="email"
+ defaultValue={register.formData.email ?? ""}
+ autoComplete="email"
+ />
+ </div>
+ </div>
+ {!realm.registrationEmailAsUsername && (
+ <div
+ className={clsx(
+ getClassName("kcFormGroupClass"),
+ messagesPerField.printIfExists("username", getClassName("kcFormGroupErrorClass"))
+ )}
+ >
+ <div className={getClassName("kcLabelWrapperClass")}>
+ <label htmlFor="username" className={getClassName("kcLabelClass")}>
+ {msg("username")}
+ </label>
+ </div>
+ <div className={getClassName("kcInputWrapperClass")}>
+ <input
+ type="text"
+ id="username"
+ className={getClassName("kcInputClass")}
+ name="username"
+ defaultValue={register.formData.username ?? ""}
+ autoComplete="username"
+ />
+ </div>
+ </div>
+ )}
+ {passwordRequired && (
+ <>
+ <div
+ className={clsx(
+ getClassName("kcFormGroupClass"),
+ messagesPerField.printIfExists("password", getClassName("kcFormGroupErrorClass"))
+ )}
+ >
+ <div className={getClassName("kcLabelWrapperClass")}>
+ <label htmlFor="password" className={getClassName("kcLabelClass")}>
+ {msg("password")}
+ </label>
+ </div>
+ <div className={getClassName("kcInputWrapperClass")}>
+ <input
+ type="password"
+ id="password"
+ className={getClassName("kcInputClass")}
+ name="password"
+ autoComplete="new-password"
+ />
+ </div>
+ </div>
+
+ <div
+ className={clsx(
+ getClassName("kcFormGroupClass"),
+ messagesPerField.printIfExists("password-confirm", getClassName("kcFormGroupErrorClass"))
+ )}
+ >
+ <div className={getClassName("kcLabelWrapperClass")}>
+ <label htmlFor="password-confirm" className={getClassName("kcLabelClass")}>
+ {msg("passwordConfirm")}
+ </label>
+ </div>
+ <div className={getClassName("kcInputWrapperClass")}>
+ <input type="password" id="password-confirm" className={getClassName("kcInputClass")} name="password-confirm" />
+ </div>
+ </div>
+ </>
+ )}
+ {recaptchaRequired && (
+ <div className="form-group">
+ <div className={getClassName("kcInputWrapperClass")}>
+ <div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey}></div>
+ </div>
+ </div>
+ )}
+ <div className={getClassName("kcFormGroupClass")}>
+ <div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
+ <div className={getClassName("kcFormOptionsWrapperClass")}>
+ <span>
+ <a href={url.loginUrl}>{msg("backToLogin")}</a>
+ </span>
+ </div>
+ </div>
+
+ <div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
+ <input
+ className={clsx(
+ getClassName("kcButtonClass"),
+ getClassName("kcButtonPrimaryClass"),
+ getClassName("kcButtonBlockClass"),
+ getClassName("kcButtonLargeClass")
+ )}
+ type="submit"
+ value={msgStr("doRegister")}
+ />
+ </div>
+ </div>
+ </form>
+ </Template>
+ );
+}
diff --git a/src/keycloak-theme/login/pages/RegisterUserProfile.tsx b/src/keycloak-theme/login/pages/RegisterUserProfile.tsx
new file mode 100644
index 0000000..1955845
--- /dev/null
+++ b/src/keycloak-theme/login/pages/RegisterUserProfile.tsx
@@ -0,0 +1,70 @@
+import { useState } from "react";
+import { clsx } from "keycloakify/tools/clsx";
+import { UserProfileFormFields } from "./shared/UserProfileCommons";
+import { type PageProps, defaultClasses } from "keycloakify/login/pages/PageProps";
+import { useGetClassName } from "keycloakify/lib/useGetClassName";
+import type { KcContext } from "../kcContext";
+import type { I18n } from "../i18n";
+
+export default function RegisterUserProfile(props: PageProps<Extract<KcContext, { pageId: "register-user-profile.ftl" }>, I18n>) {
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
+
+ const { getClassName } = useGetClassName({
+ "defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
+ classes
+ });
+
+ const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
+
+ const { msg, msgStr } = i18n;
+
+ const [isFomSubmittable, setIsFomSubmittable] = useState(false);
+
+ return (
+ <Template
+ {...{ kcContext, i18n, doUseDefaultCss, classes }}
+ displayMessage={messagesPerField.exists("global")}
+ displayRequiredFields={true}
+ headerNode={msg("registerTitle")}
+ >
+ <form id="kc-register-form" className={getClassName("kcFormClass")} action={url.registrationAction} method="post">
+ <UserProfileFormFields
+ kcContext={kcContext}
+ onIsFormSubmittableValueChange={setIsFomSubmittable}
+ i18n={i18n}
+ getClassName={getClassName}
+ />
+ {recaptchaRequired && (
+ <div className="form-group">
+ <div className={getClassName("kcInputWrapperClass")}>
+ <div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} />
+ </div>
+ </div>
+ )}
+ <div className={getClassName("kcFormGroupClass")} style={{ "marginBottom": 30 }}>
+ <div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
+ <div className={getClassName("kcFormOptionsWrapperClass")}>
+ <span>
+ <a href={url.loginUrl}>{msg("backToLogin")}</a>
+ </span>
+ </div>
+ </div>
+
+ <div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
+ <input
+ className={clsx(
+ getClassName("kcButtonClass"),
+ getClassName("kcButtonPrimaryClass"),
+ getClassName("kcButtonBlockClass"),
+ getClassName("kcButtonLargeClass")
+ )}
+ type="submit"
+ value={msgStr("doRegister")}
+ disabled={!isFomSubmittable}
+ />
+ </div>
+ </div>
+ </form>
+ </Template>
+ );
+}
diff --git a/src/keycloak-theme/login/pages/Terms.tsx b/src/keycloak-theme/login/pages/Terms.tsx
new file mode 100644
index 0000000..819db53
--- /dev/null
+++ b/src/keycloak-theme/login/pages/Terms.tsx
@@ -0,0 +1,56 @@
+import { clsx } from "keycloakify/tools/clsx";
+import { useRerenderOnStateChange } from "evt/hooks";
+import { Markdown } from "keycloakify/tools/Markdown";
+import { type PageProps, defaultClasses } from "keycloakify/login/pages/PageProps";
+import { useGetClassName } from "keycloakify/lib/useGetClassName";
+import { evtTermMarkdown } from "keycloakify/login/lib/useDownloadTerms";
+import type { KcContext } from "../kcContext";
+import type { I18n } from "../i18n";
+
+export default function Terms(props: PageProps<Extract<KcContext, { pageId: "terms.ftl" }>, I18n>) {
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
+
+ const { getClassName } = useGetClassName({
+ "defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
+ classes
+ });
+
+ const { msg, msgStr } = i18n;
+
+ useRerenderOnStateChange(evtTermMarkdown);
+
+ const { url } = kcContext;
+
+ if (evtTermMarkdown.state === undefined) {
+ return null;
+ }
+
+ return (
+ <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} displayMessage={false} headerNode={msg("termsTitle")}>
+ <div id="kc-terms-text">{evtTermMarkdown.state && <Markdown>{evtTermMarkdown.state}</Markdown>}</div>
+ <form className="form-actions" action={url.loginAction} method="POST">
+ <input
+ className={clsx(
+ getClassName("kcButtonClass"),
+ getClassName("kcButtonClass"),
+ getClassName("kcButtonClass"),
+ getClassName("kcButtonPrimaryClass"),
+ getClassName("kcButtonLargeClass")
+ )}
+ name="accept"
+ id="kc-accept"
+ type="submit"
+ value={msgStr("doAccept")}
+ />
+ <input
+ className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonDefaultClass"), getClassName("kcButtonLargeClass"))}
+ name="cancel"
+ id="kc-decline"
+ type="submit"
+ value={msgStr("doDecline")}
+ />
+ </form>
+ <div className="clearfix" />
+ </Template>
+ );
+}
diff --git a/src/keycloak-theme/pages/shared/UserProfileCommons.tsx b/src/keycloak-theme/login/pages/shared/UserProfileCommons.tsx
index 08b9442..0a50bfd 100644
--- a/src/keycloak-theme/pages/shared/UserProfileCommons.tsx
+++ b/src/keycloak-theme/login/pages/shared/UserProfileCommons.tsx
@@ -1,28 +1,22 @@
-//NOTE: Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/lib/pages/shared/UserProfileCommons.tsx
-
import { useEffect, Fragment } from "react";
-import type { KcProps } from "keycloakify/lib/KcProps";
-import { clsx } from "keycloakify/lib/tools/clsx";
-import type { I18nBase } from "keycloakify/lib/i18n";
-import type { Attribute } from "keycloakify/lib/getKcContext";
-import { useFormValidation } from "keycloakify/lib/pages/shared/UserProfileCommons";
+import type { ClassKey } from "keycloakify/login/pages/PageProps";
+import { clsx } from "keycloakify/tools/clsx";
+import { useFormValidation } from "keycloakify/login/lib/useFormValidation";
+import type { Attribute } from "keycloakify/login/kcContext/KcContext";
+import type { I18n } from "../../i18n";
export type UserProfileFormFieldsProps = {
kcContext: Parameters<typeof useFormValidation>[0]["kcContext"];
- i18n: I18nBase;
-} & KcProps &
- Partial<Record<"BeforeField" | "AfterField", (props: { attribute: Attribute }) => JSX.Element | null>> & {
- onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
- };
+ i18n: I18n;
+ getClassName: (classKey: ClassKey) => string;
+ onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
+ BeforeField?: (props: { attribute: Attribute }) => JSX.Element | null;
+ AfterField?: (props: { attribute: Attribute }) => JSX.Element | null;
+};
+
+export function UserProfileFormFields(props: UserProfileFormFieldsProps) {
+ const { kcContext, onIsFormSubmittableValueChange, i18n, getClassName, BeforeField, AfterField } = props;
-export function UserProfileFormFields({
- kcContext,
- onIsFormSubmittableValueChange,
- i18n,
- BeforeField,
- AfterField,
- ...props
-}: UserProfileFormFieldsProps) {
const { advancedMsg } = i18n;
const {
@@ -47,20 +41,23 @@ export function UserProfileFormFields({
const { value, displayableErrors } = fieldStateByAttributeName[attribute.name];
- const formGroupClassName = clsx(props.kcFormGroupClass, displayableErrors.length !== 0 && props.kcFormGroupErrorClass);
+ const formGroupClassName = clsx(
+ getClassName("kcFormGroupClass"),
+ displayableErrors.length !== 0 && getClassName("kcFormGroupErrorClass")
+ );
return (
<Fragment key={i}>
{group !== currentGroup && (currentGroup = group) !== "" && (
<div className={formGroupClassName}>
- <div className={clsx(props.kcContentWrapperClass)}>
- <label id={`header-${group}`} className={clsx(props.kcFormGroupHeader)}>
+ <div className={getClassName("kcContentWrapperClass")}>
+ <label id={`header-${group}`} className={getClassName("kcFormGroupHeader")}>
{advancedMsg(groupDisplayHeader) || currentGroup}
</label>
</div>
{groupDisplayDescription !== "" && (
- <div className={clsx(props.kcLabelWrapperClass)}>
- <label id={`description-${group}`} className={`${clsx(props.kcLabelClass)}`}>
+ <div className={getClassName("kcLabelWrapperClass")}>
+ <label id={`description-${group}`} className={getClassName("kcLabelClass")}>
{advancedMsg(groupDisplayDescription)}
</label>
</div>
@@ -71,13 +68,13 @@ export function UserProfileFormFields({
{BeforeField && <BeforeField attribute={attribute} />}
<div className={formGroupClassName}>
- <div className={clsx(props.kcLabelWrapperClass)}>
- <label htmlFor={attribute.name} className={clsx(props.kcLabelClass)}>
+ <div className={getClassName("kcLabelWrapperClass")}>
+ <label htmlFor={attribute.name} className={getClassName("kcLabelClass")}>
{advancedMsg(attribute.displayName ?? "")}
</label>
{attribute.required && <>*</>}
</div>
- <div className={clsx(props.kcInputWrapperClass)}>
+ <div className={getClassName("kcInputWrapperClass")}>
{(() => {
const { options } = attribute.validators;
@@ -137,7 +134,7 @@ export function UserProfileFormFields({
"name": attribute.name
})
}
- className={clsx(props.kcInputClass)}
+ className={getClassName("kcInputClass")}
aria-invalid={displayableErrors.length !== 0}
disabled={attribute.readOnly}
autoComplete={attribute.autocomplete}
@@ -153,7 +150,7 @@ export function UserProfileFormFields({
<style>{`#${divId} > span: { display: block; }`}</style>
<span
id={divId}
- className={clsx(props.kcInputErrorMessageClass)}
+ className={getClassName("kcInputErrorMessageClass")}
style={{
"position": displayableErrors.length === 1 ? "absolute" : undefined
}}
@@ -173,4 +170,3 @@ export function UserProfileFormFields({
</>
);
}
-
diff --git a/src/keycloak-theme/valuesTransferredOverUrl.ts b/src/keycloak-theme/login/valuesTransferredOverUrl.ts
index 18dd4f9..18dd4f9 100644
--- a/src/keycloak-theme/valuesTransferredOverUrl.ts
+++ b/src/keycloak-theme/login/valuesTransferredOverUrl.ts
diff --git a/src/keycloak-theme/pages/Login.tsx b/src/keycloak-theme/pages/Login.tsx
deleted file mode 100644
index 932f1ae..0000000
--- a/src/keycloak-theme/pages/Login.tsx
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/pages/Login.tsx
-
-import { useState, type FormEventHandler } from "react";
-import { clsx } from "keycloakify/tools/clsx";
-import { useConstCallback } from "keycloakify/tools/useConstCallback";
-import { type PageProps, defaultClasses } from "keycloakify/pages/PageProps";
-import { useGetClassName } from "keycloakify/lib/useGetClassName";
-import type { KcContext } from "../kcContext";
-import type { I18n } from "../i18n";
-
-export default function Login(props: PageProps<Extract<KcContext, { pageId: "login.ftl" }>, I18n>) {
- const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
-
- const { getClassName } = useGetClassName({
- "defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
- classes
- });
-
- const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
-
- const { msg, msgStr } = i18n;
-
- const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false);
-
- const onSubmit = useConstCallback<FormEventHandler<HTMLFormElement>>(e => {
- e.preventDefault();
-
- setIsLoginButtonDisabled(true);
-
- const formElement = e.target as HTMLFormElement;
-
- //NOTE: Even if we login with email Keycloak expect username and password in
- //the POST request.
- formElement.querySelector("input[name='email']")?.setAttribute("name", "username");
-
- formElement.submit();
- });
-
- return (
- <Template
- {...{ kcContext, i18n, doUseDefaultCss, classes }}
- displayInfo={social.displayInfo}
- displayWide={realm.password && social.providers !== undefined}
- headerNode={msg("doLogIn")}
- formNode={
- <div id="kc-form" className={clsx(realm.password && social.providers !== undefined && getClassName("kcContentWrapperClass"))}>
- <div
- id="kc-form-wrapper"
- className={clsx(
- realm.password &&
- social.providers && [getClassName("kcFormSocialAccountContentClass"), getClassName("kcFormSocialAccountClass")]
- )}
- >
- {realm.password && (
- <form id="kc-form-login" onSubmit={onSubmit} action={url.loginAction} method="post">
- <div className={getClassName("kcFormGroupClass")}>
- {(() => {
- const label = !realm.loginWithEmailAllowed
- ? "username"
- : realm.registrationEmailAsUsername
- ? "email"
- : "usernameOrEmail";
-
- const autoCompleteHelper: typeof label = label === "usernameOrEmail" ? "username" : label;
-
- return (
- <>
- <label htmlFor={autoCompleteHelper} className={getClassName("kcLabelClass")}>
- {msg(label)}
- </label>
- <input
- tabIndex={1}
- id={autoCompleteHelper}
- className={getClassName("kcInputClass")}
- //NOTE: This is used by Google Chrome auto fill so we use it to tell
- //the browser how to pre fill the form but before submit we put it back
- //to username because it is what keycloak expects.
- name={autoCompleteHelper}
- defaultValue={login.username ?? ""}
- type="text"
- {...(usernameEditDisabled
- ? { "disabled": true }
- : {
- "autoFocus": true,
- "autoComplete": "off"
- })}
- />
- </>
- );
- })()}
- </div>
- <div className={getClassName("kcFormGroupClass")}>
- <label htmlFor="password" className={getClassName("kcLabelClass")}>
- {msg("password")}
- </label>
- <input
- tabIndex={2}
- id="password"
- className={getClassName("kcInputClass")}
- name="password"
- type="password"
- autoComplete="off"
- />
- </div>
- <div className={clsx(getClassName("kcFormGroupClass"), getClassName("kcFormSettingClass"))}>
- <div id="kc-form-options">
- {realm.rememberMe && !usernameEditDisabled && (
- <div className="checkbox">
- <label>
- <input
- tabIndex={3}
- id="rememberMe"
- name="rememberMe"
- type="checkbox"
- {...(login.rememberMe
- ? {
- "checked": true
- }
- : {})}
- />
- {msg("rememberMe")}
- </label>
- </div>
- )}
- </div>
- <div className={getClassName("kcFormOptionsWrapperClass")}>
- {realm.resetPasswordAllowed && (
- <span>
- <a tabIndex={5} href={url.loginResetCredentialsUrl}>
- {msg("doForgotPassword")}
- </a>
- </span>
- )}
- </div>
- </div>
- <div id="kc-form-buttons" className={getClassName("kcFormGroupClass")}>
- <input
- type="hidden"
- id="id-hidden-input"
- name="credentialId"
- {...(auth?.selectedCredential !== undefined
- ? {
- "value": auth.selectedCredential
- }
- : {})}
- />
- <input
- tabIndex={4}
- className={clsx(
- getClassName("kcButtonClass"),
- getClassName("kcButtonPrimaryClass"),
- getClassName("kcButtonBlockClass"),
- getClassName("kcButtonLargeClass")
- )}
- name="login"
- id="kc-login"
- type="submit"
- value={msgStr("doLogIn")}
- disabled={isLoginButtonDisabled}
- />
- </div>
- </form>
- )}
- </div>
- {realm.password && social.providers !== undefined && (
- <div
- id="kc-social-providers"
- className={clsx(getClassName("kcFormSocialAccountContentClass"), getClassName("kcFormSocialAccountClass"))}
- >
- <ul
- className={clsx(
- getClassName("kcFormSocialAccountListClass"),
- social.providers.length > 4 && getClassName("kcFormSocialAccountDoubleListClass")
- )}
- >
- {social.providers.map(p => (
- <li key={p.providerId} className={getClassName("kcFormSocialAccountListLinkClass")}>
- <a href={p.loginUrl} id={`zocial-${p.alias}`} className={clsx("zocial", p.providerId)}>
- <span>{p.displayName}</span>
- </a>
- </li>
- ))}
- </ul>
- </div>
- )}
- </div>
- }
- infoNode={
- realm.password &&
- realm.registrationAllowed &&
- !registrationDisabled && (
- <div id="kc-registration">
- <span>
- {msg("noAccount")}
- <a tabIndex={6} href={url.registrationUrl}>
- {msg("doRegister")}
- </a>
- </span>
- </div>
- )
- }
- />
- );
-}
diff --git a/src/keycloak-theme/pages/Register.tsx b/src/keycloak-theme/pages/Register.tsx
deleted file mode 100644
index 666d8b8..0000000
--- a/src/keycloak-theme/pages/Register.tsx
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/pages/Register.tsx
-
-import { clsx } from "keycloakify/tools/clsx";
-import { type PageProps, defaultClasses } from "keycloakify/pages/PageProps";
-import { useGetClassName } from "keycloakify/lib/useGetClassName";
-import type { KcContext } from "../kcContext";
-import type { I18n } from "../i18n";
-
-export default function Register(props: PageProps<Extract<KcContext, { pageId: "register.ftl" }>, I18n>) {
- const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
-
- const { getClassName } = useGetClassName({
- "defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
- classes
- });
-
- const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
-
- const { msg, msgStr } = i18n;
-
- return (
- <Template
- {...{ kcContext, i18n, doUseDefaultCss, classes }}
- headerNode={msg("registerTitle")}
- formNode={
- <form id="kc-register-form" className={getClassName("kcFormClass")} action={url.registrationAction} method="post">
- <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"
- className={getClassName("kcInputClass")}
- name="firstName"
- defaultValue={register.formData.firstName ?? ""}
- />
- </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"
- className={getClassName("kcInputClass")}
- name="lastName"
- defaultValue={register.formData.lastName ?? ""}
- />
- </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"
- className={getClassName("kcInputClass")}
- name="email"
- defaultValue={register.formData.email ?? ""}
- autoComplete="email"
- />
- </div>
- </div>
- {!realm.registrationEmailAsUsername && (
- <div
- className={clsx(
- getClassName("kcFormGroupClass"),
- messagesPerField.printIfExists("username", getClassName("kcFormGroupErrorClass"))
- )}
- >
- <div className={getClassName("kcLabelWrapperClass")}>
- <label htmlFor="username" className={getClassName("kcLabelClass")}>
- {msg("username")}
- </label>
- </div>
- <div className={getClassName("kcInputWrapperClass")}>
- <input
- type="text"
- id="username"
- className={getClassName("kcInputClass")}
- name="username"
- defaultValue={register.formData.username ?? ""}
- autoComplete="username"
- />
- </div>
- </div>
- )}
- {passwordRequired && (
- <>
- <div
- className={clsx(
- getClassName("kcFormGroupClass"),
- messagesPerField.printIfExists("password", getClassName("kcFormGroupErrorClass"))
- )}
- >
- <div className={getClassName("kcLabelWrapperClass")}>
- <label htmlFor="password" className={getClassName("kcLabelClass")}>
- {msg("password")}
- </label>
- </div>
- <div className={getClassName("kcInputWrapperClass")}>
- <input
- type="password"
- id="password"
- className={getClassName("kcInputClass")}
- name="password"
- autoComplete="new-password"
- />
- </div>
- </div>
-
- <div
- className={clsx(
- getClassName("kcFormGroupClass"),
- messagesPerField.printIfExists("password-confirm", getClassName("kcFormGroupErrorClass"))
- )}
- >
- <div className={getClassName("kcLabelWrapperClass")}>
- <label htmlFor="password-confirm" className={getClassName("kcLabelClass")}>
- {msg("passwordConfirm")}
- </label>
- </div>
- <div className={getClassName("kcInputWrapperClass")}>
- <input type="password" id="password-confirm" className={getClassName("kcInputClass")} name="password-confirm" />
- </div>
- </div>
- </>
- )}
- {recaptchaRequired && (
- <div className="form-group">
- <div className={getClassName("kcInputWrapperClass")}>
- <div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey}></div>
- </div>
- </div>
- )}
- <div className={getClassName("kcFormGroupClass")}>
- <div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
- <div className={getClassName("kcFormOptionsWrapperClass")}>
- <span>
- <a href={url.loginUrl}>{msg("backToLogin")}</a>
- </span>
- </div>
- </div>
-
- <div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
- <input
- className={clsx(
- getClassName("kcButtonClass"),
- getClassName("kcButtonPrimaryClass"),
- getClassName("kcButtonBlockClass"),
- getClassName("kcButtonLargeClass")
- )}
- type="submit"
- value={msgStr("doRegister")}
- />
- </div>
- </div>
- </form>
- }
- />
- );
-}
diff --git a/src/keycloak-theme/pages/RegisterUserProfile.tsx b/src/keycloak-theme/pages/RegisterUserProfile.tsx
deleted file mode 100644
index 48d791f..0000000
--- a/src/keycloak-theme/pages/RegisterUserProfile.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/pages/RegisterUserProfile.tsx
-
-import { useState } from "react";
-import { clsx } from "keycloakify/tools/clsx";
-import { UserProfileFormFields } from "./shared/UserProfileCommons";
-import { type PageProps, defaultClasses } from "keycloakify/pages/PageProps";
-import { useGetClassName } from "keycloakify/lib/useGetClassName";
-import type { KcContext } from "../kcContext";
-import type { I18n } from "../i18n";
-
-export default function RegisterUserProfile(props: PageProps<Extract<KcContext, { pageId: "register-user-profile.ftl" }>, I18n>) {
- const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
-
- const { getClassName } = useGetClassName({
- "defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
- classes
- });
-
- const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
-
- const { msg, msgStr } = i18n;
-
- const [isFomSubmittable, setIsFomSubmittable] = useState(false);
-
- return (
- <Template
- {...{ kcContext, i18n, doUseDefaultCss, classes }}
- displayMessage={messagesPerField.exists("global")}
- displayRequiredFields={true}
- headerNode={msg("registerTitle")}
- formNode={
- <form id="kc-register-form" className={getClassName("kcFormClass")} action={url.registrationAction} method="post">
- <UserProfileFormFields
- kcContext={kcContext}
- onIsFormSubmittableValueChange={setIsFomSubmittable}
- i18n={i18n}
- getClassName={getClassName}
- />
- {recaptchaRequired && (
- <div className="form-group">
- <div className={getClassName("kcInputWrapperClass")}>
- <div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} />
- </div>
- </div>
- )}
- <div className={getClassName("kcFormGroupClass")} style={{ "marginBottom": 30 }}>
- <div id="kc-form-options" className={getClassName("kcFormOptionsClass")}>
- <div className={getClassName("kcFormOptionsWrapperClass")}>
- <span>
- <a href={url.loginUrl}>{msg("backToLogin")}</a>
- </span>
- </div>
- </div>
-
- <div id="kc-form-buttons" className={getClassName("kcFormButtonsClass")}>
- <input
- className={clsx(
- getClassName("kcButtonClass"),
- getClassName("kcButtonPrimaryClass"),
- getClassName("kcButtonBlockClass"),
- getClassName("kcButtonLargeClass")
- )}
- type="submit"
- value={msgStr("doRegister")}
- disabled={!isFomSubmittable}
- />
- </div>
- </div>
- </form>
- }
- />
- );
-}
diff --git a/src/keycloak-theme/pages/Terms.tsx b/src/keycloak-theme/pages/Terms.tsx
deleted file mode 100644
index 19d490f..0000000
--- a/src/keycloak-theme/pages/Terms.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * NOTE: You do not need to do all this to put your own Terms and conditions
- * this is if you want component level customization.
- * If the default works for you, you can just use the useDownloadTerms hook
- * in the KcApp.tsx
- * Example: https://github.com/garronej/keycloakify-starter/blob/a20c21b2aae7c6dc6dbea294f3d321955ddf9355/src/KcApp/KcApp.tsx#L14-L30
- */
-import { clsx } from "keycloakify/tools/clsx";
-import { useRerenderOnStateChange } from "evt/hooks";
-import { Markdown } from "keycloakify/tools/Markdown";
-import { type PageProps, defaultClasses } from "keycloakify/pages/PageProps";
-import { useGetClassName } from "keycloakify/lib/useGetClassName";
-import { evtTermMarkdown } from "keycloakify/lib/useDownloadTerms";
-import type { KcContext } from "../kcContext";
-import type { I18n } from "../i18n";
-
-export default function Terms(props: PageProps<Extract<KcContext, { pageId: "terms.ftl" }>, I18n>) {
- const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
-
- const { getClassName } = useGetClassName({
- "defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
- classes
- });
-
- const { msg, msgStr } = i18n;
-
- useRerenderOnStateChange(evtTermMarkdown);
-
- const { url } = kcContext;
-
- if (evtTermMarkdown.state === undefined) {
- return null;
- }
-
- return (
- <Template
- {...{ kcContext, i18n, doUseDefaultCss, classes }}
- displayMessage={false}
- headerNode={msg("termsTitle")}
- formNode={
- <>
- <div id="kc-terms-text">{evtTermMarkdown.state && <Markdown>{evtTermMarkdown.state}</Markdown>}</div>
- <form className="form-actions" action={url.loginAction} method="POST">
- <input
- className={clsx(
- getClassName("kcButtonClass"),
- getClassName("kcButtonClass"),
- getClassName("kcButtonClass"),
- getClassName("kcButtonPrimaryClass"),
- getClassName("kcButtonLargeClass")
- )}
- name="accept"
- id="kc-accept"
- type="submit"
- value={msgStr("doAccept")}
- />
- <input
- className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonDefaultClass"), getClassName("kcButtonLargeClass"))}
- name="cancel"
- id="kc-decline"
- type="submit"
- value={msgStr("doDecline")}
- />
- </form>
- <div className="clearfix" />
- </>
- }
- />
- );
-}