2025-tombutcher-auth/src/login/pages/LoginUsername.tsx
Tom Butcher a890c85c74
Some checks failed
ci / test (push) Has been cancelled
ci / Check if version upgrade (push) Has been cancelled
ci / create_github_release (push) Has been cancelled
Fixed more linting
2025-08-04 01:35:48 +01:00

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>
);
}