169 lines
6.9 KiB
TypeScript
169 lines
6.9 KiB
TypeScript
import { useState } from "react";
|
|
import { Form, Input, Checkbox, Button, Divider, Typography, Space, Row, Col, FormProps } 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";
|
|
import * as FaIcons from "react-icons/fa6";
|
|
import type { IconType } from "react-icons";
|
|
|
|
const { Text, Link } = Typography;
|
|
|
|
export default function LoginUsername(props: PageProps<Extract<KcContext, { pageId: "login-username.ftl" }>, I18n>) {
|
|
const { kcContext, i18n, Template } = props;
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [form] = Form.useForm();
|
|
|
|
const { social, realm, url, usernameHidden, login, registrationDisabled } = kcContext;
|
|
const { msg, msgStr } = i18n;
|
|
|
|
// Function to dynamically fetch the correct icon
|
|
const getIconComponent = (alias: string): IconType | undefined => {
|
|
const formattedAlias = alias
|
|
.split("-")
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join("");
|
|
const iconName = `Fa${formattedAlias}`;
|
|
return (FaIcons as Record<string, IconType>)[iconName];
|
|
};
|
|
|
|
type FieldType = {
|
|
string: string;
|
|
};
|
|
|
|
const onFinish: FormProps<FieldType>["onFinish"] = async values => {
|
|
setIsLoading(true);
|
|
|
|
// Create a new form element
|
|
const form = document.createElement("form");
|
|
form.method = "POST";
|
|
form.action = url.loginAction;
|
|
|
|
// Append input fields to the form
|
|
Object.entries(values).forEach(([key, value]) => {
|
|
const input = document.createElement("input");
|
|
input.type = "hidden";
|
|
input.name = key;
|
|
input.value = value.toString();
|
|
form.appendChild(input);
|
|
});
|
|
|
|
// Append form to the body and submit
|
|
document.body.appendChild(form);
|
|
form.submit();
|
|
};
|
|
|
|
const inputPrefix = realm.registrationEmailAsUsername ? (
|
|
<img src={"https://cdn.tombutcher.work/icons/auth/c-at.svg"} width={14} style={{ marginRight: "3px" }} />
|
|
) : (
|
|
<img src={"https://cdn.tombutcher.work/icons/auth/c-person.svg"} width={14} style={{ marginRight: "3px" }} />
|
|
);
|
|
|
|
return (
|
|
<Template
|
|
kcContext={kcContext}
|
|
i18n={i18n}
|
|
doUseDefaultCss={false}
|
|
headerNode={msg("loginAccountTitle")}
|
|
displayInfo={realm.password && realm.registrationAllowed && !registrationDisabled}
|
|
infoNode={
|
|
<Space>
|
|
<Text>{msg("noAccount")}</Text>
|
|
<Link href={url.registrationUrl}>{msg("doRegister")}</Link>
|
|
</Space>
|
|
}
|
|
socialProvidersNode={
|
|
<>
|
|
{realm.password && social?.providers !== undefined && social.providers.length !== 0 && (
|
|
<div style={{ marginTop: 24 }}>
|
|
<Divider>
|
|
<Text type="secondary">{msg("identity-provider-login-label")}</Text>
|
|
</Divider>
|
|
<Space wrap style={{ width: "100%", justifyContent: "center" }}>
|
|
{social.providers.map(p => {
|
|
const IconComponent = getIconComponent(p.alias);
|
|
return (
|
|
<Button
|
|
key={p.alias}
|
|
id={`social-${p.alias}`}
|
|
href={p.loginUrl}
|
|
icon={IconComponent ? <IconComponent /> : undefined}
|
|
>
|
|
<span dangerouslySetInnerHTML={{ __html: kcSanitize(p.displayName) }} />
|
|
</Button>
|
|
);
|
|
})}
|
|
</Space>
|
|
</div>
|
|
)}
|
|
</>
|
|
}
|
|
>
|
|
{realm.password && (
|
|
<Form
|
|
form={form}
|
|
initialValues={{
|
|
username: login.username || "",
|
|
rememberMe: !!login.rememberMe
|
|
}}
|
|
layout="vertical"
|
|
requiredMark={false}
|
|
onFinish={onFinish}
|
|
style={{ margin: "0 15px" }}
|
|
>
|
|
{!usernameHidden && (
|
|
<Form.Item
|
|
name="username"
|
|
label={
|
|
!realm.loginWithEmailAllowed
|
|
? msg("username")
|
|
: !realm.registrationEmailAsUsername
|
|
? msg("usernameOrEmail")
|
|
: msg("email")
|
|
}
|
|
>
|
|
<Input id="username" name="username" size="large" prefix={inputPrefix} autoFocus autoComplete="username" />
|
|
</Form.Item>
|
|
)}
|
|
|
|
<Row justify="space-between" align="middle" style={{ paddingTop: "2px" }}>
|
|
{realm.rememberMe && !usernameHidden && (
|
|
<Col>
|
|
<Form.Item name="rememberMe" valuePropName="checked" noStyle>
|
|
<Checkbox id="rememberMe" name="rememberMe">
|
|
{msg("rememberMe")}
|
|
</Checkbox>
|
|
</Form.Item>
|
|
</Col>
|
|
)}
|
|
</Row>
|
|
|
|
<Form.Item>
|
|
<Button
|
|
type="primary"
|
|
htmlType="submit"
|
|
id="kc-login"
|
|
name="login"
|
|
block
|
|
size="large"
|
|
disabled={isLoading}
|
|
loading={isLoading}
|
|
iconPosition="end"
|
|
style={{ marginTop: 24 }}
|
|
icon={
|
|
<img
|
|
src={"https://cdn.tombutcher.work/icons/auth/w-right.svg"}
|
|
style={{ marginTop: "4px", marginBottom: 0 }}
|
|
height={14}
|
|
/>
|
|
}
|
|
>
|
|
{msgStr("doLogIn")}
|
|
</Button>
|
|
</Form.Item>
|
|
</Form>
|
|
)}
|
|
</Template>
|
|
);
|
|
}
|