Improved design
This commit is contained in:
parent
f7789858fa
commit
e23ac4cc27
@ -26,11 +26,11 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
||||
i18n,
|
||||
doUseDefaultCss,
|
||||
children
|
||||
} = props;
|
||||
} = props;
|
||||
|
||||
const { msg, msgStr, currentLanguage, enabledLanguages } = i18n;
|
||||
|
||||
const { realm, auth, url, message, isAppInitiatedAction } = kcContext;
|
||||
const { realm, auth, url, message, isAppInitiatedAction, client } = kcContext;
|
||||
|
||||
const isMobile = useMediaQuery({ maxWidth: 600 });
|
||||
|
||||
@ -105,7 +105,7 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
||||
colorPrimary: "rgba(212, 0, 255, 1)",
|
||||
colorLink: "#6E00FF",
|
||||
colorLinkHover: "#b175ff",
|
||||
borderRadius: 15
|
||||
borderRadius: 20
|
||||
},
|
||||
algorithm: darkMode ? theme.darkAlgorithm : theme.defaultAlgorithm
|
||||
}}
|
||||
@ -113,35 +113,70 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
||||
<Layout style={{ minHeight: "var(--unit-100vh)", maxHeight: "var(--unit-100vh)" }}>
|
||||
<ParticlesBackground />
|
||||
|
||||
{loading == true ? (<div className="loadingOverlay" style={{backgroundColor: darkMode ? '#000000' : '#ffffff'}}>
|
||||
<Spin/>
|
||||
</div>) : null}
|
||||
{loading == true ? (
|
||||
<div className="loadingOverlay" style={{ backgroundColor: darkMode ? "#000000" : "#ffffff" }}>
|
||||
<Spin />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<Content
|
||||
style={{
|
||||
background: "#f5f5f5",
|
||||
|
||||
background: "#f5f5f5"
|
||||
}}
|
||||
|
||||
>
|
||||
<Flex vertical align="center" justify="center" style={{width: '100vw', height: "var(--unit-100vh)"}} gap={'50px'}>
|
||||
{!isMobile && <img src="https://cdn.tombutcher.work/logos/logo-horizontal.svg" alt="Logo" style={{ height: "65px", padding: "0 30px", zIndex: 1 }} /> }
|
||||
<Card style={{ borderRadius: isMobile ? "0px" : "20px", width: isMobile ? "100vw" : "450px", zIndex: 1, height: isMobile ? "100vh" : "unset", padding: "30px 15px", boxShadow: "0px 5px 15px 5px rgb(0 0 0 / 10%)",
|
||||
}} variant="borderless" styles={{body: {
|
||||
padding: 0
|
||||
}}}><div style={{ height: "100%" }}>
|
||||
<div style={{height: "100%", margin: "0 15px"}}>
|
||||
{isMobile && <><img src="https://cdn.tombutcher.work/logos/logo-auth.png" alt="Logo" style={{ width: "70%", margin: "0" }} /> <Divider style={{ margin: "24px 0" }} /></>}
|
||||
<Flex vertical align="center" justify="center" style={{ width: "100vw", height: "var(--unit-100vh)" }} gap={"40px"}>
|
||||
{!isMobile && (
|
||||
<img
|
||||
src="https://cdn.tombutcher.work/logos/logo-horizontal.svg"
|
||||
alt="Logo"
|
||||
style={{ height: "60px", padding: "0 30px", zIndex: 1, marginBottom: 5 }}
|
||||
/>
|
||||
)}
|
||||
<Card
|
||||
style={{
|
||||
borderRadius: isMobile ? "0px" : "20px",
|
||||
width: isMobile ? "100vw" : "450px",
|
||||
zIndex: 1,
|
||||
height: isMobile ? "100vh" : "unset",
|
||||
padding: "23px 15px 30px 15px",
|
||||
boxShadow: "0px 5px 15px 5px rgb(0 0 0 / 10%)"
|
||||
}}
|
||||
variant="borderless"
|
||||
styles={{
|
||||
body: {
|
||||
padding: 0,
|
||||
height: "100%"
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Flex style={{ height: "100%" }} vertical>
|
||||
<div style={{ height: "100%", margin: "0 15px" }}>
|
||||
{isMobile && (
|
||||
<>
|
||||
<img
|
||||
src="https://cdn.tombutcher.work/logos/logo-auth.png"
|
||||
alt="Logo"
|
||||
style={{ width: "70%", margin: "0" }}
|
||||
/>{" "}
|
||||
<Divider style={{ margin: "24px 0" }} />
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
|
||||
|
||||
<div style={{ marginBottom: "24px" }}>
|
||||
<div style={{ marginBottom: "10px" }}>
|
||||
{(() => {
|
||||
const node = !(auth !== undefined && auth.showUsername && !auth.showResetCredentials) ? (
|
||||
<Title level={2} style={{ marginBottom: "16px" }}>
|
||||
<Flex gap={"large"} align="center" style={{ paddingBottom: "8px" }}>
|
||||
{client.attributes.logoUri && (
|
||||
<img
|
||||
src={client.attributes.logoUri}
|
||||
alt={client.name || client.clientId}
|
||||
style={{ maxHeight: "64px", maxWidth: "100%", marginBottom: "5px" }}
|
||||
/>
|
||||
)}
|
||||
<Title level={2} style={{ marginBottom: "0" }}>
|
||||
{headerNode}
|
||||
</Title>
|
||||
</Flex>
|
||||
) : (
|
||||
<>
|
||||
<div style={{ display: "flex", alignItems: "center", marginBottom: "24px" }}>
|
||||
@ -169,7 +204,6 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
||||
if (displayRequiredFields) {
|
||||
return (
|
||||
<>
|
||||
|
||||
{node}
|
||||
<div style={{ marginBottom: "12px" }}>
|
||||
<Text type="secondary">
|
||||
@ -198,11 +232,11 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
||||
: "info"
|
||||
}
|
||||
showIcon
|
||||
style={{ marginBottom: "24px" }}
|
||||
style={{ marginBottom: "26px" }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ overflowY: "auto", height: isMobile ? "100%" : "unset", maxHeight: isMobile ? "100%" : "calc(100vh / 2.25)", padding: "0 15px" }}>
|
||||
|
||||
{children}
|
||||
{auth !== undefined && auth.showTryAnotherWayLink && (
|
||||
<form id="kc-select-try-another-way-form" action={url.loginAction} method="post">
|
||||
@ -220,23 +254,30 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
{displayInfo && <div style={{ marginTop: "24px", textAlign: "center" }}>{infoNode}</div>}
|
||||
</div>
|
||||
|
||||
{displayInfo && <div style={{ marginTop: "24px", textAlign: "center" }}>{infoNode}</div>}
|
||||
</Flex>
|
||||
</Card>
|
||||
{!isMobile && (
|
||||
<Flex style={{ zIndex: 1 }} gap={"large"}>
|
||||
<Text style={{ color: "#ffffff", fontWeight: 700 }}>© 2025</Text>
|
||||
{enabledLanguages.length > 1 && (
|
||||
<div style={{ margin: "0 15px"}}>
|
||||
<Divider style={{ margin: "24px 0" }} />
|
||||
<Dropdown menu={languageItems} trigger={["click"]}>
|
||||
<Button style={{ width: "100%", textAlign: "left" }}>
|
||||
<>
|
||||
<Text style={{ color: "#ffffff" }}>|</Text>
|
||||
<Dropdown menu={languageItems} trigger={["hover"]}>
|
||||
<Text style={{ color: "#ffffff", fontWeight: 700 }}>
|
||||
<Space>
|
||||
{currentLanguage.label}
|
||||
<GlobalOutlined />
|
||||
</Space>
|
||||
</Button>
|
||||
</Text>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div></Card></Flex></Content>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
</Content>
|
||||
</Layout>
|
||||
</ConfigProvider>
|
||||
);
|
||||
|
||||
@ -195,11 +195,16 @@ a.ant-typography,
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
input:-webkit-autofill:focus,
|
||||
input:-webkit-autofill:active {
|
||||
input:-webkit-autofill:active,
|
||||
input:-internal-autofill-selected {
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: #ffffff;
|
||||
transition: background-color 5000s ease-in-out 0s;
|
||||
box-shadow: inset 0 0 20px 20px #23232329;
|
||||
-webkit-text-fill-color: #7e8500;
|
||||
box-shadow: inset 0 0 20px 20px #ffffff !important;
|
||||
background-color: green !important
|
||||
}
|
||||
|
||||
input[type="password"] {
|
||||
letter-spacing: 5px;
|
||||
}
|
||||
|
||||
.ant-form-item:last-child {
|
||||
@ -216,3 +221,7 @@ input:-webkit-autofill:active {
|
||||
.ant-alert {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.ant-form-item {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
@ -282,9 +282,9 @@ h2 span * {
|
||||
font-family: "Grold-Rounded-Slim" !important;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
font-size: 40px;
|
||||
line-height: 0.9px;
|
||||
letter-spacing: 0.02em;
|
||||
font-size: 36px;
|
||||
line-height: 0.9px !important;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
h1 span {
|
||||
|
||||
@ -19,6 +19,7 @@ export default function Code(props: PageProps<Extract<KcContext, { pageId: "code
|
||||
classes={classes}
|
||||
headerNode={code.success ? msg("codeSuccessTitle") : msg("codeErrorTitle", code.error)}
|
||||
>
|
||||
<div style={{ margin: "0 15px" }}>
|
||||
{code.success ? (
|
||||
<>
|
||||
<Space direction="vertical" size="middle" style={{ width: "100%" }}>
|
||||
@ -47,6 +48,7 @@ export default function Code(props: PageProps<Extract<KcContext, { pageId: "code
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</Template>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Form, Button, Typography, Alert, Divider, List, Flex, FormProps } from "antd";
|
||||
import { Form, Button, Typography, Alert, List, Flex, FormProps } from "antd";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
@ -53,7 +53,7 @@ export default function DeleteAccountConfirm(props: PageProps<Extract<KcContext,
|
||||
|
||||
return (
|
||||
<Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("deleteAccountConfirm")}>
|
||||
<Form form={form} layout="vertical" onFinish={handleSubmit} style={{ width: "100%" }}>
|
||||
<Form form={form} layout="vertical" onFinish={handleSubmit} style={{ width: "100%", padding: "0 15px" }}>
|
||||
<Alert message={msg("irreversibleAction")} type="warning" showIcon style={{ marginBottom: 16 }} />
|
||||
|
||||
<Text>{msg("deletingImplies")}</Text>
|
||||
@ -72,9 +72,8 @@ export default function DeleteAccountConfirm(props: PageProps<Extract<KcContext,
|
||||
<Text strong style={{ marginTop: 8 }}>
|
||||
{msg("finalDeletionConfirmation")}
|
||||
</Text>
|
||||
<Divider />
|
||||
|
||||
<Flex gap={"middle"}>
|
||||
<Flex gap={"middle"} style={{ marginTop: "20px" }}>
|
||||
<Button
|
||||
type="primary"
|
||||
style={{ flexGrow: 2 }}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Button, Flex, Alert, Divider } from "antd";
|
||||
import { Button, Flex, Alert } from "antd";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
@ -17,15 +17,15 @@ export default function DeleteCredential(props: PageProps<Extract<KcContext, { p
|
||||
displayMessage={false}
|
||||
headerNode={msg("deleteCredentialTitle", credentialLabel)}
|
||||
>
|
||||
<div style={{ margin: "0 15px" }}>
|
||||
<Alert
|
||||
message={msg("deleteCredentialMessage", credentialLabel)}
|
||||
type="warning"
|
||||
showIcon
|
||||
style={{
|
||||
marginBottom: 24
|
||||
marginBottom: 26
|
||||
}}
|
||||
/>
|
||||
<Divider />
|
||||
<form action={url.loginAction} method="POST">
|
||||
<Flex gap="middle">
|
||||
<Button
|
||||
@ -46,6 +46,7 @@ export default function DeleteCredential(props: PageProps<Extract<KcContext, { p
|
||||
</Button>
|
||||
</Flex>
|
||||
</form>
|
||||
</div>
|
||||
</Template>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,9 +2,7 @@ import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import { kcSanitize } from "keycloakify/lib/kcSanitize";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
import { Alert, Button, Typography, Space, Divider } from "antd";
|
||||
|
||||
const { Title } = Typography;
|
||||
import { Alert, Button, Space} from "antd";
|
||||
|
||||
export default function Error(props: PageProps<Extract<KcContext, { pageId: "error.ftl" }>, I18n>) {
|
||||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
@ -18,19 +16,18 @@ export default function Error(props: PageProps<Extract<KcContext, { pageId: "err
|
||||
doUseDefaultCss={doUseDefaultCss}
|
||||
classes={classes}
|
||||
displayMessage={false}
|
||||
headerNode={<Title level={2}>{msg("errorTitle")}</Title>}
|
||||
headerNode={msg("errorTitle")}
|
||||
>
|
||||
<Space direction="vertical" size="middle">
|
||||
<div style={{ margin: "0 15px" }}>
|
||||
<Space direction="vertical" size="middle" style={{ marginBottom: 24 }}>
|
||||
<Alert message={<div dangerouslySetInnerHTML={{ __html: kcSanitize(message.summary) }} />} type="error" showIcon />
|
||||
</Space>
|
||||
{!skipLink && client !== undefined && client.baseUrl !== undefined && (
|
||||
<>
|
||||
<Divider />
|
||||
<Button type="primary" id="backToApplication" size={"large"} block href={client.baseUrl}>
|
||||
{msg("backToApplication")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Template>
|
||||
);
|
||||
}
|
||||
|
||||
@ -24,9 +24,9 @@ export default function FrontchannelLogout(props: PageProps<Extract<KcContext, {
|
||||
doUseDefaultCss={doUseDefaultCss}
|
||||
classes={classes}
|
||||
documentTitle={msgStr("frontchannel-logout.title")}
|
||||
headerNode={<Title level={3}>{msg("frontchannel-logout.title")}</Title>}
|
||||
headerNode={msg("frontchannel-logout.title")}
|
||||
>
|
||||
<Space direction="vertical" style={{ width: "100%" }}>
|
||||
<Space direction="vertical" style={{ width: "100%", padding: "0 15px" }}>
|
||||
<Paragraph>{msg("frontchannel-logout.message")}</Paragraph>
|
||||
|
||||
<List
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { Form, Button, Space, Divider } from "antd";
|
||||
import { Form, Button, Space } from "antd";
|
||||
import type { JSX } from "keycloakify/tools/JSX";
|
||||
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
@ -7,6 +7,7 @@ import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFo
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
import { CheckOutlined } from "@ant-design/icons";
|
||||
import { useMediaQuery } from "react-responsive";
|
||||
|
||||
type IdpReviewUserProfileProps = PageProps<Extract<KcContext, { pageId: "idp-review-user-profile.ftl" }>, I18n> & {
|
||||
UserProfileFormFields: LazyOrNot<(props: UserProfileFormFieldsProps) => JSX.Element>;
|
||||
@ -17,6 +18,7 @@ export default function IdpReviewUserProfile(props: IdpReviewUserProfileProps) {
|
||||
const { kcContext, i18n, doUseDefaultCss, Template, classes, UserProfileFormFields, doMakeUserConfirmPassword } = props;
|
||||
const { msg, msgStr } = i18n;
|
||||
const { url, messagesPerField } = kcContext;
|
||||
const isMobile = useMediaQuery({ maxWidth: 600 });
|
||||
const [isFormSubmittable, setIsFormSubmittable] = useState(false);
|
||||
|
||||
return (
|
||||
@ -26,11 +28,13 @@ export default function IdpReviewUserProfile(props: IdpReviewUserProfileProps) {
|
||||
doUseDefaultCss={doUseDefaultCss}
|
||||
classes={classes}
|
||||
displayMessage={messagesPerField.exists("global")}
|
||||
displayRequiredFields
|
||||
headerNode={msg("loginIdpReviewProfileTitle")}
|
||||
>
|
||||
<Form id="kc-idp-review-profile-form" layout="vertical" method="post" action={url.loginAction} size="large">
|
||||
<div className="kctbform" style={{ paddingBottom: 0 }}>
|
||||
<div
|
||||
className="kctbform"
|
||||
style={{ paddingBottom: 0, overflowY: "scroll", maxHeight: isMobile ? "100%" : "250px", padding: "0 15px", marginBottom: 24 }}
|
||||
>
|
||||
<UserProfileFormFields
|
||||
kcContext={kcContext}
|
||||
i18n={i18n}
|
||||
@ -39,8 +43,7 @@ export default function IdpReviewUserProfile(props: IdpReviewUserProfileProps) {
|
||||
doMakeUserConfirmPassword={doMakeUserConfirmPassword}
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
<Space direction="vertical" style={{ width: "100%" }}>
|
||||
<Space direction="vertical" style={{ width: "100%", padding: "0 15px" }}>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" icon={<CheckOutlined />} block size="large" disabled={!isFormSubmittable}>
|
||||
{msgStr("doSubmit")}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Typography, Button, Space, Divider } from "antd";
|
||||
import { Typography, Button, Space } from "antd";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
const { Text } = Typography;
|
||||
|
||||
export default function Info(props: PageProps<Extract<KcContext, { pageId: "info.ftl" }>, I18n>) {
|
||||
const { kcContext, i18n, Template } = props;
|
||||
@ -29,20 +29,16 @@ export default function Info(props: PageProps<Extract<KcContext, { pageId: "info
|
||||
|
||||
if (pageRedirectUri) {
|
||||
return (
|
||||
<>
|
||||
<Divider />
|
||||
<Button type="primary" block size={"large"} href={pageRedirectUri}>
|
||||
<Button type="primary" block size={"large"} href={pageRedirectUri} style={{ marginTop: 24 }}>
|
||||
{msg("backToApplication")}
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (actionUri) {
|
||||
return (
|
||||
<>
|
||||
<Divider />
|
||||
<Button type="primary" block size={"large"} href={actionUri}>
|
||||
<Button type="primary" block size={"large"} href={actionUri} style={{ marginTop: 24 }}>
|
||||
{msg("proceedWithAction")}
|
||||
</Button>
|
||||
</>
|
||||
@ -52,8 +48,7 @@ export default function Info(props: PageProps<Extract<KcContext, { pageId: "info
|
||||
if (client.baseUrl) {
|
||||
return (
|
||||
<>
|
||||
<Divider />
|
||||
<Button type="primary" block size={"large"} href={client.baseUrl}>
|
||||
<Button type="primary" block size={"large"} href={client.baseUrl} style={{ marginTop: 24 }}>
|
||||
{msg("backToApplication")}
|
||||
</Button>
|
||||
</>
|
||||
@ -64,19 +59,16 @@ export default function Info(props: PageProps<Extract<KcContext, { pageId: "info
|
||||
};
|
||||
|
||||
return (
|
||||
<Template
|
||||
kcContext={kcContext}
|
||||
i18n={i18n}
|
||||
doUseDefaultCss={false}
|
||||
displayMessage={false}
|
||||
headerNode={<Title level={3}>{messageHeader}</Title>}
|
||||
>
|
||||
<Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={false} displayMessage={false} headerNode={messageHeader}>
|
||||
<div style={{ padding: "0 15px" }}>
|
||||
<Space direction="vertical" size="middle">
|
||||
<Text className="instruction">
|
||||
<span dangerouslySetInnerHTML={{ __html: getMessageContent() }}></span>
|
||||
</Text>
|
||||
</Space>
|
||||
|
||||
{renderActionLink()}
|
||||
</div>
|
||||
</Template>
|
||||
);
|
||||
}
|
||||
|
||||
@ -68,22 +68,7 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
|
||||
kcContext={kcContext}
|
||||
i18n={i18n}
|
||||
doUseDefaultCss={false}
|
||||
headerNode={
|
||||
<>
|
||||
{client.attributes.logoUri ? (
|
||||
<Space align="start" direction="vertical">
|
||||
<img
|
||||
src={client.attributes.logoUri}
|
||||
alt={client.name || client.clientId}
|
||||
style={{ maxHeight: "64px", maxWidth: "100%", marginBottom: "20px" }}
|
||||
/>
|
||||
<Title level={3}>{msg("loginAccountTitle")}</Title>
|
||||
</Space>
|
||||
) : (
|
||||
<Title level={3}>{msg("loginAccountTitle")}</Title>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
headerNode={msg("loginAccountTitle")}
|
||||
displayInfo={realm.password && realm.registrationAllowed && !registrationDisabled}
|
||||
infoNode={
|
||||
<Space>
|
||||
@ -123,6 +108,7 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
|
||||
form={form}
|
||||
initialValues={{ username: login.username || "", rememberMe: !!login.rememberMe }}
|
||||
layout="vertical"
|
||||
style={{ padding: "0 15px" }}
|
||||
requiredMark={false}
|
||||
onFinish={onFinish}
|
||||
>
|
||||
@ -154,7 +140,7 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Row justify="space-between" align="middle">
|
||||
<Row justify="space-between" align="middle" style={{ paddingTop: "4px" }}>
|
||||
{realm.rememberMe && !usernameHidden && (
|
||||
<Col>
|
||||
<Form.Item name="rememberMe" valuePropName="checked" noStyle>
|
||||
@ -171,7 +157,6 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
<Divider />
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
@ -183,6 +168,7 @@ export default function Login(props: PageProps<Extract<KcContext, { pageId: "log
|
||||
disabled={isLoading}
|
||||
loading={isLoading}
|
||||
iconPosition={"end"}
|
||||
style={{ marginTop: 24 }}
|
||||
icon={
|
||||
<img
|
||||
src={"https://cdn.tombutcher.work/icons/auth/w-right.svg"}
|
||||
|
||||
@ -5,6 +5,7 @@ import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
import { Typography, List, Button, Form, Input, Checkbox, Space, Steps, Divider, Flex } from "antd";
|
||||
import { useMediaQuery } from "react-responsive";
|
||||
|
||||
const { Title, Text, Paragraph, Link } = Typography;
|
||||
|
||||
@ -67,6 +68,8 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
|
||||
onFinish({ "cancel-aia": "true" });
|
||||
};
|
||||
|
||||
const isMobile = useMediaQuery({ maxWidth: 600 });
|
||||
|
||||
return (
|
||||
<Template
|
||||
kcContext={kcContext}
|
||||
@ -76,6 +79,8 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
|
||||
headerNode={<Title level={3}>{msg("loginTotpTitle")}</Title>}
|
||||
displayMessage={!messagesPerField.existsError("totp", "userLabel")}
|
||||
>
|
||||
<Form layout="vertical" onFinish={handleSubmit} style={{ margin: "0 auto" }}>
|
||||
<div style={{ overflowY: "scroll", maxHeight: isMobile ? "100%" : "250px", padding: "0 15px", marginBottom: 24 }}>
|
||||
<Steps
|
||||
direction="vertical"
|
||||
current={3}
|
||||
@ -180,7 +185,6 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
|
||||
]}
|
||||
/>
|
||||
<Divider />
|
||||
<Form layout="vertical" onFinish={handleSubmit} style={{ margin: "0 auto" }}>
|
||||
<Form.Item
|
||||
label={
|
||||
<>
|
||||
@ -236,11 +240,11 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
|
||||
aria-invalid={messagesPerField.existsError("userLabel")}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
</div>
|
||||
<div style={{ padding: "0 15px" }}>
|
||||
<Form.Item>
|
||||
<LogoutOtherSessions kcClsx={kcClsx} i18n={i18n} />
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item>
|
||||
<Flex gap={"middle"}>
|
||||
<Button
|
||||
@ -278,6 +282,7 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
|
||||
) : null}
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form>
|
||||
</Template>
|
||||
);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Button, Divider, Form, Space } from "antd";
|
||||
import { Button, Form, Space } from "antd";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
@ -10,8 +10,7 @@ export default function LoginIdpLinkConfirm(props: PageProps<Extract<KcContext,
|
||||
|
||||
return (
|
||||
<Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={false} classes={{}} headerNode={msg("confirmLinkIdpTitle")}>
|
||||
<Divider />
|
||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||
<Space direction="vertical" size="large" style={{ width: "100%", padding: "0 15px" }}>
|
||||
<Form id="kc-register-form" action={url.loginAction} method="post">
|
||||
<Space direction="vertical" size="middle" style={{ width: "100%" }}>
|
||||
<Button type="default" htmlType="submit" id="updateProfile" name="submitAction" value="updateProfile" block size="large">
|
||||
|
||||
@ -23,7 +23,7 @@ export default function LoginIdpLinkEmail(props: PageProps<Extract<KcContext, {
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||
<Space direction="vertical" size="large" style={{ width: "100%", padding: "0 15px" }}>
|
||||
<Text id="instruction1" className="instruction">
|
||||
<span style={{ fontWeight: "800" }}>{msg("emailLinkIdp1", idpAlias, brokerContext.username, realm.displayName)}</span>
|
||||
</Text>
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { useState } from "react";
|
||||
import { Button, Divider, Form, Input } from "antd";
|
||||
import { Button, Typography, Form, Input } from "antd";
|
||||
import { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
export default function LoginOauth2DeviceVerifyUserCode(
|
||||
props: PageProps<Extract<KcContext, { pageId: "login-oauth2-device-verify-user-code.ftl" }>, I18n>
|
||||
) {
|
||||
@ -42,18 +44,20 @@ export default function LoginOauth2DeviceVerifyUserCode(
|
||||
|
||||
return (
|
||||
<Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={false} classes={{}} headerNode={msg("oauth2DeviceVerificationTitle")}>
|
||||
<Form id="kc-user-verify-device-user-code-form" layout="vertical" onFinish={handleSubmit}>
|
||||
<Form.Item label={msg("verifyOAuth2DeviceUserCode")} name="device_user_code" rules={[{ required: true, message: "Required" }]}>
|
||||
<Form id="kc-user-verify-device-user-code-form" layout="vertical" onFinish={handleSubmit} style={{ padding: "0 15px" }}>
|
||||
<Text>{msg("verifyOAuth2DeviceUserCode")}</Text>
|
||||
<Form.Item label={"Code"} name="device_user_code" rules={[{ required: true, message: "Required" }]}>
|
||||
<Input
|
||||
id="device-user-code"
|
||||
name="device_user_code"
|
||||
style={{ marginBottom: "10px" }}
|
||||
autoComplete="off"
|
||||
autoFocus
|
||||
size="large"
|
||||
prefix={<img src={"https://cdn.tombutcher.work/icons/auth/c-hash.svg"} width={14} style={{ marginRight: "3px" }} />}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
|
||||
@ -4,7 +4,7 @@ import { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
import { Typography, Space, List, Button, Flex, Divider, Alert } from "antd";
|
||||
|
||||
const { Title, Text, Link } = Typography;
|
||||
const { Text, Link } = Typography;
|
||||
|
||||
export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pageId: "login-oauth-grant.ftl" }>, I18n>) {
|
||||
const { kcContext, i18n, doUseDefaultCss, classes, Template } = props;
|
||||
@ -47,37 +47,21 @@ export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pa
|
||||
doUseDefaultCss={doUseDefaultCss}
|
||||
classes={classes}
|
||||
bodyClassName="oauth"
|
||||
headerNode={
|
||||
<>
|
||||
{client.attributes.logoUri ? (
|
||||
<Space align="start" direction="vertical">
|
||||
<img
|
||||
src={client.attributes.logoUri}
|
||||
alt={client.name || client.clientId}
|
||||
style={{ maxHeight: "64px", maxWidth: "100%", marginBottom: "20px" }}
|
||||
/>
|
||||
<Title level={3}>
|
||||
{client.name ? msg("oauthGrantTitle", advancedMsgStr(client.name)) : msg("oauthGrantTitle", client.clientId)}
|
||||
</Title>
|
||||
</Space>
|
||||
) : (
|
||||
<Title level={3}>
|
||||
{client.name ? msg("oauthGrantTitle", advancedMsgStr(client.name)) : msg("oauthGrantTitle", client.clientId)}
|
||||
</Title>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
headerNode={client.name ? msg("oauthGrantTitle", advancedMsgStr(client.name)) : msg("oauthGrantTitle", client.clientId)}
|
||||
>
|
||||
<div style={{ margin: "0 15px" }}>
|
||||
<Alert message={msg("oauthGrantRequest")} type="warning" style={{ marginBottom: "20px" }} showIcon></Alert>
|
||||
{(client.attributes.policyUri || client.attributes.tosUri) && (
|
||||
<Alert
|
||||
message={client.name ? msg("oauthGrantInformation", advancedMsgStr(client.name)) : msg("oauthGrantInformation", client.clientId)}
|
||||
message={
|
||||
client.name ? msg("oauthGrantInformation", advancedMsgStr(client.name)) : msg("oauthGrantInformation", client.clientId)
|
||||
}
|
||||
type="info"
|
||||
style={{ marginBottom: "20px" }}
|
||||
showIcon
|
||||
></Alert>
|
||||
)}
|
||||
{oauth.clientScopesRequested.length > 0 && (
|
||||
{oauth.clientScopesRequested.length > 0 && (
|
||||
<List
|
||||
dataSource={oauth.clientScopesRequested}
|
||||
bordered={true}
|
||||
@ -101,9 +85,9 @@ export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pa
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
{(client.attributes.policyUri || client.attributes.tosUri) && (
|
||||
<>
|
||||
<div style={{ marginTop: 10 }}>
|
||||
<Space direction="vertical" style={{ marginTop: "12px" }}>
|
||||
{client.attributes.tosUri && (
|
||||
<Text>
|
||||
@ -122,12 +106,9 @@ export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pa
|
||||
</Text>
|
||||
)}
|
||||
</Space>
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Divider />
|
||||
|
||||
<Flex gap="middle">
|
||||
<Flex gap="middle" style={{ marginTop: 24 }}>
|
||||
<Button
|
||||
type="primary"
|
||||
style={{ flexGrow: 1 }}
|
||||
@ -165,6 +146,7 @@ export default function LoginOauthGrant(props: PageProps<Extract<KcContext, { pa
|
||||
{msgStr("doNo")}
|
||||
</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
</Template>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { Form, Input, Button, Radio, List, Typography, Space, Divider } from "antd";
|
||||
import { Form, Input, Button, Radio, List, Typography, Space } from "antd";
|
||||
import { kcSanitize } from "keycloakify/lib/kcSanitize";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
@ -51,7 +51,7 @@ export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "
|
||||
displayMessage={!messagesPerField.existsError("totp")}
|
||||
headerNode={msg("doLogIn")}
|
||||
>
|
||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||
<Space direction="vertical" size="large" style={{ width: "100%", padding: "0 15px" }}>
|
||||
<Form id="kc-otp-login-form" layout="vertical" onFinish={handleSubmit}>
|
||||
{otpLogin.userOtpCredentials.length > 1 && (
|
||||
<Form.Item name="selectedCredentialId">
|
||||
@ -96,7 +96,6 @@ export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "
|
||||
prefix={<img src={"https://cdn.tombutcher.work/icons/auth/c-hash.svg"} width={14} style={{ marginRight: "3px" }} />}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
@ -107,6 +106,7 @@ export default function LoginOtp(props: PageProps<Extract<KcContext, { pageId: "
|
||||
size="large"
|
||||
disabled={isSubmitLoading}
|
||||
loading={isSubmitLoading}
|
||||
style={{ marginTop: 10 }}
|
||||
iconPosition={"end"}
|
||||
icon={
|
||||
<img
|
||||
|
||||
@ -3,7 +3,7 @@ import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const { Title, Text, Link } = Typography;
|
||||
const { Text, Link } = Typography;
|
||||
|
||||
export default function LoginPageExpired(props: PageProps<Extract<KcContext, { pageId: "login-page-expired.ftl" }>, I18n>) {
|
||||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
@ -11,14 +11,8 @@ export default function LoginPageExpired(props: PageProps<Extract<KcContext, { p
|
||||
const { msg } = i18n;
|
||||
|
||||
return (
|
||||
<Template
|
||||
kcContext={kcContext}
|
||||
i18n={i18n}
|
||||
doUseDefaultCss={doUseDefaultCss}
|
||||
classes={classes}
|
||||
headerNode={<Title level={3}>{msg("pageExpiredTitle")}</Title>}
|
||||
>
|
||||
<Space direction="vertical" size="small" style={{ width: "100%" }}>
|
||||
<Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("pageExpiredTitle")}>
|
||||
<Space direction="vertical" size="small" style={{ width: "100%", margin: "0 15px" }}>
|
||||
<Text id="instruction1">
|
||||
{msg("pageExpiredMsg1")}{" "}
|
||||
<Link id="loginRestartLink" href={url.loginRestartFlowUrl}>
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
import { useState } from "react";
|
||||
import { Form, Input, Button, Divider, Typography, Space, FormProps } from "antd";
|
||||
import { Form, Input, Button, Typography, FormProps } from "antd";
|
||||
import { EyeTwoTone, EyeInvisibleOutlined } from "@ant-design/icons";
|
||||
import { kcSanitize } from "keycloakify/lib/kcSanitize";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const { Title, Link } = Typography;
|
||||
const { Link } = Typography;
|
||||
|
||||
export default function LoginPassword(props: PageProps<Extract<KcContext, { pageId: "login-password.ftl" }>, I18n>) {
|
||||
const { kcContext, i18n, Template } = props;
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const { realm, url, messagesPerField, client } = kcContext;
|
||||
const { realm, url, messagesPerField } = kcContext;
|
||||
const { msg, msgStr } = i18n;
|
||||
|
||||
type FieldType = {
|
||||
@ -47,25 +47,10 @@ export default function LoginPassword(props: PageProps<Extract<KcContext, { page
|
||||
kcContext={kcContext}
|
||||
i18n={i18n}
|
||||
doUseDefaultCss={false}
|
||||
headerNode={
|
||||
<>
|
||||
{client.attributes.logoUri ? (
|
||||
<Space align="start" direction="vertical">
|
||||
<img
|
||||
src={client.attributes.logoUri}
|
||||
alt={client.name || client.clientId}
|
||||
style={{ maxHeight: "64px", maxWidth: "100%", marginBottom: "20px" }}
|
||||
/>
|
||||
<Title level={3}>{msg("loginAccountTitle")}</Title>
|
||||
</Space>
|
||||
) : (
|
||||
<Title level={3}>{msg("loginAccountTitle")}</Title>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
headerNode={msg("loginAccountTitle")}
|
||||
displayMessage={!messagesPerField.existsError("password")}
|
||||
>
|
||||
<Form form={form} layout="vertical" requiredMark={false} onFinish={onFinish}>
|
||||
<Form form={form} layout="vertical" requiredMark={false} onFinish={onFinish} style={{ padding: "0 15px" }}>
|
||||
<Form.Item
|
||||
name="password"
|
||||
label={msg("password")}
|
||||
@ -92,13 +77,11 @@ export default function LoginPassword(props: PageProps<Extract<KcContext, { page
|
||||
</Form.Item>
|
||||
|
||||
{realm.resetPasswordAllowed && (
|
||||
<div style={{ textAlign: "right", marginBottom: 16 }}>
|
||||
<div style={{ textAlign: "right", paddingTop: "2px" }}>
|
||||
<Link href={url.loginResetCredentialsUrl}>{msg("doForgotPassword")}</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Divider />
|
||||
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
@ -110,6 +93,7 @@ export default function LoginPassword(props: PageProps<Extract<KcContext, { page
|
||||
disabled={isLoading}
|
||||
loading={isLoading}
|
||||
iconPosition="end"
|
||||
style={{ marginTop: 24 }}
|
||||
icon={
|
||||
<img
|
||||
src={"https://cdn.tombutcher.work/icons/auth/w-right.svg"}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
import { Alert, Flex, Button, Checkbox, Form, Input, Space, Typography, Divider } from "antd";
|
||||
import { Alert, Flex, Button, Checkbox, Form, Input, Space, Typography } from "antd";
|
||||
import { PrinterOutlined, SaveOutlined, CopyOutlined } from "@ant-design/icons";
|
||||
import { useScript } from "keycloakify/login/pages/LoginRecoveryAuthnCodeConfig.useScript";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
@ -32,6 +32,7 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
|
||||
classes={classes}
|
||||
headerNode={msg("recovery-code-config-header")}
|
||||
>
|
||||
<div style={{ margin: "0 15px" }}>
|
||||
<Space direction="vertical" size="middle">
|
||||
<Space direction="vertical" size="large">
|
||||
<Alert message={msg("recovery-code-config-warning-title")} type="warning" showIcon />
|
||||
@ -68,14 +69,17 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
|
||||
</Space>
|
||||
|
||||
<Form id="kc-recovery-codes-settings-form" method="post" action={kcContext.url.loginAction}>
|
||||
<Input type="hidden" name="generatedRecoveryAuthnCodes" value={recoveryAuthnCodesConfigBean.generatedRecoveryAuthnCodesAsString} />
|
||||
<Input
|
||||
type="hidden"
|
||||
name="generatedRecoveryAuthnCodes"
|
||||
value={recoveryAuthnCodesConfigBean.generatedRecoveryAuthnCodesAsString}
|
||||
/>
|
||||
<Input type="hidden" name="generatedAt" value={recoveryAuthnCodesConfigBean.generatedAt} />
|
||||
<Input type="hidden" id="userLabel" name="userLabel" value={msgStr("recovery-codes-label-default")} />
|
||||
|
||||
<LogoutOtherSessions i18n={i18n} />
|
||||
|
||||
<Divider />
|
||||
|
||||
<div style={{ marginTop: 24 }}>
|
||||
{isAppInitiatedAction ? (
|
||||
<Space>
|
||||
<Button type="primary" size="large" id="saveRecoveryAuthnCodesBtn" htmlType="submit" disabled={!isConfirmed}>
|
||||
@ -90,7 +94,9 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
|
||||
{msg("recovery-codes-action-complete")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</Template>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
import { Form, Input, Button, Typography, Divider } from "antd";
|
||||
import { Form, Input, Button } from "antd";
|
||||
import { kcSanitize } from "keycloakify/lib/kcSanitize";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export default function LoginRecoveryAuthnCodeInput(props: PageProps<Extract<KcContext, { pageId: "login-recovery-authn-code-input.ftl" }>, I18n>) {
|
||||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
const { url, messagesPerField, recoveryAuthnCodesInputBean } = kcContext;
|
||||
@ -20,10 +18,17 @@ export default function LoginRecoveryAuthnCodeInput(props: PageProps<Extract<KcC
|
||||
i18n={i18n}
|
||||
doUseDefaultCss={doUseDefaultCss}
|
||||
classes={classes}
|
||||
headerNode={<Title level={3}>{msg("auth-recovery-code-header")}</Title>}
|
||||
headerNode={msg("auth-recovery-code-header")}
|
||||
displayMessage={!hasError}
|
||||
>
|
||||
<Form id="kc-recovery-code-login-form" layout="vertical" method="post" action={url.loginAction} size="large">
|
||||
<Form
|
||||
id="kc-recovery-code-login-form"
|
||||
layout="vertical"
|
||||
method="post"
|
||||
action={url.loginAction}
|
||||
size="large"
|
||||
style={{ padding: "0 15px" }}
|
||||
>
|
||||
<Form.Item
|
||||
label={msg("auth-recovery-code-prompt", `${recoveryAuthnCodesInputBean.codeNumber}`)}
|
||||
validateStatus={hasError ? "error" : ""}
|
||||
@ -41,9 +46,9 @@ export default function LoginRecoveryAuthnCodeInput(props: PageProps<Extract<KcC
|
||||
>
|
||||
<Input id="recoveryCodeInput" name="recoveryCodeInput" autoComplete="off" autoFocus aria-invalid={hasError} tabIndex={1} />
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit" block size="large" id="kc-login" name="login">
|
||||
<Button type="primary" htmlType="submit" block size="large" id="kc-login" name="login" style={{ marginTop: 12 }}>
|
||||
{msgStr("doLogIn")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
|
||||
@ -3,7 +3,7 @@ import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const { Text, Title } = Typography;
|
||||
const { Text } = Typography;
|
||||
|
||||
export default function LoginResetOtp(props: PageProps<Extract<KcContext, { pageId: "login-reset-otp.ftl" }>, I18n>) {
|
||||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
@ -17,9 +17,9 @@ export default function LoginResetOtp(props: PageProps<Extract<KcContext, { page
|
||||
doUseDefaultCss={doUseDefaultCss}
|
||||
classes={classes}
|
||||
displayMessage={!messagesPerField.existsError("totp")}
|
||||
headerNode={<Title level={3}>{msg("doLogIn")}</Title>}
|
||||
headerNode={msg("doLogIn")}
|
||||
>
|
||||
<Form id="kc-otp-reset-form" layout="vertical" method="post" action={url.loginAction}>
|
||||
<Form id="kc-otp-reset-form" layout="vertical" method="post" action={url.loginAction} style={{ padding: "0 15px" }}>
|
||||
<Space direction="vertical" style={{ width: "100%" }}>
|
||||
<Text id="kc-otp-reset-form-description">{msg("otp-reset-description")}</Text>
|
||||
|
||||
@ -32,12 +32,10 @@ export default function LoginResetOtp(props: PageProps<Extract<KcContext, { page
|
||||
id={`kc-otp-credential-${index}`}
|
||||
style={{
|
||||
display: "block",
|
||||
height: "40px",
|
||||
lineHeight: "40px",
|
||||
margin: "8px 0"
|
||||
}}
|
||||
>
|
||||
<Space style={{ marginBottom: "3px" }}>
|
||||
<Space style={{ paddingBottom: "10px" }}>
|
||||
<img
|
||||
src={"https://cdn.tombutcher.work/icons/auth/c-key.svg"}
|
||||
width={14}
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import React from "react";
|
||||
import { Form, Input, Button, Alert, Space, Divider } from "antd";
|
||||
import { Form, Input, Button, Alert, Space, Typography } from "antd";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
import { kcSanitize } from "keycloakify/lib/kcSanitize";
|
||||
|
||||
const { Link } = Typography;
|
||||
|
||||
// Define form values interface
|
||||
interface ResetPasswordFormValues {
|
||||
username: string;
|
||||
@ -22,9 +24,9 @@ export default function LoginResetPassword(props: PageProps<Extract<KcContext, {
|
||||
// Determine which label to use based on realm settings
|
||||
const inputLabel = !realm.loginWithEmailAllowed ? msg("username") : !realm.registrationEmailAsUsername ? msg("usernameOrEmail") : msg("email");
|
||||
const inputPrefix = !realm.loginWithEmailAllowed ? (
|
||||
<img src={"/c-person.svg"} width={14} style={{ marginRight: "3px" }} />
|
||||
<img src={"https://cdn.tombutcher.work/icons/auth/c-person.svg"} width={14} style={{ marginRight: "3px" }} />
|
||||
) : (
|
||||
<img src={"/c-at.svg"} width={14} />
|
||||
<img src={"https://cdn.tombutcher.work/icons/auth/c-at.svg"} width={14} />
|
||||
);
|
||||
|
||||
const handleSubmit = (): void => {
|
||||
@ -41,11 +43,10 @@ export default function LoginResetPassword(props: PageProps<Extract<KcContext, {
|
||||
kcContext={kcContext}
|
||||
i18n={i18n}
|
||||
doUseDefaultCss={false}
|
||||
infoNode={false}
|
||||
displayMessage={!messagesPerField.existsError("username")}
|
||||
headerNode={msg("emailForgotTitle")}
|
||||
>
|
||||
{displayInfo && displayMessage && <Alert message={infoMessage} type="info" showIcon style={{ marginBottom: 24 }} />}
|
||||
|
||||
<Form<ResetPasswordFormValues>
|
||||
id="kc-reset-password-form"
|
||||
layout="vertical"
|
||||
@ -54,7 +55,10 @@ export default function LoginResetPassword(props: PageProps<Extract<KcContext, {
|
||||
onFinish={handleSubmit}
|
||||
action={url.loginAction}
|
||||
method="post"
|
||||
style={{ padding: "0 15px" }}
|
||||
>
|
||||
{displayInfo && displayMessage && <Alert message={infoMessage} type="info" showIcon style={{ marginBottom: 24 }} />}
|
||||
|
||||
<Form.Item
|
||||
label={inputLabel}
|
||||
name="username"
|
||||
@ -79,17 +83,15 @@ export default function LoginResetPassword(props: PageProps<Extract<KcContext, {
|
||||
aria-invalid={messagesPerField.existsError("username") ? "true" : "false"}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Divider />
|
||||
<Form.Item style={{ marginBottom: 12 }}>
|
||||
|
||||
<Form.Item style={{ marginBottom: 22, marginTop: 26 }}>
|
||||
<Button type="primary" htmlType="submit" size="large" block>
|
||||
{msgStr("doSubmit")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
|
||||
<Space style={{ width: "100%", justifyContent: "center" }}>
|
||||
<Button type="link" href={url.loginUrl}>
|
||||
{msg("backToLogin")}
|
||||
</Button>
|
||||
<Link href={url.loginUrl}>{msg("backToLogin")}</Link>
|
||||
</Space>
|
||||
</Form>
|
||||
</Template>
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import { useState } from "react";
|
||||
import { Button, Typography, Form, Input, Checkbox, Divider, Flex } from "antd";
|
||||
import { Button, Form, Input, Checkbox, Flex } from "antd";
|
||||
import { EyeOutlined, EyeInvisibleOutlined } from "@ant-design/icons";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
type FieldType = {
|
||||
"password-new": string;
|
||||
"password-confirm": string;
|
||||
@ -56,9 +54,9 @@ export default function LoginUpdatePassword(props: PageProps<Extract<KcContext,
|
||||
doUseDefaultCss={false}
|
||||
classes={props.classes}
|
||||
displayMessage={!messagesPerField.existsError("password", "password-confirm")}
|
||||
headerNode={<Title level={2}>{msg("updatePasswordTitle")}</Title>}
|
||||
headerNode={msg("updatePasswordTitle")}
|
||||
>
|
||||
<Form id="kc-passwd-update-form" layout="vertical" onFinish={handleSubmit}>
|
||||
<Form id="kc-passwd-update-form" layout="vertical" onFinish={handleSubmit} style={{ padding: "0 15px" }}>
|
||||
<Form.Item
|
||||
label={msg("passwordNew")}
|
||||
name="password-new"
|
||||
@ -96,8 +94,6 @@ export default function LoginUpdatePassword(props: PageProps<Extract<KcContext,
|
||||
<Checkbox>{msg("logoutOtherSessions")}</Checkbox>
|
||||
</Form.Item>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Form.Item>
|
||||
<Flex gap="middle">
|
||||
<Button
|
||||
@ -105,6 +101,7 @@ export default function LoginUpdatePassword(props: PageProps<Extract<KcContext,
|
||||
size="large"
|
||||
htmlType="submit"
|
||||
iconPosition="end"
|
||||
style={{ marginTop: 6, flexGrow: 2 }}
|
||||
icon={
|
||||
<img
|
||||
src={"https://cdn.tombutcher.work/icons/auth/w-checkmark.svg"}
|
||||
@ -114,7 +111,6 @@ export default function LoginUpdatePassword(props: PageProps<Extract<KcContext,
|
||||
}
|
||||
loading={isSubmitLoading}
|
||||
disabled={isCancelLoading || isSubmitLoading}
|
||||
style={{ flexGrow: 2 }}
|
||||
>
|
||||
{msgStr("doSubmit")}
|
||||
</Button>
|
||||
|
||||
@ -64,22 +64,7 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
|
||||
kcContext={kcContext}
|
||||
i18n={i18n}
|
||||
doUseDefaultCss={false}
|
||||
headerNode={
|
||||
<>
|
||||
{client.attributes.logoUri ? (
|
||||
<Space align="start" direction="vertical">
|
||||
<img
|
||||
src={client.attributes.logoUri}
|
||||
alt={client.name || client.clientId}
|
||||
style={{ maxHeight: "64px", maxWidth: "100%", marginBottom: "20px" }}
|
||||
/>
|
||||
<Title level={3}>{msg("loginAccountTitle")}</Title>
|
||||
</Space>
|
||||
) : (
|
||||
<Title level={3}>{msg("loginAccountTitle")}</Title>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
headerNode={msg("loginAccountTitle")}
|
||||
displayInfo={realm.password && realm.registrationAllowed && !registrationDisabled}
|
||||
infoNode={
|
||||
<Space>
|
||||
@ -124,6 +109,7 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
|
||||
layout="vertical"
|
||||
requiredMark={false}
|
||||
onFinish={onFinish}
|
||||
style={{ margin: "0 15px" }}
|
||||
>
|
||||
{!usernameHidden && (
|
||||
<Form.Item
|
||||
@ -140,7 +126,7 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Row justify="space-between" align="middle">
|
||||
<Row justify="space-between" align="middle" style={{ paddingTop: "2px" }}>
|
||||
{realm.rememberMe && !usernameHidden && (
|
||||
<Col>
|
||||
<Form.Item name="rememberMe" valuePropName="checked" noStyle>
|
||||
@ -152,8 +138,6 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
|
||||
)}
|
||||
</Row>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="primary"
|
||||
@ -165,6 +149,7 @@ export default function LoginUsername(props: PageProps<Extract<KcContext, { page
|
||||
disabled={isLoading}
|
||||
loading={isLoading}
|
||||
iconPosition="end"
|
||||
style={{ marginTop: 24 }}
|
||||
icon={
|
||||
<img
|
||||
src={"https://cdn.tombutcher.work/icons/auth/w-right.svg"}
|
||||
|
||||
@ -11,8 +11,8 @@ export default function LoginVerifyEmail(props: PageProps<Extract<KcContext, { p
|
||||
const { url, user } = kcContext;
|
||||
|
||||
return (
|
||||
<Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={false} classes={{}} displayInfo headerNode={msg("emailVerifyTitle")}>
|
||||
<Space direction="vertical" size="large" style={{ width: "100%" }}>
|
||||
<Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={false} classes={{}} displayInfo={false} headerNode={msg("emailVerifyTitle")}>
|
||||
<Space direction="vertical" size="large" style={{ width: "100%", padding: "0 15px"}}>
|
||||
<Text>{msg("emailVerifyInstruction1", user?.email ?? "")}</Text>
|
||||
|
||||
<Text>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Button, Typography, Divider } from "antd";
|
||||
import { Button, Typography } from "antd";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
@ -40,16 +40,11 @@ export default function LogoutConfirm(props: PageProps<Extract<KcContext, { page
|
||||
};
|
||||
|
||||
return (
|
||||
<Template
|
||||
kcContext={kcContext}
|
||||
i18n={i18n}
|
||||
doUseDefaultCss={false}
|
||||
classes={props.classes}
|
||||
headerNode={<Title level={2}>{msg("logoutConfirmTitle")}</Title>}
|
||||
>
|
||||
<div id="kc-logout-confirm">
|
||||
<Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={false} classes={props.classes} headerNode={msg("logoutConfirmTitle")}>
|
||||
<div id="kc-logout-confirm" style={{ margin: "0 15px" }}>
|
||||
<div style={{ marginBottom: "25px" }}>
|
||||
<Text className="instruction">{msg("logoutConfirmHeader")}</Text>
|
||||
<Divider />
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
|
||||
@ -7,6 +7,7 @@ import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFo
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
import { useMediaQuery } from "react-responsive";
|
||||
|
||||
const { Title, Text, Link } = Typography;
|
||||
|
||||
@ -19,6 +20,7 @@ export default function Register(props: RegisterProps) {
|
||||
const { kcContext, i18n, UserProfileFormFields, doMakeUserConfirmPassword } = props;
|
||||
const { url, messagesPerField, recaptchaRequired, recaptchaVisible, recaptchaSiteKey, recaptchaAction, termsAcceptanceRequired } = kcContext;
|
||||
const { msg, msgStr } = i18n;
|
||||
const isMobile = useMediaQuery({ maxWidth: 600 });
|
||||
|
||||
const [areTermsAccepted, setAreTermsAccepted] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
@ -64,6 +66,7 @@ export default function Register(props: RegisterProps) {
|
||||
|
||||
return (
|
||||
<Form form={form} layout="vertical" name="registerForm" requiredMark onFinish={onFinish}>
|
||||
<div style={{ overflowY: "scroll", maxHeight: isMobile ? "100%" : "250px", padding: "0 15px" }}>
|
||||
<UserProfileFormFields
|
||||
layout="vertical"
|
||||
kcContext={kcContext}
|
||||
@ -72,7 +75,8 @@ export default function Register(props: RegisterProps) {
|
||||
doMakeUserConfirmPassword={doMakeUserConfirmPassword}
|
||||
onIsFormSubmittableValueChange={() => {}}
|
||||
/>
|
||||
|
||||
</div>
|
||||
<div style={{ padding: "0 15px" }}>
|
||||
{termsAcceptanceRequired && (
|
||||
<TermsAcceptance
|
||||
i18n={i18n}
|
||||
@ -87,8 +91,7 @@ export default function Register(props: RegisterProps) {
|
||||
<div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} data-action={recaptchaAction} />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Divider />
|
||||
<Form.Item>
|
||||
<Form.Item style={{ marginTop: 24 }}>
|
||||
<Space direction="vertical" style={{ width: "100%" }}>
|
||||
{recaptchaRequired && !recaptchaVisible && recaptchaAction !== undefined ? (
|
||||
<Button
|
||||
@ -125,6 +128,7 @@ export default function Register(props: RegisterProps) {
|
||||
</div>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ export default function SamlPostForm(props: PageProps<Extract<KcContext, { pageI
|
||||
|
||||
return (
|
||||
<Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("saml.post-form.title")}>
|
||||
<Flex align="middle" justify="start" gap={"middle"}>
|
||||
<Flex align="middle" justify="start" gap={"middle"} style={{ padding: '0 15px'}}>
|
||||
<Spin indicator={antIcon} />
|
||||
|
||||
<Text style={{ margin: 0 }}>{msg("saml.post-form.message")}</Text>
|
||||
|
||||
@ -34,7 +34,7 @@ export default function SelectAuthenticator(props: PageProps<Extract<KcContext,
|
||||
</>
|
||||
}
|
||||
>
|
||||
<form id="kc-select-credential-form" action={url.loginAction} method="post">
|
||||
<form id="kc-select-credential-form" action={url.loginAction} method="post" style={{ padding: '0 15px'}}>
|
||||
<List
|
||||
dataSource={auth.authenticationSelections}
|
||||
itemLayout="horizontal"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Button, Divider, Typography, Card, Flex } from "antd";
|
||||
import { Button, Typography, Card, Flex } from "antd";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
@ -38,14 +38,14 @@ export default function Terms(props: PageProps<Extract<KcContext, { pageId: "ter
|
||||
doUseDefaultCss={false}
|
||||
classes={props.classes}
|
||||
displayMessage={false}
|
||||
headerNode={<Title level={2}>{msg("termsTitle")}</Title>}
|
||||
headerNode={msg("termsTitle")}
|
||||
>
|
||||
<div className="terms-container">
|
||||
<div className="terms-container" style={{ padding: "0 15px" }}>
|
||||
{/* Scrollable terms box */}
|
||||
<Card
|
||||
style={{
|
||||
marginBottom: 24,
|
||||
maxHeight: "300px",
|
||||
marginBottom: 26,
|
||||
maxHeight: "250px",
|
||||
overflow: "auto"
|
||||
}}
|
||||
bordered={true}
|
||||
@ -54,7 +54,6 @@ export default function Terms(props: PageProps<Extract<KcContext, { pageId: "ter
|
||||
<Paragraph>{msg("termsText")}</Paragraph>
|
||||
</div>
|
||||
</Card>
|
||||
<Divider />
|
||||
<Flex gap="middle">
|
||||
<Button
|
||||
type="primary"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { JSX } from "keycloakify/tools/JSX";
|
||||
import { useState } from "react";
|
||||
import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
|
||||
import { Button, Checkbox, Flex, Divider } from "antd";
|
||||
import { Button, Checkbox, Flex } from "antd";
|
||||
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
|
||||
import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFieldsProps";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
@ -37,8 +37,8 @@ export default function UpdateEmail(props: UpdateEmailProps) {
|
||||
displayMessage={messagesPerField.exists("global")}
|
||||
headerNode={msg("updateEmailTitle")}
|
||||
>
|
||||
<form id="kc-update-email-form" action={url.loginAction} method="post">
|
||||
<div style={{ marginBottom: 24 }} className="kctbform">
|
||||
<form id="kc-update-email-form" action={url.loginAction} method="post" style={{ padding: '0 15px'}}>
|
||||
<div style={{ marginBottom: 15 }} className="kctbform">
|
||||
{/* Keep original UserProfileFormFields component, but wrap in Ant Design styling */}
|
||||
<UserProfileFormFields
|
||||
kcContext={kcContext}
|
||||
@ -54,7 +54,6 @@ export default function UpdateEmail(props: UpdateEmailProps) {
|
||||
{msg("logoutOtherSessions")}
|
||||
</Checkbox>
|
||||
</div>
|
||||
<Divider />
|
||||
<Flex gap={"middle"}>
|
||||
<Button
|
||||
style={{ flexGrow: 2 }}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { Fragment } from "react";
|
||||
import { Button, Form, Input, List, Typography, Space, Divider } from "antd";
|
||||
import { Button, Form, Input, List, Typography, Space } from "antd";
|
||||
import { DesktopOutlined, MobileOutlined } from "@ant-design/icons";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
@ -24,7 +24,7 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
|
||||
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
|
||||
const { url, realm, registrationDisabled, authenticators, shouldDisplayAuthenticators, client } = kcContext;
|
||||
const { url, realm, registrationDisabled, authenticators, shouldDisplayAuthenticators, client, message } = kcContext;
|
||||
|
||||
const { msg, msgStr, advancedMsg } = i18n;
|
||||
|
||||
@ -37,7 +37,7 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isLoading) return;
|
||||
if (isLoading || message != undefined) return;
|
||||
const timer = setTimeout(() => {
|
||||
const btn = document.getElementById(authButtonId) as HTMLButtonElement | null;
|
||||
if (btn && !btn.disabled) {
|
||||
@ -66,23 +66,9 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
|
||||
<Link href={url.registrationUrl}>{msg("doRegister")}</Link>
|
||||
</Space>
|
||||
}
|
||||
headerNode={
|
||||
<>
|
||||
{client.attributes.logoUri ? (
|
||||
<Space align="start" direction="vertical">
|
||||
<img
|
||||
src={client.attributes.logoUri}
|
||||
alt={client.name || client.clientId}
|
||||
style={{ maxHeight: "64px", maxWidth: "100%", marginBottom: "20px" }}
|
||||
/>
|
||||
<Title level={3}>{msg("webauthn-login-title")}</Title>
|
||||
</Space>
|
||||
) : (
|
||||
<Title level={3}>{msg("webauthn-login-title")}</Title>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
headerNode={msg("webauthn-login-title")}
|
||||
>
|
||||
<div style={{ padding: "0 15px" }}>
|
||||
<Form id="webauth" action={url.loginAction} method="post" layout="vertical">
|
||||
<Input type="hidden" id="clientDataJSON" name="clientDataJSON" />
|
||||
<Input type="hidden" id="authenticatorData" name="authenticatorData" />
|
||||
@ -103,6 +89,7 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
|
||||
{shouldDisplayAuthenticators && (
|
||||
<>
|
||||
<List
|
||||
style={{ marginBottom: "30px" }}
|
||||
bordered
|
||||
dataSource={authenticators.authenticators as Authenticator[]}
|
||||
renderItem={(authenticator: Authenticator, i: number) => (
|
||||
@ -143,9 +130,7 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
style={{ marginBottom: 0 }}
|
||||
/>
|
||||
<Divider />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
@ -172,6 +157,7 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
|
||||
<span>{msgStr("webauthn-doAuthenticate")}</span>
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Template>
|
||||
);
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ export default function WebauthnError(props: PageProps<Extract<KcContext, { page
|
||||
displayMessage
|
||||
headerNode={msg("webauthn-error-title")}
|
||||
>
|
||||
<Form onFinish={handleSubmit}>
|
||||
<Form onFinish={handleSubmit} style={{ padding: '0 15px'}}>
|
||||
<Form.Item>
|
||||
<Flex gap={"middle"}>
|
||||
<Button
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import React, { useState } from "react";
|
||||
import { Button, Form, Checkbox, Typography, Divider, Flex } from "antd";
|
||||
import { Button, Form, Checkbox, Flex } from "antd";
|
||||
import { useScript } from "keycloakify/login/pages/WebauthnRegister.useScript";
|
||||
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
interface LogoutOtherSessionsProps {
|
||||
i18n: I18n;
|
||||
}
|
||||
@ -17,7 +15,7 @@ function LogoutOtherSessions(props: LogoutOtherSessionsProps): React.ReactElemen
|
||||
const { msg } = i18n;
|
||||
|
||||
return (
|
||||
<Form.Item name="logout-sessions" valuePropName="checked" initialValue={true}>
|
||||
<Form.Item name="logout-sessions" valuePropName="checked" initialValue={true} style={{ paddingBottom: "24px" }}>
|
||||
<Checkbox id="logout-sessions" name="logout-sessions" value="on">
|
||||
{msg("logoutOtherSessions")}
|
||||
</Checkbox>
|
||||
@ -67,7 +65,8 @@ export default function WebauthnRegister(props: PageProps<Extract<KcContext, { p
|
||||
};
|
||||
|
||||
return (
|
||||
<Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={false} headerNode={<Title level={2}>{msg("webauthn-login-title")}</Title>}>
|
||||
<Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={false} headerNode={msg("webauthn-login-title")}>
|
||||
<div style={{ margin: "0 15px" }}>
|
||||
<Form id="register" layout="vertical" action={url.loginAction} method="post">
|
||||
{/* Hidden fields for WebAuthn registration data */}
|
||||
<input type="hidden" id="clientDataJSON" name="clientDataJSON" />
|
||||
@ -79,7 +78,6 @@ export default function WebauthnRegister(props: PageProps<Extract<KcContext, { p
|
||||
|
||||
<LogoutOtherSessions i18n={i18n} />
|
||||
</Form>
|
||||
<Divider />
|
||||
<Form.Item>
|
||||
<Flex gap={"middle"}>
|
||||
<Button
|
||||
@ -117,6 +115,7 @@ export default function WebauthnRegister(props: PageProps<Extract<KcContext, { p
|
||||
)}
|
||||
</Flex>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</Template>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user