/* * @Author: ZhaoYing * @Date: 2026-02-03 16:37:12 * @Last Modified by: ZhaoYing * @Last Modified time: 2026-02-04 10:05:39 */ /** * Invite Register Page * Handles user registration via workspace invitation link * Validates invite token and allows new users to set up their account */ import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate, useParams } from 'react-router-dom'; import { Button, Input, Form, Progress, App } from 'antd'; import { ExclamationCircleFilled } from '@ant-design/icons'; import type { FormProps } from 'antd'; import { useUser, type LoginInfo } from '@/store/user'; import { login } from '@/api/user' import inviteBg from '@/assets/images/login/inviteBg.png' import checkBg from '@/assets/images/login/checkBg.png' import type { LoginForm, ValidateToken } from './types'; import { validateInviteToken } from '@/api/member' import RbAlert from '@/components/RbAlert' import styles from './index.module.css' /** * Alert extra content wrapper */ const Extra = ({ children }: { children: React.ReactNode }) => (
{children}
) /** * Invite registration component */ const InviteRegister: React.FC = () => { const navigate = useNavigate(); const { t } = useTranslation(); const { token } = useParams(); const { clearUserInfo, updateLoginInfo } = useUser(); const [loading, setLoading] = useState(false); const [form] = Form.useForm(); const { message } = App.useApp(); const [passwordStrength, setPasswordStrength] = useState<'weak' | 'medium' | 'strong' | null>(null); const values = Form.useWatch([], form); useEffect(() => { clearUserInfo(); getInitalData() }, []); /** Fetch and validate invite token */ const getInitalData = () => { if (!token) { message.warning(t('user.inviteLinkInvalid')) return } validateInviteToken(token).then((res) => { const response = res as ValidateToken form.setFieldsValue({ email: response.email, }) }) } /** Validate password strength and return score */ const validatePasswordStrength = (password: string): { strength: 'weak' | 'medium' | 'strong', error: string } => { let strength: 'weak' | 'medium' | 'strong' = 'weak'; let score = 0; let error = ''; // Password length check if (password.length < 8) { error = t('login.lengthDesc'); return { strength, error }; } score += 1; // Contains number if (/\d/.test(password)) score += 1; // Contains lowercase letter if (/[a-z]/.test(password)) score += 1; // Contains uppercase letter if (/[A-Z]/.test(password)) score += 1; // Contains special character if (/[^A-Za-z0-9]/.test(password)) score += 1; // Determine strength if (score >= 4) { strength = 'strong'; } else if (score >= 3) { strength = 'medium'; } // Return message based on strength if (strength === 'weak' && score >= 1) { error = t('login.weakDesc'); } else if (strength === 'medium') { error = t('login.mediumDesc'); } return { strength, error }; }; /** Update password strength indicator on change */ const handlePasswordChange = (value: string) => { if (!value) { setPasswordStrength(null); return; } const { strength } = validatePasswordStrength(value); setPasswordStrength(strength); }; /** Validate password confirmation matches */ const validateConfirmPassword = (_: unknown, value: string) => { const password = values.password; if (!value) { return Promise.reject(new Error('Please confirm password')); } if (value !== password) { return Promise.reject(new Error('Passwords do not match')); } return Promise.resolve(); }; /** Handle registration form submission */ const handleRegister: FormProps['onFinish'] = async (values) => { setLoading(true); login({ username: values.username, email: values.email, password: values.password, invite: token }).then((res) => { const response = res as LoginInfo; updateLoginInfo(response); navigate('/'); }).finally(() => { setLoading(false); }); }; return (
{t('login.welcomeTeam')}
{t('login.welcomeTeamSubTitle')}
} className="rb:mb-6">
{t('login.invitationVerified')}
{t('login.account')}: {values?.email || '-'}
{t('login.emailAccountDesc')}} >
{t('login.passwordStrength')}: {passwordStrength ? {t(`login.${passwordStrength}`)} : {t('login.noSet')} }
{t('login.setPasswordDesc')}
} rules={[ { required: true, message: t('login.setPasswordPlaceholder') }, { validator: (_, value) => { if (!value) { return Promise.reject(new Error(t('login.lengthDesc'))); } const { error } = validatePasswordStrength(value); if (error && value.length >= 8) { return Promise.resolve(); } else if (error) { return Promise.reject(new Error(error)); } return Promise.resolve(); }, validateTrigger: ['blur'] } ]} > handlePasswordChange(e.target.value)} onBlur={(e) => handlePasswordChange(e.target.value)} /> {t('login.name')} {t('login.nameSubTitle')}} >
); }; export default InviteRegister;