Files
gpustack-ui/src/pages/login/components/local-user-form.tsx
T

190 lines
5.0 KiB
TypeScript

import externalLinks from '@/constants/external-links';
import {
InfoCircleOutlined,
LockOutlined,
UserOutlined
} from '@ant-design/icons';
import { HighlightCode, IconFont } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
import { Button, Divider, Flex, Form, FormInstance, Input } from 'antd';
import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token, css }) => {
return {
commandInfo: css`
.content {
display: flex;
align-items: flex-start;
gap: 8px;
margin-bottom: 12px;
.icon {
padding-top: 2px;
color: ${token.colorInfo};
font-size: 16px;
}
.info {
line-height: 1.5;
color: ${token.colorTextSecondary};
font-size: 14px;
}
}
`,
passwordWrapper: css`
position: relative;
.forgot-password {
position: absolute;
right: 0;
top: 74px;
}
`
};
});
interface LocalUserFormProps {
handleLogin: (values: any) => void;
form: FormInstance;
loading?: boolean;
loginOption: {
saml: boolean;
oidc: boolean;
first_time_setup: boolean;
get_initial_password_command: string;
};
}
const LocalUserForm: React.FC<LocalUserFormProps> = (props) => {
const { handleLogin, form, loginOption } = props;
const intl = useIntl();
const { styles } = useStyles();
const renderCommandInfo = () => {
if (!loginOption?.first_time_setup) {
return null;
}
return (
<div className={styles.commandInfo}>
<Divider style={{ marginTop: 16, marginBottom: 12 }} />
<div className="content">
<InfoCircleOutlined className="icon" />
<span className="info">
{intl.formatMessage({ id: 'users.login.getInitialPassword' })}
</span>
</div>
<HighlightCode
theme="dark"
code={loginOption.get_initial_password_command}
copyValue={loginOption.get_initial_password_command}
lang="bash"
></HighlightCode>
</div>
);
};
return (
<Form
form={form}
// Same shape Buttons uses: 360px design width, capped at the
// parent's inner width so the form doesn't overflow on the
// enterprise cover layout (272px inner).
layout="vertical"
requiredMark={false}
styles={{
label: {
width: '100%',
lineHeight: '24px'
}
}}
style={{ width: '360px', maxWidth: '100%', margin: '0 auto' }}
onFinish={handleLogin}
>
<Form.Item
name="username"
label={intl.formatMessage({ id: 'common.form.username' })}
style={{
marginBottom: 20
}}
rules={[
{
required: true,
message: intl.formatMessage(
{ id: 'common.form.rule.input' },
{
name: intl.formatMessage({ id: 'common.form.username' })
}
)
}
]}
>
<Input
autoComplete="username"
prefix={<UserOutlined />}
style={{ height: 44 }}
placeholder={intl.formatMessage({
id: 'common.login.username.holder'
})}
/>
</Form.Item>
<div className={`${styles.passwordWrapper} password-wrapper`}>
<Form.Item
style={{
marginBottom: 20
}}
name="password"
label={
<Flex
align="center"
justify="space-between"
style={{ width: '100%' }}
>
<span>{intl.formatMessage({ id: 'common.form.password' })}</span>
</Flex>
}
rules={[
{
required: true,
message: intl.formatMessage(
{ id: 'common.form.rule.input' },
{
name: intl.formatMessage({ id: 'common.form.password' })
}
)
}
]}
>
<Input.Password
autoComplete="current-password"
prefix={<LockOutlined />}
style={{ height: 44 }}
placeholder={intl.formatMessage({
id: 'common.login.password.holder'
})}
/>
</Form.Item>
<Button
type="link"
size="small"
className="forgot-password"
style={{ fontSize: 12, padding: 0 }}
href={externalLinks.resetPassword}
target="_blank"
>
{intl.formatMessage({ id: 'common.button.forgotpassword' })}
</Button>
</div>
<Button
htmlType="submit"
type="primary"
block
loading={props.loading}
icon={<IconFont type="icon-login" />}
style={{ height: '44px', fontSize: '14px', marginTop: 20 }}
>
{intl.formatMessage({ id: 'common.button.login' })}
</Button>
{renderCommandInfo()}
</Form>
);
};
export default LocalUserForm;