yuki / src /modules /register /RegisterPage.tsx
OhMyDitzzy
anything
1078fbb
import { useState, useEffect, useRef } from "react";
import { useParams, useNavigate } from "react-router-dom";
import "./RegisterPage.css";
interface RegistrationData {
username: string;
age: string;
password: string;
confirmPassword: string;
}
export function RegisterPage() {
const { sessionId } = useParams<{ sessionId: string }>();
const navigate = useNavigate();
const [ws, setWs] = useState<WebSocket | null>(null);
const [connected, setConnected] = useState(false);
const [sessionValid, setSessionValid] = useState<boolean | null>(null);
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [formData, setFormData] = useState<RegistrationData>({
username: "",
age: "",
password: "",
confirmPassword: "",
});
const [errors, setErrors] = useState<Partial<RegistrationData>>({});
const [successMessage, setSuccessMessage] = useState("");
const [errorMessage, setErrorMessage] = useState("");
const wsRef = useRef<WebSocket | null>(null);
useEffect(() => {
if (!sessionId) {
setSessionValid(false);
setLoading(false);
return;
}
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
const wsUrl = `${protocol}//${window.location.host}/ws`;
const websocket = new WebSocket(wsUrl);
wsRef.current = websocket;
websocket.onopen = () => {
console.log("[WebSocket] Connected");
setConnected(true);
websocket.send(JSON.stringify({
type: "user_connect",
sessionId: sessionId,
}));
};
websocket.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
handleWebSocketMessage(message);
} catch (error) {
console.error("[WebSocket] Error parsing message:", error);
}
};
websocket.onerror = (error) => {
console.error("[WebSocket] Error:", error);
setErrorMessage("Connection error. Please try again.");
setLoading(false);
};
websocket.onclose = () => {
console.log("[WebSocket] Disconnected");
setConnected(false);
};
setWs(websocket);
return () => {
websocket.close();
};
}, [sessionId]);
const handleWebSocketMessage = (message: any) => {
switch (message.type) {
case "connected":
console.log("[WebSocket]", message.message);
break;
case "session_valid":
setSessionValid(true);
setLoading(false);
break;
case "session_invalid":
setSessionValid(false);
setLoading(false);
setErrorMessage(message.message || "Session not found or expired");
break;
case "registration_result":
setSubmitting(false);
if (message.success) {
setSuccessMessage(message.message);
setErrorMessage("");
setTimeout(() => {
window.close();
}, 3000);
} else {
setErrorMessage(message.message);
setSuccessMessage("");
}
break;
}
};
const validateForm = (): boolean => {
const newErrors: Partial<RegistrationData> = {};
if (!formData.username.trim()) {
newErrors.username = "Username is required";
} else if (formData.username.trim().length < 3) {
newErrors.username = "Username must be at least 3 characters";
}
const ageNum = parseInt(formData.age);
if (!formData.age) {
newErrors.age = "Age is required";
} else if (isNaN(ageNum) || ageNum < 13 || ageNum > 100) {
newErrors.age = "Age must be between 13 and 100";
}
if (!formData.password) {
newErrors.password = "Password is required";
} else if (formData.password.length < 6 || formData.password.length > 20) {
newErrors.password = "Password must be 6-20 characters";
}
if (!formData.confirmPassword) {
newErrors.confirmPassword = "Please confirm your password";
} else if (formData.password !== formData.confirmPassword) {
newErrors.confirmPassword = "Passwords do not match";
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!validateForm()) return;
if (!ws || ws.readyState !== WebSocket.OPEN) {
setErrorMessage("Connection lost. Please refresh the page.");
return;
}
setSubmitting(true);
setErrorMessage("");
setSuccessMessage("");
const submitData = {
username: formData.username.trim(),
age: parseInt(formData.age),
password: formData.password,
confirmPassword: formData.confirmPassword,
};
ws.send(JSON.stringify({
type: "registration_submit",
sessionId: sessionId,
data: submitData,
}));
};
const handleInputChange = (field: keyof RegistrationData, value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));
if (errors[field]) {
setErrors(prev => ({ ...prev, [field]: "" }));
}
};
if (loading) {
return (
<div className="register-page">
<div className="register-card">
<div className="loading-spinner">
<div className="spinner"></div>
<p>Validating session...</p>
</div>
</div>
</div>
);
}
if (sessionValid === false) {
return (
<div className="register-page">
<div className="register-card error-card">
<div className="error-icon">โŒ</div>
<h2>Invalid Session</h2>
<p>{errorMessage || "This registration link is invalid or has expired."}</p>
<button
className="back-button"
onClick={() => navigate("/")}
>
Go to Homepage
</button>
</div>
</div>
);
}
if (successMessage) {
return (
<div className="register-page">
<div className="register-card success-card">
<div className="success-icon">โœ…</div>
<h2>Registration Successful!</h2>
<p>{successMessage}</p>
<p className="success-subtitle">Your account has been activated automatically.</p>
<div className="success-animation">
<div className="confetti"></div>
</div>
</div>
</div>
);
}
return (
<div className="register-page">
<div className="register-card">
<div className="register-header">
<h1>๐ŸŽ‰ Account Registration</h1>
<p>Join Yuki Botz and unlock amazing features!</p>
{!connected && (
<div className="connection-status offline">
<span className="status-dot"></span>
Reconnecting...
</div>
)}
</div>
<form onSubmit={handleSubmit} className="register-form">
<div className="form-group">
<label htmlFor="username">
<span className="label-icon">๐Ÿ‘ค</span>
Username
</label>
<input
type="text"
id="username"
value={formData.username}
onChange={(e) => handleInputChange("username", e.target.value)}
placeholder="Enter your username"
className={errors.username ? "error" : ""}
disabled={submitting}
/>
{errors.username && <span className="error-text">{errors.username}</span>}
</div>
<div className="form-group">
<label htmlFor="age">
<span className="label-icon">๐ŸŽ‚</span>
Age
</label>
<input
type="number"
id="age"
value={formData.age}
onChange={(e) => handleInputChange("age", e.target.value)}
placeholder="Enter your age"
className={errors.age ? "error" : ""}
disabled={submitting}
min="13"
max="100"
/>
{errors.age && <span className="error-text">{errors.age}</span>}
</div>
<div className="password-section">
<div className="section-header">
<h3>๐Ÿ”‘ Password</h3>
</div>
<div className="bonus-badge">
<span className="badge-icon">๐ŸŽ</span>
<span>Get bonus rewards with your account!</span>
</div>
<div className="rewards-list">
<div className="reward-item">๐Ÿ’Ž +100 Limit</div>
<div className="reward-item">๐Ÿช™ +10,000 Money</div>
<div className="reward-item">โญ +50 EXP</div>
<div className="reward-item">๐ŸŽฎ RPG Access</div>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
value={formData.password}
onChange={(e) => handleInputChange("password", e.target.value)}
placeholder="6-20 characters"
className={errors.password ? "error" : ""}
disabled={submitting}
/>
{errors.password && <span className="error-text">{errors.password}</span>}
</div>
<div className="form-group">
<label htmlFor="confirmPassword">Confirm Password</label>
<input
type="password"
id="confirmPassword"
value={formData.confirmPassword}
onChange={(e) => handleInputChange("confirmPassword", e.target.value)}
placeholder="Re-enter password"
className={errors.confirmPassword ? "error" : ""}
disabled={submitting}
/>
{errors.confirmPassword && <span className="error-text">{errors.confirmPassword}</span>}
</div>
</div>
{errorMessage && (
<div className="alert alert-error">
<span className="alert-icon">โš ๏ธ</span>
{errorMessage}
</div>
)}
<button
type="submit"
className="submit-button"
disabled={submitting || !connected}
>
{submitting ? (
<>
<span className="button-spinner"></span>
Processing...
</>
) : (
<>
<span>๐Ÿš€</span>
Complete Registration
</>
)}
</button>
</form>
</div>
</div>
);
}