Update setup
This commit is contained in:
parent
df76640100
commit
cee8ea7ecb
|
@ -87,7 +87,7 @@ and remove unnecessary files.
|
|||
|
||||
```bash
|
||||
rm -r src/App
|
||||
rm src/keycloak-theme/valuesTransferredOverUrl.ts
|
||||
rm src/keycloak-theme/login/valuesTransferredOverUrl.ts
|
||||
mv src/keycloak-theme/* src/
|
||||
rm -r src/keycloak-theme
|
||||
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
"extraLoginPages": [
|
||||
"my-extra-page-1.ftl",
|
||||
"my-extra-page-2.ftl"
|
||||
],
|
||||
"extraAccountPages": [
|
||||
"my-extra-page-1.ftl",
|
||||
"my-extra-page-2.ftl"
|
||||
]
|
||||
},
|
||||
"author": "u/garronej",
|
||||
|
|
|
@ -5,9 +5,12 @@ import { createOidcClientProvider, useOidcClient } from "./oidc";
|
|||
import { addFooToQueryParams, addBarToQueryParams } from "../keycloak-theme/login/valuesTransferredOverUrl";
|
||||
import jwt_decode from "jwt-decode";
|
||||
|
||||
const keycloakUrl = "https://auth.code.gouv.fr"
|
||||
const keycloakRealm = "keycloakify";
|
||||
|
||||
const { OidcClientProvider } = createOidcClientProvider({
|
||||
url: "https://auth.code.gouv.fr/auth",
|
||||
realm: "keycloakify",
|
||||
url: `${keycloakUrl}/auth`,
|
||||
realm: keycloakRealm,
|
||||
clientId: "starter",
|
||||
//This function will be called just before redirecting,
|
||||
//it should return the current langue.
|
||||
|
@ -42,6 +45,8 @@ function ContextualizedApp() {
|
|||
oidcClient.isUserLoggedIn ?
|
||||
<>
|
||||
<h1>You are authenticated !</h1>
|
||||
{/* On older Keycloak version its /auth/realms instead of /realms */}
|
||||
<a href={`${keycloakUrl}/realms/${keycloakRealm}/account`} target="_blank">Link to your Keycloak account</a>
|
||||
<pre style={{ textAlign: "left" }}>{JSON.stringify(jwt_decode(oidcClient.getAccessToken()), null, 2)}</pre>
|
||||
<button onClick={() => oidcClient.logout({ redirectTo: "home" })}>Logout</button>
|
||||
</>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { createRoot } from "react-dom/client";
|
||||
import { StrictMode, lazy, Suspense } from "react";
|
||||
import { kcContext as kcLoginThemeContext } from "./keycloak-theme/login/kcContext";
|
||||
import { kcContext as kcAccountThemeContext } from "./keycloak-theme/account/kcContext";
|
||||
|
||||
const App = lazy(() => import("./App"));
|
||||
const KcLoginThemeApp = lazy(() => import("./keycloak-theme/login/KcApp"));
|
||||
const KcAccountThemeApp = lazy(() => import("./keycloak-theme/account/KcApp"));
|
||||
const App = lazy(() => import("./App"));
|
||||
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<StrictMode>
|
||||
|
@ -14,6 +16,10 @@ createRoot(document.getElementById("root")!).render(
|
|||
return <KcLoginThemeApp kcContext={kcLoginThemeContext} />;
|
||||
}
|
||||
|
||||
if( kcAccountThemeContext !== undefined ){
|
||||
return <KcAccountThemeApp kcContext={kcAccountThemeContext} />;
|
||||
}
|
||||
|
||||
return <App />;
|
||||
|
||||
})()}
|
||||
|
|
5
src/keycloak-theme/account/KcApp.css
Normal file
5
src/keycloak-theme/account/KcApp.css
Normal file
|
@ -0,0 +1,5 @@
|
|||
|
||||
|
||||
.my-root-class {
|
||||
background: url(./assets/background.svg) no-repeat center center fixed;
|
||||
}
|
41
src/keycloak-theme/account/KcApp.tsx
Normal file
41
src/keycloak-theme/account/KcApp.tsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
import "./KcApp.css";
|
||||
import { lazy, Suspense } from "react";
|
||||
import Fallback, { type PageProps } from "keycloakify/account";
|
||||
import type { KcContext } from "./kcContext";
|
||||
import { useI18n } from "./i18n";
|
||||
|
||||
const Template = lazy(() => import("./Template"));
|
||||
const DefaultTemplate = lazy(() => import("keycloakify/account/Template"));
|
||||
|
||||
const Password = lazy(() => import("./pages/Password"));
|
||||
const MyExtraPage1 = lazy(() => import("./pages/MyExtraPage1"));
|
||||
const MyExtraPage2 = lazy(() => import("./pages/MyExtraPage2"));
|
||||
|
||||
const classes: PageProps<any, any>["classes"] = {
|
||||
"kcBodyClass": "my-root-class"
|
||||
};
|
||||
|
||||
export default function App(props: { kcContext: KcContext; }) {
|
||||
|
||||
const { kcContext } = props;
|
||||
|
||||
const i18n = useI18n({ kcContext });
|
||||
|
||||
if (i18n === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense>
|
||||
{(() => {
|
||||
switch (kcContext.pageId) {
|
||||
case "password.ftl": return <Password {...{ 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} />;
|
||||
default: return <Fallback {...{ kcContext, i18n, classes }} Template={DefaultTemplate} doUseDefaultCss={true} />;
|
||||
}
|
||||
})()}
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
}
|
132
src/keycloak-theme/account/Template.tsx
Normal file
132
src/keycloak-theme/account/Template.tsx
Normal file
|
@ -0,0 +1,132 @@
|
|||
// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/login/Template.tsx
|
||||
|
||||
import { clsx } from "keycloakify/tools/clsx";
|
||||
import { usePrepareTemplate } from "keycloakify/lib/usePrepareTemplate";
|
||||
import { type TemplateProps } from "keycloakify/account/TemplateProps";
|
||||
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
|
||||
import type { KcContext } from "./kcContext";
|
||||
import type { I18n } from "./i18n";
|
||||
import { assert } from "keycloakify/tools/assert";
|
||||
|
||||
export default function Template(props: TemplateProps<KcContext, I18n>) {
|
||||
const { kcContext, i18n, doUseDefaultCss, active, classes, children } = props;
|
||||
|
||||
const { getClassName } = useGetClassName({ doUseDefaultCss, classes });
|
||||
|
||||
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n;
|
||||
|
||||
const { locale, url, features, realm, message, referrer } = kcContext;
|
||||
|
||||
const { isReady } = usePrepareTemplate({
|
||||
"doFetchDefaultThemeResources": doUseDefaultCss,
|
||||
url,
|
||||
"stylesCommon": ["node_modules/patternfly/dist/css/patternfly.min.css", "node_modules/patternfly/dist/css/patternfly-additions.min.css"],
|
||||
"styles": ["css/account.css"],
|
||||
"htmlClassName": undefined,
|
||||
"bodyClassName": clsx("admin-console", "user", getClassName("kcBodyClass"))
|
||||
});
|
||||
|
||||
if (!isReady) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="navbar navbar-default navbar-pf navbar-main header">
|
||||
<nav className="navbar" role="navigation">
|
||||
<div className="navbar-header">
|
||||
<div className="container">
|
||||
<h1 className="navbar-title">Keycloak</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div className="navbar-collapse navbar-collapse-1">
|
||||
<div className="container">
|
||||
<ul className="nav navbar-nav navbar-utility">
|
||||
{realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && (
|
||||
<li>
|
||||
<div className="kc-dropdown" id="kc-locale-dropdown">
|
||||
<a href="#" id="kc-current-locale-link">
|
||||
{labelBySupportedLanguageTag[currentLanguageTag]}
|
||||
</a>
|
||||
<ul>
|
||||
{locale.supported.map(({ languageTag }) => (
|
||||
<li key={languageTag} className="kc-dropdown-item">
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
||||
<a href="#" onClick={() => changeLocale(languageTag)}>
|
||||
{labelBySupportedLanguageTag[languageTag]}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
{referrer?.url !== undefined && (
|
||||
<li>
|
||||
<a href={referrer.url} id="referrer">
|
||||
{msg("backTo", referrer.name)}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
<li>
|
||||
<a href={url.getLogoutUrl()}>{msg("doSignOut")}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div className="container">
|
||||
<div className="bs-sidebar col-sm-3">
|
||||
<ul>
|
||||
<li className={clsx(active === "account" && "active")}>
|
||||
<a href={url.accountUrl}>{msg("account")}</a>
|
||||
</li>
|
||||
{features.passwordUpdateSupported && (
|
||||
<li className={clsx(active === "password" && "active")}>
|
||||
<a href={url.passwordUrl}>{msg("password")}</a>
|
||||
</li>
|
||||
)}
|
||||
<li className={clsx(active === "totp" && "active")}>
|
||||
<a href={url.totpUrl}>{msg("authenticator")}</a>
|
||||
</li>
|
||||
{features.identityFederation && (
|
||||
<li className={clsx(active === "social" && "active")}>
|
||||
<a href={url.socialUrl}>{msg("federatedIdentity")}</a>
|
||||
</li>
|
||||
)}
|
||||
<li className={clsx(active === "sessions" && "active")}>
|
||||
<a href={url.sessionsUrl}>{msg("sessions")}</a>
|
||||
</li>
|
||||
<li className={clsx(active === "applications" && "active")}>
|
||||
<a href={url.applicationsUrl}>{msg("applications")}</a>
|
||||
</li>
|
||||
{features.log && (
|
||||
<li className={clsx(active === "log" && "active")}>
|
||||
<a href={url.logUrl}>{msg("log")}</a>
|
||||
</li>
|
||||
)}
|
||||
{realm.userManagedAccessAllowed && features.authorization && (
|
||||
<li className={clsx(active === "authorization" && "active")}>
|
||||
<a href={url.resourceUrl}>{msg("myResources")}</a>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="col-sm-9 content-area">
|
||||
{message !== undefined && (
|
||||
<div className={clsx("alert", `alert-${message.type}`)}>
|
||||
{message.type === "success" && <span className="pficon pficon-ok"></span>}
|
||||
{message.type === "error" && <span className="pficon pficon-error-circle-o"></span>}
|
||||
<span className="kc-feedback-text">{message.summary}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
132
src/keycloak-theme/account/assets/background.svg
Normal file
132
src/keycloak-theme/account/assets/background.svg
Normal file
|
@ -0,0 +1,132 @@
|
|||
<svg width="1521" height="961" viewBox="0 0 1521 961" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.4">
|
||||
<g filter="url(#filter0_dd)">
|
||||
<path d="M289.342 250.792L427.47 389.611C471.444 433.805 542.707 433.805 586.621 389.611L724.749 250.792L507.046 32L289.342 250.792Z" fill="#EFEEEE"/>
|
||||
<path d="M586.267 389.258L586.267 389.258C542.548 433.256 471.603 433.256 427.824 389.258L290.047 250.792L507.046 32.7089L724.044 250.792L586.267 389.258Z" stroke="white" stroke-opacity="0.01"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_dd)">
|
||||
<path d="M32 509.755L170.128 648.573C214.103 692.767 285.365 692.767 329.28 648.573L467.408 509.755L249.704 290.962L32 509.755Z" fill="#EFEEEE"/>
|
||||
<path d="M328.925 648.221L328.925 648.221C285.206 692.218 214.262 692.219 170.483 648.221L32.7054 509.755L249.704 291.671L466.702 509.755L328.925 648.221Z" stroke="white" stroke-opacity="0.01"/>
|
||||
</g>
|
||||
<g filter="url(#filter2_dd)">
|
||||
<path d="M289.281 767.036L427.409 905.854C471.384 950.048 542.646 950.048 586.561 905.854L724.689 767.036L506.985 548.243L289.281 767.036Z" fill="#EFEEEE"/>
|
||||
<path d="M586.206 905.502L586.206 905.502C542.487 949.499 471.543 949.5 427.764 905.502L289.986 767.036L506.985 548.952L723.983 767.036L586.206 905.502Z" stroke="white" stroke-opacity="0.01"/>
|
||||
</g>
|
||||
<g filter="url(#filter3_dd)">
|
||||
<path d="M546.562 509.755L684.69 648.573C728.665 692.767 799.927 692.767 843.842 648.573L981.97 509.755L764.266 290.962L546.562 509.755Z" fill="#EFEEEE"/>
|
||||
<path d="M843.487 648.221L843.487 648.221C799.768 692.218 728.824 692.219 685.044 648.221L547.267 509.755L764.266 291.671L981.264 509.755L843.487 648.221Z" stroke="white" stroke-opacity="0.01"/>
|
||||
</g>
|
||||
<g filter="url(#filter4_dd)">
|
||||
<path d="M803.843 250.792L941.971 389.611C985.945 433.805 1057.21 433.805 1101.12 389.611L1239.25 250.792L1021.55 32L803.843 250.792Z" fill="#EFEEEE"/>
|
||||
<path d="M1100.77 389.258L1100.77 389.258C1057.05 433.256 986.105 433.256 942.325 389.258L804.548 250.792L1021.55 32.7089L1238.55 250.792L1100.77 389.258Z" stroke="white" stroke-opacity="0.01"/>
|
||||
</g>
|
||||
<g filter="url(#filter5_dd)">
|
||||
<path d="M1062.81 509.755L1200.93 648.573C1244.91 692.767 1316.17 692.767 1360.08 648.573L1498.21 509.755L1280.51 290.962L1062.81 509.755Z" fill="#EFEEEE"/>
|
||||
<path d="M1359.73 648.221L1359.73 648.221C1316.01 692.218 1245.07 692.219 1201.29 648.221L1063.51 509.755L1280.51 291.671L1497.51 509.755L1359.73 648.221Z" stroke="white" stroke-opacity="0.01"/>
|
||||
</g>
|
||||
<g filter="url(#filter6_dd)">
|
||||
<path d="M805.524 767.036L943.653 905.854C987.627 950.048 1058.89 950.048 1102.8 905.854L1240.93 767.036L1023.23 548.243L805.524 767.036Z" fill="#EFEEEE"/>
|
||||
<path d="M1102.45 905.502L1102.45 905.502C1058.73 949.499 987.786 949.5 944.007 905.502L806.23 767.036L1023.23 548.952L1240.23 767.036L1102.45 905.502Z" stroke="white" stroke-opacity="0.01"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_dd" x="257.342" y="0" width="489.408" height="444.757" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="6" dy="6"/>
|
||||
<feGaussianBlur stdDeviation="8"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.75 0 0 0 0 0.71011 0 0 0 0 0.653125 0 0 0 0.51 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="-6" dy="-6"/>
|
||||
<feGaussianBlur stdDeviation="13"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.83 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter1_dd" x="0" y="258.962" width="489.408" height="444.757" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="6" dy="6"/>
|
||||
<feGaussianBlur stdDeviation="8"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.75 0 0 0 0 0.71011 0 0 0 0 0.653125 0 0 0 0.51 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="-6" dy="-6"/>
|
||||
<feGaussianBlur stdDeviation="13"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.83 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter2_dd" x="257.281" y="516.243" width="489.408" height="444.757" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="6" dy="6"/>
|
||||
<feGaussianBlur stdDeviation="8"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.75 0 0 0 0 0.71011 0 0 0 0 0.653125 0 0 0 0.51 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="-6" dy="-6"/>
|
||||
<feGaussianBlur stdDeviation="13"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.83 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter3_dd" x="514.562" y="258.962" width="489.408" height="444.757" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="6" dy="6"/>
|
||||
<feGaussianBlur stdDeviation="8"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.75 0 0 0 0 0.71011 0 0 0 0 0.653125 0 0 0 0.51 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="-6" dy="-6"/>
|
||||
<feGaussianBlur stdDeviation="13"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.83 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter4_dd" x="771.843" y="0" width="489.408" height="444.757" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="6" dy="6"/>
|
||||
<feGaussianBlur stdDeviation="8"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.75 0 0 0 0 0.71011 0 0 0 0 0.653125 0 0 0 0.51 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="-6" dy="-6"/>
|
||||
<feGaussianBlur stdDeviation="13"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.83 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter5_dd" x="1030.81" y="258.962" width="489.408" height="444.757" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="6" dy="6"/>
|
||||
<feGaussianBlur stdDeviation="8"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.75 0 0 0 0 0.71011 0 0 0 0 0.653125 0 0 0 0.51 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="-6" dy="-6"/>
|
||||
<feGaussianBlur stdDeviation="13"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.83 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter6_dd" x="773.524" y="516.243" width="489.408" height="444.757" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="6" dy="6"/>
|
||||
<feGaussianBlur stdDeviation="8"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.75 0 0 0 0 0.71011 0 0 0 0 0.653125 0 0 0 0.51 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dx="-6" dy="-6"/>
|
||||
<feGaussianBlur stdDeviation="13"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.83 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 9.3 KiB |
6
src/keycloak-theme/account/i18n.ts
Normal file
6
src/keycloak-theme/account/i18n.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { createUseI18n } from "keycloakify/account";
|
||||
|
||||
//NOTE: See src/login/i18n.ts for instructions on customization of i18n messages.
|
||||
export const { useI18n } = createUseI18n({});
|
||||
|
||||
export type I18n = NonNullable<ReturnType<typeof useI18n>>;
|
17
src/keycloak-theme/account/kcContext.ts
Normal file
17
src/keycloak-theme/account/kcContext.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { getKcContext } from "keycloakify/account";
|
||||
|
||||
export type KcContextExtension =
|
||||
| { pageId: "my-extra-page-1.ftl"; }
|
||||
| { pageId: "my-extra-page-2.ftl"; someCustomValue: string; };
|
||||
|
||||
export const { kcContext } = getKcContext<KcContextExtension>({
|
||||
//mockPageId: "password.ftl",
|
||||
mockData: [
|
||||
{
|
||||
pageId: "my-extra-page-2.ftl",
|
||||
someCustomValue: "foo bar"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
export type KcContext = NonNullable<typeof kcContext>;
|
15
src/keycloak-theme/account/pages/MyExtraPage1.tsx
Normal file
15
src/keycloak-theme/account/pages/MyExtraPage1.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import type { PageProps } from "keycloakify/account/pages/PageProps";
|
||||
import type { KcContext } from "../kcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
export default function MyExtraPage1(props: PageProps<Extract<KcContext, { pageId: "my-extra-page-1.ftl"; }>, I18n>) {
|
||||
|
||||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
|
||||
return (
|
||||
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="my-extra-page-1" >
|
||||
<h1>Hello world 1</h1>
|
||||
</Template>
|
||||
);
|
||||
|
||||
}
|
18
src/keycloak-theme/account/pages/MyExtraPage2.tsx
Normal file
18
src/keycloak-theme/account/pages/MyExtraPage2.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import type { PageProps } from "keycloakify/account/pages/PageProps";
|
||||
import type { KcContext } from "../kcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
export default function MyExtraPage1(props: PageProps<Extract<KcContext, { pageId: "my-extra-page-2.ftl"; }>, I18n>) {
|
||||
|
||||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
|
||||
// someCustomValue is declared by you in ../kcContext.ts
|
||||
console.log(`TODO: Do something with: ${kcContext.someCustomValue}`);
|
||||
|
||||
return (
|
||||
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="my-extra-page-2" >
|
||||
<h1>Hello world 2</h1>
|
||||
</Template>
|
||||
);
|
||||
|
||||
}
|
105
src/keycloak-theme/account/pages/Password.tsx
Normal file
105
src/keycloak-theme/account/pages/Password.tsx
Normal file
|
@ -0,0 +1,105 @@
|
|||
import { clsx } from "keycloakify/tools/clsx";
|
||||
import type { PageProps } from "keycloakify/account/pages/PageProps";
|
||||
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
|
||||
import type { KcContext } from "../kcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
export default function LogoutConfirm(props: PageProps<Extract<KcContext, { pageId: "password.ftl" }>, I18n>) {
|
||||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
|
||||
const { getClassName } = useGetClassName({
|
||||
doUseDefaultCss,
|
||||
"classes": {
|
||||
...classes,
|
||||
"kcBodyClass": clsx(classes?.kcBodyClass, "password")
|
||||
}
|
||||
});
|
||||
|
||||
const { url, password, account } = kcContext;
|
||||
|
||||
const { msg } = i18n;
|
||||
|
||||
return (
|
||||
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="password">
|
||||
<div className="row">
|
||||
<div className="col-md-10">
|
||||
<h2>{msg("changePasswordHtmlTitle")}</h2>
|
||||
</div>
|
||||
<div className="col-md-2 subtitle">
|
||||
<span className="subtitle">${msg("allFieldsRequired")}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action={url.passwordUrl} className="form-horizontal" method="post">
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
name="username"
|
||||
value={account.username ?? ""}
|
||||
autoComplete="username"
|
||||
readOnly
|
||||
style={{ "display": "none;" }}
|
||||
/>
|
||||
|
||||
{password.passwordSet && (
|
||||
<div className="form-group">
|
||||
<div className="col-sm-2 col-md-2">
|
||||
<label htmlFor="password" className="control-label">
|
||||
{msg("password")}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="col-sm-10 col-md-10">
|
||||
<input type="password" className="form-control" id="password" name="password" autoFocus autoComplete="current-password" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker}" />
|
||||
|
||||
<div className="form-group">
|
||||
<div className="col-sm-2 col-md-2">
|
||||
<label htmlFor="password-new" className="control-label">
|
||||
{msg("passwordNew")}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="col-sm-10 col-md-10">
|
||||
<input type="password" className="form-control" id="password-new" name="password-new" autoComplete="new-password" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<div className="col-sm-2 col-md-2">
|
||||
<label htmlFor="password-confirm" className="control-label two-lines">
|
||||
{msg("passwordConfirm")}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="col-sm-10 col-md-10">
|
||||
<input type="password" className="form-control" id="password-confirm" name="password-confirm" autoComplete="new-password" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<div id="kc-form-buttons" className="col-md-offset-2 col-md-10 submit">
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
className={clsx(
|
||||
getClassName("kcButtonClass"),
|
||||
getClassName("kcButtonPrimaryClass"),
|
||||
getClassName("kcButtonLargeClass")
|
||||
)}
|
||||
name="submitAction"
|
||||
value="Save"
|
||||
>
|
||||
{msg("doSave")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Template>
|
||||
);
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/Template.tsx
|
||||
// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/login/Template.tsx
|
||||
|
||||
import { assert } from "keycloakify/tools/assert";
|
||||
import { clsx } from "keycloakify/tools/clsx";
|
||||
import { usePrepareTemplate } from "keycloakify/lib/usePrepareTemplate";
|
||||
import { type TemplateProps, defaultTemplateClasses } from "keycloakify/login/TemplateProps";
|
||||
import { useGetClassName } from "keycloakify/lib/useGetClassName";
|
||||
import { type TemplateProps } from "keycloakify/login/TemplateProps";
|
||||
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
|
||||
import type { KcContext } from "./kcContext";
|
||||
import type { I18n } from "./i18n";
|
||||
|
||||
|
@ -24,10 +25,7 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
|||
children
|
||||
} = props;
|
||||
|
||||
const { getClassName } = useGetClassName({
|
||||
"defaultClasses": !doUseDefaultCss ? undefined : defaultTemplateClasses,
|
||||
classes
|
||||
});
|
||||
const { getClassName } = useGetClassName({ doUseDefaultCss, classes });
|
||||
|
||||
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n;
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// ejected using 'npx eject-keycloak-page'
|
||||
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 { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
|
||||
import type { KcContext } from "../kcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
|
@ -10,7 +11,7 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
|
|||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
|
||||
const { getClassName } = useGetClassName({
|
||||
"defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
|
||||
doUseDefaultCss,
|
||||
classes
|
||||
});
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// ejected using 'npx eject-keycloak-page'
|
||||
import { clsx } from "keycloakify/tools/clsx";
|
||||
import { type PageProps, defaultClasses } from "keycloakify/login/pages/PageProps";
|
||||
import { useGetClassName } from "keycloakify/lib/useGetClassName";
|
||||
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";
|
||||
|
||||
|
@ -8,7 +9,7 @@ export default function Register(props: PageProps<Extract<KcContext, { pageId: "
|
|||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
|
||||
const { getClassName } = useGetClassName({
|
||||
"defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
|
||||
doUseDefaultCss,
|
||||
classes
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// ejected using 'npx eject-keycloak-page'
|
||||
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 { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
|
||||
import type { KcContext } from "../kcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
|
@ -10,7 +11,7 @@ export default function RegisterUserProfile(props: PageProps<Extract<KcContext,
|
|||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
|
||||
const { getClassName } = useGetClassName({
|
||||
"defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
|
||||
doUseDefaultCss,
|
||||
classes
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// ejected using 'npx eject-keycloak-page'
|
||||
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 type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
|
||||
import { evtTermMarkdown } from "keycloakify/login/lib/useDownloadTerms";
|
||||
import type { KcContext } from "../kcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
@ -11,7 +12,7 @@ export default function Terms(props: PageProps<Extract<KcContext, { pageId: "ter
|
|||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
|
||||
const { getClassName } = useGetClassName({
|
||||
"defaultClasses": !doUseDefaultCss ? undefined : defaultClasses,
|
||||
doUseDefaultCss,
|
||||
classes
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, Fragment } from "react";
|
||||
import type { ClassKey } from "keycloakify/login/pages/PageProps";
|
||||
import type { ClassKey } from "keycloakify/login/TemplateProps";
|
||||
import { clsx } from "keycloakify/tools/clsx";
|
||||
import { useFormValidation } from "keycloakify/login/lib/useFormValidation";
|
||||
import type { Attribute } from "keycloakify/login/kcContext/KcContext";
|
||||
|
|
Loading…
Reference in a new issue