2023-02-26 11:32:22 +00:00
|
|
|
// Copy pasted from: https://github.com/InseeFrLab/keycloakify/blob/main/src/lib/components/shared/Template.tsx
|
2023-02-27 09:52:15 +00:00
|
|
|
|
2023-02-26 11:32:22 +00:00
|
|
|
// You can replace all relative imports by cherry picking files from the keycloakify module.
|
|
|
|
// For example, the following import:
|
2023-02-27 09:52:15 +00:00
|
|
|
// import { assert } from "./tools/assert";
|
2023-02-26 11:32:22 +00:00
|
|
|
// becomes:
|
|
|
|
import { assert } from "keycloakify/lib/tools/assert";
|
|
|
|
import { clsx } from "keycloakify/lib/tools/clsx";
|
|
|
|
import type { TemplateProps } from "keycloakify/lib/KcProps";
|
2023-02-27 09:52:15 +00:00
|
|
|
import { usePrepareTemplate } from "keycloakify/lib/Template";
|
2023-02-26 11:32:22 +00:00
|
|
|
import type { KcContext } from "./kcContext";
|
|
|
|
import type { I18n } from "./i18n";
|
|
|
|
|
|
|
|
export default function Template(props: TemplateProps<KcContext, I18n>) {
|
|
|
|
const {
|
|
|
|
displayInfo = false,
|
|
|
|
displayMessage = true,
|
|
|
|
displayRequiredFields = false,
|
|
|
|
displayWide = false,
|
|
|
|
showAnotherWayIfPresent = true,
|
|
|
|
headerNode,
|
|
|
|
showUsernameNode = null,
|
|
|
|
formNode,
|
|
|
|
infoNode = null,
|
|
|
|
kcContext,
|
|
|
|
i18n,
|
2023-02-27 09:52:15 +00:00
|
|
|
doFetchDefaultThemeResources,
|
|
|
|
stylesCommon,
|
|
|
|
styles,
|
|
|
|
scripts,
|
|
|
|
kcHtmlClass
|
2023-02-26 11:32:22 +00:00
|
|
|
} = props;
|
|
|
|
|
|
|
|
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n;
|
|
|
|
|
|
|
|
const { realm, locale, auth, url, message, isAppInitiatedAction } = kcContext;
|
|
|
|
|
2023-02-27 09:52:15 +00:00
|
|
|
const { isReady } = usePrepareTemplate({
|
|
|
|
doFetchDefaultThemeResources,
|
|
|
|
stylesCommon,
|
|
|
|
styles,
|
|
|
|
scripts,
|
|
|
|
url,
|
|
|
|
kcHtmlClass
|
|
|
|
});
|
2023-02-26 11:32:22 +00:00
|
|
|
|
2023-02-27 09:52:15 +00:00
|
|
|
if (!isReady) {
|
2023-02-26 11:32:22 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className={clsx(props.kcLoginClass)}>
|
|
|
|
<div id="kc-header" className={clsx(props.kcHeaderClass)}>
|
|
|
|
<div id="kc-header-wrapper" className={clsx(props.kcHeaderWrapperClass)}>
|
|
|
|
{msg("loginTitleHtml", realm.displayNameHtml)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className={clsx(props.kcFormCardClass, displayWide && props.kcFormCardAccountClass)}>
|
|
|
|
<header className={clsx(props.kcFormHeaderClass)}>
|
|
|
|
{realm.internationalizationEnabled && (assert(locale !== undefined), true) && locale.supported.length > 1 && (
|
|
|
|
<div id="kc-locale">
|
|
|
|
<div id="kc-locale-wrapper" className={clsx(props.kcLocaleWrapperClass)}>
|
|
|
|
<div className="kc-dropdown" id="kc-locale-dropdown">
|
|
|
|
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
|
|
|
<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 */}
|
2023-02-26 13:07:11 +00:00
|
|
|
<a href="#" onClick={() => changeLocale(languageTag)}>
|
2023-02-26 11:32:22 +00:00
|
|
|
{labelBySupportedLanguageTag[languageTag]}
|
|
|
|
</a>
|
|
|
|
</li>
|
|
|
|
))}
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
{!(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? (
|
|
|
|
displayRequiredFields ? (
|
|
|
|
<div className={clsx(props.kcContentWrapperClass)}>
|
|
|
|
<div className={clsx(props.kcLabelWrapperClass, "subtitle")}>
|
|
|
|
<span className="subtitle">
|
|
|
|
<span className="required">*</span>
|
|
|
|
{msg("requiredFields")}
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
<div className="col-md-10">
|
|
|
|
<h1 id="kc-page-title">{headerNode}</h1>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
<h1 id="kc-page-title">{headerNode}</h1>
|
|
|
|
)
|
|
|
|
) : displayRequiredFields ? (
|
|
|
|
<div className={clsx(props.kcContentWrapperClass)}>
|
|
|
|
<div className={clsx(props.kcLabelWrapperClass, "subtitle")}>
|
|
|
|
<span className="subtitle">
|
|
|
|
<span className="required">*</span> {msg("requiredFields")}
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
<div className="col-md-10">
|
|
|
|
{showUsernameNode}
|
|
|
|
<div className={clsx(props.kcFormGroupClass)}>
|
|
|
|
<div id="kc-username">
|
|
|
|
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
|
|
|
|
<a id="reset-login" href={url.loginRestartFlowUrl}>
|
|
|
|
<div className="kc-login-tooltip">
|
|
|
|
<i className={clsx(props.kcResetFlowIcon)}></i>
|
|
|
|
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
|
|
|
|
</div>
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
) : (
|
|
|
|
<>
|
|
|
|
{showUsernameNode}
|
|
|
|
<div className={clsx(props.kcFormGroupClass)}>
|
|
|
|
<div id="kc-username">
|
|
|
|
<label id="kc-attempted-username">{auth?.attemptedUsername}</label>
|
|
|
|
<a id="reset-login" href={url.loginRestartFlowUrl}>
|
|
|
|
<div className="kc-login-tooltip">
|
|
|
|
<i className={clsx(props.kcResetFlowIcon)}></i>
|
|
|
|
<span className="kc-tooltip-text">{msg("restartLoginTooltip")}</span>
|
|
|
|
</div>
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</header>
|
|
|
|
<div id="kc-content">
|
|
|
|
<div id="kc-content-wrapper">
|
|
|
|
{/* App-initiated actions should not see warning messages about the need to complete the action during login. */}
|
|
|
|
{displayMessage && message !== undefined && (message.type !== "warning" || !isAppInitiatedAction) && (
|
|
|
|
<div className={clsx("alert", `alert-${message.type}`)}>
|
|
|
|
{message.type === "success" && <span className={clsx(props.kcFeedbackSuccessIcon)}></span>}
|
|
|
|
{message.type === "warning" && <span className={clsx(props.kcFeedbackWarningIcon)}></span>}
|
|
|
|
{message.type === "error" && <span className={clsx(props.kcFeedbackErrorIcon)}></span>}
|
|
|
|
{message.type === "info" && <span className={clsx(props.kcFeedbackInfoIcon)}></span>}
|
|
|
|
<span
|
|
|
|
className="kc-feedback-text"
|
|
|
|
dangerouslySetInnerHTML={{
|
|
|
|
"__html": message.summary
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
{formNode}
|
|
|
|
{auth !== undefined && auth.showTryAnotherWayLink && showAnotherWayIfPresent && (
|
|
|
|
<form
|
|
|
|
id="kc-select-try-another-way-form"
|
|
|
|
action={url.loginAction}
|
|
|
|
method="post"
|
|
|
|
className={clsx(displayWide && props.kcContentWrapperClass)}
|
|
|
|
>
|
|
|
|
<div className={clsx(displayWide && [props.kcFormSocialAccountContentClass, props.kcFormSocialAccountClass])}>
|
|
|
|
<div className={clsx(props.kcFormGroupClass)}>
|
|
|
|
<input type="hidden" name="tryAnotherWay" value="on" />
|
|
|
|
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
2023-02-26 13:07:11 +00:00
|
|
|
<a href="#" id="try-another-way" onClick={() => {
|
2023-02-26 11:32:22 +00:00
|
|
|
document.forms["kc-select-try-another-way-form" as never].submit();
|
|
|
|
return false;
|
|
|
|
}}>
|
|
|
|
{msg("doTryAnotherWay")}
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</form>
|
|
|
|
)}
|
|
|
|
{displayInfo && (
|
|
|
|
<div id="kc-info" className={clsx(props.kcSignUpClass)}>
|
|
|
|
<div id="kc-info-wrapper" className={clsx(props.kcInfoAreaWrapperClass)}>
|
|
|
|
{infoNode}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|