/*
* @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 (
);
};
export default InviteRegister;