diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..ac72e07 --- /dev/null +++ b/flake.lock @@ -0,0 +1,64 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1713248628, + "narHash": "sha256-NLznXB5AOnniUtZsyy/aPWOk8ussTuePp2acb9U+ISA=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "5672bc9dbf9d88246ddab5ac454e82318d094bb8", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1711703276, + "narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d8fe5e6c92d0d190646fb9f1056741a229980089", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..9e935ff --- /dev/null +++ b/flake.nix @@ -0,0 +1,28 @@ +{ + description = "pub.solar keycloak theme"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + }; + + outputs = inputs@{ self, ... }: + inputs.flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ "x86_64-linux" "aarch64-linux" ]; + + perSystem = { system, pkgs, config, ... }: { + _module.args = { + inherit inputs; + pkgs = import inputs.nixpkgs { inherit system; }; + }; + + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ + yarn + nodejs + maven + ]; + }; + }; + }; +} diff --git a/src/keycloak-theme/account/Template.tsx b/src/keycloak-theme/account/Template.tsx index 7f8236a..ce896a6 100644 --- a/src/keycloak-theme/account/Template.tsx +++ b/src/keycloak-theme/account/Template.tsx @@ -20,8 +20,6 @@ export default function Template(props: TemplateProps) { const { isReady } = usePrepareTemplate({ "doFetchDefaultThemeResources": doUseDefaultCss, "styles": [ - `${url.resourcesCommonPath}/node_modules/patternfly/dist/css/patternfly.min.css`, - `${url.resourcesCommonPath}/node_modules/patternfly/dist/css/patternfly-additions.min.css`, `${url.resourcesPath}/css/account.css` ], "htmlClassName": getClassName("kcHtmlClass"), diff --git a/src/keycloak-theme/login/KcApp.tsx b/src/keycloak-theme/login/KcApp.tsx index 5366ad6..96a3be5 100644 --- a/src/keycloak-theme/login/KcApp.tsx +++ b/src/keycloak-theme/login/KcApp.tsx @@ -44,15 +44,15 @@ export default function KcApp(props: { kcContext: KcContext; }) { {(() => { switch (kcContext.pageId) { - case "login.ftl": return ; - case "register.ftl": return ; - case "register-user-profile.ftl": return - case "terms.ftl": return ; + case "login.ftl": return ; + case "register.ftl": return ; + case "register-user-profile.ftl": return + case "terms.ftl": return ; // Removes those pages in you project. They are included to show you how to implement keycloak pages // that are not yes implemented by Keycloakify. // See: https://docs.keycloakify.dev/limitations#some-pages-still-have-the-default-theme.-why - case "my-extra-page-1.ftl": return ; - case "my-extra-page-2.ftl": return ; + case "my-extra-page-1.ftl": return ; + case "my-extra-page-2.ftl": return ; // We choose to use the default Template for the Info page and to download the theme resources. // This is just an example to show you what is possible. You likely don't want to keep this as is. case "info.ftl": return ( diff --git a/src/keycloak-theme/login/Template.tsx b/src/keycloak-theme/login/Template.tsx index cc05dbf..034cb74 100644 --- a/src/keycloak-theme/login/Template.tsx +++ b/src/keycloak-theme/login/Template.tsx @@ -2,211 +2,177 @@ import { useState, useEffect } from "react"; import { assert } from "keycloakify/tools/assert"; -import { clsx } from "keycloakify/tools/clsx"; import { usePrepareTemplate } from "keycloakify/lib/usePrepareTemplate"; import { type TemplateProps } from "keycloakify/login/TemplateProps"; -import { useGetClassName } from "keycloakify/login/lib/useGetClassName"; import type { KcContext } from "./kcContext"; import type { I18n } from "./i18n"; -import keycloakifyLogoPngUrl from "./assets/keycloakify-logo.png"; export default function Template(props: TemplateProps) { - const { - displayInfo = false, - displayMessage = true, - displayRequiredFields = false, - displayWide = false, - showAnotherWayIfPresent = true, - headerNode, - showUsernameNode = null, - infoNode = null, - kcContext, - i18n, - doUseDefaultCss, - classes, - children - } = props; + const { + displayInfo = false, + displayMessage = true, + displayRequiredFields = false, + displayWide = false, + showAnotherWayIfPresent = true, + headerNode, + showUsernameNode = null, + infoNode = null, + kcContext, + i18n, + doUseDefaultCss, + classes, + children + } = props; + const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n; - const { getClassName } = useGetClassName({ doUseDefaultCss, classes }); + const { realm, locale, auth, url, message, isAppInitiatedAction } = kcContext; - const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n; + const { isReady } = usePrepareTemplate({ + "doFetchDefaultThemeResources": doUseDefaultCss, + "styles": [ + `${url.resourcesPath}/css/login.css` + ], + "htmlClassName": "ps-html", + "bodyClassName": "ps-body", + }); - const { realm, locale, auth, url, message, isAppInitiatedAction } = kcContext; + useState(()=> { document.title = i18n.msgStr("loginTitle", kcContext.realm.displayName); }); - const { isReady } = usePrepareTemplate({ - "doFetchDefaultThemeResources": doUseDefaultCss, - "styles": [ - `${url.resourcesCommonPath}/node_modules/patternfly/dist/css/patternfly.min.css`, - `${url.resourcesCommonPath}/node_modules/patternfly/dist/css/patternfly-additions.min.css`, - `${url.resourcesCommonPath}/lib/zocial/zocial.css`, - `${url.resourcesPath}/css/login.css` - ], - "htmlClassName": getClassName("kcHtmlClass"), - "bodyClassName": getClassName("kcBodyClass") - }); + useEffect(() => { + console.log(`Value of MY_ENV_VARIABLE on the Keycloak server: "${kcContext.properties.MY_ENV_VARIABLE}"`); + }, []); - useState(()=> { document.title = i18n.msgStr("loginTitle", kcContext.realm.displayName); }); + if (!isReady) { + return null; + } - useEffect(() => { - console.log(`Value of MY_ENV_VARIABLE on the Keycloak server: "${kcContext.properties.MY_ENV_VARIABLE}"`); - }, []); - - if (!isReady) { - return null; - } - - return ( -
-
-
- {/* - Here we are referencing the `keycloakify-logo.png` in the `public` directory. - When possible don't use this approach, instead ... - */} - Keycloakify logo - {msg("loginTitleHtml", realm.displayNameHtml)}!!! - {/* ...rely on the bundler to import your assets, it's more efficient */} - Keycloakify logo -
-
- -
-
- {realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && ( - - )} - {!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? ( - displayRequiredFields ? ( -
-
- - * - {msg("requiredFields")} - -
-
-

{headerNode}

-
-
- ) : ( -

{headerNode}

- ) - ) : displayRequiredFields ? ( -
-
- - * {msg("requiredFields")} - -
-
- {showUsernameNode} -
-
- - -
- - {msg("restartLoginTooltip")} -
-
-
-
-
-
- ) : ( - <> - {showUsernameNode} -
-
- - -
- - {msg("restartLoginTooltip")} -
-
-
-
- - )} -
-
-
- {/* App-initiated actions should not see warning messages about the need to complete the action during login. */} - {displayMessage && message !== undefined && (message.type !== "warning" || !isAppInitiatedAction) && ( -
- {message.type === "success" && } - {message.type === "warning" && } - {message.type === "error" && } - {message.type === "info" && } - -
- )} - {children} - {auth !== undefined && auth.showTryAnotherWayLink && showAnotherWayIfPresent && ( -
- -
- )} - {displayInfo && ( -
-
- {infoNode} -
-
- )} -
-
-
-
- ); + return (<> +
+ {realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && ( + + )} + {!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? ( + displayRequiredFields ? ( +
+
+ + * + {msg("requiredFields")} + +
+
+

{headerNode}

+
+
+ ) : ( +

{headerNode}

+ ) + ) : displayRequiredFields ? ( +
+
+ + * {msg("requiredFields")} + +
+
+ {showUsernameNode} +
+
+ + +
+ + {msg("restartLoginTooltip")} +
+
+
+
+
+
+ ) : ( + <> + {showUsernameNode} +
+
+ + +
+ + {msg("restartLoginTooltip")} +
+
+
+
+ + )} +
+
+
+ {/* App-initiated actions should not see warning messages about the need to complete the action during login. */} + {displayMessage && message !== undefined && (message.type !== "warning" || !isAppInitiatedAction) && ( +
+ {message.type === "success" && } + {message.type === "warning" && } + {message.type === "error" && } + {message.type === "info" && } + +
+ )} + {children} + {auth !== undefined && auth.showTryAnotherWayLink && showAnotherWayIfPresent && ( +
+ +
+ )} + {displayInfo && ( +
+
+ {infoNode} +
+
+ )} +
+
+ + ); } diff --git a/src/keycloak-theme/login/kcContext.ts b/src/keycloak-theme/login/kcContext.ts index 53ea538..346e2a5 100644 --- a/src/keycloak-theme/login/kcContext.ts +++ b/src/keycloak-theme/login/kcContext.ts @@ -20,7 +20,7 @@ export const { getKcContext } = createGetKcContext({ pageId: "login.ftl", locale: { //When we test the login page we do it in french - currentLanguageTag: "fr", + currentLanguageTag: "en", }, //Uncomment the following line for hiding the Alert message //"message": undefined @@ -35,7 +35,7 @@ export const { getKcContext } = createGetKcContext({ //NOTE: You will either use register.ftl (legacy) or register-user-profile.ftl, not both pageId: "register-user-profile.ftl", locale: { - currentLanguageTag: "fr" + currentLanguageTag: "en" }, profile: { attributes: [ @@ -97,8 +97,8 @@ export const { getKcContext } = createGetKcContext({ export const { kcContext } = getKcContext({ // Uncomment to test the login page for development. - //mockPageId: "login.ftl", + mockPageId: "login.ftl", }); -export type KcContext = NonNullable["kcContext"]>; \ No newline at end of file +export type KcContext = NonNullable["kcContext"]>; diff --git a/src/keycloak-theme/login/pages/Login.css b/src/keycloak-theme/login/pages/Login.css new file mode 100644 index 0000000..9cacff3 --- /dev/null +++ b/src/keycloak-theme/login/pages/Login.css @@ -0,0 +1,3 @@ +.ps-form-group { + background: red; +} diff --git a/src/keycloak-theme/login/pages/Login.tsx b/src/keycloak-theme/login/pages/Login.tsx index 6f3e575..d1ee8f4 100644 --- a/src/keycloak-theme/login/pages/Login.tsx +++ b/src/keycloak-theme/login/pages/Login.tsx @@ -1,3 +1,4 @@ +import "./Login.css"; import { useState, type FormEventHandler } from "react"; import { clsx } from "keycloakify/tools/clsx"; import { useConstCallback } from "keycloakify/tools/useConstCallback"; @@ -9,196 +10,196 @@ import type { I18n } from "../i18n"; const my_custom_param = new URL(window.location.href).searchParams.get("my_custom_param"); if (my_custom_param !== null) { - console.log("my_custom_param:", my_custom_param); + console.log("my_custom_param:", my_custom_param); } export default function Login(props: PageProps, I18n>) { - const { kcContext, i18n, doUseDefaultCss, Template, classes } = props; + const { kcContext, i18n, doUseDefaultCss, Template, classes } = props; - const { getClassName } = useGetClassName({ - doUseDefaultCss, - classes - }); + const { getClassName } = useGetClassName({ + doUseDefaultCss, + classes + }); - const { social, realm, url, usernameHidden, login, auth, registrationDisabled } = kcContext; + const { social, realm, url, usernameHidden, login, auth, registrationDisabled } = kcContext; - const { msg, msgStr } = i18n; + const { msg, msgStr } = i18n; - const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false); + const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false); - const onSubmit = useConstCallback>(e => { - e.preventDefault(); + const onSubmit = useConstCallback>(e => { + e.preventDefault(); - setIsLoginButtonDisabled(true); + setIsLoginButtonDisabled(true); - const formElement = e.target as HTMLFormElement; + 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"); + //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(); - }); + formElement.submit(); + }); - return ( - + ); }