bolt.new/app/components/auth/Register.tsx
2024-10-22 04:28:24 +00:00

162 lines
6.3 KiB
TypeScript

import React, { useState, useRef } from 'react';
import { useNavigate } from '@remix-run/react';
import { useAuth } from '~/hooks/useAuth';
import type { RegisterResponse } from '~/routes/api.auth.register';
import { uploadToOSS } from '~/utils/uploadToOSS';
export function Register() {
const [phone, setPhone] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [nickname, setNickname] = useState('');
const [avatar, setAvatar] = useState<File | null>(null);
const [avatarPreview, setAvatarPreview] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const navigate = useNavigate();
const { login } = useAuth();
const handleAvatarChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
setAvatar(file);
const reader = new FileReader();
reader.onloadend = () => {
setAvatarPreview(reader.result as string);
};
reader.readAsDataURL(file);
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (password !== confirmPassword) {
alert('两次输入的密码不一致');
return;
}
if (!avatar) {
alert('请上传头像');
return;
}
try {
const avatarUrl = await uploadToOSS(avatar);
const registerResponse = await fetch('/api/auth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
phone,
password,
nickname,
avatarUrl,
}),
});
const data = await registerResponse.json() as RegisterResponse;
if (registerResponse.ok && data.token && data.user) {
login(data.token, data.user);
} else {
alert(data.error || '注册失败,请稍后再试');
}
} catch (error) {
console.error('Registration failed:', error);
alert('注册失败,请稍后再试');
}
};
return (
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="phone" className="block text-sm font-medium text-bolt-elements-textPrimary">
</label>
<input
type="tel"
id="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
required
className="mt-1 block w-full px-3 py-2 bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor rounded-md shadow-sm focus:outline-none focus:ring-bolt-elements-button-primary-background focus:border-bolt-elements-button-primary-background"
/>
</div>
<div>
<label htmlFor="nickname" className="block text-sm font-medium text-bolt-elements-textPrimary">
</label>
<input
type="text"
id="nickname"
value={nickname}
onChange={(e) => setNickname(e.target.value)}
required
className="mt-1 block w-full px-3 py-2 bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor rounded-md shadow-sm focus:outline-none focus:ring-bolt-elements-button-primary-background focus:border-bolt-elements-button-primary-background"
/>
</div>
<div>
<label htmlFor="avatar" className="block text-sm font-medium text-bolt-elements-textPrimary mb-2">
</label>
<div className="flex items-center space-x-4">
<div className="w-24 h-24 border-2 border-dashed border-bolt-elements-borderColor rounded-full flex items-center justify-center overflow-hidden">
{avatarPreview ? (
<img src={avatarPreview} alt="Avatar preview" className="w-full h-full object-cover" />
) : (
<span className="text-bolt-elements-textSecondary">No image</span>
)}
</div>
<button
type="button"
onClick={() => fileInputRef.current?.click()}
className="px-4 py-2 bg-bolt-elements-button-secondary-background text-bolt-elements-button-secondary-text rounded-md hover:bg-bolt-elements-button-secondary-backgroundHover focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-bolt-elements-button-secondary-background"
>
</button>
<input
ref={fileInputRef}
type="file"
id="avatar"
accept="image/*"
onChange={handleAvatarChange}
className="hidden"
/>
</div>
</div>
<div>
<label htmlFor="password" className="block text-sm font-medium text-bolt-elements-textPrimary">
</label>
<input
type="password"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className="mt-1 block w-full px-3 py-2 bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor rounded-md shadow-sm focus:outline-none focus:ring-bolt-elements-button-primary-background focus:border-bolt-elements-button-primary-background"
/>
</div>
<div>
<label htmlFor="confirmPassword" className="block text-sm font-medium text-bolt-elements-textPrimary">
</label>
<input
type="password"
id="confirmPassword"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
className="mt-1 block w-full px-3 py-2 bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor rounded-md shadow-sm focus:outline-none focus:ring-bolt-elements-button-primary-background focus:border-bolt-elements-button-primary-background"
/>
</div>
<div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-bolt-elements-button-primary-text bg-bolt-elements-button-primary-background hover:bg-bolt-elements-button-primary-backgroundHover focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-bolt-elements-button-primary-background"
>
</button>
</div>
</form>
);
}